From 15d692eb11e6b75c98ca2fc6aa17f53d532a06cd Mon Sep 17 00:00:00 2001 From: Leonardo Razovic <4128940+lrazovic@users.noreply.github.com> Date: Wed, 24 Apr 2024 09:00:17 +0200 Subject: [PATCH 01/12] feat: decode the CID from the JWT --- pallets/dispenser/src/lib.rs | 8 +- pallets/funding/src/lib.rs | 28 +++---- polimec-common/common/src/credentials/mod.rs | 28 +++++-- polimec-common/test-utils/src/lib.rs | 86 +++++++++++--------- 4 files changed, 90 insertions(+), 60 deletions(-) diff --git a/pallets/dispenser/src/lib.rs b/pallets/dispenser/src/lib.rs index 8f7827f00..e9519bb4e 100644 --- a/pallets/dispenser/src/lib.rs +++ b/pallets/dispenser/src/lib.rs @@ -23,7 +23,7 @@ pub use frame_support::traits::{ tokens::{currency::VestingSchedule, Balance}, Currency, ExistenceRequirement, }; -pub use polimec_common::credentials::{Did, EnsureOriginWithCredentials, InvestorType, UntrustedToken}; +pub use polimec_common::credentials::{Cid, Did, EnsureOriginWithCredentials, InvestorType, UntrustedToken}; pub use sp_runtime::traits::Convert; pub mod extensions; @@ -77,7 +77,7 @@ pub mod pallet { /// The Origin that can dispense funds from the dispenser. The Origin must contain a valid JWT token. type InvestorOrigin: EnsureOriginWithCredentials< ::RuntimeOrigin, - Success = (AccountIdOf, Did, InvestorType), + Success = (AccountIdOf, Did, InvestorType, Cid), >; /// The period of time that the dispensed funds are locked. Used to calculate the @@ -137,7 +137,7 @@ pub mod pallet { #[pallet::call] impl Pallet { #[pallet::feeless_if( | origin: &OriginFor, jwt: &UntrustedToken | -> bool { - if let Ok((_, did, _)) = T::InvestorOrigin::ensure_origin(origin.clone(), jwt, T::VerifierPublicKey::get()) { + if let Ok((_, did, _, _)) = T::InvestorOrigin::ensure_origin(origin.clone(), jwt, T::VerifierPublicKey::get()) { return Dispensed::::get(did).is_none() } else { return false @@ -146,7 +146,7 @@ pub mod pallet { #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::dispense())] pub fn dispense(origin: OriginFor, jwt: UntrustedToken) -> DispatchResultWithPostInfo { - let (who, did, _investor_type) = + let (who, did, _investor_type, _) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; ensure!(Dispensed::::get(&did).is_none(), Error::::DispensedAlreadyToDid); diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index a5276fdc5..03ea44f18 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -129,7 +129,7 @@ use frame_system::pallet_prelude::BlockNumberFor; pub use pallet::*; use pallet_xcm::ensure_response; use polimec_common::{ - credentials::{Did, EnsureOriginWithCredentials, InvestorType, UntrustedToken}, + credentials::{Cid, Did, EnsureOriginWithCredentials, InvestorType, UntrustedToken}, migration_types::*, }; use polkadot_parachain_primitives::primitives::Id as ParaId; @@ -295,7 +295,7 @@ pub mod pallet { /// Credentialized investor Origin, ensures users are of investing type Retail, or Professional, or Institutional. type InvestorOrigin: EnsureOriginWithCredentials< ::RuntimeOrigin, - Success = (AccountIdOf, Did, InvestorType), + Success = (AccountIdOf, Did, InvestorType, Cid), >; /// How long an issuer has to accept or reject the funding of a project if the funding is between two thresholds. @@ -734,7 +734,7 @@ pub mod pallet { jwt: UntrustedToken, project: ProjectMetadataOf, ) -> DispatchResult { - let (account, did, investor_type) = + let (account, did, investor_type, _cid) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; ensure!(investor_type == InvestorType::Institutional, Error::::WrongInvestorType); Self::do_create_project(&account, project, did) @@ -743,7 +743,7 @@ pub mod pallet { #[pallet::call_index(35)] #[pallet::weight(Weight::from_parts(100_000, 10_000))] pub fn remove_project(origin: OriginFor, jwt: UntrustedToken, project_id: ProjectId) -> DispatchResult { - let (account, did, investor_type) = + let (account, did, investor_type, _cid) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; ensure!(investor_type == InvestorType::Institutional, Error::::WrongInvestorType); Self::do_remove_project(account, project_id, did) @@ -758,7 +758,7 @@ pub mod pallet { project_id: ProjectId, new_project_metadata: ProjectMetadataOf, ) -> DispatchResult { - let (account, _did, investor_type) = + let (account, _did, investor_type, _cid) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; ensure!(investor_type == InvestorType::Institutional, Error::::WrongInvestorType); Self::do_edit_project(account, project_id, new_project_metadata) @@ -772,7 +772,7 @@ pub mod pallet { jwt: UntrustedToken, project_id: ProjectId, ) -> DispatchResultWithPostInfo { - let (account, _did, investor_type) = + let (account, _did, investor_type, _cid) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; ensure!(investor_type == InvestorType::Institutional, Error::::WrongInvestorType); Self::do_start_evaluation(account, project_id) @@ -788,7 +788,7 @@ pub mod pallet { jwt: UntrustedToken, project_id: ProjectId, ) -> DispatchResultWithPostInfo { - let (account, _did, investor_type) = + let (account, _did, investor_type, _cid) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; ensure!(investor_type == InvestorType::Institutional, Error::::WrongInvestorType); Self::do_auction_opening(account, project_id) @@ -805,7 +805,7 @@ pub mod pallet { project_id: ProjectId, #[pallet::compact] usd_amount: BalanceOf, ) -> DispatchResultWithPostInfo { - let (account, did, investor_type) = + let (account, did, investor_type, _cid) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; Self::do_evaluate(&account, project_id, usd_amount, did, investor_type) } @@ -828,7 +828,7 @@ pub mod pallet { multiplier: T::Multiplier, asset: AcceptedFundingAsset, ) -> DispatchResultWithPostInfo { - let (account, did, investor_type) = + let (account, did, investor_type, _cid) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; Self::do_bid(&account, project_id, ct_amount, multiplier, asset, did, investor_type) } @@ -852,7 +852,7 @@ pub mod pallet { multiplier: MultiplierOf, asset: AcceptedFundingAsset, ) -> DispatchResultWithPostInfo { - let (account, did, investor_type) = + let (account, did, investor_type, _cid) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; Self::do_community_contribute(&account, project_id, amount, multiplier, asset, did, investor_type) } @@ -876,7 +876,7 @@ pub mod pallet { multiplier: MultiplierOf, asset: AcceptedFundingAsset, ) -> DispatchResultWithPostInfo { - let (account, did, investor_type) = + let (account, did, investor_type, _cid) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; Self::do_remaining_contribute(&account, project_id, amount, multiplier, asset, did, investor_type) } @@ -891,7 +891,7 @@ pub mod pallet { project_id: ProjectId, outcome: FundingOutcomeDecision, ) -> DispatchResultWithPostInfo { - let (account, _did, investor_type) = + let (account, _did, investor_type, _cid) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; ensure!(investor_type == InvestorType::Institutional, Error::::WrongInvestorType); @@ -988,7 +988,7 @@ pub mod pallet { project_id: ProjectId, para_id: ParaId, ) -> DispatchResult { - let (account, _did, investor_type) = + let (account, _did, investor_type, _cid) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; ensure!(investor_type == InvestorType::Institutional, Error::::WrongInvestorType); @@ -1002,7 +1002,7 @@ pub mod pallet { jwt: UntrustedToken, project_id: ProjectId, ) -> DispatchResult { - let (account, _did, investor_type) = + let (account, _did, investor_type, _cid) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; ensure!(investor_type == InvestorType::Institutional, Error::::WrongInvestorType); Self::do_start_migration_readiness_check(&account, project_id) diff --git a/polimec-common/common/src/credentials/mod.rs b/polimec-common/common/src/credentials/mod.rs index d765bb98b..e1a992adb 100644 --- a/polimec-common/common/src/credentials/mod.rs +++ b/polimec-common/common/src/credentials/mod.rs @@ -57,12 +57,15 @@ pub struct SampleClaims { pub subject: AccountId, #[serde(rename = "iss")] pub issuer: String, + #[serde(rename = "aud", deserialize_with = "from_bounded_cid")] + pub ipfs_cid: Cid, pub investor_type: InvestorType, - #[serde(deserialize_with = "from_bounded_vec")] + #[serde(deserialize_with = "from_bounded_did")] pub did: Did, } pub type Did = BoundedVec>; +pub type Cid = BoundedVec>; pub struct EnsureInvestor(sp_std::marker::PhantomData); impl EnsureOriginWithCredentials for EnsureInvestor @@ -70,7 +73,7 @@ where T: frame_system::Config + pallet_timestamp::Config, { type Claims = SampleClaims; - type Success = (T::AccountId, Did, InvestorType); + type Success = (T::AccountId, Did, InvestorType, Cid); fn try_origin( origin: T::RuntimeOrigin, @@ -85,7 +88,7 @@ where let Some(date_time) = claims.expiration else { return Err(origin) }; if claims.custom.subject == who && (date_time.timestamp_millis() as u64) >= now { - return Ok((who, claims.custom.did.clone(), claims.custom.investor_type.clone())); + return Ok((who, claims.custom.did.clone(), claims.custom.investor_type.clone(), claims.custom.ipfs_cid.clone())); } Err(origin) @@ -127,7 +130,16 @@ where } } -pub fn from_bounded_vec<'de, D>(deserializer: D) -> Result>, D::Error> +pub fn from_bounded_did<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + String::deserialize(deserializer) + .map(|string| string.as_bytes().to_vec()) + .and_then(|vec| vec.try_into().map_err(|_| Error::custom("failed to deserialize"))) +} + +pub fn from_bounded_cid<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, { @@ -145,12 +157,18 @@ where S: Serializer, { // Define how many fields we are serializing. - let mut state = serializer.serialize_struct("SampleClaims", 4)?; + let mut state = serializer.serialize_struct("SampleClaims", 5)?; // Serialize each field. // Fields like `subject`, `issuer`, and `investor_type` can be serialized directly. state.serialize_field("sub", &self.subject)?; state.serialize_field("iss", &self.issuer)?; + // For the `ipfs_cid_string` field, you'd use your custom logic to convert it to a string or another format suitable for serialization. + // Assuming `cid` is a `BoundedVec>` and you're encoding it as a UTF-8 string. + let ipfs_cid_bytes: scale_info::prelude::vec::Vec = self.ipfs_cid.clone().into(); // Convert BoundedVec to Vec + let ipfs_cid_string = String::from_utf8_lossy(&ipfs_cid_bytes); // Convert Vec to String + state.serialize_field("aud", &ipfs_cid_string)?; + state.serialize_field("investor_type", &self.investor_type)?; // For the `did` field, you'd use your custom logic to convert it to a string or another format suitable for serialization. diff --git a/polimec-common/test-utils/src/lib.rs b/polimec-common/test-utils/src/lib.rs index 2cdf64541..6194ea5a8 100644 --- a/polimec-common/test-utils/src/lib.rs +++ b/polimec-common/test-utils/src/lib.rs @@ -38,46 +38,58 @@ pub fn get_test_jwt( res } +fn create_jwt( + account_id: AccountId, + investor_type: InvestorType, + did: BoundedVec>, + ipfs_cid: Option>>, +) -> UntrustedToken { + use chrono::{TimeZone, Utc}; + use jwt_compact::{alg::SigningKey, Claims}; + + // Create a signing key from raw bytes. + let key = SigningKey::from_slice([ + 80, 168, 164, 18, 76, 133, 92, 116, 50, 20, 155, 28, 33, 89, 151, 207, 199, 247, 113, 185, 127, 156, 2, + 132, 65, 58, 76, 156, 143, 109, 29, 251, + ].as_ref()).unwrap(); + + let header: Header = Header::empty(); + + // Handle optional IPFS CID + let ipfs_cid = ipfs_cid.unwrap_or_else(|| BoundedVec::with_bounded_capacity(96)); + + let custom_claims = SampleClaims { + subject: account_id, + investor_type, + issuer: "verifier".to_string(), + did, + ipfs_cid, + }; + + let mut claims = Claims::new(custom_claims); + claims.expiration = Some(Utc.with_ymd_and_hms(2030, 1, 1, 0, 0, 0).unwrap()); + + let token_string = Ed25519.token(&header, &claims, &key).unwrap(); + UntrustedToken::new(&token_string).expect("Failed to parse the JWT") +} + // The `Serialize` trait is needed to serialize the `account_id` into a `SampleClaims` struct. pub fn get_mock_jwt( - account_id: AccountId, - investor_type: InvestorType, - did: BoundedVec>, + account_id: AccountId, + investor_type: InvestorType, + did: BoundedVec>, +) -> UntrustedToken { + create_jwt(account_id, investor_type, did, None) +} + +// The `Serialize` trait is needed to serialize the `account_id` into a `SampleClaims` struct. +pub fn get_mock_jwt_with_cid( + account_id: AccountId, + investor_type: InvestorType, + did: BoundedVec>, + ipfs_cid: BoundedVec>, ) -> UntrustedToken { - use chrono::{TimeZone, Utc}; - use jwt_compact::{alg::SigningKey, Claims}; - - #[allow(unused)] - // Needed to convert the "issuer" field to a string. - use parity_scale_codec::alloc::string::ToString; - - // Create a signing key from raw bytes. - let key = SigningKey::from_slice( - [ - 80, 168, 164, 18, 76, 133, 92, 116, 50, 20, 155, 28, 33, 89, 151, 207, 199, 247, 113, 185, 127, 156, 2, - 132, 65, 58, 76, 156, 143, 109, 29, 251, - ] - .as_ref(), - ) - .unwrap(); - // We don't need any custom fields in the header, so we use the empty. - let header: Header = Header::empty(); - - // Create the custom part of the `Claims` struct. - let custom_claims: SampleClaims = - SampleClaims { subject: account_id, investor_type, issuer: "verifier".to_string(), did }; - - // Wrap the SampleClaims` struct in the `Claims` struct. - let mut claims = Claims::new(custom_claims); - // Set the expiration date to 2030-01-01. - // We need to unwrap the `Utc::with_ymd_and_hms` because it returns a `LocalResult>` but we ned a `DateTime. - claims.expiration = Some(Utc.with_ymd_and_hms(2030, 1, 1, 0, 0, 0).unwrap()); - - // Create a JWT using the Ed25519 algorithm. - let token_string = Ed25519.token(&header, &claims, &key).unwrap(); - - // Create an `UntrustedToken` from the signed JWT string. - UntrustedToken::new(&token_string).expect("Failed to parse the JWT") + create_jwt(account_id, investor_type, did, Some(ipfs_cid)) } /// Fetches a JWT from a dummy Polimec JWT producer that will return a JWT with the specified From 087843a078d0be55c2d7d8e3a5fa51e49ef64f87 Mon Sep 17 00:00:00 2001 From: Leonardo Razovic <4128940+lrazovic@users.noreply.github.com> Date: Wed, 24 Apr 2024 09:36:43 +0200 Subject: [PATCH 02/12] test: update the test endpoint --- polimec-common/test-utils/src/lib.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/polimec-common/test-utils/src/lib.rs b/polimec-common/test-utils/src/lib.rs index 6194ea5a8..a14e286d0 100644 --- a/polimec-common/test-utils/src/lib.rs +++ b/polimec-common/test-utils/src/lib.rs @@ -26,11 +26,22 @@ pub fn get_test_jwt( account_id: AccountId, investor_type: InvestorType, ) -> UntrustedToken { - let jwt = reqwest::blocking::get(format!( - "http://jws-producer.polimec.workers.dev/mock/{}/{}", + // TODO: Accept the DID as a parameter. + let did = "did:polimec:0x1234"; + // TODO: Accept the CID as a parameter. + + let cid = "QmeuJ24ffwLAZppQcgcggJs3n689bewednYkuc8Bx5Gngz"; + + let url = format!( + "http://jws-producer.polimec.workers.dev/mock/{}/{}/{}/{}", account_id, - investor_type.as_str() - )) + investor_type.as_str(), + did, + cid + ); + println!("URL: {}", url); + // TODO: This should be a POST with everything in the body. + let jwt = reqwest::blocking::get(url) .expect("Failed to perform the HTTP GET") .text() .expect("Failed to get the response body (jwt) from the specified endpoint"); From deb79eebea71595dd6098beff0033f07e74f053f Mon Sep 17 00:00:00 2001 From: Leonardo Razovic <4128940+lrazovic@users.noreply.github.com> Date: Wed, 24 Apr 2024 09:50:07 +0200 Subject: [PATCH 03/12] feat: add decode CID tests --- polimec-common/test-utils/src/lib.rs | 113 ++++++++++++++++----------- 1 file changed, 68 insertions(+), 45 deletions(-) diff --git a/polimec-common/test-utils/src/lib.rs b/polimec-common/test-utils/src/lib.rs index a14e286d0..611544408 100644 --- a/polimec-common/test-utils/src/lib.rs +++ b/polimec-common/test-utils/src/lib.rs @@ -28,7 +28,7 @@ pub fn get_test_jwt( ) -> UntrustedToken { // TODO: Accept the DID as a parameter. let did = "did:polimec:0x1234"; - // TODO: Accept the CID as a parameter. + // TODO: Accept the CID as a parameter. let cid = "QmeuJ24ffwLAZppQcgcggJs3n689bewednYkuc8Bx5Gngz"; @@ -42,65 +42,64 @@ pub fn get_test_jwt( println!("URL: {}", url); // TODO: This should be a POST with everything in the body. let jwt = reqwest::blocking::get(url) - .expect("Failed to perform the HTTP GET") - .text() - .expect("Failed to get the response body (jwt) from the specified endpoint"); + .expect("Failed to perform the HTTP GET") + .text() + .expect("Failed to get the response body (jwt) from the specified endpoint"); let res = UntrustedToken::new(&jwt).expect("Failed to parse the JWT"); res } fn create_jwt( - account_id: AccountId, - investor_type: InvestorType, - did: BoundedVec>, - ipfs_cid: Option>>, + account_id: AccountId, + investor_type: InvestorType, + did: BoundedVec>, + ipfs_cid: Option>>, ) -> UntrustedToken { - use chrono::{TimeZone, Utc}; - use jwt_compact::{alg::SigningKey, Claims}; - - // Create a signing key from raw bytes. - let key = SigningKey::from_slice([ - 80, 168, 164, 18, 76, 133, 92, 116, 50, 20, 155, 28, 33, 89, 151, 207, 199, 247, 113, 185, 127, 156, 2, - 132, 65, 58, 76, 156, 143, 109, 29, 251, - ].as_ref()).unwrap(); - - let header: Header = Header::empty(); - - // Handle optional IPFS CID - let ipfs_cid = ipfs_cid.unwrap_or_else(|| BoundedVec::with_bounded_capacity(96)); - - let custom_claims = SampleClaims { - subject: account_id, - investor_type, - issuer: "verifier".to_string(), - did, - ipfs_cid, - }; - - let mut claims = Claims::new(custom_claims); - claims.expiration = Some(Utc.with_ymd_and_hms(2030, 1, 1, 0, 0, 0).unwrap()); - - let token_string = Ed25519.token(&header, &claims, &key).unwrap(); - UntrustedToken::new(&token_string).expect("Failed to parse the JWT") + use chrono::{TimeZone, Utc}; + use jwt_compact::{alg::SigningKey, Claims}; + + // Create a signing key from raw bytes. + let key = SigningKey::from_slice( + [ + 80, 168, 164, 18, 76, 133, 92, 116, 50, 20, 155, 28, 33, 89, 151, 207, 199, 247, 113, 185, 127, 156, 2, + 132, 65, 58, 76, 156, 143, 109, 29, 251, + ] + .as_ref(), + ) + .unwrap(); + + let header: Header = Header::empty(); + + // Handle optional IPFS CID + let ipfs_cid = ipfs_cid.unwrap_or_else(|| BoundedVec::with_bounded_capacity(96)); + + let custom_claims = + SampleClaims { subject: account_id, investor_type, issuer: "verifier".to_string(), did, ipfs_cid }; + + let mut claims = Claims::new(custom_claims); + claims.expiration = Some(Utc.with_ymd_and_hms(2030, 1, 1, 0, 0, 0).unwrap()); + + let token_string = Ed25519.token(&header, &claims, &key).unwrap(); + UntrustedToken::new(&token_string).expect("Failed to parse the JWT") } // The `Serialize` trait is needed to serialize the `account_id` into a `SampleClaims` struct. pub fn get_mock_jwt( - account_id: AccountId, - investor_type: InvestorType, - did: BoundedVec>, + account_id: AccountId, + investor_type: InvestorType, + did: BoundedVec>, ) -> UntrustedToken { - create_jwt(account_id, investor_type, did, None) + create_jwt(account_id, investor_type, did, None) } // The `Serialize` trait is needed to serialize the `account_id` into a `SampleClaims` struct. pub fn get_mock_jwt_with_cid( - account_id: AccountId, - investor_type: InvestorType, - did: BoundedVec>, - ipfs_cid: BoundedVec>, + account_id: AccountId, + investor_type: InvestorType, + did: BoundedVec>, + ipfs_cid: BoundedVec>, ) -> UntrustedToken { - create_jwt(account_id, investor_type, did, Some(ipfs_cid)) + create_jwt(account_id, investor_type, did, Some(ipfs_cid)) } /// Fetches a JWT from a dummy Polimec JWT producer that will return a JWT with the specified @@ -148,7 +147,7 @@ pub fn do_request(url: &str) -> String { #[cfg(test)] mod tests { - use crate::{generate_did_from_account, get_mock_jwt}; + use crate::{generate_did_from_account, get_mock_jwt, get_mock_jwt_with_cid}; use jwt_compact::{ alg::{Ed25519, VerifyingKey}, AlgorithmExt, @@ -169,4 +168,28 @@ mod tests { let res = Ed25519.validator::>(&verifying_key).validate(&token); assert!(res.is_ok()); } + + #[test] + fn test_get_test_jwt_with_cid() { + let verifying_key = VerifyingKey::from_slice( + [ + 32, 118, 30, 171, 58, 212, 197, 27, 146, 122, 255, 243, 34, 245, 90, 244, 221, 37, 253, 195, 18, 202, + 111, 55, 39, 48, 123, 17, 101, 78, 215, 94, + ] + .as_ref(), + ) + .unwrap(); + let cid: &str = "QmeuJ24ffwLAZppQcgcggJs3n689bewednYkuc8Bx5Gngz"; + let bounded_cid = frame_support::BoundedVec::try_from(cid.as_bytes().to_vec()).unwrap(); + let token = + get_mock_jwt_with_cid("0x1234", InvestorType::Institutional, generate_did_from_account(40u64), bounded_cid.clone()); + let res = Ed25519.validator::>(&verifying_key).validate(&token); + assert!(res.is_ok()); + let validated_token = res.unwrap(); + let claims = validated_token.claims(); + assert_eq!(claims.custom.ipfs_cid, bounded_cid); + let cid_from_token = std::str::from_utf8(&claims.custom.ipfs_cid).unwrap(); + assert_eq!(cid_from_token, cid); + + } } From 68aabb0ce84c3367f1babf321abe2eb8e02193d6 Mon Sep 17 00:00:00 2001 From: Leonardo Razovic <4128940+lrazovic@users.noreply.github.com> Date: Wed, 24 Apr 2024 09:55:46 +0200 Subject: [PATCH 04/12] feat: add demo --- pallets/funding/src/lib.rs | 8 ++++++-- pallets/funding/src/types.rs | 7 ++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index 03ea44f18..9a3f7b7cc 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -171,7 +171,7 @@ pub type EvaluatorsOutcomeOf = EvaluatorsOutcome>; pub type TicketSizeOf = TicketSize>; pub type ProjectMetadataOf = - ProjectMetadata>, BalanceOf, PriceOf, AccountIdOf>; + ProjectMetadata>, BalanceOf, PriceOf, AccountIdOf, Cid>; pub type ProjectDetailsOf = ProjectDetails, Did, BlockNumberFor, PriceOf, BalanceOf, EvaluationRoundInfoOf>; pub type EvaluationRoundInfoOf = EvaluationRoundInfo>; @@ -805,8 +805,12 @@ pub mod pallet { project_id: ProjectId, #[pallet::compact] usd_amount: BalanceOf, ) -> DispatchResultWithPostInfo { - let (account, did, investor_type, _cid) = + let (account, did, investor_type, cid) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; + + // JUST FOR DEMO PURPOSES + let project = ProjectsMetadata::::get(project_id).unwrap(); + ensure!(project.policy_ipfs_cid == Some(cid), Error::::IssuerError(IssuerErrorReason::NotIssuer)); Self::do_evaluate(&account, project_id, usd_amount, did, investor_type) } diff --git a/pallets/funding/src/types.rs b/pallets/funding/src/types.rs index 64235b36e..366ed38e6 100644 --- a/pallets/funding/src/types.rs +++ b/pallets/funding/src/types.rs @@ -173,7 +173,7 @@ pub mod storage_types { #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] - pub struct ProjectMetadata { + pub struct ProjectMetadata { /// Token Metadata pub token_information: CurrencyMetadata, /// Mainnet Token Max Supply @@ -195,7 +195,7 @@ pub mod storage_types { BoundedVec>, pub funding_destination_account: AccountId, /// Additional metadata - pub policy_ipfs_cid: Option, + pub policy_ipfs_cid: Option, } impl< @@ -203,7 +203,8 @@ pub mod storage_types { Balance: From + PartialOrd + Copy + FixedPointOperand, Price: FixedPointNumber, AccountId, - > ProjectMetadata + Cid + > ProjectMetadata { /// Validate issuer metadata for the following checks: /// - Minimum price is not zero From 847261503053b8143b3506cfce774ba903f65d6b Mon Sep 17 00:00:00 2001 From: Leonardo Razovic <4128940+lrazovic@users.noreply.github.com> Date: Wed, 24 Apr 2024 10:01:51 +0200 Subject: [PATCH 05/12] chore: update error and add comment --- pallets/funding/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index 9a3f7b7cc..f493da87b 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -810,7 +810,8 @@ pub mod pallet { // JUST FOR DEMO PURPOSES let project = ProjectsMetadata::::get(project_id).unwrap(); - ensure!(project.policy_ipfs_cid == Some(cid), Error::::IssuerError(IssuerErrorReason::NotIssuer)); + // Create a new Error to comunicate that the used JWT is not validated against this project's policy. + ensure!(project.policy_ipfs_cid == Some(cid), Error::::BadMetadata(MetadataError::CidNotProvided)); Self::do_evaluate(&account, project_id, usd_amount, did, investor_type) } From ca959eb2941cd5ca9499c2bcf8c08b4a02dd191e Mon Sep 17 00:00:00 2001 From: Leonardo Razovic <4128940+lrazovic@users.noreply.github.com> Date: Wed, 24 Apr 2024 10:16:00 +0200 Subject: [PATCH 06/12] fmt --- pallets/funding/src/types.rs | 2 +- polimec-common/common/src/credentials/mod.rs | 7 ++++++- polimec-common/test-utils/src/lib.rs | 9 ++++++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/pallets/funding/src/types.rs b/pallets/funding/src/types.rs index 366ed38e6..110b51d52 100644 --- a/pallets/funding/src/types.rs +++ b/pallets/funding/src/types.rs @@ -203,7 +203,7 @@ pub mod storage_types { Balance: From + PartialOrd + Copy + FixedPointOperand, Price: FixedPointNumber, AccountId, - Cid + Cid, > ProjectMetadata { /// Validate issuer metadata for the following checks: diff --git a/polimec-common/common/src/credentials/mod.rs b/polimec-common/common/src/credentials/mod.rs index e1a992adb..3a73b10ce 100644 --- a/polimec-common/common/src/credentials/mod.rs +++ b/polimec-common/common/src/credentials/mod.rs @@ -88,7 +88,12 @@ where let Some(date_time) = claims.expiration else { return Err(origin) }; if claims.custom.subject == who && (date_time.timestamp_millis() as u64) >= now { - return Ok((who, claims.custom.did.clone(), claims.custom.investor_type.clone(), claims.custom.ipfs_cid.clone())); + return Ok(( + who, + claims.custom.did.clone(), + claims.custom.investor_type.clone(), + claims.custom.ipfs_cid.clone(), + )); } Err(origin) diff --git a/polimec-common/test-utils/src/lib.rs b/polimec-common/test-utils/src/lib.rs index 611544408..a7e4b1a45 100644 --- a/polimec-common/test-utils/src/lib.rs +++ b/polimec-common/test-utils/src/lib.rs @@ -181,8 +181,12 @@ mod tests { .unwrap(); let cid: &str = "QmeuJ24ffwLAZppQcgcggJs3n689bewednYkuc8Bx5Gngz"; let bounded_cid = frame_support::BoundedVec::try_from(cid.as_bytes().to_vec()).unwrap(); - let token = - get_mock_jwt_with_cid("0x1234", InvestorType::Institutional, generate_did_from_account(40u64), bounded_cid.clone()); + let token = get_mock_jwt_with_cid( + "0x1234", + InvestorType::Institutional, + generate_did_from_account(40u64), + bounded_cid.clone(), + ); let res = Ed25519.validator::>(&verifying_key).validate(&token); assert!(res.is_ok()); let validated_token = res.unwrap(); @@ -190,6 +194,5 @@ mod tests { assert_eq!(claims.custom.ipfs_cid, bounded_cid); let cid_from_token = std::str::from_utf8(&claims.custom.ipfs_cid).unwrap(); assert_eq!(cid_from_token, cid); - } } From 4f3e944e78304611edbad7e9ebd538d83d8fc31e Mon Sep 17 00:00:00 2001 From: Juan Ignacio Rios Date: Wed, 24 Apr 2024 18:18:40 +0200 Subject: [PATCH 07/12] fixing all references --- integration-tests/src/tests/defaults.rs | 2 +- pallets/dispenser/src/benchmarking.rs | 1 + pallets/funding/src/benchmarking.rs | 72 ++++++-- pallets/funding/src/functions.rs | 28 ++- .../src/instantiator/chain_interactions.rs | 9 + pallets/funding/src/lib.rs | 41 +++-- pallets/funding/src/tests/1_application.rs | 167 +++++++++++++++--- pallets/funding/src/tests/2_evaluation.rs | 134 ++++++++++---- pallets/funding/src/tests/3_auction.rs | 102 +++++++---- pallets/funding/src/tests/4_community.rs | 136 +++++++++++--- pallets/funding/src/tests/5_remainder.rs | 109 +++++++++--- pallets/funding/src/tests/mod.rs | 4 +- pallets/funding/src/types.rs | 2 + pallets/sandbox/src/lib.rs | 3 + polimec-common/test-utils/src/lib.rs | 7 +- scripts/zombienet/native/local-testnet.toml | 2 +- 16 files changed, 651 insertions(+), 168 deletions(-) diff --git a/integration-tests/src/tests/defaults.rs b/integration-tests/src/tests/defaults.rs index 5ed097248..ef2464359 100644 --- a/integration-tests/src/tests/defaults.rs +++ b/integration-tests/src/tests/defaults.rs @@ -49,7 +49,7 @@ pub fn bounded_name() -> BoundedVec> { pub fn bounded_symbol() -> BoundedVec> { BoundedVec::try_from("CTEST".as_bytes().to_vec()).unwrap() } -pub fn ipfs_hash() -> BoundedVec> { +pub fn ipfs_hash() -> BoundedVec> { BoundedVec::try_from(IPFS_CID.as_bytes().to_vec()).unwrap() } pub fn default_weights() -> Vec { diff --git a/pallets/dispenser/src/benchmarking.rs b/pallets/dispenser/src/benchmarking.rs index 7b7c267c7..f418550ab 100644 --- a/pallets/dispenser/src/benchmarking.rs +++ b/pallets/dispenser/src/benchmarking.rs @@ -33,6 +33,7 @@ fn assert_last_event(generic_event: ::RuntimeEvent) { #[benchmarks] mod benchmarks { use super::*; + use polimec_common_test_utils::get_mock_jwt_with_cid; #[benchmark] fn dispense() { diff --git a/pallets/funding/src/benchmarking.rs b/pallets/funding/src/benchmarking.rs index 094ccf785..e9ee232c2 100644 --- a/pallets/funding/src/benchmarking.rs +++ b/pallets/funding/src/benchmarking.rs @@ -355,7 +355,7 @@ pub fn run_blocks_to_execute_next_transition( mod benchmarks { use super::*; use itertools::Itertools; - use polimec_common_test_utils::generate_did_from_account; + use polimec_common_test_utils::{generate_did_from_account, get_mock_jwt_with_cid}; impl_benchmark_test_suite!(PalletFunding, crate::mock::new_test_ext(), crate::mock::TestRuntime); @@ -384,7 +384,12 @@ mod benchmarks { issuer.clone(), ed * 2u64.into() + metadata_deposit + ct_treasury_account_deposit, )]); - let jwt = get_mock_jwt(issuer.clone(), InvestorType::Institutional, generate_did_from_account(issuer.clone())); + let jwt = get_mock_jwt_with_cid( + issuer.clone(), + InvestorType::Institutional, + generate_did_from_account(issuer.clone()), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); #[extrinsic_call] create_project(RawOrigin::Signed(issuer.clone()), jwt, project_metadata.clone()); @@ -419,7 +424,12 @@ mod benchmarks { let project_metadata = default_project::(issuer.clone()); let project_id = inst.create_new_project(project_metadata.clone(), issuer.clone()); - let jwt = get_mock_jwt(issuer.clone(), InvestorType::Institutional, generate_did_from_account(issuer.clone())); + let jwt = get_mock_jwt_with_cid( + issuer.clone(), + InvestorType::Institutional, + generate_did_from_account(issuer.clone()), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); #[extrinsic_call] remove_project(RawOrigin::Signed(issuer.clone()), jwt, project_id); @@ -521,7 +531,12 @@ mod benchmarks { policy_ipfs_cid: Some(BoundedVec::try_from(IPFS_CID.as_bytes().to_vec()).unwrap()), }; - let jwt = get_mock_jwt(issuer.clone(), InvestorType::Institutional, generate_did_from_account(issuer.clone())); + let jwt = get_mock_jwt_with_cid( + issuer.clone(), + InvestorType::Institutional, + generate_did_from_account(issuer.clone()), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); #[extrinsic_call] edit_project(RawOrigin::Signed(issuer), jwt, project_id, project_metadata.clone()); @@ -553,13 +568,18 @@ mod benchmarks { whitelist_account!(issuer); let project_metadata = default_project::(issuer.clone()); - let project_id = inst.create_new_project(project_metadata, issuer.clone()); + let project_id = inst.create_new_project(project_metadata.clone(), issuer.clone()); // start_evaluation fn will try to add an automatic transition 1 block after the last evaluation block let block_number: BlockNumberFor = inst.current_block() + T::EvaluationDuration::get() + One::one(); // fill the `ProjectsToUpdate` vectors from @ block_number to @ block_number+x, to benchmark all the failed insertion attempts fill_projects_to_update::(x, block_number); - let jwt = get_mock_jwt(issuer.clone(), InvestorType::Institutional, generate_did_from_account(issuer.clone())); + let jwt = get_mock_jwt_with_cid( + issuer.clone(), + InvestorType::Institutional, + generate_did_from_account(issuer.clone()), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); #[extrinsic_call] start_evaluation(RawOrigin::Signed(issuer), jwt, project_id); @@ -601,7 +621,7 @@ mod benchmarks { whitelist_account!(issuer); let project_metadata = default_project::(issuer.clone()); - let project_id = inst.create_evaluating_project(project_metadata, issuer.clone()); + let project_id = inst.create_evaluating_project(project_metadata.clone(), issuer.clone()); let evaluations = default_evaluations(); let plmc_for_evaluating = BenchInstantiator::::calculate_evaluation_plmc_spent(evaluations.clone()); @@ -624,7 +644,12 @@ mod benchmarks { fill_projects_to_update::(x, insertion_block_number); - let jwt = get_mock_jwt(issuer.clone(), InvestorType::Institutional, generate_did_from_account(issuer.clone())); + let jwt = get_mock_jwt_with_cid( + issuer.clone(), + InvestorType::Institutional, + generate_did_from_account(issuer.clone()), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); #[extrinsic_call] start_auction(RawOrigin::Signed(issuer), jwt, project_id); @@ -656,7 +681,7 @@ mod benchmarks { whitelist_account!(test_evaluator); let project_metadata = default_project::(issuer.clone()); - let project_id = inst.create_evaluating_project(project_metadata, issuer); + let project_id = inst.create_evaluating_project(project_metadata.clone(), issuer); let existing_evaluation = UserToUSDBalance::new(test_evaluator.clone(), (200 * US_DOLLAR).into()); let extrinsic_evaluation = UserToUSDBalance::new(test_evaluator.clone(), (1_000 * US_DOLLAR).into()); @@ -684,10 +709,11 @@ mod benchmarks { plmc_for_extrinsic_evaluation.clone(), ]); - let jwt = get_mock_jwt( + let jwt = get_mock_jwt_with_cid( extrinsic_evaluation.account.clone(), InvestorType::Institutional, generate_did_from_account(extrinsic_evaluation.account.clone()), + project_metadata.clone().policy_ipfs_cid.unwrap(), ); #[extrinsic_call] evaluate( @@ -1037,10 +1063,11 @@ mod benchmarks { total_usdt_locked, ) = bid_setup::(x, y); - let jwt = get_mock_jwt( + let jwt = get_mock_jwt_with_cid( original_extrinsic_bid.bidder.clone(), InvestorType::Institutional, generate_did_from_account(original_extrinsic_bid.bidder.clone()), + project_metadata.clone().policy_ipfs_cid.unwrap(), ); #[extrinsic_call] bid( @@ -1296,10 +1323,11 @@ mod benchmarks { total_ct_sold, ) = contribution_setup::(x, ends_round); - let jwt = get_mock_jwt( + let jwt = get_mock_jwt_with_cid( extrinsic_contribution.contributor.clone(), InvestorType::Retail, generate_did_from_account(extrinsic_contribution.contributor.clone()), + project_metadata.clone().policy_ipfs_cid.unwrap(), ); #[extrinsic_call] @@ -1346,10 +1374,11 @@ mod benchmarks { total_ct_sold, ) = contribution_setup::(x, ends_round); - let jwt = get_mock_jwt( + let jwt = get_mock_jwt_with_cid( extrinsic_contribution.contributor.clone(), InvestorType::Retail, generate_did_from_account(extrinsic_contribution.contributor.clone()), + project_metadata.clone().policy_ipfs_cid.unwrap(), ); #[extrinsic_call] @@ -1412,8 +1441,14 @@ mod benchmarks { default_community_contributor_multipliers(), ); - let project_id = - inst.create_finished_project(project_metadata, issuer.clone(), evaluations, bids, contributions, vec![]); + let project_id = inst.create_finished_project( + project_metadata.clone(), + issuer.clone(), + evaluations, + bids, + contributions, + vec![], + ); inst.advance_time(One::one()).unwrap(); @@ -1421,7 +1456,12 @@ mod benchmarks { let insertion_block_number: BlockNumberFor = current_block + One::one(); fill_projects_to_update::(x, insertion_block_number); - let jwt = get_mock_jwt(issuer.clone(), InvestorType::Institutional, generate_did_from_account(issuer.clone())); + let jwt = get_mock_jwt_with_cid( + issuer.clone(), + InvestorType::Institutional, + generate_did_from_account(issuer.clone()), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); #[extrinsic_call] decide_project_outcome(RawOrigin::Signed(issuer), jwt, project_id, FundingOutcomeDecision::AcceptFunding); diff --git a/pallets/funding/src/functions.rs b/pallets/funding/src/functions.rs index 6670de59e..b955e4d64 100644 --- a/pallets/funding/src/functions.rs +++ b/pallets/funding/src/functions.rs @@ -961,8 +961,11 @@ impl Pallet { usd_amount: BalanceOf, did: Did, investor_type: InvestorType, + whitelisted_policy: Cid, ) -> DispatchResultWithPostInfo { // * Get variables * + let project_metadata = ProjectsMetadata::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectMetadataNotFound))?; let mut project_details = ProjectsDetails::::get(project_id) .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; let now = >::block_number(); @@ -973,8 +976,13 @@ impl Pallet { let evaluation_round_info = &mut project_details.evaluation_round_info; let total_evaluations_count = EvaluationCounts::::get(project_id); let user_evaluations_count = Evaluations::::iter_prefix((project_id, evaluator)).count() as u32; + let project_policy = project_metadata.policy_ipfs_cid.ok_or(Error::::ImpossibleState)?; // * Validity Checks * + ensure!( + project_policy == whitelisted_policy, + Error::::ParticipationFailed(ParticipationError::PolicyMismatch) + ); ensure!( usd_amount >= T::MinUsdPerEvaluation::get(), Error::::ParticipationFailed(ParticipationError::TooLow) @@ -1077,12 +1085,13 @@ impl Pallet { funding_asset: AcceptedFundingAsset, did: Did, investor_type: InvestorType, + whitelisted_policy: Cid, ) -> DispatchResultWithPostInfo { // * Get variables * - let project_details = ProjectsDetails::::get(project_id) - .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; let project_metadata = ProjectsMetadata::::get(project_id) .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectMetadataNotFound))?; + let project_details = ProjectsDetails::::get(project_id) + .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; let plmc_usd_price = T::PriceProvider::get_price(PLMC_FOREIGN_ID).ok_or(Error::::PriceNotFound)?; // Fetch current bucket details and other required info @@ -1091,6 +1100,7 @@ impl Pallet { let now = >::block_number(); let mut amount_to_bid = ct_amount; let total_bids_for_project = BidCounts::::get(project_id); + let project_policy = project_metadata.policy_ipfs_cid.ok_or(Error::::ImpossibleState)?; // User will spend at least this amount of USD for his bid(s). More if the bid gets split into different buckets let min_total_ticket_size = @@ -1114,6 +1124,10 @@ impl Pallet { }; // * Validity checks * + ensure!( + project_policy == whitelisted_policy, + Error::::ParticipationFailed(ParticipationError::PolicyMismatch) + ); ensure!( matches!(investor_type, InvestorType::Institutional | InvestorType::Professional), DispatchError::from("Retail investors are not allowed to bid") @@ -1297,6 +1311,7 @@ impl Pallet { asset: AcceptedFundingAsset, did: Did, investor_type: InvestorType, + whitelisted_policy: Cid, ) -> DispatchResultWithPostInfo { let mut project_details = ProjectsDetails::::get(project_id) .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; @@ -1320,6 +1335,7 @@ impl Pallet { asset, investor_type, did, + whitelisted_policy, ) } @@ -1341,6 +1357,7 @@ impl Pallet { asset: AcceptedFundingAsset, did: Did, investor_type: InvestorType, + whitelisted_policy: Cid, ) -> DispatchResultWithPostInfo { let mut project_details = ProjectsDetails::::get(project_id) .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectDetailsNotFound))?; @@ -1364,6 +1381,7 @@ impl Pallet { asset, investor_type, did, + whitelisted_policy, ) } @@ -1377,6 +1395,7 @@ impl Pallet { funding_asset: AcceptedFundingAsset, investor_type: InvestorType, did: Did, + whitelisted_policy: Cid, ) -> DispatchResultWithPostInfo { let project_metadata = ProjectsMetadata::::get(project_id) .ok_or(Error::::ProjectError(ProjectErrorReason::ProjectMetadataNotFound))?; @@ -1389,6 +1408,7 @@ impl Pallet { let plmc_usd_price = T::PriceProvider::get_price(PLMC_FOREIGN_ID).ok_or(Error::::PriceNotFound)?; let funding_asset_usd_price = T::PriceProvider::get_price(funding_asset.to_assethub_id()).ok_or(Error::::PriceNotFound)?; + let project_policy = project_metadata.policy_ipfs_cid.ok_or(Error::::ImpossibleState)?; let ticket_size = ct_usd_price.checked_mul_int(buyable_tokens).ok_or(Error::::BadMath)?; let contributor_ticket_size = match investor_type { @@ -1411,6 +1431,10 @@ impl Pallet { InvestorType::Institutional => INSTITUTIONAL_MAX_MULTIPLIER, }; // * Validity checks * + ensure!( + project_policy == whitelisted_policy, + Error::::ParticipationFailed(ParticipationError::PolicyMismatch) + ); ensure!( multiplier.into() <= max_multiplier && multiplier.into() > 0u8, Error::::ParticipationFailed(ParticipationError::ForbiddenMultiplier) diff --git a/pallets/funding/src/instantiator/chain_interactions.rs b/pallets/funding/src/instantiator/chain_interactions.rs index 132651d2a..2dd7f7ec5 100644 --- a/pallets/funding/src/instantiator/chain_interactions.rs +++ b/pallets/funding/src/instantiator/chain_interactions.rs @@ -410,6 +410,7 @@ impl< project_id: ProjectId, bonds: Vec>, ) -> DispatchResultWithPostInfo { + let project_policy = self.get_project_metadata(project_id).policy_ipfs_cid.unwrap(); for UserToUSDBalance { account, usd_amount } in bonds { self.execute(|| { crate::Pallet::::do_evaluate( @@ -418,6 +419,7 @@ impl< usd_amount, generate_did_from_account(account), InvestorType::Professional, + project_policy.clone(), ) })?; } @@ -480,6 +482,8 @@ impl< } pub fn bid_for_users(&mut self, project_id: ProjectId, bids: Vec>) -> DispatchResultWithPostInfo { + let project_policy = self.get_project_metadata(project_id).policy_ipfs_cid.unwrap(); + for bid in bids { self.execute(|| { let did = generate_did_from_account(bid.bidder.clone()); @@ -491,6 +495,7 @@ impl< bid.asset, did, InvestorType::Institutional, + project_policy.clone(), ) })?; } @@ -609,6 +614,8 @@ impl< project_id: ProjectId, contributions: Vec>, ) -> DispatchResultWithPostInfo { + let project_policy = self.get_project_metadata(project_id).policy_ipfs_cid.unwrap(); + match self.get_project_details(project_id).status { ProjectStatus::CommunityRound => for cont in contributions { @@ -623,6 +630,7 @@ impl< cont.asset, did, investor_type, + project_policy.clone(), ) })?; }, @@ -639,6 +647,7 @@ impl< cont.asset, did, investor_type, + project_policy.clone(), ) })?; }, diff --git a/pallets/funding/src/lib.rs b/pallets/funding/src/lib.rs index f493da87b..85ccada1f 100644 --- a/pallets/funding/src/lib.rs +++ b/pallets/funding/src/lib.rs @@ -805,14 +805,10 @@ pub mod pallet { project_id: ProjectId, #[pallet::compact] usd_amount: BalanceOf, ) -> DispatchResultWithPostInfo { - let (account, did, investor_type, cid) = + let (account, did, investor_type, whitelisted_policy) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; - // JUST FOR DEMO PURPOSES - let project = ProjectsMetadata::::get(project_id).unwrap(); - // Create a new Error to comunicate that the used JWT is not validated against this project's policy. - ensure!(project.policy_ipfs_cid == Some(cid), Error::::BadMetadata(MetadataError::CidNotProvided)); - Self::do_evaluate(&account, project_id, usd_amount, did, investor_type) + Self::do_evaluate(&account, project_id, usd_amount, did, investor_type, whitelisted_policy) } /// Bid for a project in the Auction round @@ -833,9 +829,10 @@ pub mod pallet { multiplier: T::Multiplier, asset: AcceptedFundingAsset, ) -> DispatchResultWithPostInfo { - let (account, did, investor_type, _cid) = + let (account, did, investor_type, whitelisted_policy) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; - Self::do_bid(&account, project_id, ct_amount, multiplier, asset, did, investor_type) + + Self::do_bid(&account, project_id, ct_amount, multiplier, asset, did, investor_type, whitelisted_policy) } /// Buy tokens in the Community or Remainder round at the price set in the Auction Round @@ -857,9 +854,19 @@ pub mod pallet { multiplier: MultiplierOf, asset: AcceptedFundingAsset, ) -> DispatchResultWithPostInfo { - let (account, did, investor_type, _cid) = + let (account, did, investor_type, whitelisted_policy) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; - Self::do_community_contribute(&account, project_id, amount, multiplier, asset, did, investor_type) + + Self::do_community_contribute( + &account, + project_id, + amount, + multiplier, + asset, + did, + investor_type, + whitelisted_policy, + ) } /// Buy tokens in the Community or Remainder round at the price set in the Auction Round @@ -881,9 +888,19 @@ pub mod pallet { multiplier: MultiplierOf, asset: AcceptedFundingAsset, ) -> DispatchResultWithPostInfo { - let (account, did, investor_type, _cid) = + let (account, did, investor_type, whitelisted_policy) = T::InvestorOrigin::ensure_origin(origin, &jwt, T::VerifierPublicKey::get())?; - Self::do_remaining_contribute(&account, project_id, amount, multiplier, asset, did, investor_type) + + Self::do_remaining_contribute( + &account, + project_id, + amount, + multiplier, + asset, + did, + investor_type, + whitelisted_policy, + ) } #[pallet::call_index(8)] diff --git a/pallets/funding/src/tests/1_application.rs b/pallets/funding/src/tests/1_application.rs index 0da98928e..043b205a2 100644 --- a/pallets/funding/src/tests/1_application.rs +++ b/pallets/funding/src/tests/1_application.rs @@ -1,6 +1,6 @@ use super::*; use polimec_common::credentials::InvestorType; -use polimec_common_test_utils::{generate_did_from_account, get_mock_jwt}; +use polimec_common_test_utils::{generate_did_from_account, get_mock_jwt_with_cid}; #[cfg(test)] mod round_flow { @@ -29,6 +29,7 @@ mod create_project_extrinsic { #[cfg(test)] mod success { use super::*; + use polimec_common_test_utils::get_mock_jwt_with_cid; #[test] fn project_id_autoincrement_works() { @@ -116,8 +117,13 @@ mod create_project_extrinsic { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); let issuer: AccountId = ISSUER_1; let did: Did = BoundedVec::new(); - let jwt: UntrustedToken = get_mock_jwt(ISSUER_1, InvestorType::Institutional, did); let project_metadata: ProjectMetadataOf = default_project_metadata(issuer); + let jwt: UntrustedToken = get_mock_jwt_with_cid( + ISSUER_1, + InvestorType::Institutional, + did, + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); let failing_bids = vec![(BIDDER_1, 1000 * ASSET_UNIT).into(), (BIDDER_2, 1000 * ASSET_UNIT).into()]; @@ -223,7 +229,12 @@ mod create_project_extrinsic { project_metadata.minimum_price = FixedU128::from_float(LOW_PRICE); inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt(ISSUER_1, InvestorType::Institutional, generate_did_from_account(ISSUER_1)); + let jwt = get_mock_jwt_with_cid( + ISSUER_1, + InvestorType::Institutional, + generate_did_from_account(ISSUER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); inst.execute(|| { assert_ok!(crate::Pallet::::create_project( RuntimeOrigin::signed(ISSUER_1), @@ -243,7 +254,12 @@ mod create_project_extrinsic { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); let project_metadata = default_project_metadata(ISSUER_1); inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt(ISSUER_1, InvestorType::Retail, generate_did_from_account(ISSUER_1)); + let jwt = get_mock_jwt_with_cid( + ISSUER_1, + InvestorType::Retail, + generate_did_from_account(ISSUER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); inst.execute(|| { assert_noop!( crate::Pallet::::create_project( @@ -255,7 +271,12 @@ mod create_project_extrinsic { ); }); - let jwt = get_mock_jwt(ISSUER_1, InvestorType::Professional, generate_did_from_account(ISSUER_1)); + let jwt = get_mock_jwt_with_cid( + ISSUER_1, + InvestorType::Professional, + generate_did_from_account(ISSUER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); inst.execute(|| { assert_noop!( crate::Pallet::::create_project( @@ -275,7 +296,12 @@ mod create_project_extrinsic { let ed = MockInstantiator::get_ed(); let issuer_mint: UserToPLMCBalance = (ISSUER_1, ed * 2).into(); // Create a first project - let jwt = get_mock_jwt(ISSUER_1, InvestorType::Institutional, generate_did_from_account(ISSUER_1)); + let jwt = get_mock_jwt_with_cid( + ISSUER_1, + InvestorType::Institutional, + generate_did_from_account(ISSUER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); inst.mint_plmc_to(vec![issuer_mint.clone()]); inst.execute(|| { assert_ok!(Pallet::::create_project( @@ -286,7 +312,12 @@ mod create_project_extrinsic { }); // different account, same did - let jwt = get_mock_jwt(ISSUER_2, InvestorType::Institutional, generate_did_from_account(ISSUER_1)); + let jwt = get_mock_jwt_with_cid( + ISSUER_2, + InvestorType::Institutional, + generate_did_from_account(ISSUER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); inst.execute(|| { assert_noop!( Pallet::::create_project( @@ -305,7 +336,12 @@ mod create_project_extrinsic { let project_metadata = default_project_metadata(ISSUER_1); let ed = MockInstantiator::get_ed(); inst.mint_plmc_to(vec![UserToPLMCBalance::new(ISSUER_1, ed)]); - let jwt = get_mock_jwt(ISSUER_1, InvestorType::Institutional, generate_did_from_account(ISSUER_1)); + let jwt = get_mock_jwt_with_cid( + ISSUER_1, + InvestorType::Institutional, + generate_did_from_account(ISSUER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); inst.execute(|| { assert_noop!( Pallet::::create_project(RuntimeOrigin::signed(ISSUER_1), jwt, project_metadata,), @@ -452,7 +488,12 @@ mod create_project_extrinsic { project_metadata.total_allocation_size = 0; inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt(ISSUER_1, InvestorType::Institutional, generate_did_from_account(ISSUER_1)); + let jwt = get_mock_jwt_with_cid( + ISSUER_1, + InvestorType::Institutional, + generate_did_from_account(ISSUER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); inst.execute(|| { assert_noop!( Pallet::::create_project(RuntimeOrigin::signed(ISSUER_1), jwt, project_metadata), @@ -468,7 +509,12 @@ mod create_project_extrinsic { project_metadata.auction_round_allocation_percentage = Percent::from_percent(0); inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt(ISSUER_1, InvestorType::Institutional, generate_did_from_account(ISSUER_1)); + let jwt = get_mock_jwt_with_cid( + ISSUER_1, + InvestorType::Institutional, + generate_did_from_account(ISSUER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); inst.execute(|| { assert_noop!( Pallet::::create_project(RuntimeOrigin::signed(ISSUER_1), jwt, project_metadata), @@ -485,7 +531,12 @@ mod create_project_extrinsic { project_metadata.total_allocation_size = 999u128; inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt(ISSUER_1, InvestorType::Institutional, generate_did_from_account(ISSUER_1)); + let jwt = get_mock_jwt_with_cid( + ISSUER_1, + InvestorType::Institutional, + generate_did_from_account(ISSUER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); inst.execute(|| { assert_noop!( Pallet::::create_project( @@ -516,12 +567,18 @@ mod edit_project_extrinsic { #[cfg(test)] mod success { use super::*; + use polimec_common_test_utils::get_mock_jwt; #[test] fn project_id_stays_the_same() { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); let mut project_metadata = default_project_metadata(ISSUER_1); inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt(ISSUER_1, InvestorType::Institutional, generate_did_from_account(ISSUER_1)); + let jwt = get_mock_jwt_with_cid( + ISSUER_1, + InvestorType::Institutional, + generate_did_from_account(ISSUER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1); project_metadata.minimum_price = PriceOf::::from_float(15.0); @@ -544,7 +601,12 @@ mod edit_project_extrinsic { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); let project_metadata = default_project_metadata(ISSUER_1); inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt(ISSUER_1, InvestorType::Institutional, generate_did_from_account(ISSUER_1)); + let jwt = get_mock_jwt_with_cid( + ISSUER_1, + InvestorType::Institutional, + generate_did_from_account(ISSUER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1); let mut new_metadata_1 = project_metadata.clone(); let new_policy_hash = ipfs_hash(); @@ -615,7 +677,7 @@ mod edit_project_extrinsic { } #[test] - fn adding_offchain_hash() { + fn adding_project_policy() { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); let mut project_metadata = default_project_metadata(ISSUER_1); project_metadata.policy_ipfs_cid = None; @@ -639,7 +701,12 @@ mod edit_project_extrinsic { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); let project_metadata = default_project_metadata(ISSUER_1); inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt(ISSUER_1, InvestorType::Institutional, generate_did_from_account(ISSUER_1)); + let jwt = get_mock_jwt_with_cid( + ISSUER_1, + InvestorType::Institutional, + generate_did_from_account(ISSUER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1); let mut new_metadata = project_metadata.clone(); new_metadata.total_allocation_size = 100_000 * ASSET_UNIT; @@ -679,8 +746,18 @@ mod edit_project_extrinsic { inst.mint_plmc_to(vec![issuer_1_mint.clone(), issuer_2_mint.clone()]); - let jwt_1 = get_mock_jwt(ISSUER_1, InvestorType::Institutional, generate_did_from_account(ISSUER_1)); - let jwt_2 = get_mock_jwt(ISSUER_2, InvestorType::Institutional, generate_did_from_account(ISSUER_2)); + let jwt_1 = get_mock_jwt_with_cid( + ISSUER_1, + InvestorType::Institutional, + generate_did_from_account(ISSUER_1), + project_metadata_1.clone().policy_ipfs_cid.unwrap(), + ); + let jwt_2 = get_mock_jwt_with_cid( + ISSUER_2, + InvestorType::Institutional, + generate_did_from_account(ISSUER_2), + project_metadata_2.clone().policy_ipfs_cid.unwrap(), + ); let project_id_1 = inst.create_new_project(project_metadata_1.clone(), ISSUER_1); let project_id_2 = inst.create_new_project(project_metadata_2.clone(), ISSUER_2); @@ -712,7 +789,12 @@ mod edit_project_extrinsic { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); let project_metadata = default_project_metadata(ISSUER_1); inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt(ISSUER_1, InvestorType::Institutional, generate_did_from_account(ISSUER_1)); + let jwt = get_mock_jwt_with_cid( + ISSUER_1, + InvestorType::Institutional, + generate_did_from_account(ISSUER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1); inst.start_evaluation(project_id, ISSUER_1).unwrap(); inst.execute(|| { @@ -733,7 +815,12 @@ mod edit_project_extrinsic { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); let project_metadata = default_project_metadata(ISSUER_1); inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt(ISSUER_1, InvestorType::Retail, generate_did_from_account(ISSUER_1)); + let jwt = get_mock_jwt_with_cid( + ISSUER_1, + InvestorType::Retail, + generate_did_from_account(ISSUER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1); @@ -749,7 +836,12 @@ mod edit_project_extrinsic { ); }); - let jwt = get_mock_jwt(ISSUER_1, InvestorType::Professional, generate_did_from_account(ISSUER_1)); + let jwt = get_mock_jwt_with_cid( + ISSUER_1, + InvestorType::Professional, + generate_did_from_account(ISSUER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); inst.execute(|| { assert_noop!( crate::Pallet::::edit_project( @@ -778,7 +870,12 @@ mod remove_project_extrinsic { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); let project_metadata = default_project_metadata(ISSUER_1); inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt(ISSUER_1, InvestorType::Institutional, generate_did_from_account(ISSUER_1)); + let jwt = get_mock_jwt_with_cid( + ISSUER_1, + InvestorType::Institutional, + generate_did_from_account(ISSUER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1); assert_ok!(inst.execute(|| crate::Pallet::::remove_project( RuntimeOrigin::signed(ISSUER_1), @@ -800,7 +897,12 @@ mod remove_project_extrinsic { let ed = MockInstantiator::get_ed(); let issuer_mint: UserToPLMCBalance = (ISSUER_1, ed * 2).into(); // Create a first project - let jwt = get_mock_jwt(ISSUER_1, InvestorType::Institutional, generate_did_from_account(ISSUER_1)); + let jwt = get_mock_jwt_with_cid( + ISSUER_1, + InvestorType::Institutional, + generate_did_from_account(ISSUER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); inst.mint_plmc_to(vec![issuer_mint.clone()]); inst.execute(|| { assert_ok!(Pallet::::create_project( @@ -848,7 +950,12 @@ mod remove_project_extrinsic { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); let project_metadata = default_project_metadata(ISSUER_1); inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt(ISSUER_1, InvestorType::Professional, generate_did_from_account(ISSUER_1)); + let jwt = get_mock_jwt_with_cid( + ISSUER_1, + InvestorType::Professional, + generate_did_from_account(ISSUER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1); inst.execute(|| { assert_noop!( @@ -867,7 +974,12 @@ mod remove_project_extrinsic { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); let project_metadata = default_project_metadata(ISSUER_1); inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt(ISSUER_2, InvestorType::Institutional, generate_did_from_account(ISSUER_2)); + let jwt = get_mock_jwt_with_cid( + ISSUER_2, + InvestorType::Institutional, + generate_did_from_account(ISSUER_2), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1); @@ -888,7 +1000,12 @@ mod remove_project_extrinsic { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); let project_metadata = default_project_metadata(ISSUER_1); inst.mint_plmc_to(default_plmc_balances()); - let jwt = get_mock_jwt(ISSUER_1, InvestorType::Institutional, generate_did_from_account(ISSUER_1)); + let jwt = get_mock_jwt_with_cid( + ISSUER_1, + InvestorType::Institutional, + generate_did_from_account(ISSUER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1); inst.start_evaluation(project_id, ISSUER_1).unwrap(); inst.execute(|| { diff --git a/pallets/funding/src/tests/2_evaluation.rs b/pallets/funding/src/tests/2_evaluation.rs index db563a1cd..f9121a2f3 100644 --- a/pallets/funding/src/tests/2_evaluation.rs +++ b/pallets/funding/src/tests/2_evaluation.rs @@ -140,8 +140,13 @@ mod start_evaluation_extrinsic { let issuer = ISSUER_1; let project_metadata = default_project_metadata(issuer); - let project_id = inst.create_new_project(project_metadata, issuer); - let jwt = get_mock_jwt(issuer, InvestorType::Institutional, generate_did_from_account(issuer)); + let project_id = inst.create_new_project(project_metadata.clone(), issuer); + let jwt = get_mock_jwt_with_cid( + issuer, + InvestorType::Institutional, + generate_did_from_account(issuer), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::Application); assert_ok!(inst.execute(|| PolimecFunding::start_evaluation( RuntimeOrigin::signed(issuer), @@ -159,7 +164,12 @@ mod start_evaluation_extrinsic { let project_metadata = default_project_metadata(issuer); let project_id = inst.create_new_project(project_metadata.clone(), issuer); - let jwt = get_mock_jwt(issuer, InvestorType::Institutional, issuer_did.clone()); + let jwt = get_mock_jwt_with_cid( + issuer, + InvestorType::Institutional, + issuer_did.clone(), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); let expected_details = ProjectDetailsOf:: { issuer_account: ISSUER_1, issuer_did, @@ -210,6 +220,7 @@ mod start_evaluation_extrinsic { #[cfg(test)] mod failure { use super::*; + use polimec_common_test_utils::get_mock_jwt; #[test] fn non_institutional_jwt() { @@ -217,15 +228,20 @@ mod start_evaluation_extrinsic { let issuer = ISSUER_1; let project_metadata = default_project_metadata(issuer); - let project_id = inst.create_new_project(project_metadata, issuer); + let project_id = inst.create_new_project(project_metadata.clone(), issuer); assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::Application); inst.execute(|| { assert_noop!( PolimecFunding::start_evaluation( RuntimeOrigin::signed(issuer), - get_mock_jwt(issuer, InvestorType::Professional, generate_did_from_account(issuer)), - project_id + get_mock_jwt_with_cid( + issuer, + InvestorType::Professional, + generate_did_from_account(issuer), + project_metadata.clone().policy_ipfs_cid.unwrap() + ), + project_id, ), Error::::WrongInvestorType ); @@ -235,8 +251,13 @@ mod start_evaluation_extrinsic { assert_noop!( PolimecFunding::start_evaluation( RuntimeOrigin::signed(issuer), - get_mock_jwt(issuer, InvestorType::Retail, generate_did_from_account(issuer)), - project_id + get_mock_jwt_with_cid( + issuer, + InvestorType::Retail, + generate_did_from_account(issuer), + project_metadata.clone().policy_ipfs_cid.unwrap() + ), + project_id, ), Error::::WrongInvestorType ); @@ -249,8 +270,13 @@ mod start_evaluation_extrinsic { let issuer = ISSUER_1; let project_metadata = default_project_metadata(issuer); - let project_id = inst.create_new_project(project_metadata, issuer); - let jwt = get_mock_jwt(issuer, InvestorType::Institutional, generate_did_from_account(issuer)); + let project_id = inst.create_new_project(project_metadata.clone(), issuer); + let jwt = get_mock_jwt_with_cid( + issuer, + InvestorType::Institutional, + generate_did_from_account(issuer), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::Application); assert_ok!(inst.execute(|| PolimecFunding::start_evaluation( RuntimeOrigin::signed(issuer), @@ -274,7 +300,7 @@ mod start_evaluation_extrinsic { let mut project_metadata = default_project_metadata(issuer); project_metadata.policy_ipfs_cid = None; - let project_id = inst.create_new_project(project_metadata, issuer); + let project_id = inst.create_new_project(project_metadata.clone(), issuer); let jwt = get_mock_jwt(issuer, InvestorType::Institutional, generate_did_from_account(issuer)); assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::Application); inst.execute(|| { @@ -290,8 +316,13 @@ mod start_evaluation_extrinsic { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); let project_metadata = default_project_metadata(ISSUER_1); - let project_id = inst.create_new_project(project_metadata, ISSUER_1); - let jwt = get_mock_jwt(ISSUER_1, InvestorType::Institutional, generate_did_from_account(ISSUER_1)); + let project_id = inst.create_new_project(project_metadata.clone(), ISSUER_1); + let jwt = get_mock_jwt_with_cid( + ISSUER_1, + InvestorType::Institutional, + generate_did_from_account(ISSUER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); assert_eq!(inst.get_project_details(project_id).status, ProjectStatus::Application); assert_ok!(inst.execute(|| PolimecFunding::start_evaluation( RuntimeOrigin::signed(ISSUER_1), @@ -304,8 +335,13 @@ mod start_evaluation_extrinsic { assert_noop!( PolimecFunding::start_evaluation( RuntimeOrigin::signed(ISSUER_2), - get_mock_jwt(ISSUER_2, InvestorType::Institutional, generate_did_from_account(ISSUER_2)), - project_id + get_mock_jwt_with_cid( + ISSUER_2, + InvestorType::Institutional, + generate_did_from_account(ISSUER_2), + project_metadata.clone().policy_ipfs_cid.unwrap() + ), + project_id, ), Error::::IssuerError(IssuerErrorReason::NotIssuer) ); @@ -342,10 +378,11 @@ mod evaluate_extrinsic { assert_ok!(inst.execute(|| PolimecFunding::evaluate( RuntimeOrigin::signed(evaluations[0].account), - get_mock_jwt( + get_mock_jwt_with_cid( evaluations[0].account, InvestorType::Institutional, - generate_did_from_account(evaluations[0].account) + generate_did_from_account(evaluations[0].account), + project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, evaluations[0].usd_amount, @@ -353,10 +390,11 @@ mod evaluate_extrinsic { assert_ok!(inst.execute(|| PolimecFunding::evaluate( RuntimeOrigin::signed(evaluations[1].account), - get_mock_jwt( + get_mock_jwt_with_cid( evaluations[1].account, InvestorType::Professional, - generate_did_from_account(evaluations[1].account) + generate_did_from_account(evaluations[1].account), + project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, evaluations[1].usd_amount, @@ -364,10 +402,11 @@ mod evaluate_extrinsic { assert_ok!(inst.execute(|| PolimecFunding::evaluate( RuntimeOrigin::signed(evaluations[2].account), - get_mock_jwt( + get_mock_jwt_with_cid( evaluations[2].account, InvestorType::Retail, - generate_did_from_account(evaluations[2].account) + generate_did_from_account(evaluations[2].account), + project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, evaluations[2].usd_amount, @@ -394,16 +433,22 @@ mod evaluate_extrinsic { assert_ok!(inst.execute(|| PolimecFunding::evaluate( RuntimeOrigin::signed(evaluation.account), - get_mock_jwt(evaluation.account, InvestorType::Retail, generate_did_from_account(evaluation.account)), + get_mock_jwt_with_cid( + evaluation.account, + InvestorType::Retail, + generate_did_from_account(evaluation.account), + project_metadata.clone().policy_ipfs_cid.unwrap() + ), project_id, - evaluation.usd_amount, + evaluation.usd_amount ))); } #[test] fn storage_check() { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_id = inst.create_evaluating_project(default_project_metadata(ISSUER_1), ISSUER_1); + let project_metadata = default_project_metadata(ISSUER_1); + let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1); let evaluation = UserToUSDBalance::new(EVALUATOR_1, 500 * US_DOLLAR); let necessary_plmc = MockInstantiator::calculate_evaluation_plmc_spent(vec![evaluation.clone()]); let plmc_existential_deposits = necessary_plmc.accounts().existential_deposits(); @@ -416,9 +461,14 @@ mod evaluate_extrinsic { assert_ok!(inst.execute(|| PolimecFunding::evaluate( RuntimeOrigin::signed(evaluation.account), - get_mock_jwt(evaluation.account, InvestorType::Retail, generate_did_from_account(evaluation.account)), + get_mock_jwt_with_cid( + evaluation.account, + InvestorType::Retail, + generate_did_from_account(evaluation.account), + project_metadata.clone().policy_ipfs_cid.unwrap() + ), project_id, - evaluation.usd_amount, + evaluation.usd_amount ))); inst.execute(|| { @@ -455,7 +505,12 @@ mod evaluate_extrinsic { assert_noop!( PolimecFunding::evaluate( RuntimeOrigin::signed(EVALUATOR_1), - get_mock_jwt(EVALUATOR_1, InvestorType::Retail, generate_did_from_account(EVALUATOR_1)), + get_mock_jwt_with_cid( + EVALUATOR_1, + InvestorType::Retail, + generate_did_from_account(EVALUATOR_1), + project_metadata.clone().policy_ipfs_cid.unwrap() + ), project_id, 500 * US_DOLLAR, ), @@ -598,10 +653,11 @@ mod evaluate_extrinsic { assert_noop!( PolimecFunding::evaluate( RuntimeOrigin::signed(evaluation.account), - get_mock_jwt( + get_mock_jwt_with_cid( evaluation.account, InvestorType::Retail, - generate_did_from_account(evaluation.account) + generate_did_from_account(evaluation.account), + project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, evaluation.usd_amount, @@ -622,7 +678,8 @@ mod evaluate_extrinsic { project_id, 500 * US_DOLLAR, generate_did_from_account(ISSUER_1), - InvestorType::Institutional + InvestorType::Institutional, + project_metadata.clone().policy_ipfs_cid.unwrap(), )), Error::::IssuerError(IssuerErrorReason::ParticipationToOwnProject) ); @@ -645,10 +702,11 @@ mod evaluate_extrinsic { inst.execute(|| { assert_ok!(PolimecFunding::evaluate( RuntimeOrigin::signed(evaluation.account), - get_mock_jwt( + get_mock_jwt_with_cid( evaluation.account, InvestorType::Retail, - generate_did_from_account(evaluation.account) + generate_did_from_account(evaluation.account), + project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, evaluation.usd_amount, @@ -659,10 +717,11 @@ mod evaluate_extrinsic { assert_noop!( PolimecFunding::evaluate( RuntimeOrigin::signed(evaluation.account), - get_mock_jwt( + get_mock_jwt_with_cid( evaluation.account, InvestorType::Retail, - generate_did_from_account(evaluation.account) + generate_did_from_account(evaluation.account), + project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, evaluation.usd_amount, @@ -679,7 +738,12 @@ mod evaluate_extrinsic { let project_metadata = default_project_metadata(issuer); let project_id = inst.create_evaluating_project(project_metadata.clone(), issuer); let evaluator = EVALUATOR_1; - let jwt = get_mock_jwt(evaluator, InvestorType::Retail, generate_did_from_account(evaluator)); + let jwt = get_mock_jwt_with_cid( + evaluator, + InvestorType::Retail, + generate_did_from_account(evaluator), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); inst.mint_plmc_to(vec![(evaluator.clone(), 2000 * PLMC).into()]); diff --git a/pallets/funding/src/tests/3_auction.rs b/pallets/funding/src/tests/3_auction.rs index 8e3ec7058..4e3f0c24d 100644 --- a/pallets/funding/src/tests/3_auction.rs +++ b/pallets/funding/src/tests/3_auction.rs @@ -510,7 +510,8 @@ mod round_flow { #[test] fn contribute_does_not_work() { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_id = inst.create_evaluating_project(default_project_metadata(ISSUER_1), ISSUER_1); + let project_metadata = default_project_metadata(ISSUER_1); + let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1); let did = generate_did_from_account(ISSUER_1); let investor_type = InvestorType::Retail; inst.execute(|| { @@ -522,7 +523,8 @@ mod round_flow { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, did, - investor_type + investor_type, + project_metadata.clone().policy_ipfs_cid.unwrap(), ), Error::::ProjectRoundError(RoundError::IncorrectRound) ); @@ -852,7 +854,13 @@ mod bid_extrinsic { investor_type: InvestorType, u8_multiplier: u8, ) -> DispatchResultWithPostInfo { - let jwt = get_mock_jwt(bidder.clone(), investor_type, generate_did_from_account(BIDDER_1)); + let project_policy = inst.get_project_metadata(project_id).policy_ipfs_cid.unwrap(); + let jwt = get_mock_jwt_with_cid( + bidder.clone(), + investor_type, + generate_did_from_account(BIDDER_1), + project_policy, + ); let amount = 1000 * ASSET_UNIT; let multiplier = Multiplier::force_new(u8_multiplier); @@ -1057,9 +1065,11 @@ mod bid_extrinsic { #[test] fn cannot_bid_before_auction_round() { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let _ = inst.create_evaluating_project(default_project_metadata(ISSUER_1), ISSUER_1); + let project_metadata = default_project_metadata(ISSUER_1); + let _ = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1); let did = generate_did_from_account(BIDDER_2); let investor_type = InvestorType::Institutional; + inst.execute(|| { assert_noop!( PolimecFunding::do_bid( @@ -1069,7 +1079,8 @@ mod bid_extrinsic { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, did, - investor_type + investor_type, + project_metadata.clone().policy_ipfs_cid.unwrap(), ), Error::::ProjectRoundError(RoundError::IncorrectRound) ); @@ -1125,6 +1136,7 @@ mod bid_extrinsic { project_metadata.clone(), Some(current_bucket), ); + let plmc_existential_deposits = plmc_for_failing_bid.accounts().existential_deposits(); let usdt_for_bidding = MockInstantiator::calculate_auction_funding_asset_charged_from_all_bids_made_or_with_bucket( @@ -1141,10 +1153,11 @@ mod bid_extrinsic { assert_noop!( PolimecFunding::bid( RuntimeOrigin::signed(failing_bid.bidder), - get_mock_jwt( + get_mock_jwt_with_cid( failing_bid.bidder, InvestorType::Professional, - generate_did_from_account(failing_bid.bidder) + generate_did_from_account(failing_bid.bidder), + project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, failing_bid.amount, @@ -1159,10 +1172,11 @@ mod bid_extrinsic { inst.execute(|| { assert_ok!(PolimecFunding::bid( RuntimeOrigin::signed(failing_bid.bidder), - get_mock_jwt( + get_mock_jwt_with_cid( failing_bid.bidder, InvestorType::Professional, - generate_did_from_account(failing_bid.bidder) + generate_did_from_account(failing_bid.bidder), + project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, remaining_ct, @@ -1174,10 +1188,11 @@ mod bid_extrinsic { assert_noop!( PolimecFunding::bid( RuntimeOrigin::signed(failing_bid.bidder), - get_mock_jwt( + get_mock_jwt_with_cid( failing_bid.bidder, InvestorType::Professional, - generate_did_from_account(failing_bid.bidder) + generate_did_from_account(failing_bid.bidder), + project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, 5000 * ASSET_UNIT, @@ -1252,10 +1267,11 @@ mod bid_extrinsic { assert_noop!( PolimecFunding::bid( RuntimeOrigin::signed(failing_bid.bidder), - get_mock_jwt( + get_mock_jwt_with_cid( failing_bid.bidder, InvestorType::Professional, - generate_did_from_account(failing_bid.bidder) + generate_did_from_account(failing_bid.bidder), + project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, failing_bid.amount, @@ -1270,10 +1286,11 @@ mod bid_extrinsic { inst.execute(|| { assert_ok!(PolimecFunding::bid( RuntimeOrigin::signed(failing_bid.bidder), - get_mock_jwt( + get_mock_jwt_with_cid( failing_bid.bidder, InvestorType::Professional, - generate_did_from_account(failing_bid.bidder) + generate_did_from_account(failing_bid.bidder), + project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, remaining_ct, @@ -1285,10 +1302,11 @@ mod bid_extrinsic { assert_noop!( PolimecFunding::bid( RuntimeOrigin::signed(failing_bid.bidder), - get_mock_jwt( + get_mock_jwt_with_cid( failing_bid.bidder, InvestorType::Professional, - generate_did_from_account(failing_bid.bidder) + generate_did_from_account(failing_bid.bidder), + project_metadata.clone().policy_ipfs_cid.unwrap() ), project_id, 5000 * ASSET_UNIT, @@ -1332,7 +1350,8 @@ mod bid_extrinsic { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, generate_did_from_account(BIDDER_1), - InvestorType::Professional + InvestorType::Professional, + project_metadata.clone().policy_ipfs_cid.unwrap(), ), Error::::ParticipationFailed(ParticipationError::TooLow) ); @@ -1347,7 +1366,8 @@ mod bid_extrinsic { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, generate_did_from_account(BIDDER_1), - InvestorType::Institutional + InvestorType::Institutional, + project_metadata.clone().policy_ipfs_cid.unwrap(), ), Error::::ParticipationFailed(ParticipationError::TooLow) ); @@ -1401,7 +1421,8 @@ mod bid_extrinsic { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, generate_did_from_account(BIDDER_1), - InvestorType::Professional + InvestorType::Professional, + project_metadata.clone().policy_ipfs_cid.unwrap(), )); }); let smallest_ct_amount_at_20k_usd = PriceOf::::from_float(1.1) @@ -1419,7 +1440,8 @@ mod bid_extrinsic { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, generate_did_from_account(BIDDER_1), - InvestorType::Institutional + InvestorType::Institutional, + project_metadata.clone().policy_ipfs_cid.unwrap(), )); }); } @@ -1457,9 +1479,18 @@ mod bid_extrinsic { (BIDDER_4, 500_000 * US_DOLLAR).into(), ]); - let bidder_1_jwt = get_mock_jwt(BIDDER_1, InvestorType::Professional, generate_did_from_account(BIDDER_1)); - let bidder_2_jwt_same_did = - get_mock_jwt(BIDDER_2, InvestorType::Professional, generate_did_from_account(BIDDER_1)); + let bidder_1_jwt = get_mock_jwt_with_cid( + BIDDER_1, + InvestorType::Professional, + generate_did_from_account(BIDDER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); + let bidder_2_jwt_same_did = get_mock_jwt_with_cid( + BIDDER_2, + InvestorType::Professional, + generate_did_from_account(BIDDER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); // total bids with same DID above 10k CT (100k USD) should fail for professionals inst.execute(|| { assert_ok!(Pallet::::bid( @@ -1496,9 +1527,18 @@ mod bid_extrinsic { )); }); - let bidder_3_jwt = get_mock_jwt(BIDDER_3, InvestorType::Institutional, generate_did_from_account(BIDDER_3)); - let bidder_4_jwt_same_did = - get_mock_jwt(BIDDER_4, InvestorType::Institutional, generate_did_from_account(BIDDER_3)); + let bidder_3_jwt = get_mock_jwt_with_cid( + BIDDER_3, + InvestorType::Institutional, + generate_did_from_account(BIDDER_3), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); + let bidder_4_jwt_same_did = get_mock_jwt_with_cid( + BIDDER_4, + InvestorType::Institutional, + generate_did_from_account(BIDDER_3), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); // total bids with same DID above 50k CT (500k USD) should fail for institutionals inst.execute(|| { assert_ok!(Pallet::::bid( @@ -1549,7 +1589,8 @@ mod bid_extrinsic { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, generate_did_from_account(ISSUER_1), - InvestorType::Institutional + InvestorType::Institutional, + project_metadata.clone().policy_ipfs_cid.unwrap(), )), Error::::IssuerError(IssuerErrorReason::ParticipationToOwnProject) ); @@ -1558,8 +1599,8 @@ mod bid_extrinsic { #[test] fn bid_with_asset_not_accepted() { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); - let project_id = - inst.create_auctioning_project(default_project_metadata(ISSUER_1), ISSUER_1, default_evaluations()); + let project_metadata = default_project_metadata(ISSUER_1); + let project_id = inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, default_evaluations()); let bids = vec![BidParams::::new(BIDDER_1, 10_000, 1u8, AcceptedFundingAsset::USDC)]; let did = generate_did_from_account(bids[0].bidder); @@ -1574,6 +1615,7 @@ mod bid_extrinsic { bids[0].asset, did, investor_type, + project_metadata.clone().policy_ipfs_cid.unwrap(), ) }); frame_support::assert_err!( diff --git a/pallets/funding/src/tests/4_community.rs b/pallets/funding/src/tests/4_community.rs index d581cf62b..843f19ce2 100644 --- a/pallets/funding/src/tests/4_community.rs +++ b/pallets/funding/src/tests/4_community.rs @@ -202,7 +202,12 @@ mod community_contribute_extrinsic { assert_noop!( Pallet::::community_contribute( RuntimeOrigin::signed(BOB), - get_mock_jwt(BOB, InvestorType::Retail, generate_did_from_account(BOB)), + get_mock_jwt_with_cid( + BOB, + InvestorType::Retail, + generate_did_from_account(BOB), + project_metadata.clone().policy_ipfs_cid.unwrap() + ), project_id, usable_ct + slashable_ct, 1u8.try_into().unwrap(), @@ -221,7 +226,12 @@ mod community_contribute_extrinsic { inst.execute(|| { assert_ok!(Pallet::::community_contribute( RuntimeOrigin::signed(BOB), - get_mock_jwt(BOB, InvestorType::Retail, generate_did_from_account(BOB)), + get_mock_jwt_with_cid( + BOB, + InvestorType::Retail, + generate_did_from_account(BOB), + project_metadata.clone().policy_ipfs_cid.unwrap() + ), project_id, usable_ct / 2, 1u8.try_into().unwrap(), @@ -236,7 +246,12 @@ mod community_contribute_extrinsic { inst.execute(|| { assert_ok!(Pallet::::community_contribute( RuntimeOrigin::signed(CARL), - get_mock_jwt(CARL, InvestorType::Retail, generate_did_from_account(CARL)), + get_mock_jwt_with_cid( + CARL, + InvestorType::Retail, + generate_did_from_account(CARL), + project_metadata.clone().policy_ipfs_cid.unwrap() + ), project_id, usable_ct, 1u8.try_into().unwrap(), @@ -307,7 +322,12 @@ mod community_contribute_extrinsic { inst.execute(|| { assert_ok!(Pallet::::community_contribute( RuntimeOrigin::signed(bob), - get_mock_jwt(bob, InvestorType::Retail, generate_did_from_account(bob)), + get_mock_jwt_with_cid( + bob, + InvestorType::Retail, + generate_did_from_account(bob), + project_metadata.clone().policy_ipfs_cid.unwrap() + ), project_id, usable_ct, 1u8.try_into().unwrap(), @@ -371,7 +391,13 @@ mod community_contribute_extrinsic { investor_type: InvestorType, u8_multiplier: u8, ) -> DispatchResultWithPostInfo { - let jwt = get_mock_jwt(contributor.clone(), investor_type, generate_did_from_account(contributor)); + let project_policy = inst.get_project_metadata(project_id).policy_ipfs_cid.unwrap(); + let jwt = get_mock_jwt_with_cid( + contributor.clone(), + investor_type, + generate_did_from_account(contributor), + project_policy, + ); let amount = 1000 * ASSET_UNIT; let multiplier = Multiplier::force_new(u8_multiplier); let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); @@ -622,7 +648,12 @@ mod community_contribute_extrinsic { inst.execute(|| { assert_ok!(Pallet::::community_contribute( RuntimeOrigin::signed(account), - get_mock_jwt(account, investor_type, generate_did_from_account(did_acc)), + get_mock_jwt_with_cid( + account, + investor_type, + generate_did_from_account(did_acc), + project_metadata.clone().policy_ipfs_cid.unwrap() + ), project_id, 10 * ASSET_UNIT, 1u8.try_into().unwrap(), @@ -761,7 +792,8 @@ mod community_contribute_extrinsic { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, generate_did_from_account(ISSUER_1), - InvestorType::Institutional + InvestorType::Institutional, + project_metadata.clone().policy_ipfs_cid.unwrap(), )), Error::::IssuerError(IssuerErrorReason::ParticipationToOwnProject) ); @@ -792,7 +824,12 @@ mod community_contribute_extrinsic { assert_noop!( Pallet::::community_contribute( RuntimeOrigin::signed(account), - get_mock_jwt(account, investor_type, generate_did_from_account(did_acc)), + get_mock_jwt_with_cid( + account, + investor_type, + generate_did_from_account(did_acc), + project_metadata.clone().policy_ipfs_cid.unwrap() + ), project_id, 10 * ASSET_UNIT, 1u8.try_into().unwrap(), @@ -860,7 +897,12 @@ mod community_contribute_extrinsic { ]); // contribution below 1 CT (10 USD) should fail for retail - let jwt = get_mock_jwt(BUYER_1, InvestorType::Retail, generate_did_from_account(BUYER_1)); + let jwt = get_mock_jwt_with_cid( + BUYER_1, + InvestorType::Retail, + generate_did_from_account(BUYER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); inst.execute(|| { assert_noop!( Pallet::::community_contribute( @@ -875,7 +917,12 @@ mod community_contribute_extrinsic { ); }); // contribution below 10_000 CT (100k USD) should fail for professionals - let jwt = get_mock_jwt(BUYER_2, InvestorType::Professional, generate_did_from_account(BUYER_2)); + let jwt = get_mock_jwt_with_cid( + BUYER_2, + InvestorType::Professional, + generate_did_from_account(BUYER_2), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); inst.execute(|| { assert_noop!( Pallet::::community_contribute( @@ -891,7 +938,12 @@ mod community_contribute_extrinsic { }); // contribution below 20_000 CT (200k USD) should fail for institutionals - let jwt = get_mock_jwt(BUYER_3, InvestorType::Professional, generate_did_from_account(BUYER_3)); + let jwt = get_mock_jwt_with_cid( + BUYER_3, + InvestorType::Professional, + generate_did_from_account(BUYER_3), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); inst.execute(|| { assert_noop!( Pallet::::community_contribute( @@ -943,8 +995,18 @@ mod community_contribute_extrinsic { (BUYER_6, 500_000 * US_DOLLAR).into(), ]); - let buyer_1_jwt = get_mock_jwt(BUYER_1, InvestorType::Retail, generate_did_from_account(BUYER_1)); - let buyer_2_jwt_same_did = get_mock_jwt(BUYER_2, InvestorType::Retail, generate_did_from_account(BUYER_1)); + let buyer_1_jwt = get_mock_jwt_with_cid( + BUYER_1, + InvestorType::Retail, + generate_did_from_account(BUYER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); + let buyer_2_jwt_same_did = get_mock_jwt_with_cid( + BUYER_2, + InvestorType::Retail, + generate_did_from_account(BUYER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); // total contributions with same DID above 10k CT (100k USD) should fail for retail inst.execute(|| { assert_ok!(Pallet::::community_contribute( @@ -981,9 +1043,18 @@ mod community_contribute_extrinsic { )); }); - let buyer_3_jwt = get_mock_jwt(BUYER_3, InvestorType::Professional, generate_did_from_account(BUYER_3)); - let buyer_4_jwt_same_did = - get_mock_jwt(BUYER_4, InvestorType::Professional, generate_did_from_account(BUYER_3)); + let buyer_3_jwt = get_mock_jwt_with_cid( + BUYER_3, + InvestorType::Professional, + generate_did_from_account(BUYER_3), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); + let buyer_4_jwt_same_did = get_mock_jwt_with_cid( + BUYER_4, + InvestorType::Professional, + generate_did_from_account(BUYER_3), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); // total contributions with same DID above 2k CT (20k USD) should fail for professionals inst.execute(|| { assert_ok!(Pallet::::community_contribute( @@ -1020,9 +1091,18 @@ mod community_contribute_extrinsic { )); }); - let buyer_5_jwt = get_mock_jwt(BUYER_5, InvestorType::Institutional, generate_did_from_account(BUYER_5)); - let buyer_6_jwt_same_did = - get_mock_jwt(BUYER_6, InvestorType::Institutional, generate_did_from_account(BUYER_5)); + let buyer_5_jwt = get_mock_jwt_with_cid( + BUYER_5, + InvestorType::Institutional, + generate_did_from_account(BUYER_5), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); + let buyer_6_jwt_same_did = get_mock_jwt_with_cid( + BUYER_6, + InvestorType::Institutional, + generate_did_from_account(BUYER_5), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); // total contributions with same DID above 5k CT (50 USD) should fail for institutionals inst.execute(|| { assert_ok!(Pallet::::community_contribute( @@ -1063,14 +1143,20 @@ mod community_contribute_extrinsic { #[test] fn insufficient_funds() { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); + let project_metadata = default_project_metadata(ISSUER_1); let project_id = inst.create_community_contributing_project( - default_project_metadata(ISSUER_1), + project_metadata.clone(), ISSUER_1, default_evaluations(), default_bids(), ); - let jwt = get_mock_jwt(BUYER_1, InvestorType::Retail, generate_did_from_account(BUYER_1)); + let jwt = get_mock_jwt_with_cid( + BUYER_1, + InvestorType::Retail, + generate_did_from_account(BUYER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); let contribution = ContributionParams::new(BUYER_1, 1_000 * ASSET_UNIT, 1u8, AcceptedFundingAsset::USDT); let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); @@ -1161,11 +1247,17 @@ mod community_contribute_extrinsic { let projects = vec![created_project, evaluating_project, auctioning_project, remaining_project, finished_project]; for project in projects { + let project_policy = inst.get_project_metadata(project).policy_ipfs_cid.unwrap(); inst.execute(|| { assert_noop!( PolimecFunding::community_contribute( RuntimeOrigin::signed(BUYER_1), - get_mock_jwt(BUYER_1, InvestorType::Retail, generate_did_from_account(BUYER_1)), + get_mock_jwt_with_cid( + BUYER_1, + InvestorType::Retail, + generate_did_from_account(BUYER_1), + project_policy + ), project, 1000 * ASSET_UNIT, 1u8.try_into().unwrap(), diff --git a/pallets/funding/src/tests/5_remainder.rs b/pallets/funding/src/tests/5_remainder.rs index 9d17c3ffe..e7e9aa669 100644 --- a/pallets/funding/src/tests/5_remainder.rs +++ b/pallets/funding/src/tests/5_remainder.rs @@ -378,7 +378,12 @@ mod remaining_contribute_extrinsic { let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); // Professional bids: 0x multiplier should fail - let jwt = get_mock_jwt(BUYER_1, InvestorType::Professional, generate_did_from_account(BUYER_1)); + let jwt = get_mock_jwt_with_cid( + BUYER_1, + InvestorType::Professional, + generate_did_from_account(BUYER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); inst.execute(|| { assert_noop!( Pallet::::remaining_contribute( @@ -394,7 +399,12 @@ mod remaining_contribute_extrinsic { }); // Professional bids: 1 - 10x multiplier should work for multiplier in 1..=10u8 { - let jwt = get_mock_jwt(BUYER_1, InvestorType::Professional, generate_did_from_account(BUYER_1)); + let jwt = get_mock_jwt_with_cid( + BUYER_1, + InvestorType::Professional, + generate_did_from_account(BUYER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); let bidder_plmc = MockInstantiator::calculate_contributed_plmc_spent( vec![(BUYER_1, 1_000 * ASSET_UNIT, Multiplier::force_new(multiplier)).into()], wap, @@ -418,7 +428,12 @@ mod remaining_contribute_extrinsic { } // Professional bids: >=11x multiplier should fail for multiplier in 11..=50u8 { - let jwt = get_mock_jwt(BUYER_1, InvestorType::Professional, generate_did_from_account(BUYER_1)); + let jwt = get_mock_jwt_with_cid( + BUYER_1, + InvestorType::Professional, + generate_did_from_account(BUYER_1), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); let bidder_plmc = MockInstantiator::calculate_contributed_plmc_spent( vec![(BUYER_1, 1_000 * ASSET_UNIT, Multiplier::force_new(multiplier)).into()], wap, @@ -447,7 +462,12 @@ mod remaining_contribute_extrinsic { } // Institutional bids: 0x multiplier should fail - let jwt = get_mock_jwt(BUYER_2, InvestorType::Institutional, generate_did_from_account(BUYER_2)); + let jwt = get_mock_jwt_with_cid( + BUYER_2, + InvestorType::Institutional, + generate_did_from_account(BUYER_2), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); inst.execute(|| { assert_noop!( Pallet::::remaining_contribute( @@ -463,7 +483,12 @@ mod remaining_contribute_extrinsic { }); // Institutional bids: 1 - 25x multiplier should work for multiplier in 1..=25u8 { - let jwt = get_mock_jwt(BUYER_2, InvestorType::Institutional, generate_did_from_account(BUYER_2)); + let jwt = get_mock_jwt_with_cid( + BUYER_2, + InvestorType::Institutional, + generate_did_from_account(BUYER_2), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); let bidder_plmc = MockInstantiator::calculate_contributed_plmc_spent( vec![(BUYER_2, 1_000 * ASSET_UNIT, Multiplier::force_new(multiplier)).into()], wap, @@ -487,7 +512,12 @@ mod remaining_contribute_extrinsic { } // Institutional bids: >=26x multiplier should fail for multiplier in 26..=50u8 { - let jwt = get_mock_jwt(BUYER_2, InvestorType::Institutional, generate_did_from_account(BUYER_2)); + let jwt = get_mock_jwt_with_cid( + BUYER_2, + InvestorType::Institutional, + generate_did_from_account(BUYER_2), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); let bidder_plmc = MockInstantiator::calculate_contributed_plmc_spent( vec![(BUYER_2, 1_000 * ASSET_UNIT, Multiplier::force_new(multiplier)).into()], wap, @@ -534,7 +564,13 @@ mod remaining_contribute_extrinsic { ) }; let contribute = |inst: &mut MockInstantiator, project_id, multiplier| { - let jwt = get_mock_jwt(BUYER_1, InvestorType::Retail, generate_did_from_account(BUYER_1)); + let project_policy = inst.get_project_metadata(project_id).policy_ipfs_cid.unwrap(); + let jwt = get_mock_jwt_with_cid( + BUYER_1, + InvestorType::Retail, + generate_did_from_account(BUYER_1), + project_policy, + ); let wap = inst.get_project_details(project_id).weighted_average_price.unwrap(); let contributor_plmc = MockInstantiator::calculate_contributed_plmc_spent( vec![(BUYER_1, 1_000 * ASSET_UNIT, Multiplier::force_new(multiplier)).into()], @@ -582,11 +618,17 @@ mod remaining_contribute_extrinsic { // 0x multiplier should fail // Professional bids: 0x multiplier should fail + let project_policy = inst.get_project_metadata(project_id).policy_ipfs_cid.unwrap(); inst.execute(|| { assert_noop!( Pallet::::remaining_contribute( RuntimeOrigin::signed(BUYER_1), - get_mock_jwt(BUYER_1, InvestorType::Retail, generate_did_from_account(BUYER_1)), + get_mock_jwt_with_cid( + BUYER_1, + InvestorType::Retail, + generate_did_from_account(BUYER_1), + project_policy + ), project_id, 1000 * ASSET_UNIT, Multiplier::force_new(0), @@ -637,7 +679,8 @@ mod remaining_contribute_extrinsic { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, generate_did_from_account(ISSUER_1), - InvestorType::Institutional + InvestorType::Institutional, + project_metadata.clone().policy_ipfs_cid.unwrap(), )), Error::::IssuerError(IssuerErrorReason::ParticipationToOwnProject) ); @@ -689,7 +732,12 @@ mod remaining_contribute_extrinsic { ]); // contribution below 1 CT (10 USD) should fail for retail - let jwt = get_mock_jwt(BUYER_4, InvestorType::Retail, generate_did_from_account(BUYER_4)); + let jwt = get_mock_jwt_with_cid( + BUYER_4, + InvestorType::Retail, + generate_did_from_account(BUYER_4), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); inst.execute(|| { assert_noop!( Pallet::::remaining_contribute( @@ -704,7 +752,12 @@ mod remaining_contribute_extrinsic { ); }); // contribution below 10_000 CT (100k USD) should fail for professionals - let jwt = get_mock_jwt(BUYER_5, InvestorType::Professional, generate_did_from_account(BUYER_5)); + let jwt = get_mock_jwt_with_cid( + BUYER_5, + InvestorType::Professional, + generate_did_from_account(BUYER_5), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); inst.execute(|| { assert_noop!( Pallet::::remaining_contribute( @@ -720,7 +773,12 @@ mod remaining_contribute_extrinsic { }); // contribution below 20_000 CT (200k USD) should fail for institutionals - let jwt = get_mock_jwt(BUYER_6, InvestorType::Institutional, generate_did_from_account(BUYER_6)); + let jwt = get_mock_jwt_with_cid( + BUYER_6, + InvestorType::Institutional, + generate_did_from_account(BUYER_6), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); inst.execute(|| { assert_noop!( Pallet::::remaining_contribute( @@ -796,7 +854,8 @@ mod remaining_contribute_extrinsic { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, generate_did_from_account(BUYER_4), - InvestorType::Retail + InvestorType::Retail, + project_metadata.clone().policy_ipfs_cid.unwrap(), )); }); inst.execute(|| { @@ -809,7 +868,8 @@ mod remaining_contribute_extrinsic { AcceptedFundingAsset::USDT, // note we use the same did as bidder 1, on a different account generate_did_from_account(BUYER_4), - InvestorType::Retail + InvestorType::Retail, + project_metadata.clone().policy_ipfs_cid.unwrap(), ), Error::::ParticipationFailed(ParticipationError::TooHigh) ); @@ -824,7 +884,8 @@ mod remaining_contribute_extrinsic { AcceptedFundingAsset::USDT, // note we use the same did as bidder 1, on a different account generate_did_from_account(BUYER_4), - InvestorType::Retail + InvestorType::Retail, + project_metadata.clone().policy_ipfs_cid.unwrap(), )); }); @@ -837,7 +898,8 @@ mod remaining_contribute_extrinsic { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, generate_did_from_account(BUYER_6), - InvestorType::Professional + InvestorType::Professional, + project_metadata.clone().policy_ipfs_cid.unwrap(), )); }); inst.execute(|| { @@ -850,7 +912,8 @@ mod remaining_contribute_extrinsic { AcceptedFundingAsset::USDT, // note we use the same did as bidder 1, on a different account generate_did_from_account(BUYER_6), - InvestorType::Professional + InvestorType::Professional, + project_metadata.clone().policy_ipfs_cid.unwrap(), ), Error::::ParticipationFailed(ParticipationError::TooHigh) ); @@ -865,7 +928,8 @@ mod remaining_contribute_extrinsic { AcceptedFundingAsset::USDT, // note we use the same did as bidder 1, on a different account generate_did_from_account(BUYER_6), - InvestorType::Professional + InvestorType::Professional, + project_metadata.clone().policy_ipfs_cid.unwrap(), )); }); @@ -878,7 +942,8 @@ mod remaining_contribute_extrinsic { 1u8.try_into().unwrap(), AcceptedFundingAsset::USDT, generate_did_from_account(BUYER_8), - InvestorType::Institutional + InvestorType::Institutional, + project_metadata.clone().policy_ipfs_cid.unwrap(), )); }); inst.execute(|| { @@ -891,7 +956,8 @@ mod remaining_contribute_extrinsic { AcceptedFundingAsset::USDT, // note we use the same did as bidder 3, on a different account generate_did_from_account(BUYER_8), - InvestorType::Institutional + InvestorType::Institutional, + project_metadata.clone().policy_ipfs_cid.unwrap(), ), Error::::ParticipationFailed(ParticipationError::TooHigh) ); @@ -906,7 +972,8 @@ mod remaining_contribute_extrinsic { AcceptedFundingAsset::USDT, // note we use the same did as bidder 3, on a different account generate_did_from_account(BUYER_8), - InvestorType::Institutional + InvestorType::Institutional, + project_metadata.clone().policy_ipfs_cid.unwrap(), )); }); } diff --git a/pallets/funding/src/tests/mod.rs b/pallets/funding/src/tests/mod.rs index 4c8ea3d65..ecc819b1e 100644 --- a/pallets/funding/src/tests/mod.rs +++ b/pallets/funding/src/tests/mod.rs @@ -16,7 +16,7 @@ use frame_support::{ use itertools::Itertools; use parachains_common::DAYS; use polimec_common::ReleaseSchedule; -use polimec_common_test_utils::{generate_did_from_account, get_mock_jwt}; +use polimec_common_test_utils::{generate_did_from_account, get_mock_jwt_with_cid}; use sp_arithmetic::{traits::Zero, Percent, Perquintill}; use sp_runtime::{BuildStorage, TokenError}; use sp_std::{cell::RefCell, marker::PhantomData}; @@ -252,7 +252,7 @@ pub mod defaults { pub fn bounded_symbol() -> BoundedVec> { BoundedVec::try_from("CTEST".as_bytes().to_vec()).unwrap() } - pub fn ipfs_hash() -> BoundedVec> { + pub fn ipfs_hash() -> BoundedVec> { BoundedVec::try_from(IPFS_CID.as_bytes().to_vec()).unwrap() } diff --git a/pallets/funding/src/types.rs b/pallets/funding/src/types.rs index 110b51d52..9e27c2ccf 100644 --- a/pallets/funding/src/types.rs +++ b/pallets/funding/src/types.rs @@ -696,6 +696,8 @@ pub mod inner_types { UserHasWinningBid, /// The user does not have enough funds (funding asset or PLMC) to cover the participation. NotEnoughFunds, + /// The JWT included has a wrong policy for participating in this project. Front-end should ensure this never happens. + PolicyMismatch, } /// Errors related to the project state. This can either be project info not found, or diff --git a/pallets/sandbox/src/lib.rs b/pallets/sandbox/src/lib.rs index 6f187e8ab..fbecdaaa6 100644 --- a/pallets/sandbox/src/lib.rs +++ b/pallets/sandbox/src/lib.rs @@ -51,6 +51,7 @@ pub mod pallet { asset_id: AcceptedFundingAsset, did: polimec_common::credentials::Did, investor_type: polimec_common::credentials::InvestorType, + whitelisted_policy: polimec_common::credentials::Cid, ) -> DispatchResultWithPostInfo { let retail_user = ensure_signed(origin)?; let project_id: ProjectId = project_id; @@ -74,6 +75,7 @@ pub mod pallet { ); let multiplier: MultiplierOf = 1u8.try_into().map_err(|_| Error::::ProjectNotFound)?; + // Buy tokens with the default multiplier >::do_community_contribute( &retail_user, @@ -83,6 +85,7 @@ pub mod pallet { asset_id, did, investor_type, + whitelisted_policy, ) } } diff --git a/polimec-common/test-utils/src/lib.rs b/polimec-common/test-utils/src/lib.rs index a7e4b1a45..cebad5036 100644 --- a/polimec-common/test-utils/src/lib.rs +++ b/polimec-common/test-utils/src/lib.rs @@ -164,7 +164,12 @@ mod tests { .as_ref(), ) .unwrap(); - let token = get_mock_jwt("0x1234", InvestorType::Institutional, generate_did_from_account(40u64)); + let token = get_mock_jwt_with_cid( + "0x1234", + InvestorType::Institutional, + generate_did_from_account(40u64), + project_metadata.clone().policy_ipfs_cid.unwrap(), + ); let res = Ed25519.validator::>(&verifying_key).validate(&token); assert!(res.is_ok()); } diff --git a/scripts/zombienet/native/local-testnet.toml b/scripts/zombienet/native/local-testnet.toml index 772d7dc5a..6859d661d 100644 --- a/scripts/zombienet/native/local-testnet.toml +++ b/scripts/zombienet/native/local-testnet.toml @@ -4,7 +4,7 @@ provider = "native" # Using Rococo 1.7.0 as relay. [relaychain] -default_command = "./bin/polkadot" +default_command = "/usr/local/bin/polkadot" chain = "rococo-local" [[relaychain.nodes]] From 3877df449b13a051c38f4cd300fd25db913e9e15 Mon Sep 17 00:00:00 2001 From: Juan Ignacio Rios Date: Thu, 25 Apr 2024 10:13:27 +0200 Subject: [PATCH 08/12] tests --- pallets/funding/src/tests/2_evaluation.rs | 26 +++++++++++++++++- pallets/funding/src/tests/3_auction.rs | 26 ++++++++++++++++++ pallets/funding/src/tests/4_community.rs | 31 ++++++++++++++++++++++ pallets/funding/src/tests/5_remainder.rs | 32 +++++++++++++++++++++++ 4 files changed, 114 insertions(+), 1 deletion(-) diff --git a/pallets/funding/src/tests/2_evaluation.rs b/pallets/funding/src/tests/2_evaluation.rs index f9121a2f3..c22d40907 100644 --- a/pallets/funding/src/tests/2_evaluation.rs +++ b/pallets/funding/src/tests/2_evaluation.rs @@ -294,7 +294,7 @@ mod start_evaluation_extrinsic { } #[test] - fn no_offchain_hash_provided() { + fn no_policy_provided() { let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); let issuer = ISSUER_1; let mut project_metadata = default_project_metadata(issuer); @@ -773,5 +773,29 @@ mod evaluate_extrinsic { ); }); } + + #[test] + fn wrong_policy_on_jwt() { + let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); + let project_metadata = default_project_metadata(ISSUER_1); + let project_id = inst.create_evaluating_project(project_metadata.clone(), ISSUER_1); + + inst.execute(|| { + assert_noop!( + PolimecFunding::evaluate( + RuntimeOrigin::signed(EVALUATOR_1), + get_mock_jwt_with_cid( + EVALUATOR_1, + InvestorType::Retail, + generate_did_from_account(EVALUATOR_1), + "wrong_cid".as_bytes().to_vec().try_into().unwrap() + ), + project_id, + 500 * US_DOLLAR, + ), + Error::::ParticipationFailed(ParticipationError::PolicyMismatch) + ); + }); + } } } diff --git a/pallets/funding/src/tests/3_auction.rs b/pallets/funding/src/tests/3_auction.rs index 4e3f0c24d..b930a9bc7 100644 --- a/pallets/funding/src/tests/3_auction.rs +++ b/pallets/funding/src/tests/3_auction.rs @@ -1623,5 +1623,31 @@ mod bid_extrinsic { Error::::ParticipationFailed(ParticipationError::FundingAssetNotAccepted) ); } + + #[test] + fn wrong_policy_on_jwt() { + let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); + let project_metadata = default_project_metadata(ISSUER_1); + let project_id = inst.create_auctioning_project(project_metadata.clone(), ISSUER_1, default_evaluations()); + + inst.execute(|| { + assert_noop!( + PolimecFunding::bid( + RuntimeOrigin::signed(BIDDER_1), + get_mock_jwt_with_cid( + BIDDER_1, + InvestorType::Professional, + generate_did_from_account(BIDDER_1), + "wrong_cid".as_bytes().to_vec().try_into().unwrap() + ), + project_id, + 5000 * ASSET_UNIT, + 1u8.try_into().unwrap(), + AcceptedFundingAsset::USDT + ), + Error::::ParticipationFailed(ParticipationError::PolicyMismatch) + ); + }); + } } } diff --git a/pallets/funding/src/tests/4_community.rs b/pallets/funding/src/tests/4_community.rs index 843f19ce2..17348abca 100644 --- a/pallets/funding/src/tests/4_community.rs +++ b/pallets/funding/src/tests/4_community.rs @@ -1342,5 +1342,36 @@ mod community_contribute_extrinsic { Error::::ParticipationFailed(ParticipationError::FundingAssetNotAccepted) ); } + + #[test] + fn wrong_policy_on_jwt() { + let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); + let project_metadata = default_project_metadata(ISSUER_1); + let project_id = inst.create_community_contributing_project( + project_metadata.clone(), + ISSUER_1, + default_evaluations(), + default_bids(), + ); + + inst.execute(|| { + assert_noop!( + PolimecFunding::community_contribute( + RuntimeOrigin::signed(BUYER_1), + get_mock_jwt_with_cid( + BUYER_1, + InvestorType::Retail, + generate_did_from_account(BUYER_1), + "wrong_cid".as_bytes().to_vec().try_into().unwrap() + ), + project_id, + 5000 * ASSET_UNIT, + 1u8.try_into().unwrap(), + AcceptedFundingAsset::USDT + ), + Error::::ParticipationFailed(ParticipationError::PolicyMismatch) + ); + }); + } } } diff --git a/pallets/funding/src/tests/5_remainder.rs b/pallets/funding/src/tests/5_remainder.rs index e7e9aa669..6f00669b8 100644 --- a/pallets/funding/src/tests/5_remainder.rs +++ b/pallets/funding/src/tests/5_remainder.rs @@ -977,5 +977,37 @@ mod remaining_contribute_extrinsic { )); }); } + + #[test] + fn wrong_policy_on_jwt() { + let mut inst = MockInstantiator::new(Some(RefCell::new(new_test_ext()))); + let project_metadata = default_project_metadata(ISSUER_1); + let project_id = inst.create_remainder_contributing_project( + project_metadata.clone(), + ISSUER_1, + default_evaluations(), + default_bids(), + vec![], + ); + + inst.execute(|| { + assert_noop!( + PolimecFunding::remaining_contribute( + RuntimeOrigin::signed(BUYER_1), + get_mock_jwt_with_cid( + BUYER_1, + InvestorType::Retail, + generate_did_from_account(BUYER_1), + "wrong_cid".as_bytes().to_vec().try_into().unwrap() + ), + project_id, + 5000 * ASSET_UNIT, + 1u8.try_into().unwrap(), + AcceptedFundingAsset::USDT + ), + Error::::ParticipationFailed(ParticipationError::PolicyMismatch) + ); + }); + } } } From 18d6e857f8b04963f80f6dced01c7d915029060d Mon Sep 17 00:00:00 2001 From: Juan Ignacio Rios Date: Thu, 25 Apr 2024 10:17:00 +0200 Subject: [PATCH 09/12] fixes --- pallets/funding/src/tests/3_auction.rs | 9 +++++---- pallets/funding/src/tests/4_community.rs | 7 ++++--- pallets/funding/src/tests/5_remainder.rs | 3 ++- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/pallets/funding/src/tests/3_auction.rs b/pallets/funding/src/tests/3_auction.rs index d7d4e926e..7ec427482 100644 --- a/pallets/funding/src/tests/3_auction.rs +++ b/pallets/funding/src/tests/3_auction.rs @@ -1034,7 +1034,7 @@ mod bid_extrinsic { #[cfg(test)] mod failure { - use super::*; + use super::*; #[test] fn cannot_use_all_of_evaluation_bond_on_bid() { @@ -1077,7 +1077,7 @@ mod bid_extrinsic { let evaluator_bid = BidParams::new(evaluator_bidder, 600 * ASSET_UNIT, 1u8, AcceptedFundingAsset::USDT); evaluations_1.push((evaluator_bidder, evaluation_amount).into()); - let project_id_1 = inst.create_auctioning_project(project_metadata_1.clone(), ISSUER_1, evaluations_1); + let _project_id_1 = inst.create_auctioning_project(project_metadata_1.clone(), ISSUER_1, evaluations_1); let project_id_2 = inst.create_auctioning_project(project_metadata_2.clone(), ISSUER_2, evaluations_2); @@ -1107,10 +1107,11 @@ mod bid_extrinsic { assert_noop!( PolimecFunding::bid( RuntimeOrigin::signed(evaluator_bidder), - get_mock_jwt( + get_mock_jwt_with_cid( evaluator_bidder, InvestorType::Professional, - generate_did_from_account(evaluator_bidder) + generate_did_from_account(evaluator_bidder), + project_metadata_2.clone().policy_ipfs_cid.unwrap() ), project_id_2, evaluator_bid.amount, diff --git a/pallets/funding/src/tests/4_community.rs b/pallets/funding/src/tests/4_community.rs index 0206ef845..8c195ab77 100644 --- a/pallets/funding/src/tests/4_community.rs +++ b/pallets/funding/src/tests/4_community.rs @@ -1358,7 +1358,7 @@ mod community_contribute_extrinsic { let evaluator_contribution = ContributionParams::new(evaluator_contributor, 600 * ASSET_UNIT, 1u8, AcceptedFundingAsset::USDT); evaluations_1.push((evaluator_contributor, evaluation_amount).into()); - let project_id_1 = inst.create_community_contributing_project(project_metadata_1.clone(), ISSUER_1, evaluations_1, default_bids()); + let _project_id_1 = inst.create_community_contributing_project(project_metadata_1.clone(), ISSUER_1, evaluations_1, default_bids()); let project_id_2 = inst.create_community_contributing_project(project_metadata_2.clone(), ISSUER_2, evaluations_2, default_bids()); let wap = inst.get_project_details(project_id_2).weighted_average_price.unwrap(); @@ -1389,10 +1389,11 @@ mod community_contribute_extrinsic { assert_noop!( PolimecFunding::community_contribute( RuntimeOrigin::signed(evaluator_contributor), - get_mock_jwt( + get_mock_jwt_with_cid( evaluator_contributor, InvestorType::Retail, - generate_did_from_account(evaluator_contributor) + generate_did_from_account(evaluator_contributor), + project_metadata_2.clone().policy_ipfs_cid.unwrap() ), project_id_2, evaluator_contribution.amount, diff --git a/pallets/funding/src/tests/5_remainder.rs b/pallets/funding/src/tests/5_remainder.rs index 1e4e8fd2c..2e5b51cb2 100644 --- a/pallets/funding/src/tests/5_remainder.rs +++ b/pallets/funding/src/tests/5_remainder.rs @@ -993,7 +993,7 @@ mod remaining_contribute_extrinsic { let evaluator_contribution = ContributionParams::new(evaluator_contributor, 600 * ASSET_UNIT, 1u8, AcceptedFundingAsset::USDT); evaluations_1.push((evaluator_contributor, evaluation_amount).into()); - let project_id_1 = inst.create_remainder_contributing_project(project_metadata_1.clone(), ISSUER_1, evaluations_1, default_bids(), vec![]); + let _project_id_1 = inst.create_remainder_contributing_project(project_metadata_1.clone(), ISSUER_1, evaluations_1, default_bids(), vec![]); let project_id_2 = inst.create_remainder_contributing_project(project_metadata_2.clone(), ISSUER_2, evaluations_2, default_bids(), vec![]); let wap = inst.get_project_details(project_id_2).weighted_average_price.unwrap(); @@ -1028,6 +1028,7 @@ mod remaining_contribute_extrinsic { evaluator_contributor, InvestorType::Retail, generate_did_from_account(evaluator_contributor), + project_metadata_2.clone().policy_ipfs_cid.unwrap(), ), project_id_2, evaluator_contribution.amount, From 5a0e555a437c050ae28a4e4b5537977b2dd59a86 Mon Sep 17 00:00:00 2001 From: Leonardo Razovic <4128940+lrazovic@users.noreply.github.com> Date: Thu, 25 Apr 2024 14:30:25 +0200 Subject: [PATCH 10/12] test: use the right call --- polimec-common/test-utils/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/polimec-common/test-utils/src/lib.rs b/polimec-common/test-utils/src/lib.rs index cebad5036..7c99e2f3d 100644 --- a/polimec-common/test-utils/src/lib.rs +++ b/polimec-common/test-utils/src/lib.rs @@ -164,11 +164,10 @@ mod tests { .as_ref(), ) .unwrap(); - let token = get_mock_jwt_with_cid( + let token = get_mock_jwt( "0x1234", InvestorType::Institutional, generate_did_from_account(40u64), - project_metadata.clone().policy_ipfs_cid.unwrap(), ); let res = Ed25519.validator::>(&verifying_key).validate(&token); assert!(res.is_ok()); From fcac9f47fab3a9d0941382729793227a17e6da16 Mon Sep 17 00:00:00 2001 From: Leonardo Razovic <4128940+lrazovic@users.noreply.github.com> Date: Thu, 25 Apr 2024 14:36:23 +0200 Subject: [PATCH 11/12] test: remove unused import --- pallets/dispenser/src/benchmarking.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/pallets/dispenser/src/benchmarking.rs b/pallets/dispenser/src/benchmarking.rs index f418550ab..479de8a97 100644 --- a/pallets/dispenser/src/benchmarking.rs +++ b/pallets/dispenser/src/benchmarking.rs @@ -33,8 +33,6 @@ fn assert_last_event(generic_event: ::RuntimeEvent) { #[benchmarks] mod benchmarks { use super::*; - use polimec_common_test_utils::get_mock_jwt_with_cid; - #[benchmark] fn dispense() { let caller: T::AccountId = whitelisted_caller(); From 00de08dfe375a05b87a5d46c99165eaacd8b01d8 Mon Sep 17 00:00:00 2001 From: Leonardo Razovic <4128940+lrazovic@users.noreply.github.com> Date: Thu, 25 Apr 2024 14:37:03 +0200 Subject: [PATCH 12/12] fmt --- pallets/funding/src/tests/3_auction.rs | 8 ++--- pallets/funding/src/tests/4_community.rs | 37 +++++++++++++--------- pallets/funding/src/tests/5_remainder.rs | 39 +++++++++++++++--------- polimec-common/test-utils/src/lib.rs | 6 +--- 4 files changed, 50 insertions(+), 40 deletions(-) diff --git a/pallets/funding/src/tests/3_auction.rs b/pallets/funding/src/tests/3_auction.rs index 7ec427482..221f831e5 100644 --- a/pallets/funding/src/tests/3_auction.rs +++ b/pallets/funding/src/tests/3_auction.rs @@ -1034,7 +1034,7 @@ mod bid_extrinsic { #[cfg(test)] mod failure { - use super::*; + use super::*; #[test] fn cannot_use_all_of_evaluation_bond_on_bid() { @@ -1068,7 +1068,6 @@ mod bid_extrinsic { let project_metadata_1 = default_project_metadata(ISSUER_1); let project_metadata_2 = default_project_metadata(ISSUER_2); - let mut evaluations_1 = default_evaluations(); let evaluations_2 = default_evaluations(); @@ -1080,19 +1079,18 @@ mod bid_extrinsic { let _project_id_1 = inst.create_auctioning_project(project_metadata_1.clone(), ISSUER_1, evaluations_1); let project_id_2 = inst.create_auctioning_project(project_metadata_2.clone(), ISSUER_2, evaluations_2); - // Necessary Mints let already_bonded_plmc = MockInstantiator::calculate_evaluation_plmc_spent(vec![(evaluator_bidder, evaluation_amount).into()]) [0] - .plmc_amount; + .plmc_amount; let usable_evaluation_plmc = already_bonded_plmc - ::EvaluatorSlash::get() * already_bonded_plmc; let necessary_plmc_for_bid = MockInstantiator::calculate_auction_plmc_charged_with_given_price( &vec![evaluator_bid.clone()], project_metadata_2.minimum_price, )[0] - .plmc_amount; + .plmc_amount; let necessary_usdt_for_bid = MockInstantiator::calculate_auction_funding_asset_charged_with_given_price( &vec![evaluator_bid.clone()], project_metadata_2.minimum_price, diff --git a/pallets/funding/src/tests/4_community.rs b/pallets/funding/src/tests/4_community.rs index 8c195ab77..5ef3677d9 100644 --- a/pallets/funding/src/tests/4_community.rs +++ b/pallets/funding/src/tests/4_community.rs @@ -1349,36 +1349,43 @@ mod community_contribute_extrinsic { let project_metadata_1 = default_project_metadata(ISSUER_1); let project_metadata_2 = default_project_metadata(ISSUER_2); - let mut evaluations_1 = default_evaluations(); let evaluations_2 = default_evaluations(); let evaluator_contributor = 69; let evaluation_amount = 420 * US_DOLLAR; - let evaluator_contribution = ContributionParams::new(evaluator_contributor, 600 * ASSET_UNIT, 1u8, AcceptedFundingAsset::USDT); + let evaluator_contribution = + ContributionParams::new(evaluator_contributor, 600 * ASSET_UNIT, 1u8, AcceptedFundingAsset::USDT); evaluations_1.push((evaluator_contributor, evaluation_amount).into()); - let _project_id_1 = inst.create_community_contributing_project(project_metadata_1.clone(), ISSUER_1, evaluations_1, default_bids()); - let project_id_2 = inst.create_community_contributing_project(project_metadata_2.clone(), ISSUER_2, evaluations_2, default_bids()); + let _project_id_1 = inst.create_community_contributing_project( + project_metadata_1.clone(), + ISSUER_1, + evaluations_1, + default_bids(), + ); + let project_id_2 = inst.create_community_contributing_project( + project_metadata_2.clone(), + ISSUER_2, + evaluations_2, + default_bids(), + ); let wap = inst.get_project_details(project_id_2).weighted_average_price.unwrap(); // Necessary Mints let already_bonded_plmc = - MockInstantiator::calculate_evaluation_plmc_spent(vec![(evaluator_contributor, evaluation_amount).into()]) - [0] + MockInstantiator::calculate_evaluation_plmc_spent(vec![ + (evaluator_contributor, evaluation_amount).into() + ])[0] .plmc_amount; let usable_evaluation_plmc = already_bonded_plmc - ::EvaluatorSlash::get() * already_bonded_plmc; - let necessary_plmc_for_contribution = MockInstantiator::calculate_contributed_plmc_spent( - vec![evaluator_contribution.clone()], - wap, - )[0] - .plmc_amount; - let necessary_usdt_for_contribution = MockInstantiator::calculate_contributed_funding_asset_spent( - vec![evaluator_contribution.clone()], - wap, - ); + let necessary_plmc_for_contribution = + MockInstantiator::calculate_contributed_plmc_spent(vec![evaluator_contribution.clone()], wap)[0] + .plmc_amount; + let necessary_usdt_for_contribution = + MockInstantiator::calculate_contributed_funding_asset_spent(vec![evaluator_contribution.clone()], wap); inst.mint_plmc_to(vec![UserToPLMCBalance::new( evaluator_contributor, necessary_plmc_for_contribution - usable_evaluation_plmc, diff --git a/pallets/funding/src/tests/5_remainder.rs b/pallets/funding/src/tests/5_remainder.rs index 2e5b51cb2..5bfa191f9 100644 --- a/pallets/funding/src/tests/5_remainder.rs +++ b/pallets/funding/src/tests/5_remainder.rs @@ -984,36 +984,45 @@ mod remaining_contribute_extrinsic { let project_metadata_1 = default_project_metadata(ISSUER_1); let project_metadata_2 = default_project_metadata(ISSUER_2); - let mut evaluations_1 = default_evaluations(); let evaluations_2 = default_evaluations(); let evaluator_contributor = 69; let evaluation_amount = 420 * US_DOLLAR; - let evaluator_contribution = ContributionParams::new(evaluator_contributor, 600 * ASSET_UNIT, 1u8, AcceptedFundingAsset::USDT); + let evaluator_contribution = + ContributionParams::new(evaluator_contributor, 600 * ASSET_UNIT, 1u8, AcceptedFundingAsset::USDT); evaluations_1.push((evaluator_contributor, evaluation_amount).into()); - let _project_id_1 = inst.create_remainder_contributing_project(project_metadata_1.clone(), ISSUER_1, evaluations_1, default_bids(), vec![]); - let project_id_2 = inst.create_remainder_contributing_project(project_metadata_2.clone(), ISSUER_2, evaluations_2, default_bids(), vec![]); + let _project_id_1 = inst.create_remainder_contributing_project( + project_metadata_1.clone(), + ISSUER_1, + evaluations_1, + default_bids(), + vec![], + ); + let project_id_2 = inst.create_remainder_contributing_project( + project_metadata_2.clone(), + ISSUER_2, + evaluations_2, + default_bids(), + vec![], + ); let wap = inst.get_project_details(project_id_2).weighted_average_price.unwrap(); // Necessary Mints let already_bonded_plmc = - MockInstantiator::calculate_evaluation_plmc_spent(vec![(evaluator_contributor, evaluation_amount).into()]) - [0] + MockInstantiator::calculate_evaluation_plmc_spent(vec![ + (evaluator_contributor, evaluation_amount).into() + ])[0] .plmc_amount; let usable_evaluation_plmc = already_bonded_plmc - ::EvaluatorSlash::get() * already_bonded_plmc; - let necessary_plmc_for_contribution = MockInstantiator::calculate_contributed_plmc_spent( - vec![evaluator_contribution.clone()], - wap, - )[0] - .plmc_amount; - let necessary_usdt_for_contribution = MockInstantiator::calculate_contributed_funding_asset_spent( - vec![evaluator_contribution.clone()], - wap, - ); + let necessary_plmc_for_contribution = + MockInstantiator::calculate_contributed_plmc_spent(vec![evaluator_contribution.clone()], wap)[0] + .plmc_amount; + let necessary_usdt_for_contribution = + MockInstantiator::calculate_contributed_funding_asset_spent(vec![evaluator_contribution.clone()], wap); inst.mint_plmc_to(vec![UserToPLMCBalance::new( evaluator_contributor, necessary_plmc_for_contribution - usable_evaluation_plmc, diff --git a/polimec-common/test-utils/src/lib.rs b/polimec-common/test-utils/src/lib.rs index 7c99e2f3d..a7e4b1a45 100644 --- a/polimec-common/test-utils/src/lib.rs +++ b/polimec-common/test-utils/src/lib.rs @@ -164,11 +164,7 @@ mod tests { .as_ref(), ) .unwrap(); - let token = get_mock_jwt( - "0x1234", - InvestorType::Institutional, - generate_did_from_account(40u64), - ); + let token = get_mock_jwt("0x1234", InvestorType::Institutional, generate_did_from_account(40u64)); let res = Ed25519.validator::>(&verifying_key).validate(&token); assert!(res.is_ok()); }