diff --git a/packages/rs-dpp/src/data_trigger/reward_share_data_triggers/mod.rs b/packages/rs-dpp/src/data_trigger/reward_share_data_triggers/mod.rs index 81d6ea4cf45..35ceca53453 100644 --- a/packages/rs-dpp/src/data_trigger/reward_share_data_triggers/mod.rs +++ b/packages/rs-dpp/src/data_trigger/reward_share_data_triggers/mod.rs @@ -21,7 +21,7 @@ const MAX_DOCUMENTS: usize = 16; pub async fn create_masternode_reward_shares_data_trigger<'a, SR>( document_transition: &DocumentTransition, context: &DataTriggerExecutionContext<'a, SR>, - _top_level_identity: Option<&Identifier>, + _top_level_identifier: Option<&Identifier>, ) -> Result where SR: StateRepositoryLike, @@ -70,7 +70,7 @@ where // payToId identity exists let pay_to_identifier = Identifier::from_bytes(&pay_to_id_bytes)?; - let maybe_identifier: Option> = context + let maybe_identity = context .state_repository .fetch_identity( &pay_to_identifier, @@ -78,7 +78,7 @@ where ) .await?; - if !is_dry_run && maybe_identifier.is_none() { + if !is_dry_run && maybe_identity.is_none() { let err = create_error( context, dt_create, @@ -139,6 +139,7 @@ mod test { use itertools::Itertools; use serde_json::json; + use crate::identity::Identity; use crate::{ data_contract::DataContract, data_trigger::DataTriggerExecutionContext, @@ -160,22 +161,23 @@ mod test { }; struct TestData { - top_level_identity: Identifier, + top_level_identifier: Identifier, data_contract: DataContract, sml_store: SMLStore, documents: Vec, document_transition: DocumentTransition, + identity: Identity, } fn setup_test() -> TestData { - let top_level_identity_hex = + let top_level_identifier_hex = "c286807d463b06c7aba3b9a60acf64c1fc03da8c1422005cd9b4293f08cf0562"; - let top_level_identity = - Identifier::from_bytes(&hex::decode(top_level_identity_hex).unwrap()).unwrap(); + let top_level_identifier = + Identifier::from_bytes(&hex::decode(top_level_identifier_hex).unwrap()).unwrap(); let sml_entries: Vec = vec![ SMLEntry { - pro_reg_tx_hash: top_level_identity_hex.to_string(), + pro_reg_tx_hash: top_level_identifier_hex.to_string(), confirmed_hash: "4eb56228c535db3b234907113fd41d57bcc7cdcb8e0e00e57590af27ee88c119".to_string(), service: "192.168.65.2:20101".to_string(), pub_key_operator: "809519c5f6f3be1c08782ac42ae9a83b6c7205eba43f9a96a4f032ec7a73f1a7c25fa78cce0d6d9c135f7e2c28527179".to_string(), @@ -208,9 +210,10 @@ mod test { TestData { documents, data_contract, - top_level_identity, + top_level_identifier, sml_store, document_transition: document_transitions[0].clone(), + identity: Identity::default(), } } @@ -238,7 +241,7 @@ mod test { documents, sml_store, data_contract, - top_level_identity, + top_level_identifier, .. } = setup_test(); @@ -247,7 +250,7 @@ mod test { .expect_fetch_sml_store() .returning(move || Ok(sml_store.clone())); state_repository_mock - .expect_fetch_identity::>() + .expect_fetch_identity() .returning(|_, _| Ok(None)); state_repository_mock .expect_fetch_documents() @@ -259,7 +262,7 @@ mod test { let execution_context = StateTransitionExecutionContext::default(); let context = DataTriggerExecutionContext { data_contract: &data_contract, - owner_id: &top_level_identity, + owner_id: &top_level_identifier, state_repository: &state_repository_mock, state_transition_execution_context: &execution_context, }; @@ -281,7 +284,7 @@ mod test { document_transition, sml_store, data_contract, - top_level_identity, + top_level_identifier, .. } = setup_test(); @@ -290,7 +293,7 @@ mod test { .expect_fetch_sml_store() .returning(move || Ok(sml_store.clone())); state_repository_mock - .expect_fetch_identity::>() + .expect_fetch_identity() .returning(move |_, _| Ok(None)); state_repository_mock .expect_fetch_documents::() @@ -299,7 +302,7 @@ mod test { let execution_context = StateTransitionExecutionContext::default(); let context = DataTriggerExecutionContext { data_contract: &data_contract, - owner_id: &top_level_identity, + owner_id: &top_level_identifier, state_repository: &state_repository_mock, state_transition_execution_context: &execution_context, }; @@ -338,7 +341,7 @@ mod test { .expect_fetch_sml_store() .returning(move || Ok(sml_store.clone())); state_repository_mock - .expect_fetch_identity::>() + .expect_fetch_identity() .returning(move |_, _| Ok(None)); state_repository_mock .expect_fetch_documents::() @@ -368,18 +371,18 @@ mod test { document_transition, sml_store, data_contract, - top_level_identity, + top_level_identifier, + identity, .. } = setup_test(); let mut state_repository_mock = MockStateRepositoryLike::new(); - let identity_to_return = top_level_identity.as_bytes().to_vec(); state_repository_mock .expect_fetch_sml_store() .returning(move || Ok(sml_store.clone())); state_repository_mock - .expect_fetch_identity::>() - .returning(move |_, _| Ok(Some(identity_to_return.clone()))); + .expect_fetch_identity() + .returning(move |_, _| Ok(Some(identity.clone()))); state_repository_mock .expect_fetch_documents::() .returning(move |_, _, _, _| Ok(vec![])); @@ -387,7 +390,7 @@ mod test { let execution_context = StateTransitionExecutionContext::default(); let context = DataTriggerExecutionContext { data_contract: &data_contract, - owner_id: &top_level_identity, + owner_id: &top_level_identifier, state_repository: &state_repository_mock, state_transition_execution_context: &execution_context, }; @@ -404,18 +407,18 @@ mod test { document_transition, sml_store, data_contract, - top_level_identity, + top_level_identifier, + identity, .. } = setup_test(); let mut state_repository_mock = MockStateRepositoryLike::new(); - let identity_to_return = top_level_identity.as_bytes().to_vec(); state_repository_mock .expect_fetch_sml_store() .returning(move || Ok(sml_store.clone())); state_repository_mock - .expect_fetch_identity::>() - .returning(move |_, _| Ok(Some(identity_to_return.clone()))); + .expect_fetch_identity() + .returning(move |_, _| Ok(Some(identity.clone()))); let documents_to_return: Vec = (0..16).map(|_| Document::default()).collect(); state_repository_mock .expect_fetch_documents::() @@ -424,7 +427,7 @@ mod test { let execution_context = StateTransitionExecutionContext::default(); let context = DataTriggerExecutionContext { data_contract: &data_contract, - owner_id: &top_level_identity, + owner_id: &top_level_identifier, state_repository: &state_repository_mock, state_transition_execution_context: &execution_context, }; @@ -445,13 +448,13 @@ mod test { let TestData { document_transition, data_contract, - top_level_identity, + top_level_identifier, .. } = setup_test(); let mut state_repository_mock = MockStateRepositoryLike::new(); state_repository_mock - .expect_fetch_identity::>() + .expect_fetch_identity() .returning(move |_, _| Ok(None)); state_repository_mock .expect_fetch_documents::() @@ -462,7 +465,7 @@ mod test { let context = DataTriggerExecutionContext { data_contract: &data_contract, - owner_id: &top_level_identity, + owner_id: &top_level_identifier, state_repository: &state_repository_mock, state_transition_execution_context: &execution_context, }; diff --git a/packages/rs-dpp/src/errors/consensus/basic/identity/invalid_asset_lock_proof_transaction_height_error.rs b/packages/rs-dpp/src/errors/consensus/basic/identity/invalid_asset_lock_proof_transaction_height_error.rs index c74cc2fce6f..3ee4535e2f6 100644 --- a/packages/rs-dpp/src/errors/consensus/basic/identity/invalid_asset_lock_proof_transaction_height_error.rs +++ b/packages/rs-dpp/src/errors/consensus/basic/identity/invalid_asset_lock_proof_transaction_height_error.rs @@ -21,7 +21,7 @@ impl InvalidAssetLockProofTransactionHeightError { self.proof_core_chain_locked_height } - pub fn current_core_chain_locked_height(&self) -> Option { + pub fn transaction_height(&self) -> Option { self.transaction_height } } diff --git a/packages/rs-dpp/src/errors/errors.rs b/packages/rs-dpp/src/errors/errors.rs index fcd9deae1f3..9402bc3edb5 100644 --- a/packages/rs-dpp/src/errors/errors.rs +++ b/packages/rs-dpp/src/errors/errors.rs @@ -106,6 +106,9 @@ pub enum ProtocolError { #[error("Public key is disabled")] PublicKeyIsDisabledError { public_key: IdentityPublicKey }, + + #[error("Identity is not present")] + IdentityNotPresentError { id: Identifier }, } impl From for ProtocolError { diff --git a/packages/rs-dpp/src/errors/non_consensus_error.rs b/packages/rs-dpp/src/errors/non_consensus_error.rs index 3e54407a1e1..44ad0a627d1 100644 --- a/packages/rs-dpp/src/errors/non_consensus_error.rs +++ b/packages/rs-dpp/src/errors/non_consensus_error.rs @@ -4,7 +4,7 @@ use crate::{ CompatibleProtocolVersionIsNotDefinedError, InvalidVectorSizeError, SerdeParsingError, }; -#[derive(Debug, Error, Clone)] +#[derive(Debug, Error)] pub enum NonConsensusError { #[error("Unexpected serde parsing error: {0:#}")] SerdeParsingError(SerdeParsingError), @@ -34,6 +34,9 @@ pub enum NonConsensusError { object_name: &'static str, details: String, }, + + #[error(transparent)] + Error(#[from] anyhow::Error), } pub mod object_names { diff --git a/packages/rs-dpp/src/identity/errors/unknown_asset_lock_proof_type_error.rs b/packages/rs-dpp/src/identity/errors/unknown_asset_lock_proof_type_error.rs index fca6a73799d..8e42d49a268 100644 --- a/packages/rs-dpp/src/identity/errors/unknown_asset_lock_proof_type_error.rs +++ b/packages/rs-dpp/src/identity/errors/unknown_asset_lock_proof_type_error.rs @@ -3,15 +3,15 @@ use thiserror::Error; #[derive(Error, Debug, Clone, PartialEq, Eq)] #[error("'Unknown Asset lock proof type'")] pub struct UnknownAssetLockProofTypeError { - asset_lock_type: u32, + asset_lock_type: Option, } impl UnknownAssetLockProofTypeError { - pub fn new(asset_lock_type: u32) -> Self { + pub fn new(asset_lock_type: Option) -> Self { Self { asset_lock_type } } - pub fn asset_lock_type(&self) -> u32 { + pub fn asset_lock_type(&self) -> Option { self.asset_lock_type } } diff --git a/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/asset_lock_transaction_output_fetcher.rs b/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/asset_lock_transaction_output_fetcher.rs index 94919c39f50..01f1162207f 100644 --- a/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/asset_lock_transaction_output_fetcher.rs +++ b/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/asset_lock_transaction_output_fetcher.rs @@ -1,3 +1,4 @@ +use std::convert::TryInto; use std::sync::Arc; use dashcore::hashes::hex::ToHex; @@ -50,8 +51,8 @@ pub async fn fetch_asset_lock_transaction_output( let output_index = out_point.vout as usize; let transaction_hash = out_point.txid; - let maybe_raw_transaction = state_repository - .fetch_transaction::>(&transaction_hash.to_hex(), execution_context) + let transaction_data = state_repository + .fetch_transaction(&transaction_hash.to_hex(), execution_context) .await .map_err(|_| DPPError::InvalidAssetLockTransaction)?; @@ -62,9 +63,14 @@ pub async fn fetch_asset_lock_transaction_output( }); } - if let Some(raw_transaction) = maybe_raw_transaction { + let transaction_data = transaction_data + .try_into() + .map_err(|_| DPPError::InvalidAssetLockTransaction)?; + + if let Some(raw_transaction) = transaction_data.data { let transaction = Transaction::deserialize(&raw_transaction) .map_err(|_| DPPError::InvalidAssetLockTransaction)?; + transaction .output .get(output_index) @@ -81,6 +87,7 @@ pub async fn fetch_asset_lock_transaction_output( #[cfg(test)] mod test { + use crate::state_repository::FetchTransactionResponse; use crate::{ identity::state_transition::asset_lock_proof::chain::ChainAssetLockProof, state_repository::MockStateRepositoryLike, @@ -95,8 +102,8 @@ mod test { let execution_context = StateTransitionExecutionContext::default(); state_repository_mock - .expect_fetch_transaction::>() - .return_once(|_, _| Ok(None)); + .expect_fetch_transaction() + .return_once(|_, _| Ok(FetchTransactionResponse::default())); execution_context.enable_dry_run(); let result = fetch_asset_lock_transaction_output( diff --git a/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/asset_lock_transaction_validator.rs b/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/asset_lock_transaction_validator.rs index de6b356c37e..1093aef9830 100644 --- a/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/asset_lock_transaction_validator.rs +++ b/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/asset_lock_transaction_validator.rs @@ -83,7 +83,9 @@ where } let out_point = OutPoint::new(transaction.txid(), output_index as u32); - let out_point_buf = consensus::serialize(&out_point); + let mut out_point_buf = consensus::serialize(&out_point); + let (tx_id, _) = out_point_buf.split_at_mut(32); + tx_id.reverse(); let is_out_point_used = self .state_repository diff --git a/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/chain/chain_asset_lock_proof.rs b/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/chain/chain_asset_lock_proof.rs index 2ef2187074a..a2c659af88b 100644 --- a/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/chain/chain_asset_lock_proof.rs +++ b/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/chain/chain_asset_lock_proof.rs @@ -1,11 +1,12 @@ use serde::{Deserialize, Serialize}; use serde_big_array::BigArray; -use crate::identifier::Identifier; -use crate::util::hash::hash; -use crate::util::vec::vec_to_array; +use crate::{ + errors::NonConsensusError, identifier::Identifier, util::hash::hash, util::vec::vec_to_array, +}; #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct ChainAssetLockProof { #[serde(rename = "type")] asset_lock_type: u8, @@ -34,16 +35,22 @@ impl ChainAssetLockProof { self.core_chain_locked_height } + pub fn set_core_chain_locked_height(&mut self, value: u32) { + self.core_chain_locked_height = value; + } + /// Get out_point pub fn out_point(&self) -> &[u8; 36] { &self.out_point } + pub fn set_out_point(&mut self, out_point: [u8; 36]) { + self.out_point = out_point; + } + /// Create identifier - pub fn create_identifier(&self) -> Identifier { - return Identifier::new( - vec_to_array(hash(self.out_point()).as_ref()) - .expect("Expected hash function to give a 32 byte output"), - ); + pub fn create_identifier(&self) -> Result { + let array = vec_to_array(hash(self.out_point).as_ref())?; + Ok(Identifier::new(array)) } } diff --git a/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/chain/chain_asset_lock_proof_structure_validator.rs b/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/chain/chain_asset_lock_proof_structure_validator.rs index 2a0b248ad2a..9f816d1006a 100644 --- a/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/chain/chain_asset_lock_proof_structure_validator.rs +++ b/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/chain/chain_asset_lock_proof_structure_validator.rs @@ -1,3 +1,4 @@ +use std::convert::TryInto; use std::sync::Arc; use dashcore::consensus::Decodable; @@ -86,14 +87,12 @@ where let proof_core_chain_locked_height = proof.core_chain_locked_height(); - let latest_platform_block_header: PlatformBlockHeader = self + let current_core_chain_locked_height = self .state_repository - .fetch_latest_platform_block_header() + .fetch_latest_platform_core_chain_locked_height() .await - .map_err(|e| NonConsensusError::StateRepositoryFetchError(e.to_string()))?; - - let current_core_chain_locked_height = - latest_platform_block_header.current_core_chain_locked_height; + .map_err(|e| NonConsensusError::StateRepositoryFetchError(e.to_string()))? + .unwrap_or(0); if current_core_chain_locked_height < proof_core_chain_locked_height { result.add_error(InvalidAssetLockProofCoreChainHeightError::new( @@ -112,38 +111,39 @@ where let transaction_hash = out_point.txid; let transaction_hash_string = transaction_hash.to_hex(); - let maybe_transaction_fetch_result: Option = self + let transaction_fetch_result = self .state_repository .fetch_transaction(&transaction_hash_string, execution_context) .await .map_err(|e| NonConsensusError::StateRepositoryFetchError(e.to_string()))?; - return if let Some(transaction_result) = maybe_transaction_fetch_result { - if let Some(tx_height) = transaction_result.height { - if proof_core_chain_locked_height < tx_height { - result.add_error(InvalidAssetLockProofTransactionHeightError::new( - proof_core_chain_locked_height, - Some(tx_height), - )); + let transaction_result = transaction_fetch_result + .try_into() + .map_err(Into::into) + .map_err(|e| NonConsensusError::StateRepositoryFetchError(e.to_string()))?; - return Ok(result); - } - } else { + if let Some(tx_height) = transaction_result.height { + if proof_core_chain_locked_height < tx_height { result.add_error(InvalidAssetLockProofTransactionHeightError::new( proof_core_chain_locked_height, - None, + Some(tx_height), )); return Ok(result); } + } else { + result.add_error(InvalidAssetLockProofTransactionHeightError::new( + proof_core_chain_locked_height, + None, + )); + + return Ok(result); + } + if let Some(raw_tx) = transaction_result.data { let validate_asset_lock_transaction_result = self .asset_lock_transaction_validator - .validate( - &transaction_result.data, - output_index as usize, - execution_context, - ) + .validate(&raw_tx, output_index as usize, execution_context) .await?; let validation_result_data = if validate_asset_lock_transaction_result.is_valid() { @@ -162,11 +162,11 @@ where Ok(result) } else { - result.add_error(IdentityAssetLockTransactionIsNotFoundError::new( - transaction_hash.as_hash().into_inner(), - )); + let mut hash = transaction_hash.as_hash().into_inner(); + hash.reverse(); + result.add_error(IdentityAssetLockTransactionIsNotFoundError::new(hash)); Ok(result) - }; + } } } diff --git a/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/instant/instant_asset_lock_proof.rs b/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/instant/instant_asset_lock_proof.rs index 7fc3b182785..cba9d3f6a8f 100644 --- a/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/instant/instant_asset_lock_proof.rs +++ b/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/instant/instant_asset_lock_proof.rs @@ -102,7 +102,13 @@ impl InstantAssetLockProof { } pub fn out_point(&self) -> Option<[u8; 36]> { - self.transaction.out_point_buffer(self.output_index()) + let out_point_buffer = self.transaction.out_point_buffer(self.output_index()); + + out_point_buffer.map(|mut buffer| { + let (tx_id, _) = buffer.split_at_mut(32); + tx_id.reverse(); + buffer + }) } pub fn output(&self) -> Option<&TxOut> { @@ -110,15 +116,11 @@ impl InstantAssetLockProof { } pub fn create_identifier(&self) -> Result { - let buffer = hash( - self.transaction() - .out_point_buffer(self.output_index()) - .ok_or_else(|| { - NonConsensusError::IdentifierCreateError(String::from( - "No output at a given index", - )) - })?, - ); + let out_point = self.out_point().ok_or_else(|| { + NonConsensusError::IdentifierCreateError(String::from("No output at a given index")) + })?; + + let buffer = hash(out_point); Ok(Identifier::new(vec_to_array(&buffer)?)) } diff --git a/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/mod.rs b/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/mod.rs index 336159e3057..fa8efd7afb9 100644 --- a/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/mod.rs +++ b/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/mod.rs @@ -114,10 +114,7 @@ impl AssetLockProof { pub fn create_identifier(&self) -> Result { match self { AssetLockProof::Instant(instant_proof) => instant_proof.create_identifier(), - AssetLockProof::Chain(chain_proof) => { - // TODO: fix return type - Ok(chain_proof.create_identifier()) - } + AssetLockProof::Chain(chain_proof) => chain_proof.create_identifier(), } } diff --git a/packages/rs-dpp/src/identity/state_transition/identity_create_transition/apply_identity_create_transition.rs b/packages/rs-dpp/src/identity/state_transition/identity_create_transition/apply_identity_create_transition.rs index 2baa56809aa..79856c89bf8 100644 --- a/packages/rs-dpp/src/identity/state_transition/identity_create_transition/apply_identity_create_transition.rs +++ b/packages/rs-dpp/src/identity/state_transition/identity_create_transition/apply_identity_create_transition.rs @@ -22,6 +22,17 @@ impl ApplyIdentityCreateTransition where SR: StateRepositoryLike, { + pub fn new(state_repository: Arc) -> Self { + let asset_lock_transaction_output_fetcher = Arc::new( + AssetLockTransactionOutputFetcher::new(state_repository.clone()), + ); + + Self { + state_repository, + asset_lock_transaction_output_fetcher, + } + } + pub async fn apply_identity_create_transition( &self, state_transition: &IdentityCreateTransition, diff --git a/packages/rs-dpp/src/identity/state_transition/identity_create_transition/identity_create_transition.rs b/packages/rs-dpp/src/identity/state_transition/identity_create_transition/identity_create_transition.rs index f9f7e0773c9..d41d93deb18 100644 --- a/packages/rs-dpp/src/identity/state_transition/identity_create_transition/identity_create_transition.rs +++ b/packages/rs-dpp/src/identity/state_transition/identity_create_transition/identity_create_transition.rs @@ -104,13 +104,17 @@ impl IdentityCreateTransition { .iter() .map(|val| serde_json::from_value(val.clone())) .collect::, serde_json::Error>>()?; - state_transition = state_transition.set_public_keys(keys); + state_transition.set_public_keys(keys); } if let Some(proof) = transition_map.get(property_names::ASSET_LOCK_PROOF) { state_transition.set_asset_lock_proof(AssetLockProof::try_from(proof)?)?; } + if let Some(protocol_version) = transition_map.get(property_names::PROTOCOL_VERSION) { + state_transition.protocol_version = protocol_version.as_u64().unwrap() as u32; + } + Ok(state_transition) } @@ -142,14 +146,14 @@ impl IdentityCreateTransition { } /// Replaces existing set of public keys with a new one - pub fn set_public_keys(mut self, public_keys: Vec) -> Self { + pub fn set_public_keys(&mut self, public_keys: Vec) -> &mut Self { self.public_keys = public_keys; self } /// Adds public keys to the existing public keys array - pub fn add_public_keys(mut self, public_keys: &mut Vec) -> Self { + pub fn add_public_keys(&mut self, public_keys: &mut Vec) -> &mut Self { self.public_keys.append(public_keys); self @@ -172,6 +176,11 @@ impl IdentityCreateTransition { ) -> Result { let mut json_map = JsonValue::Object(Default::default()); + json_map.insert( + property_names::TRANSITION_TYPE.to_string(), + serde_json::Value::from(Self::get_type() as u8), + )?; + if !options.skip_signature { let sig = self.signature.iter().map(|num| JsonValue::from(*num)); json_map.insert( @@ -250,6 +259,16 @@ impl StateTransitionConvert for IdentityCreateTransition { } } + let mut public_keys: Vec = vec![]; + for key in self.public_keys.iter() { + public_keys.push(key.to_raw_json_object(skip_signature)?); + } + + json_value.insert( + property_names::PUBLIC_KEYS.to_owned(), + JsonValue::Array(public_keys), + )?; + Ok(json_value) } @@ -258,6 +277,11 @@ impl StateTransitionConvert for IdentityCreateTransition { // TODO: super.toJSON() + json.insert( + property_names::TRANSITION_TYPE.to_string(), + serde_json::Value::from(Self::get_type() as u8), + )?; + json.insert( property_names::ASSET_LOCK_PROOF.to_string(), self.asset_lock_proof.as_ref().try_into()?, @@ -292,7 +316,7 @@ impl StateTransitionLike for IdentityCreateTransition { } /// returns the type of State Transition fn get_type(&self) -> StateTransitionType { - StateTransitionType::IdentityTopUp + StateTransitionType::IdentityCreate } /// returns the signature as a byte-array fn get_signature(&self) -> &Vec { diff --git a/packages/rs-dpp/src/identity/state_transition/identity_create_transition/validation/basic/identity_create_transition_basic_validator.rs b/packages/rs-dpp/src/identity/state_transition/identity_create_transition/validation/basic/identity_create_transition_basic_validator.rs index 825c8ef080b..e505dfe1f2a 100644 --- a/packages/rs-dpp/src/identity/state_transition/identity_create_transition/validation/basic/identity_create_transition_basic_validator.rs +++ b/packages/rs-dpp/src/identity/state_transition/identity_create_transition/validation/basic/identity_create_transition_basic_validator.rs @@ -24,7 +24,7 @@ const ASSET_LOCK_PROOF_PROPERTY_NAME: &str = "assetLockProof"; pub struct IdentityCreateTransitionBasicValidator { - protocol_version_validator: Arc, + protocol_version_validator: ProtocolVersionValidator, json_schema_validator: JsonSchemaValidator, public_keys_validator: Arc, public_keys_in_identity_transition_validator: Arc, @@ -42,7 +42,7 @@ impl< > IdentityCreateTransitionBasicValidator { pub fn new( - protocol_version_validator: Arc, + protocol_version_validator: ProtocolVersionValidator, public_keys_validator: Arc, public_keys_in_identity_transition_validator: Arc, asset_lock_proof_validator: Arc>, @@ -128,4 +128,8 @@ impl< Ok(result) } + + pub fn protocol_version_validator(&mut self) -> &mut ProtocolVersionValidator { + &mut self.protocol_version_validator + } } diff --git a/packages/rs-dpp/src/identity/state_transition/identity_create_transition/validation/state/mod.rs b/packages/rs-dpp/src/identity/state_transition/identity_create_transition/validation/state/mod.rs index 69ff0aa0d01..5e1f38a73d6 100644 --- a/packages/rs-dpp/src/identity/state_transition/identity_create_transition/validation/state/mod.rs +++ b/packages/rs-dpp/src/identity/state_transition/identity_create_transition/validation/state/mod.rs @@ -1,10 +1,10 @@ use crate::consensus::state::identity::IdentityAlreadyExistsError; use crate::identity::state_transition::identity_create_transition::IdentityCreateTransition; -use crate::prelude::Identity; use crate::state_repository::StateRepositoryLike; use crate::state_transition::StateTransitionLike; use crate::validation::ValidationResult; use crate::NonConsensusError; +use std::convert::TryInto; /// Validate that identity exists /// @@ -21,8 +21,11 @@ pub async fn validate_identity_create_transition_state( let identity_id = state_transition.get_identity_id(); let maybe_identity = state_repository - .fetch_identity::(identity_id, state_transition.get_execution_context()) - .await + .fetch_identity(identity_id, state_transition.get_execution_context()) + .await? + .map(TryInto::try_into) + .transpose() + .map_err(Into::into) .map_err(|e| NonConsensusError::StateRepositoryFetchError(e.to_string()))?; if state_transition.get_execution_context().is_dry_run() { diff --git a/packages/rs-dpp/src/identity/state_transition/identity_credit_withdrawal_transition/apply_identity_credit_withdrawal_transition_factory.rs b/packages/rs-dpp/src/identity/state_transition/identity_credit_withdrawal_transition/apply_identity_credit_withdrawal_transition_factory.rs index 36a11505bf0..ec63d880dde 100644 --- a/packages/rs-dpp/src/identity/state_transition/identity_credit_withdrawal_transition/apply_identity_credit_withdrawal_transition_factory.rs +++ b/packages/rs-dpp/src/identity/state_transition/identity_credit_withdrawal_transition/apply_identity_credit_withdrawal_transition_factory.rs @@ -7,9 +7,10 @@ use dashcore::{ Script, TxOut, }; use lazy_static::__Deref; +use std::convert::TryInto; use crate::{ - identity::convert_credits_to_satoshi, prelude::Identity, state_repository::StateRepositoryLike, + identity::convert_credits_to_satoshi, state_repository::StateRepositoryLike, state_transition::StateTransitionLike, }; @@ -67,13 +68,16 @@ where .enqueue_withdrawal_transaction(latest_withdrawal_index, transaction_buffer) .await?; - let maybe_existing_identity: Option = self + let maybe_existing_identity = self .state_repository .fetch_identity( &state_transition.identity_id, state_transition.get_execution_context(), ) - .await?; + .await? + .map(TryInto::try_into) + .transpose() + .map_err(Into::into)?; let mut existing_identity = maybe_existing_identity.ok_or_else(|| anyhow!("Identity not found"))?; diff --git a/packages/rs-dpp/src/identity/state_transition/identity_credit_withdrawal_transition/validation/state/validate_identity_credit_withdrawal_transition_state.rs b/packages/rs-dpp/src/identity/state_transition/identity_credit_withdrawal_transition/validation/state/validate_identity_credit_withdrawal_transition_state.rs index bd4c98a892f..47ffc48f528 100644 --- a/packages/rs-dpp/src/identity/state_transition/identity_credit_withdrawal_transition/validation/state/validate_identity_credit_withdrawal_transition_state.rs +++ b/packages/rs-dpp/src/identity/state_transition/identity_credit_withdrawal_transition/validation/state/validate_identity_credit_withdrawal_transition_state.rs @@ -1,3 +1,4 @@ +use std::convert::TryInto; use std::sync::Arc; use anyhow::Result; @@ -5,7 +6,6 @@ use anyhow::Result; use crate::{ consensus::basic::{identity::IdentityInsufficientBalanceError, BasicError}, identity::state_transition::identity_credit_withdrawal_transition::IdentityCreditWithdrawalTransition, - prelude::Identity, state_repository::StateRepositoryLike, state_transition::StateTransitionLike, validation::ValidationResult, @@ -33,14 +33,17 @@ where ) -> Result, NonConsensusError> { let mut result: ValidationResult<()> = ValidationResult::default(); - let maybe_existing_identity: Option = self + let maybe_existing_identity = self .state_repository .fetch_identity( &state_transition.identity_id, state_transition.get_execution_context(), ) - .await - .map_err(|err| NonConsensusError::StateRepositoryFetchError(err.to_string()))?; + .await? + .map(TryInto::try_into) + .transpose() + .map_err(Into::into) + .map_err(|e| NonConsensusError::StateRepositoryFetchError(e.to_string()))?; let existing_identity = match maybe_existing_identity { None => { diff --git a/packages/rs-dpp/src/identity/state_transition/identity_topup_transition/apply_identity_topup_transition.rs b/packages/rs-dpp/src/identity/state_transition/identity_topup_transition/apply_identity_topup_transition.rs index 805b22826b2..b1526ad7248 100644 --- a/packages/rs-dpp/src/identity/state_transition/identity_topup_transition/apply_identity_topup_transition.rs +++ b/packages/rs-dpp/src/identity/state_transition/identity_topup_transition/apply_identity_topup_transition.rs @@ -1,10 +1,11 @@ +use std::convert::TryInto; use std::sync::Arc; use anyhow::{anyhow, Result}; use crate::identity::state_transition::asset_lock_proof::AssetLockTransactionOutputFetcher; use crate::identity::state_transition::identity_topup_transition::IdentityTopUpTransition; -use crate::identity::{convert_satoshi_to_credits, get_biggest_possible_identity, Identity}; +use crate::identity::{convert_satoshi_to_credits, get_biggest_possible_identity}; use crate::state_repository::StateRepositoryLike; use crate::state_transition::StateTransitionLike; @@ -47,11 +48,13 @@ where .out_point() .ok_or_else(|| anyhow!("Out point is missing from asset lock proof"))?; let identity_id = state_transition.get_identity_id(); - let mut maybe_identity = self .state_repository - .fetch_identity::(identity_id, state_transition.get_execution_context()) - .await?; + .fetch_identity(identity_id, state_transition.get_execution_context()) + .await? + .map(TryInto::try_into) + .transpose() + .map_err(Into::into)?; if is_dry_run { maybe_identity = Some(get_biggest_possible_identity()) @@ -86,7 +89,6 @@ mod test { asset_lock_proof::AssetLockTransactionOutputFetcher, identity_topup_transition::IdentityTopUpTransition, }, - prelude::Identity, state_repository::MockStateRepositoryLike, state_transition::StateTransitionLike, tests::fixtures::identity_topup_transition_fixture_json, @@ -103,7 +105,7 @@ mod test { let state_repository_for_fetcher = MockStateRepositoryLike::new(); state_repository_for_apply - .expect_fetch_identity::() + .expect_fetch_identity() .return_once(|_, _| Ok(None)); state_repository_for_apply .expect_update_identity() diff --git a/packages/rs-dpp/src/identity/state_transition/identity_topup_transition/identity_topup_transition.rs b/packages/rs-dpp/src/identity/state_transition/identity_topup_transition/identity_topup_transition.rs index 5bd5bff781a..f9538a3aaf4 100644 --- a/packages/rs-dpp/src/identity/state_transition/identity_topup_transition/identity_topup_transition.rs +++ b/packages/rs-dpp/src/identity/state_transition/identity_topup_transition/identity_topup_transition.rs @@ -143,7 +143,11 @@ impl IdentityTopUpTransition { } if !options.skip_identifiers_conversion { - let bytes = self.signature.iter().map(|num| JsonValue::from(*num)); + let bytes = self + .identity_id + .buffer + .iter() + .map(|num| JsonValue::from(*num)); json_map.insert( property_names::IDENTITY_ID.to_string(), JsonValue::Array(bytes.collect()), diff --git a/packages/rs-dpp/src/identity/state_transition/identity_update_transition/apply_identity_update_transition.rs b/packages/rs-dpp/src/identity/state_transition/identity_update_transition/apply_identity_update_transition.rs index 84ba3a3b743..bb436958c00 100644 --- a/packages/rs-dpp/src/identity/state_transition/identity_update_transition/apply_identity_update_transition.rs +++ b/packages/rs-dpp/src/identity/state_transition/identity_update_transition/apply_identity_update_transition.rs @@ -1,9 +1,10 @@ +use std::convert::TryInto; use std::sync::Arc; use crate::{ consensus::{basic::BasicError, ConsensusError}, identity::get_biggest_possible_identity, - prelude::{Identifier, Identity}, + prelude::Identifier, state_repository::StateRepositoryLike, state_transition::StateTransitionLike, ProtocolError, @@ -33,12 +34,15 @@ pub async fn apply_identity_update_transition( state_repository: &impl StateRepositoryLike, state_transition: IdentityUpdateTransition, ) -> Result<(), ProtocolError> { - let mut maybe_identity: Option = state_repository + let mut maybe_identity = state_repository .fetch_identity( state_transition.get_identity_id(), state_transition.get_execution_context(), ) - .await?; + .await? + .map(TryInto::try_into) + .transpose() + .map_err(Into::into)?; if state_transition.get_execution_context().is_dry_run() { maybe_identity = Some(get_biggest_possible_identity()) diff --git a/packages/rs-dpp/src/identity/state_transition/identity_update_transition/validate_identity_update_transition_state.rs b/packages/rs-dpp/src/identity/state_transition/identity_update_transition/validate_identity_update_transition_state.rs index 7d40e69a0fe..75ffaef5837 100644 --- a/packages/rs-dpp/src/identity/state_transition/identity_update_transition/validate_identity_update_transition_state.rs +++ b/packages/rs-dpp/src/identity/state_transition/identity_update_transition/validate_identity_update_transition_state.rs @@ -1,12 +1,12 @@ use dashcore::BlockHeader; use serde_json::Value; +use std::convert::TryInto; use std::sync::Arc; use crate::{ block_time_window::validate_time_in_block_time_window::validate_time_in_block_time_window, consensus::basic::BasicError, identity::validation::{RequiredPurposeAndSecurityLevelValidator, TPublicKeysValidator}, - prelude::Identity, state_repository::StateRepositoryLike, state_transition::StateTransitionLike, validation::SimpleValidationResult, @@ -38,13 +38,16 @@ where ) -> Result { let mut validation_result = SimpleValidationResult::default(); - let maybe_stored_identity: Option = self + let maybe_stored_identity = self .state_repository .fetch_identity( state_transition.get_identity_id(), state_transition.get_execution_context(), ) - .await + .await? + .map(TryInto::try_into) + .transpose() + .map_err(Into::into) .map_err(|e| NonConsensusError::StateRepositoryFetchError(e.to_string()))?; if state_transition.get_execution_context().is_dry_run() { diff --git a/packages/rs-dpp/src/state_repository.rs b/packages/rs-dpp/src/state_repository.rs index 1aa13ffcf3f..8bbd4891c1d 100644 --- a/packages/rs-dpp/src/state_repository.rs +++ b/packages/rs-dpp/src/state_repository.rs @@ -1,11 +1,10 @@ -use std::convert::{Infallible, TryInto}; - use anyhow::Result as AnyResult; use async_trait::async_trait; use dashcore::InstantLock; #[cfg(any(test, feature = "mocks"))] use mockall::{automock, predicate::*}; use serde_json::Value as JsonValue; +use std::convert::{Infallible, TryInto}; use crate::{ prelude::*, @@ -18,12 +17,25 @@ impl From for ProtocolError { } } +#[derive(Clone, Default)] +pub struct FetchTransactionResponse { + pub height: Option, + pub data: Option>, +} + // Let StateRepositoryLike mock return DataContracts instead of bytes to simplify things a bit. -#[cfg_attr(any(test, feature="mocks"), automock(type ConversionError=Infallible; type FetchDataContract=DataContract;))] +#[cfg_attr(any(test, feature="mocks"), automock( + type ConversionError=Infallible; + type FetchDataContract=DataContract; + type FetchIdentity=Identity; + type FetchTransaction=FetchTransactionResponse; +))] #[async_trait] pub trait StateRepositoryLike: Send + Sync { type ConversionError: Into; type FetchDataContract: TryInto; + type FetchIdentity: TryInto; + type FetchTransaction: TryInto; /// Fetch the Data Contract by ID /// By default, the method should return data as bytes (`Vec`), but the deserialization to [`DataContract`] should be also possible @@ -77,23 +89,19 @@ pub trait StateRepositoryLike: Send + Sync { /// Fetch the Transaction /// By default, the method should return data as bytes (`Vec`), but the deserialization to [`Transaction`] should be also possible - async fn fetch_transaction( + async fn fetch_transaction( &self, id: &str, execution_context: &StateTransitionExecutionContext, - ) -> AnyResult> - where - T: for<'de> serde::de::Deserialize<'de> + 'static; + ) -> AnyResult; /// Fetch Identity by ID /// By default, the method should return data as bytes (`Vec`), but the deserialization to [`Identity`] should be also possible - async fn fetch_identity( + async fn fetch_identity( &self, id: &Identifier, execution_context: &StateTransitionExecutionContext, - ) -> AnyResult> - where - T: for<'de> serde::de::Deserialize<'de> + 'static; + ) -> AnyResult>; /// Store Public Key hashes and Identity id pair async fn store_identity_public_key_hashes( @@ -161,6 +169,9 @@ pub trait StateRepositoryLike: Send + Sync { // Get latest (in a queue) withdrawal transaction index async fn fetch_latest_withdrawal_transaction_index(&self) -> AnyResult; + // Get latest (in a queue) withdrawal transaction index + async fn fetch_latest_platform_core_chain_locked_height(&self) -> AnyResult>; + // Enqueue withdrawal transaction async fn enqueue_withdrawal_transaction( &self, diff --git a/packages/rs-dpp/src/state_transition/validation/validate_state_transition_fee.rs b/packages/rs-dpp/src/state_transition/validation/validate_state_transition_fee.rs index edaac1cc1ad..b9fcdf39e90 100644 --- a/packages/rs-dpp/src/state_transition/validation/validate_state_transition_fee.rs +++ b/packages/rs-dpp/src/state_transition/validation/validate_state_transition_fee.rs @@ -1,4 +1,5 @@ use anyhow::Context; +use std::convert::TryInto; use crate::{ consensus::fee::FeeError, @@ -6,7 +7,6 @@ use crate::{ convert_satoshi_to_credits, state_transition::asset_lock_proof::AssetLockTransactionOutputFetcher, }, - prelude::Identity, state_repository::StateRepositoryLike, state_transition::{StateTransition, StateTransitionIdentitySigned, StateTransitionLike}, validation::SimpleValidationResult, @@ -66,11 +66,17 @@ where })?; let balance = convert_satoshi_to_credits(output.value); let identity_id = st.get_owner_id(); - let identity: Identity = self + let identity = self .state_repository - .fetch_identity(identity_id, execution_context) + .fetch_identity(identity_id, st.get_execution_context()) .await? - .with_context(|| format!("identity with ID {}' doesn't exist", identity_id))?; + .map(TryInto::try_into) + .transpose() + .map_err(Into::into)? + .ok_or_else(|| ProtocolError::IdentityNotPresentError { + id: identity_id.clone(), + })?; + if execution_context.is_dry_run() { return Ok(result); } @@ -128,11 +134,16 @@ where st: &impl StateTransitionIdentitySigned, ) -> Result { let identity_id = st.get_owner_id(); - let identity: Identity = self + let identity = self .state_repository .fetch_identity(identity_id, st.get_execution_context()) .await? - .with_context(|| format!("identity with ID {}' doesn't exist", identity_id))?; + .map(TryInto::try_into) + .transpose() + .map_err(Into::into)? + .ok_or_else(|| ProtocolError::IdentityNotPresentError { + id: identity_id.clone(), + })?; Ok(identity.get_balance()) } diff --git a/packages/rs-dpp/src/state_transition/validation/validate_state_transition_identity_signature.rs b/packages/rs-dpp/src/state_transition/validation/validate_state_transition_identity_signature.rs index 83129c46ddc..b21714ef049 100644 --- a/packages/rs-dpp/src/state_transition/validation/validate_state_transition_identity_signature.rs +++ b/packages/rs-dpp/src/state_transition/validation/validate_state_transition_identity_signature.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::{collections::HashSet, convert::TryInto}; use anyhow::Context; use lazy_static::lazy_static; @@ -6,7 +6,6 @@ use lazy_static::lazy_static; use crate::{ consensus::{signature::SignatureError, ConsensusError}, identity::KeyType, - prelude::Identity, state_repository::StateRepositoryLike, state_transition::{ fee::operations::{Operation, SignatureVerificationOperation}, @@ -41,8 +40,11 @@ pub async fn validate_state_transition_identity_signature( // Owner must exist let maybe_identity = state_repository - .fetch_identity::(state_transition.get_owner_id(), &tmp_execution_context) - .await?; + .fetch_identity(state_transition.get_owner_id(), &tmp_execution_context) + .await? + .map(TryInto::try_into) + .transpose() + .map_err(Into::into)?; // Collect operations back from temporary context state_transition @@ -327,7 +329,7 @@ mod test { state_transition.owner_id = owner_id.clone(); state_repository_mock - .expect_fetch_identity::() + .expect_fetch_identity() .returning(move |_, _| Ok(None)); let result = validate_state_transition_identity_signature( diff --git a/packages/rs-dpp/src/state_transition/validation/validate_state_transition_key_signature.rs b/packages/rs-dpp/src/state_transition/validation/validate_state_transition_key_signature.rs index 0d532f9a872..49471cc7ffc 100644 --- a/packages/rs-dpp/src/state_transition/validation/validate_state_transition_key_signature.rs +++ b/packages/rs-dpp/src/state_transition/validation/validate_state_transition_key_signature.rs @@ -10,7 +10,6 @@ use crate::{ state_transition::asset_lock_proof::{AssetLockProof, AssetLockPublicKeyHashFetcher}, KeyType, }, - prelude::Identity, state_repository::StateRepositoryLike, state_transition::{ fee::operations::{Operation, SignatureVerificationOperation}, @@ -76,7 +75,7 @@ pub async fn validate_state_transition_key_signature( // Target identity must exist let identity = state_repository - .fetch_identity::(target_identity_id, &tmp_execution_context) + .fetch_identity(target_identity_id, &tmp_execution_context) .await?; // Collect operations back from temporary context @@ -146,7 +145,6 @@ mod test { }, KeyType, }, - prelude::Identity, state_repository::MockStateRepositoryLike, state_transition::{StateTransition, StateTransitionConvert, StateTransitionLike}, tests::{ @@ -302,7 +300,7 @@ mod test { .into(); state_repository - .expect_fetch_identity::() + .expect_fetch_identity() .return_once(|_, _| Ok(None)); let result = validate_state_transition_key_signature( diff --git a/packages/rs-dpp/src/tests/identity/state_transition/identity_create_transition/validation/basic/identity_create_transition_basic_validator_spec.rs b/packages/rs-dpp/src/tests/identity/state_transition/identity_create_transition/validation/basic/identity_create_transition_basic_validator_spec.rs index 17d5613bab5..b9bba63176e 100644 --- a/packages/rs-dpp/src/tests/identity/state_transition/identity_create_transition/validation/basic/identity_create_transition_basic_validator_spec.rs +++ b/packages/rs-dpp/src/tests/identity/state_transition/identity_create_transition/validation/basic/identity_create_transition_basic_validator_spec.rs @@ -68,7 +68,7 @@ pub fn setup_test( ( crate::tests::fixtures::identity_create_transition_fixture_json(None), IdentityCreateTransitionBasicValidator::new( - Arc::new(protocol_version_validator), + protocol_version_validator, public_keys_validator, public_keys_transition_validator, asset_lock_proof_validator, diff --git a/packages/rs-dpp/src/tests/identity/state_transition/identity_credit_withdrawal_transition/apply_identity_credit_withdrawal_transition_factory_spec.rs b/packages/rs-dpp/src/tests/identity/state_transition/identity_credit_withdrawal_transition/apply_identity_credit_withdrawal_transition_factory_spec.rs index 49e9fbd5395..292e0835ea5 100644 --- a/packages/rs-dpp/src/tests/identity/state_transition/identity_credit_withdrawal_transition/apply_identity_credit_withdrawal_transition_factory_spec.rs +++ b/packages/rs-dpp/src/tests/identity/state_transition/identity_credit_withdrawal_transition/apply_identity_credit_withdrawal_transition_factory_spec.rs @@ -25,7 +25,7 @@ mod apply_identity_credit_withdrawal_transition_factory { .returning(|_, _| anyhow::Ok(())); state_repository - .expect_fetch_identity::() + .expect_fetch_identity() .times(1) .withf(|id, _| *id == Identifier::default()) .returning(|_, _| { diff --git a/packages/rs-dpp/src/tests/identity/state_transition/identity_credit_withdrawal_transition/validation/state/validate_identity_credit_withdrawal_state_spec.rs b/packages/rs-dpp/src/tests/identity/state_transition/identity_credit_withdrawal_transition/validation/state/validate_identity_credit_withdrawal_state_spec.rs index 9bfdff46f49..43457371d21 100644 --- a/packages/rs-dpp/src/tests/identity/state_transition/identity_credit_withdrawal_transition/validation/state/validate_identity_credit_withdrawal_state_spec.rs +++ b/packages/rs-dpp/src/tests/identity/state_transition/identity_credit_withdrawal_transition/validation/state/validate_identity_credit_withdrawal_state_spec.rs @@ -43,7 +43,7 @@ mod validate_identity_credit_withdrawal_transition_state_factory { let mut state_repository = MockStateRepositoryLike::default(); state_repository - .expect_fetch_identity::() + .expect_fetch_identity() .times(1) .withf(|id, _| *id == Identifier::default()) .returning(|_, _| anyhow::Ok(None)); @@ -67,7 +67,7 @@ mod validate_identity_credit_withdrawal_transition_state_factory { let mut state_repository = MockStateRepositoryLike::default(); state_repository - .expect_fetch_identity::() + .expect_fetch_identity() .times(1) .withf(|id, _| *id == Identifier::default()) .returning(|_, _| { @@ -97,7 +97,7 @@ mod validate_identity_credit_withdrawal_transition_state_factory { let mut state_repository = MockStateRepositoryLike::default(); state_repository - .expect_fetch_identity::() + .expect_fetch_identity() .times(1) .withf(|id, _| *id == Identifier::default()) .returning(|_, _| Err(Error::msg("Some error"))); @@ -119,7 +119,7 @@ mod validate_identity_credit_withdrawal_transition_state_factory { let mut state_repository = MockStateRepositoryLike::default(); state_repository - .expect_fetch_identity::() + .expect_fetch_identity() .times(1) .withf(|id, _| *id == Identifier::default()) .returning(|_, _| { diff --git a/packages/rs-dpp/src/tests/identity/state_transition/identity_update_transition/apply_identity_udpate_transition_spec.rs b/packages/rs-dpp/src/tests/identity/state_transition/identity_update_transition/apply_identity_udpate_transition_spec.rs index fd39d5ea825..49f359cc89d 100644 --- a/packages/rs-dpp/src/tests/identity/state_transition/identity_update_transition/apply_identity_udpate_transition_spec.rs +++ b/packages/rs-dpp/src/tests/identity/state_transition/identity_update_transition/apply_identity_udpate_transition_spec.rs @@ -3,7 +3,6 @@ use crate::{ apply_identity_update_transition::apply_identity_update_transition, identity_update_transition::IdentityUpdateTransition, }, - prelude::Identity, state_repository::MockStateRepositoryLike, state_transition::StateTransitionLike, tests::fixtures::{get_identity_update_transition_fixture, identity_fixture}, @@ -66,7 +65,7 @@ async fn should_use_max_identity_on_dry_run() { let mut state_repository_mock = MockStateRepositoryLike::new(); state_repository_mock - .expect_fetch_identity::() + .expect_fetch_identity() .returning(|_, _| Ok(None)); state_repository_mock .expect_store_identity_public_key_hashes() diff --git a/packages/rs-dpp/src/tests/identity/state_transition/identity_update_transition/validate_identity_update_transition_basic_spec.rs b/packages/rs-dpp/src/tests/identity/state_transition/identity_update_transition/validate_identity_update_transition_basic_spec.rs index 43ad97fb8cd..d6f171951ba 100644 --- a/packages/rs-dpp/src/tests/identity/state_transition/identity_update_transition/validate_identity_update_transition_basic_spec.rs +++ b/packages/rs-dpp/src/tests/identity/state_transition/identity_update_transition/validate_identity_update_transition_basic_spec.rs @@ -116,7 +116,6 @@ fn property_should_be_present(property: &str) { let TestData { protocol_version_validator, validate_public_keys_mock, - public_keys_signatures_validator, mut raw_state_transition, .. } = setup_test(); diff --git a/packages/rs-dpp/src/tests/identity/state_transition/identity_update_transition/validate_identity_update_transition_state_spec.rs b/packages/rs-dpp/src/tests/identity/state_transition/identity_update_transition/validate_identity_update_transition_state_spec.rs index a07ed282af1..f3a3d23dace 100644 --- a/packages/rs-dpp/src/tests/identity/state_transition/identity_update_transition/validate_identity_update_transition_state_spec.rs +++ b/packages/rs-dpp/src/tests/identity/state_transition/identity_update_transition/validate_identity_update_transition_state_spec.rs @@ -397,7 +397,7 @@ async fn should_return_valid_result_on_dry_run() { let mut state_repository_mock = MockStateRepositoryLike::new(); state_repository_mock - .expect_fetch_identity::() + .expect_fetch_identity() .return_once(|_, _| Ok(None)); let validator = IdentityUpdateTransitionStateValidator::new( diff --git a/packages/rs-dpp/src/validation/validation_result.rs b/packages/rs-dpp/src/validation/validation_result.rs index a5b9ab0342e..386518a3312 100644 --- a/packages/rs-dpp/src/validation/validation_result.rs +++ b/packages/rs-dpp/src/validation/validation_result.rs @@ -17,6 +17,16 @@ impl ValidationResult { } } + pub fn map(self, f: F) -> ValidationResult + where + F: FnOnce(TData) -> U, + { + ValidationResult { + errors: self.errors, + data: self.data.map(f), + } + } + pub fn add_error(&mut self, error: T) where T: Into, diff --git a/packages/rs-dpp/src/version/protocol_version_validator.rs b/packages/rs-dpp/src/version/protocol_version_validator.rs index d39ef8b777e..9fbefb45e25 100644 --- a/packages/rs-dpp/src/version/protocol_version_validator.rs +++ b/packages/rs-dpp/src/version/protocol_version_validator.rs @@ -104,4 +104,8 @@ impl ProtocolVersionValidator { pub fn protocol_version(&self) -> u32 { self.current_protocol_version } + + pub fn set_current_protocol_version(&mut self, protocol_version: u32) { + self.current_protocol_version = protocol_version; + } } diff --git a/packages/wasm-dpp/karma.conf.js b/packages/wasm-dpp/karma.conf.js index dc391088263..b73064f7145 100644 --- a/packages/wasm-dpp/karma.conf.js +++ b/packages/wasm-dpp/karma.conf.js @@ -9,6 +9,11 @@ const webpackConfig = require('./webpack.config'); module.exports = (config) => { config.set({ + client: { + mocha: { + timeout: 4000, + }, + }, frameworks: ['mocha', 'chai', 'webpack'], files: [ 'lib/test/karma/loader.js', diff --git a/packages/wasm-dpp/lib/test/mocks/getBlsAdapterMock.js b/packages/wasm-dpp/lib/test/mocks/getBlsAdapterMock.js new file mode 100644 index 00000000000..847264bd542 --- /dev/null +++ b/packages/wasm-dpp/lib/test/mocks/getBlsAdapterMock.js @@ -0,0 +1,36 @@ +const BlsSignatures = require('@dashevo/dpp/lib/bls/bls'); + +module.exports = async function getBlsAdapterMock() { + const bls = await BlsSignatures.getInstance(); + + const blsAdapter = { + validatePublicKey(publicKeyBuffer) { + let pk; + + try { + pk = bls.PublicKey.fromBytes(publicKeyBuffer); + } catch (e) { + return false; + } + + return Boolean(pk); + }, + sign(data, key) { + const blsKey = bls.PrivateKey.fromBytes(key, true); + const signature = blsKey.sign(data); + return Buffer.from(signature.serialize()); + }, + verifySignature(signature, data, publicKey) { + const { PublicKey, Signature: BlsSignature, AggregationInfo } = bls; + + const blsKey = PublicKey.fromBytes(publicKey); + + const aggregationInfo = AggregationInfo.fromMsg(blsKey, data); + const blsSignature = BlsSignature.fromBytesAndAggregationInfo(signature, aggregationInfo); + + return blsSignature.verify(); + }, + }; + + return blsAdapter; +}; diff --git a/packages/wasm-dpp/src/bls_adapter.rs b/packages/wasm-dpp/src/bls_adapter.rs index 62d0176ed48..56346e69821 100644 --- a/packages/wasm-dpp/src/bls_adapter.rs +++ b/packages/wasm-dpp/src/bls_adapter.rs @@ -1,9 +1,11 @@ use dpp::dashcore::anyhow::anyhow; use dpp::{BlsModule, PublicKeyValidationError}; use wasm_bindgen::prelude::*; +use wasm_bindgen::JsCast; #[wasm_bindgen] extern "C" { + #[derive(Clone)] pub type JsBlsAdapter; #[wasm_bindgen(method)] @@ -66,6 +68,19 @@ impl BlsModule for BlsAdapter { self.0 .sign(data, private_key) .map(|arr| arr.to_vec()) - .map_err(|_v| anyhow!("Can't sign").into()) + .map_err(|e: JsValue| { + let error = e.dyn_into::(); + + match error { + Ok(e) => { + let message: String = e + .message() + .as_string() + .unwrap_or_else(|| String::from("Unknown error, can't sign")); + anyhow!(message).into() + } + Err(_) => anyhow!("Unknown error, can't sign").into(), + } + }) } } diff --git a/packages/wasm-dpp/src/data_contract/state_transition/data_contract_create_transition/validation.rs b/packages/wasm-dpp/src/data_contract/state_transition/data_contract_create_transition/validation.rs index 74c754d1aa5..cafee55de3f 100644 --- a/packages/wasm-dpp/src/data_contract/state_transition/data_contract_create_transition/validation.rs +++ b/packages/wasm-dpp/src/data_contract/state_transition/data_contract_create_transition/validation.rs @@ -14,11 +14,11 @@ pub async fn validate_data_contract_create_transition_state( state_transition: DataContractCreateTransitionWasm, ) -> Result { let wrapped_state_repository = ExternalStateRepositoryLikeWrapper::new(state_repository); - dpp_validate_data_contract_create_transition_state( + let validation_result = dpp_validate_data_contract_create_transition_state( &wrapped_state_repository, &state_transition.into(), ) .await - .map(Into::::into) - .map_err(from_dpp_err) + .map_err(from_dpp_err)?; + Ok(validation_result.map(|_| JsValue::undefined()).into()) } diff --git a/packages/wasm-dpp/src/data_contract/state_transition/data_contract_update_transition/validation.rs b/packages/wasm-dpp/src/data_contract/state_transition/data_contract_update_transition/validation.rs index c6664209f79..a740541bcc8 100644 --- a/packages/wasm-dpp/src/data_contract/state_transition/data_contract_update_transition/validation.rs +++ b/packages/wasm-dpp/src/data_contract/state_transition/data_contract_update_transition/validation.rs @@ -19,13 +19,14 @@ pub async fn validate_data_contract_update_transition_state( state_transition: DataContractUpdateTransitionWasm, ) -> Result { let wrapped_state_repository = ExternalStateRepositoryLikeWrapper::new(state_repository); - dpp_validate_data_contract_update_transition_state( + let result = dpp_validate_data_contract_update_transition_state( &wrapped_state_repository, &state_transition.into(), ) .await - .map(Into::into) - .map_err(from_dpp_err) + .map_err(from_dpp_err)?; + + Ok(result.map(|_| JsValue::undefined()).into()) } #[wasm_bindgen(js_name=validateIndicesAreBackwardCompatible)] @@ -40,7 +41,9 @@ pub fn validate_indices_are_backward_compatible( new_documents_schema, )?; - dpp_validate_indices_are_backward_compatible(old_documents.iter(), new_documents.iter()) - .map(Into::into) - .map_err(from_protocol_error) + let result = + dpp_validate_indices_are_backward_compatible(old_documents.iter(), new_documents.iter()) + .map_err(from_protocol_error)?; + + Ok(result.map(|_| JsValue::undefined()).into()) } diff --git a/packages/wasm-dpp/src/data_contract_factory/data_contract_factory.rs b/packages/wasm-dpp/src/data_contract_factory/data_contract_factory.rs index 00d3ed8ff03..0de231dd61b 100644 --- a/packages/wasm-dpp/src/data_contract_factory/data_contract_factory.rs +++ b/packages/wasm-dpp/src/data_contract_factory/data_contract_factory.rs @@ -42,15 +42,14 @@ impl DataContractValidatorWasm { DataContractValidator::new(std::sync::Arc::new(ProtocolVersionValidator::default())).into() } - #[wasm_bindgen(js_name=validate)] + #[wasm_bindgen] pub fn validate(&self, raw_data_contract: JsValue) -> Result { let parameters: DataContractParameters = with_js_error!(serde_wasm_bindgen::from_value(raw_data_contract))?; let json_object = serde_json::to_value(parameters).expect("Implements Serialize"); - self.0 - .validate(&json_object) - .map(Into::into) - .map_err(from_protocol_error) + let validation_result = self.0.validate(&json_object).map_err(from_protocol_error)?; + + Ok(validation_result.map(|_| JsValue::undefined()).into()) } } diff --git a/packages/wasm-dpp/src/errors/consensus/basic/identity/identity_asset_lock_proof_locked_transaction_mismatch_error.rs b/packages/wasm-dpp/src/errors/consensus/basic/identity/identity_asset_lock_proof_locked_transaction_mismatch_error.rs index 2769b8809de..a8c39bb9632 100644 --- a/packages/wasm-dpp/src/errors/consensus/basic/identity/identity_asset_lock_proof_locked_transaction_mismatch_error.rs +++ b/packages/wasm-dpp/src/errors/consensus/basic/identity/identity_asset_lock_proof_locked_transaction_mismatch_error.rs @@ -1,5 +1,6 @@ use dpp::consensus::basic::identity::IdentityAssetLockProofLockedTransactionMismatchError; use dpp::consensus::ConsensusError; +use dpp::dashcore::hashes::Hash; use wasm_bindgen::prelude::*; use crate::buffer::Buffer; @@ -22,13 +23,16 @@ impl IdentityAssetLockProofLockedTransactionMismatchErrorWasm { #[wasm_bindgen(js_name=getInstantLockTransactionId)] pub fn instant_lock_transaction_id(&self) -> Buffer { let tx_id = self.inner.instant_lock_transaction_id(); - Buffer::from_bytes(tx_id.to_vec().as_ref()) + Buffer::from_bytes(&tx_id) } #[wasm_bindgen(js_name=getAssetLockTransactionId)] pub fn asset_lock_transaction_id(&self) -> Buffer { let tx_id = self.inner.asset_lock_transaction_id(); - Buffer::from_bytes(tx_id.to_vec().as_ref()) + let mut hash_bytes = tx_id.as_hash().into_inner(); + hash_bytes.reverse(); + + Buffer::from_bytes(&hash_bytes) } #[wasm_bindgen(js_name=getCode)] diff --git a/packages/wasm-dpp/src/errors/consensus/basic/identity/identity_asset_lock_transaction_out_point_already_exists_error.rs b/packages/wasm-dpp/src/errors/consensus/basic/identity/identity_asset_lock_transaction_out_point_already_exists_error.rs index cea35f0c8da..50504144be7 100644 --- a/packages/wasm-dpp/src/errors/consensus/basic/identity/identity_asset_lock_transaction_out_point_already_exists_error.rs +++ b/packages/wasm-dpp/src/errors/consensus/basic/identity/identity_asset_lock_transaction_out_point_already_exists_error.rs @@ -1,5 +1,6 @@ use dpp::consensus::basic::identity::IdentityAssetLockTransactionOutPointAlreadyExistsError; use dpp::consensus::ConsensusError; +use dpp::dashcore::hashes::Hash; use wasm_bindgen::prelude::*; use crate::buffer::Buffer; @@ -19,7 +20,7 @@ impl From<&IdentityAssetLockTransactionOutPointAlreadyExistsError> #[wasm_bindgen(js_class=IdentityAssetLockTransactionOutPointAlreadyExistsError)] impl IdentityAssetLockTransactionOutPointAlreadyExistsErrorWasm { - #[wasm_bindgen(js_name=getDuplicatedIds)] + #[wasm_bindgen(js_name=getOutputIndex)] pub fn output_index(&self) -> usize { self.inner.output_index() } @@ -27,7 +28,9 @@ impl IdentityAssetLockTransactionOutPointAlreadyExistsErrorWasm { #[wasm_bindgen(js_name=getTransactionId)] pub fn transaction_id(&self) -> Buffer { let tx_id = self.inner.transaction_id(); - Buffer::from_bytes(tx_id.to_vec().as_ref()) + let mut tx_id_bytes = tx_id.as_hash().into_inner(); + tx_id_bytes.reverse(); + Buffer::from_bytes(&tx_id_bytes) } #[wasm_bindgen(js_name=getCode)] diff --git a/packages/wasm-dpp/src/errors/consensus/basic/identity/invalid_asset_lock_proof_transaction_height_error.rs b/packages/wasm-dpp/src/errors/consensus/basic/identity/invalid_asset_lock_proof_transaction_height_error.rs index 6a8befc8dbb..d2a54f55e26 100644 --- a/packages/wasm-dpp/src/errors/consensus/basic/identity/invalid_asset_lock_proof_transaction_height_error.rs +++ b/packages/wasm-dpp/src/errors/consensus/basic/identity/invalid_asset_lock_proof_transaction_height_error.rs @@ -22,9 +22,9 @@ impl InvalidAssetLockProofTransactionHeightErrorWasm { self.inner.proof_core_chain_locked_height() } - #[wasm_bindgen(js_name=getCurrentCoreChainLockedHeight)] - pub fn current_core_chain_locked_height(&self) -> Option { - self.inner.current_core_chain_locked_height() + #[wasm_bindgen(js_name=getTransactionHeight)] + pub fn transaction_height(&self) -> Option { + self.inner.transaction_height() } #[wasm_bindgen(js_name=getCode)] diff --git a/packages/wasm-dpp/src/errors/dpp_error.rs b/packages/wasm-dpp/src/errors/dpp_error.rs new file mode 100644 index 00000000000..b68bb74833a --- /dev/null +++ b/packages/wasm-dpp/src/errors/dpp_error.rs @@ -0,0 +1,17 @@ +use crate::identity::errors::{ + AssetLockOutputNotFoundErrorWasm, AssetLockTransactionIsNotFoundErrorWasm, +}; +use dpp::DPPError; +use wasm_bindgen::JsValue; + +pub fn from_dpp_error_ref(e: &DPPError) -> JsValue { + match e { + DPPError::AssetLockOutputNotFoundError(e) => { + AssetLockOutputNotFoundErrorWasm::from(e).into() + } + DPPError::AssetLockTransactionIsNotFoundError(e) => { + AssetLockTransactionIsNotFoundErrorWasm::from(e).into() + } + _ => unimplemented!(), + } +} diff --git a/packages/wasm-dpp/src/errors/js_conversion.rs b/packages/wasm-dpp/src/errors/js_conversion.rs index f4a6c55517d..8d3e9377721 100644 --- a/packages/wasm-dpp/src/errors/js_conversion.rs +++ b/packages/wasm-dpp/src/errors/js_conversion.rs @@ -1,9 +1,7 @@ -use dpp::dashcore::anyhow::bail; use serde_json; use serde_wasm_bindgen::Error; use thiserror::Error; use wasm_bindgen::JsValue; -use web_sys::console::log_1; /// This is a rust-specific errors. In addition to all errors defined in `js-dpp`, the /// error might be triggered when using JS bindings diff --git a/packages/wasm-dpp/src/errors/mod.rs b/packages/wasm-dpp/src/errors/mod.rs index 0cfd5a0dbd8..c211b8159c9 100644 --- a/packages/wasm-dpp/src/errors/mod.rs +++ b/packages/wasm-dpp/src/errors/mod.rs @@ -9,3 +9,5 @@ pub mod protocol_error; mod public_key_validation_error; pub use public_key_validation_error::*; + +pub mod dpp_error; diff --git a/packages/wasm-dpp/src/identifier/mod.rs b/packages/wasm-dpp/src/identifier/mod.rs index e9349482179..eda8c747f4e 100644 --- a/packages/wasm-dpp/src/identifier/mod.rs +++ b/packages/wasm-dpp/src/identifier/mod.rs @@ -79,7 +79,7 @@ impl IdentifierWrapper { #[wasm_bindgen(js_name=toBuffer)] pub fn to_buffer(&self) -> Buffer { - Buffer::from_bytes(self.wrapped.buffer.as_slice()) + Buffer::from_bytes(&self.wrapped.buffer) } #[wasm_bindgen(js_name=toJSON)] diff --git a/packages/wasm-dpp/src/identity/errors/asset_lock_output_not_found_error.rs b/packages/wasm-dpp/src/identity/errors/asset_lock_output_not_found_error.rs new file mode 100644 index 00000000000..4e137b2dfc2 --- /dev/null +++ b/packages/wasm-dpp/src/identity/errors/asset_lock_output_not_found_error.rs @@ -0,0 +1,11 @@ +use dpp::identity::errors::AssetLockOutputNotFoundError; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen(js_name=AssetLockOutputNotFoundError)] +pub struct AssetLockOutputNotFoundErrorWasm(AssetLockOutputNotFoundError); + +impl From<&AssetLockOutputNotFoundError> for AssetLockOutputNotFoundErrorWasm { + fn from(e: &AssetLockOutputNotFoundError) -> Self { + Self(e.clone()) + } +} diff --git a/packages/wasm-dpp/src/identity/errors/asset_lock_transaction_is_not_found_error.rs b/packages/wasm-dpp/src/identity/errors/asset_lock_transaction_is_not_found_error.rs new file mode 100644 index 00000000000..3687cd2c721 --- /dev/null +++ b/packages/wasm-dpp/src/identity/errors/asset_lock_transaction_is_not_found_error.rs @@ -0,0 +1,20 @@ +use dpp::dashcore::hashes::hex::ToHex; +use dpp::identity::errors::AssetLockTransactionIsNotFoundError; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen(js_name=AssetLockTransactionIsNotFoundError)] +pub struct AssetLockTransactionIsNotFoundErrorWasm(AssetLockTransactionIsNotFoundError); + +impl From<&AssetLockTransactionIsNotFoundError> for AssetLockTransactionIsNotFoundErrorWasm { + fn from(e: &AssetLockTransactionIsNotFoundError) -> Self { + Self(e.clone()) + } +} + +#[wasm_bindgen(js_class=AssetLockTransactionIsNotFoundError)] +impl AssetLockTransactionIsNotFoundErrorWasm { + #[wasm_bindgen(js_name=getTransactionId)] + pub fn transaction_id(&self) -> JsValue { + self.0.transaction_id().to_hex().into() + } +} diff --git a/packages/wasm-dpp/src/identity/errors/mod.rs b/packages/wasm-dpp/src/identity/errors/mod.rs new file mode 100644 index 00000000000..d4812382a93 --- /dev/null +++ b/packages/wasm-dpp/src/identity/errors/mod.rs @@ -0,0 +1,7 @@ +pub use asset_lock_output_not_found_error::*; +pub use asset_lock_transaction_is_not_found_error::*; +pub use unknown_asset_lock_proof_type_error::*; + +mod asset_lock_output_not_found_error; +mod asset_lock_transaction_is_not_found_error; +mod unknown_asset_lock_proof_type_error; diff --git a/packages/wasm-dpp/src/identity/errors/unknown_asset_lock_proof_type_error.rs b/packages/wasm-dpp/src/identity/errors/unknown_asset_lock_proof_type_error.rs new file mode 100644 index 00000000000..a56c2f13a2c --- /dev/null +++ b/packages/wasm-dpp/src/identity/errors/unknown_asset_lock_proof_type_error.rs @@ -0,0 +1,26 @@ +use dpp::identity::errors::UnknownAssetLockProofTypeError; +use wasm_bindgen::prelude::*; + +#[derive(Clone)] +#[wasm_bindgen(js_name=UnknownAssetLockProofTypeError)] +pub struct UnknownAssetLockProofTypeErrorWasm(UnknownAssetLockProofTypeError); + +impl From<&UnknownAssetLockProofTypeError> for UnknownAssetLockProofTypeErrorWasm { + fn from(e: &UnknownAssetLockProofTypeError) -> Self { + Self(e.clone()) + } +} + +impl From for UnknownAssetLockProofTypeErrorWasm { + fn from(e: UnknownAssetLockProofTypeError) -> Self { + Self(e) + } +} + +#[wasm_bindgen(js_class=UnknownAssetLockProofTypeError)] +impl UnknownAssetLockProofTypeErrorWasm { + #[wasm_bindgen(js_name=getType)] + pub fn get_type(&self) -> Option { + self.0.asset_lock_type() + } +} diff --git a/packages/wasm-dpp/src/identity/identity_facade.rs b/packages/wasm-dpp/src/identity/identity_facade.rs index 63e0c5cc87f..878c2a663a3 100644 --- a/packages/wasm-dpp/src/identity/identity_facade.rs +++ b/packages/wasm-dpp/src/identity/identity_facade.rs @@ -38,10 +38,8 @@ impl IdentityFacadeWasm { let identity_json = serde_wasm_bindgen::from_value(raw_identity_object) .expect("unable to serialize identity"); // TODO: handle the case when - self.0 - .validate(&identity_json) - .map(|res| res.into()) - .map_err(|err| err.into()) + let validation_result = self.0.validate(&identity_json)?; + Ok(validation_result.map(|_| JsValue::undefined()).into()) } } diff --git a/packages/wasm-dpp/src/identity/identity_public_key/mod.rs b/packages/wasm-dpp/src/identity/identity_public_key/mod.rs index 20f12b34ce6..e00cc58d0c9 100644 --- a/packages/wasm-dpp/src/identity/identity_public_key/mod.rs +++ b/packages/wasm-dpp/src/identity/identity_public_key/mod.rs @@ -136,32 +136,52 @@ impl IdentityPublicKeyWasm { } #[wasm_bindgen(js_name=toObject)] - pub fn to_object(&self, some_option: Option) -> Result { + pub fn to_object(&self, skip_signatures: Option) -> Result { let val = self .0 - .to_raw_json_object(some_option.unwrap_or(false)) + .to_raw_json_object(skip_signatures.unwrap_or(false)) .map_err(|e| from_dpp_err(e.into()))?; let data_buffer = Buffer::from_bytes(self.0.get_data()); - let signature_buffer = Buffer::from_bytes(self.0.get_signature()); let json = val.to_string(); let js_object = js_sys::JSON::parse(&json)?; js_sys::Reflect::set( &js_object, - &"data".to_owned().into(), - &JsValue::from(data_buffer), + &JsValue::from_str("type"), + &JsValue::from(self.get_type()), )?; js_sys::Reflect::set( &js_object, - &"signature".to_owned().into(), - &JsValue::from(signature_buffer), + &"data".to_owned().into(), + &JsValue::from(data_buffer), )?; + if !skip_signatures.unwrap_or(false) { + let signature = self.0.get_signature(); + if !signature.is_empty() { + js_sys::Reflect::set( + &js_object, + &"signature".to_owned().into(), + &JsValue::from(Buffer::from_bytes(signature)), + )?; + } + } + Ok(js_object) } + + #[wasm_bindgen(js_name=setSignature)] + pub fn set_signature(&mut self, signature: Vec) { + self.0.set_signature(signature); + } + + #[wasm_bindgen(js_name=getSignature)] + pub fn get_signature(&mut self) -> Buffer { + Buffer::from_bytes(self.0.get_signature()) + } } impl IdentityPublicKeyWasm { diff --git a/packages/wasm-dpp/src/identity/mod.rs b/packages/wasm-dpp/src/identity/mod.rs index 5ce3e41a897..b244377c85d 100644 --- a/packages/wasm-dpp/src/identity/mod.rs +++ b/packages/wasm-dpp/src/identity/mod.rs @@ -19,15 +19,24 @@ use crate::utils::to_vec_of_serde_values; use crate::MetadataWasm; pub use identity_public_key::*; +pub use state_transition::*; + +pub mod errors; +pub mod state_transition; + #[wasm_bindgen(js_name=Identity)] #[derive(Clone)] pub struct IdentityWasm(Identity); -#[wasm_bindgen(js_name=AssetLockProof)] -pub struct AssetLockProofWasm(AssetLockProof); -impl From for AssetLockProofWasm { - fn from(v: AssetLockProof) -> Self { - AssetLockProofWasm(v) +impl From for Identity { + fn from(identity: IdentityWasm) -> Self { + identity.0 + } +} + +impl From for IdentityWasm { + fn from(identity: Identity) -> Self { + Self(identity) } } diff --git a/packages/wasm-dpp/src/identity/state_transition/asset_lock_proof/chain/chain_asset_lock_proof.rs b/packages/wasm-dpp/src/identity/state_transition/asset_lock_proof/chain/chain_asset_lock_proof.rs new file mode 100644 index 00000000000..8cdcbcf7ee3 --- /dev/null +++ b/packages/wasm-dpp/src/identity/state_transition/asset_lock_proof/chain/chain_asset_lock_proof.rs @@ -0,0 +1,132 @@ +use serde::{Deserialize, Serialize}; +use std::convert::TryInto; +use wasm_bindgen::prelude::*; + +use crate::{ + buffer::Buffer, + errors::{from_dpp_err, RustConversionError}, + identifier::IdentifierWrapper, + with_js_error, +}; +use dpp::identity::state_transition::asset_lock_proof::chain::ChainAssetLockProof; +use dpp::util::string_encoding; +use dpp::util::string_encoding::Encoding; + +#[wasm_bindgen(js_name=ChainAssetLockProof)] +#[derive(Clone)] +pub struct ChainAssetLockProofWasm(ChainAssetLockProof); + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct ChainAssetLockProofParams { + #[serde(rename = "type")] + lock_type: u8, + core_chain_locked_height: u32, + out_point: Vec, +} + +impl From for ChainAssetLockProofWasm { + fn from(v: ChainAssetLockProof) -> Self { + ChainAssetLockProofWasm(v) + } +} + +impl From for ChainAssetLockProof { + fn from(v: ChainAssetLockProofWasm) -> Self { + v.0 + } +} + +#[wasm_bindgen(js_class = ChainAssetLockProof)] +impl ChainAssetLockProofWasm { + #[wasm_bindgen(constructor)] + pub fn new(raw_parameters: JsValue) -> Result { + let parameters: ChainAssetLockProofParams = + with_js_error!(serde_wasm_bindgen::from_value(raw_parameters))?; + + let out_point: [u8; 36] = parameters.out_point.try_into().map_err(|_| { + RustConversionError::Error(String::from("outPoint must be a 36 byte array")) + .to_js_value() + })?; + + let chain_asset_lock_proof = + ChainAssetLockProof::new(parameters.core_chain_locked_height, out_point); + + Ok(ChainAssetLockProofWasm(chain_asset_lock_proof)) + } + + #[wasm_bindgen(js_name=getType)] + pub fn get_type(&self) -> u8 { + ChainAssetLockProof::asset_lock_type() + } + + #[wasm_bindgen(js_name=getCoreChainLockedHeight)] + pub fn get_core_chain_locked_height(&self) -> u32 { + self.0.core_chain_locked_height() + } + + #[wasm_bindgen(js_name=setCoreChainLockedHeight)] + pub fn set_core_chain_locked_height(&mut self, value: u32) { + self.0.set_core_chain_locked_height(value); + } + + #[wasm_bindgen(js_name=getOutPoint)] + pub fn get_out_point(&self) -> Buffer { + Buffer::from_bytes(self.0.out_point()) + } + + #[wasm_bindgen(js_name=setOutPoint)] + pub fn set_out_point(&mut self, out_point: Vec) -> Result<(), JsValue> { + let out_point: [u8; 36] = out_point.try_into().map_err(|_| { + RustConversionError::Error(String::from("outPoint must be a 36 byte array")) + .to_js_value() + })?; + self.0.set_out_point(out_point); + + Ok(()) + } + + #[wasm_bindgen(js_name=toJSON)] + pub fn to_json(&self) -> Result { + let js_object = self.to_object()?; + + let out_point_base64 = string_encoding::encode(self.0.out_point(), Encoding::Base64); + + js_sys::Reflect::set( + &js_object, + &"outPoint".to_owned().into(), + &JsValue::from(out_point_base64), + )?; + + Ok(js_object) + } + + #[wasm_bindgen(js_name=toObject)] + pub fn to_object(&self) -> Result { + let asset_lock_json = + serde_json::to_value(self.0.clone()).map_err(|e| from_dpp_err(e.into()))?; + + let asset_lock_json_string = + serde_json::to_string(&asset_lock_json).map_err(|e| from_dpp_err(e.into()))?; + let js_object = js_sys::JSON::parse(&asset_lock_json_string)?; + + let out_point = self.get_out_point(); + + js_sys::Reflect::set( + &js_object, + &"outPoint".to_owned().into(), + &JsValue::from(out_point), + )?; + + Ok(js_object) + } + + #[wasm_bindgen(js_name=createIdentifier)] + pub fn create_identifier(&self) -> Result { + let identifier = self + .0 + .create_identifier() + .map_err(|e| from_dpp_err(e.into()))?; + Ok(identifier.into()) + } +} diff --git a/packages/wasm-dpp/src/identity/state_transition/asset_lock_proof/chain/chain_asset_lock_proof_structure_validator.rs b/packages/wasm-dpp/src/identity/state_transition/asset_lock_proof/chain/chain_asset_lock_proof_structure_validator.rs new file mode 100644 index 00000000000..9393008cbd8 --- /dev/null +++ b/packages/wasm-dpp/src/identity/state_transition/asset_lock_proof/chain/chain_asset_lock_proof_structure_validator.rs @@ -0,0 +1,72 @@ +use dpp::identity::state_transition::asset_lock_proof::{ + AssetLockTransactionValidator, ChainAssetLockProofStructureValidator, +}; +use dpp::state_transition::state_transition_execution_context::StateTransitionExecutionContext; +use dpp::ProtocolError; +use std::sync::Arc; +use wasm_bindgen::prelude::*; + +use crate::buffer::Buffer; +use crate::utils::ToSerdeJSONExt; +use crate::{ + errors::from_dpp_err, + state_repository::{ExternalStateRepositoryLike, ExternalStateRepositoryLikeWrapper}, + validation::ValidationResultWasm, + StateTransitionExecutionContextWasm, +}; + +#[wasm_bindgen(js_name = ChainAssetLockProofStructureValidator)] +pub struct ChainAssetLockProofStructureValidatorWasm( + ChainAssetLockProofStructureValidator, +); + +impl From> + for ChainAssetLockProofStructureValidatorWasm +{ + fn from( + validator: ChainAssetLockProofStructureValidator, + ) -> Self { + Self(validator) + } +} + +#[wasm_bindgen(js_class = ChainAssetLockProofStructureValidator)] +impl ChainAssetLockProofStructureValidatorWasm { + #[wasm_bindgen(constructor)] + pub fn new( + state_repository: ExternalStateRepositoryLike, + ) -> Result { + let state_repository_wrapper = + Arc::new(ExternalStateRepositoryLikeWrapper::new(state_repository)); + + let tx_validator = Arc::new(AssetLockTransactionValidator::new( + state_repository_wrapper.clone(), + )); + + let validator = + ChainAssetLockProofStructureValidator::new(state_repository_wrapper, tx_validator) + .map_err(|e| from_dpp_err(ProtocolError::Generic(e.to_string())))?; + + Ok(validator.into()) + } + + #[wasm_bindgen] + pub async fn validate( + &self, + raw_asset_lock_proof: JsValue, + execution_context: &StateTransitionExecutionContextWasm, + ) -> Result { + let asset_lock_proof_json = raw_asset_lock_proof.with_serde_to_json_value()?; + + let context: &StateTransitionExecutionContext = execution_context.into(); + let validation_result = self + .0 + .validate(&asset_lock_proof_json, context) + .await + .map_err(|e| from_dpp_err(e.into()))?; + + Ok(validation_result + .map(|item| JsValue::from(Buffer::from_bytes(&item))) + .into()) + } +} diff --git a/packages/wasm-dpp/src/identity/state_transition/asset_lock_proof/chain/mod.rs b/packages/wasm-dpp/src/identity/state_transition/asset_lock_proof/chain/mod.rs new file mode 100644 index 00000000000..0b72aa9341d --- /dev/null +++ b/packages/wasm-dpp/src/identity/state_transition/asset_lock_proof/chain/mod.rs @@ -0,0 +1,4 @@ +pub use chain_asset_lock_proof::*; + +pub mod chain_asset_lock_proof; +pub mod chain_asset_lock_proof_structure_validator; diff --git a/packages/wasm-dpp/src/identity/state_transition/asset_lock_proof/instant/instant_asset_lock_proof.rs b/packages/wasm-dpp/src/identity/state_transition/asset_lock_proof/instant/instant_asset_lock_proof.rs new file mode 100644 index 00000000000..44dcc7dae9b --- /dev/null +++ b/packages/wasm-dpp/src/identity/state_transition/asset_lock_proof/instant/instant_asset_lock_proof.rs @@ -0,0 +1,177 @@ +use dpp::{ + dashcore::{ + blockdata::{script::Script, transaction::txout::TxOut}, + consensus::encode::serialize, + }, + util::string_encoding, + util::string_encoding::Encoding, +}; + +use serde::{Deserialize, Serialize}; +use std::convert::TryInto; +use wasm_bindgen::prelude::*; + +use crate::{ + buffer::Buffer, + errors::{from_dpp_err, RustConversionError}, + identifier::IdentifierWrapper, + with_js_error, +}; +use dpp::identity::state_transition::asset_lock_proof::instant::{ + InstantAssetLockProof, RawInstantLock, +}; + +#[derive(Serialize, Deserialize)] +#[serde(remote = "TxOut")] +struct TxOutJS { + #[serde(rename = "satoshis")] + value: u64, + #[serde(rename = "script")] + script_pubkey: Script, +} + +#[derive(Serialize)] +struct TxOutSerdeHelper<'a>(#[serde(with = "TxOutJS")] &'a TxOut); + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[wasm_bindgen(js_name=InstantAssetLockProof)] +pub struct InstantAssetLockProofWasm(InstantAssetLockProof); + +impl From for InstantAssetLockProofWasm { + fn from(v: InstantAssetLockProof) -> Self { + InstantAssetLockProofWasm(v) + } +} + +impl From for InstantAssetLockProof { + fn from(v: InstantAssetLockProofWasm) -> Self { + v.0 + } +} + +#[wasm_bindgen(js_class = InstantAssetLockProof)] +impl InstantAssetLockProofWasm { + #[wasm_bindgen(constructor)] + pub fn new(raw_parameters: JsValue) -> Result { + let raw_instant_lock: RawInstantLock = + with_js_error!(serde_wasm_bindgen::from_value(raw_parameters))?; + + let instant_asset_lock_proof: InstantAssetLockProof = + raw_instant_lock.try_into().map_err(|_| { + RustConversionError::Error(String::from("object passed is not a raw Instant Lock")) + .to_js_value() + })?; + + Ok(instant_asset_lock_proof.into()) + } + + #[wasm_bindgen(js_name=getType)] + pub fn get_type(&self) -> u8 { + self.0.asset_lock_type() + } + + #[wasm_bindgen(js_name=getOutputIndex)] + pub fn get_output_index(&self) -> usize { + self.0.output_index() + } + + #[wasm_bindgen(js_name=getOutPoint)] + pub fn get_out_point(&self) -> Option { + self.0 + .out_point() + .map(|out_point| Buffer::from_bytes(&out_point)) + } + + #[wasm_bindgen(js_name=getOutput)] + pub fn get_output(&self) -> Result { + let output = self.0.output().unwrap(); + let output_json_string = + serde_json::to_string(&TxOutSerdeHelper(output)).map_err(|e| from_dpp_err(e.into()))?; + + let js_object = js_sys::JSON::parse(&output_json_string)?; + Ok(js_object) + } + + #[wasm_bindgen(js_name=createIdentifier)] + pub fn create_identifier(&self) -> Result { + let identifier = self + .0 + .create_identifier() + .map_err(|e| from_dpp_err(e.into()))?; + Ok(identifier.into()) + } + + #[wasm_bindgen(js_name=getInstantLock)] + pub fn get_instant_lock(&self) -> Buffer { + let instant_lock = self.0.instant_lock(); + let serialized_instant_lock = serialize(instant_lock); + Buffer::from_bytes(&serialized_instant_lock) + } + + #[wasm_bindgen(js_name=getTransaction)] + pub fn get_transaction(&self) -> Buffer { + let transaction = self.0.transaction(); + let serialized_transaction = serialize(transaction); + Buffer::from_bytes(&serialized_transaction) + } + + #[wasm_bindgen(js_name=toObject)] + pub fn to_object(&self) -> Result { + let asset_lock_json = + serde_json::to_value(self.0.clone()).map_err(|e| from_dpp_err(e.into()))?; + + let asset_lock_json_string = + serde_json::to_string(&asset_lock_json).map_err(|e| from_dpp_err(e.into()))?; + let js_object = js_sys::JSON::parse(&asset_lock_json_string)?; + + let transaction = self.get_transaction(); + let instant_lock = self.get_instant_lock(); + + js_sys::Reflect::set( + &js_object, + &"transaction".to_owned().into(), + &JsValue::from(transaction), + )?; + + js_sys::Reflect::set( + &js_object, + &"instantLock".to_owned().into(), + &JsValue::from(instant_lock), + )?; + + Ok(js_object) + } + + #[wasm_bindgen(js_name=toJSON)] + pub fn to_json(&self) -> Result { + let js_object = self.to_object()?; + + let transaction = self.0.transaction(); + let serialized_transaction = serialize(transaction); + + let instant_lock = self.0.instant_lock(); + let serialized_instant_lock = serialize(instant_lock); + + let instant_lock_base64 = + string_encoding::encode(serialized_instant_lock.as_slice(), Encoding::Base64); + + let mut transaction_hex = String::new(); + for &byte in serialized_transaction.as_slice() { + transaction_hex.push_str(&format!("{:02x}", byte)); + } + + js_sys::Reflect::set( + &js_object, + &"transaction".to_owned().into(), + &JsValue::from(transaction_hex), + )?; + + js_sys::Reflect::set( + &js_object, + &"instantLock".to_owned().into(), + &JsValue::from(instant_lock_base64), + )?; + + Ok(js_object) + } +} diff --git a/packages/wasm-dpp/src/identity/state_transition/asset_lock_proof/instant/instant_asset_lock_proof_structure_validator.rs b/packages/wasm-dpp/src/identity/state_transition/asset_lock_proof/instant/instant_asset_lock_proof_structure_validator.rs new file mode 100644 index 00000000000..c63068a14b7 --- /dev/null +++ b/packages/wasm-dpp/src/identity/state_transition/asset_lock_proof/instant/instant_asset_lock_proof_structure_validator.rs @@ -0,0 +1,72 @@ +use dpp::identity::state_transition::asset_lock_proof::{ + AssetLockTransactionValidator, InstantAssetLockProofStructureValidator, +}; +use dpp::state_transition::state_transition_execution_context::StateTransitionExecutionContext; +use dpp::ProtocolError; +use std::sync::Arc; +use wasm_bindgen::prelude::*; + +use crate::buffer::Buffer; +use crate::utils::ToSerdeJSONExt; +use crate::{ + errors::from_dpp_err, + state_repository::{ExternalStateRepositoryLike, ExternalStateRepositoryLikeWrapper}, + validation::ValidationResultWasm, + StateTransitionExecutionContextWasm, +}; + +#[wasm_bindgen(js_name = InstantAssetLockProofStructureValidator)] +pub struct InstantAssetLockProofStructureValidatorWasm( + InstantAssetLockProofStructureValidator, +); + +impl From> + for InstantAssetLockProofStructureValidatorWasm +{ + fn from( + validator: InstantAssetLockProofStructureValidator, + ) -> Self { + Self(validator) + } +} + +#[wasm_bindgen(js_class = InstantAssetLockProofStructureValidator)] +impl InstantAssetLockProofStructureValidatorWasm { + #[wasm_bindgen(constructor)] + pub fn new( + state_repository: ExternalStateRepositoryLike, + ) -> Result { + let state_repository_wrapper = + Arc::new(ExternalStateRepositoryLikeWrapper::new(state_repository)); + + let tx_validator = Arc::new(AssetLockTransactionValidator::new( + state_repository_wrapper.clone(), + )); + + let validator = + InstantAssetLockProofStructureValidator::new(state_repository_wrapper, tx_validator) + .map_err(|e| from_dpp_err(ProtocolError::Generic(e.to_string())))?; + + Ok(validator.into()) + } + + #[wasm_bindgen] + pub async fn validate( + &self, + raw_asset_lock_proof: JsValue, + execution_context: &StateTransitionExecutionContextWasm, + ) -> Result { + let asset_lock_proof_json = raw_asset_lock_proof.with_serde_to_json_value()?; + + let context: &StateTransitionExecutionContext = execution_context.into(); + let validation_result = self + .0 + .validate(&asset_lock_proof_json, context) + .await + .map_err(|e| from_dpp_err(e.into()))?; + + Ok(validation_result + .map(|item| JsValue::from(Buffer::from_bytes(&item))) + .into()) + } +} diff --git a/packages/wasm-dpp/src/identity/state_transition/asset_lock_proof/instant/mod.rs b/packages/wasm-dpp/src/identity/state_transition/asset_lock_proof/instant/mod.rs new file mode 100644 index 00000000000..0910a2360a5 --- /dev/null +++ b/packages/wasm-dpp/src/identity/state_transition/asset_lock_proof/instant/mod.rs @@ -0,0 +1,4 @@ +pub use instant_asset_lock_proof::*; + +pub mod instant_asset_lock_proof; +pub mod instant_asset_lock_proof_structure_validator; diff --git a/packages/wasm-dpp/src/identity/state_transition/asset_lock_proof/mod.rs b/packages/wasm-dpp/src/identity/state_transition/asset_lock_proof/mod.rs new file mode 100644 index 00000000000..c312b95e12b --- /dev/null +++ b/packages/wasm-dpp/src/identity/state_transition/asset_lock_proof/mod.rs @@ -0,0 +1,269 @@ +mod chain; +mod instant; + +pub use chain::*; +pub use instant::*; +use std::convert::TryInto; +use std::sync::Arc; +use wasm_bindgen::JsCast; +use wasm_bindgen::__rt::Ref; + +use crate::errors::{from_dpp_err, RustConversionError}; +use dpp::dashcore::consensus; +use dpp::dashcore::hashes::hex::FromHex; +use dpp::identity::errors::UnknownAssetLockProofTypeError; +use wasm_bindgen::prelude::*; + +use crate::buffer::Buffer; +use crate::errors::dpp_error::from_dpp_error_ref; +use crate::identifier::IdentifierWrapper; +use crate::identity::errors::UnknownAssetLockProofTypeErrorWasm; +use crate::state_repository::{ExternalStateRepositoryLike, ExternalStateRepositoryLikeWrapper}; +use crate::utils::generic_of_js_val; +use crate::validation::ValidationResultWasm; +use crate::{Deserialize, StateTransitionExecutionContextWasm}; +use dpp::identity::state_transition::asset_lock_proof::{ + AssetLockProof, AssetLockProofType, AssetLockPublicKeyHashFetcher, + AssetLockTransactionOutputFetcher, AssetLockTransactionValidator, +}; + +#[derive(Deserialize, Clone)] +#[wasm_bindgen(js_name=AssetLockProof)] +pub struct AssetLockProofWasm(AssetLockProof); + +impl From for AssetLockProofWasm { + fn from(v: AssetLockProof) -> Self { + AssetLockProofWasm(v) + } +} + +impl From for AssetLockProof { + fn from(v: AssetLockProofWasm) -> Self { + v.0 + } +} + +impl From for AssetLockProofWasm { + fn from(f: InstantAssetLockProofWasm) -> Self { + AssetLockProof::Instant(f.into()).into() + } +} + +impl From for AssetLockProofWasm { + fn from(f: ChainAssetLockProofWasm) -> Self { + AssetLockProof::Chain(f.into()).into() + } +} + +pub trait AssetLockProofLike { + fn to_object(&self) -> Result; +} + +#[wasm_bindgen(js_class = AssetLockProof)] +impl AssetLockProofWasm { + #[wasm_bindgen(constructor)] + pub fn new(raw_asset_lock_proof: JsValue) -> Result { + let lock_type = get_lock_type(&raw_asset_lock_proof)?; + + match lock_type { + AssetLockProofType::Instant => { + Ok(InstantAssetLockProofWasm::new(raw_asset_lock_proof)?.into()) + } + AssetLockProofType::Chain => { + Ok(ChainAssetLockProofWasm::new(raw_asset_lock_proof)?.into()) + } + } + } + + #[wasm_bindgen(js_name=createIdentifier)] + pub fn create_identifier(&self) -> Result { + match &self.0 { + AssetLockProof::Instant(instant) => { + InstantAssetLockProofWasm::from(instant.to_owned()).create_identifier() + } + AssetLockProof::Chain(chain) => { + ChainAssetLockProofWasm::from(chain.to_owned()).create_identifier() + } + } + } + + #[wasm_bindgen(js_name=toObject)] + pub fn to_object(&self) -> Result { + match &self.0 { + AssetLockProof::Instant(instant) => { + InstantAssetLockProofWasm::from(instant.to_owned()).to_object() + } + AssetLockProof::Chain(chain) => { + ChainAssetLockProofWasm::from(chain.to_owned()).to_object() + } + } + } +} + +fn get_lock_type(raw_asset_lock_proof: &JsValue) -> Result { + (js_sys::Reflect::get(raw_asset_lock_proof, &JsValue::from_str("type")) + .map_err(|_| { + RustConversionError::Error(String::from("error getting type from raw asset lock")) + .to_js_value() + })? + .as_f64() + .ok_or_else(|| JsValue::from_str("asset lock type must be a number"))? as u64) + .try_into() + .map_err(|_| JsValue::from_str("unrecognized asset lock proof type")) +} + +#[wasm_bindgen(js_name=createAssetLockProofInstance)] +pub fn create_asset_lock_proof_instance(raw_parameters: JsValue) -> Result { + let lock_type = get_lock_type(&raw_parameters)?; + + match lock_type { + AssetLockProofType::Instant => { + InstantAssetLockProofWasm::new(raw_parameters).map(|v| v.into()) + } + AssetLockProofType::Chain => ChainAssetLockProofWasm::new(raw_parameters).map(|v| v.into()), + } +} + +pub fn create_asset_lock_proof_from_wasm_instance( + js_value: &JsValue, +) -> Result { + let default_error: UnknownAssetLockProofTypeErrorWasm = + UnknownAssetLockProofTypeError::new(None).into(); + + let get_type_value = js_sys::Reflect::get(js_value, &JsValue::from_str("getType")) + .map_err(|_| default_error.clone())?; + + let get_type_function: &js_sys::Function = get_type_value + .dyn_ref::() + .ok_or_else(|| default_error.clone())?; + + let raw_lock_type = get_type_function + .call0(js_value)? + .as_f64() + .ok_or(default_error)? as u64; + + let lock_type: AssetLockProofType = raw_lock_type.try_into().map_err(|_| { + UnknownAssetLockProofTypeErrorWasm::from(UnknownAssetLockProofTypeError::new(Some( + raw_lock_type as u8, + ))) + })?; + + match lock_type { + AssetLockProofType::Instant => { + let instant: Ref = + generic_of_js_val::(js_value, "InstantAssetLockProof")?; + + Ok(AssetLockProof::Instant(instant.clone().into())) + } + AssetLockProofType::Chain => { + let chain: Ref = + generic_of_js_val::(js_value, "ChainAssetLockProof")?; + + Ok(AssetLockProof::Chain(chain.clone().into())) + } + } +} + +#[wasm_bindgen(js_name=validateAssetLockTransaction)] +pub async fn validate_asset_lock_transaction( + state_repository: ExternalStateRepositoryLike, + raw_transaction: String, + output_index: usize, + execution_context: &StateTransitionExecutionContextWasm, +) -> Result { + let tx_bytes = Vec::from_hex(&raw_transaction) + .map_err(|_| RustConversionError::Error(String::from("invalid transaction hex")))?; + + let state_repository_wrapper = + Arc::new(ExternalStateRepositoryLikeWrapper::new(state_repository)); + + let validator = AssetLockTransactionValidator::new(state_repository_wrapper); + + let result = validator + .validate(tx_bytes.as_slice(), output_index, execution_context.into()) + .await + .map_err(|e| from_dpp_err(e.into()))?; + + let validation_result = result.map(|item| { + let object = js_sys::Object::new(); + + js_sys::Reflect::set( + &object, + &"publicKeyHash".to_owned().into(), + &Buffer::from_bytes(&item.public_key_hash), + ) + .unwrap(); + + let deserialized_tx = consensus::serialize(&item.transaction); + + js_sys::Reflect::set( + &object, + &"transaction".to_owned().into(), + &Buffer::from_bytes(&deserialized_tx), + ) + .unwrap(); + + object + }); + + Ok(validation_result.into()) +} + +#[wasm_bindgen(js_name=fetchAssetLockTransactionOutput)] +pub async fn fetch_asset_lock_transaction_output( + state_repository: ExternalStateRepositoryLike, + raw_asset_lock_proof: JsValue, + execution_context: &StateTransitionExecutionContextWasm, +) -> Result { + let asset_lock_proof = create_asset_lock_proof_from_wasm_instance(&raw_asset_lock_proof)?; + + let state_repository_wrapper = + Arc::new(ExternalStateRepositoryLikeWrapper::new(state_repository)); + + let fetcher = AssetLockTransactionOutputFetcher::new(state_repository_wrapper); + + let fetch_result = fetcher + .fetch(&asset_lock_proof, execution_context.into()) + .await + .map_err(|e| from_dpp_error_ref(&e))?; + + let tx_out = js_sys::Object::new(); + + js_sys::Reflect::set( + &tx_out, + &"satoshis".to_owned().into(), + &(fetch_result.value as u32).into(), + ) + .unwrap(); + + let script_hex = format!("{:x}", fetch_result.script_pubkey); + + js_sys::Reflect::set(&tx_out, &"script".to_owned().into(), &script_hex.into()).unwrap(); + + Ok(tx_out.into()) +} + +#[wasm_bindgen(js_name=fetchAssetLockPublicKeyHash)] +pub async fn fetch_asset_lock_public_key_hash( + state_repository: ExternalStateRepositoryLike, + raw_asset_lock_proof: JsValue, + execution_context: &StateTransitionExecutionContextWasm, +) -> Result { + let asset_lock_proof = create_asset_lock_proof_from_wasm_instance(&raw_asset_lock_proof)?; + + let state_repository_wrapper = + Arc::new(ExternalStateRepositoryLikeWrapper::new(state_repository)); + + let tx_output_fetcher = + AssetLockTransactionOutputFetcher::new(state_repository_wrapper.clone()); + + let public_key_hash_fetcher = + AssetLockPublicKeyHashFetcher::new(state_repository_wrapper, tx_output_fetcher); + + let fetch_result = public_key_hash_fetcher + .fetch_public_key_hash(asset_lock_proof, execution_context.into()) + .await + .map_err(|e| from_dpp_error_ref(&e))?; + + Ok(Buffer::from_bytes(&fetch_result).into()) +} diff --git a/packages/wasm-dpp/src/identity/state_transition/identity_create_transition/apply_identity_create_transition.rs b/packages/wasm-dpp/src/identity/state_transition/identity_create_transition/apply_identity_create_transition.rs new file mode 100644 index 00000000000..5c61e49ecd8 --- /dev/null +++ b/packages/wasm-dpp/src/identity/state_transition/identity_create_transition/apply_identity_create_transition.rs @@ -0,0 +1,26 @@ +use dpp::identity::state_transition::identity_create_transition::ApplyIdentityCreateTransition; +use std::sync::Arc; +use wasm_bindgen::prelude::*; + +use crate::{ + state_repository::{ExternalStateRepositoryLike, ExternalStateRepositoryLikeWrapper}, + IdentityCreateTransitionWasm, +}; + +#[wasm_bindgen(js_name=applyIdentityCreateTransition)] +pub async fn apply_identity_create_transition_wasm( + state_repository: ExternalStateRepositoryLike, + state_transition: &IdentityCreateTransitionWasm, +) -> Result<(), JsValue> { + let wrapped_state_repository = + Arc::new(ExternalStateRepositoryLikeWrapper::new(state_repository)); + + let instance = ApplyIdentityCreateTransition::new(wrapped_state_repository); + + instance + .apply_identity_create_transition(&state_transition.to_owned().into()) + .await + .map_err(|e| JsValue::from_str(&e.to_string()))?; + + Ok(()) +} diff --git a/packages/wasm-dpp/src/identity/state_transition/identity_create_transition/identity_create_transition.rs b/packages/wasm-dpp/src/identity/state_transition/identity_create_transition/identity_create_transition.rs new file mode 100644 index 00000000000..91b7ca66ea4 --- /dev/null +++ b/packages/wasm-dpp/src/identity/state_transition/identity_create_transition/identity_create_transition.rs @@ -0,0 +1,360 @@ +use std::convert::TryInto; +use std::default::Default; + +use serde::{Deserialize, Serialize}; +use wasm_bindgen::__rt::Ref; +use wasm_bindgen::prelude::*; + +use crate::identifier::IdentifierWrapper; +use crate::identity::state_transition::AssetLockProofWasm; + +use crate::{ + buffer::Buffer, + create_asset_lock_proof_from_wasm_instance, + errors::RustConversionError, + identity::{ + state_transition::asset_lock_proof::{ChainAssetLockProofWasm, InstantAssetLockProofWasm}, + IdentityPublicKeyWasm, + }, + state_transition::StateTransitionExecutionContextWasm, + with_js_error, +}; + +use crate::bls_adapter::{BlsAdapter, JsBlsAdapter}; +use crate::errors::from_dpp_err; +use crate::utils::generic_of_js_val; +use dpp::{ + identifier::Identifier, + identity::{ + state_transition::{ + asset_lock_proof::AssetLockProof, identity_create_transition::IdentityCreateTransition, + }, + IdentityPublicKey, + }, + state_transition::StateTransitionLike, + util::string_encoding, + util::string_encoding::Encoding, +}; + +#[wasm_bindgen(js_name=IdentityCreateTransition)] +#[derive(Clone)] +pub struct IdentityCreateTransitionWasm(IdentityCreateTransition); + +#[derive(Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +struct IdentityCreateTransitionParams { + public_keys: Vec, + signature: Option>, + protocol_version: u32, +} + +impl From for IdentityCreateTransitionWasm { + fn from(v: IdentityCreateTransition) -> Self { + IdentityCreateTransitionWasm(v) + } +} + +impl From for IdentityCreateTransition { + fn from(v: IdentityCreateTransitionWasm) -> Self { + v.0 + } +} + +#[wasm_bindgen(js_class = IdentityCreateTransition)] +impl IdentityCreateTransitionWasm { + #[wasm_bindgen(constructor)] + pub fn new(raw_parameters: JsValue) -> Result { + let raw_asset_lock_proof = + js_sys::Reflect::get(&raw_parameters, &"assetLockProof".to_owned().into())?; + + let parameters: IdentityCreateTransitionParams = + with_js_error!(serde_wasm_bindgen::from_value(raw_parameters))?; + + let raw_state_transition = with_js_error!(serde_json::to_value(¶meters))?; + + let mut identity_create_transition = IdentityCreateTransition::new(raw_state_transition) + .map_err(|e| RustConversionError::Error(e.to_string()).to_js_value())?; + + let asset_lock_proof = AssetLockProofWasm::new(raw_asset_lock_proof)?; + identity_create_transition + .set_asset_lock_proof(asset_lock_proof.into()) + .map_err(|e| RustConversionError::Error(e.to_string()).to_js_value())?; + + if let Some(signature) = parameters.signature { + identity_create_transition.set_signature(signature); + } + + Ok(identity_create_transition.into()) + } + + #[wasm_bindgen(js_name=setAssetLockProof)] + pub fn set_asset_lock_proof(&mut self, asset_lock_proof: JsValue) -> Result<(), JsValue> { + let asset_lock_proof = create_asset_lock_proof_from_wasm_instance(&asset_lock_proof)?; + + self.0 + .set_asset_lock_proof(asset_lock_proof) + .map_err(|e| RustConversionError::Error(e.to_string()).to_js_value())?; + + Ok(()) + } + + #[wasm_bindgen(getter, js_name=assetLockProof)] + pub fn asset_lock_proof(&self) -> JsValue { + self.get_asset_lock_proof() + } + + #[wasm_bindgen(js_name=getAssetLockProof)] + pub fn get_asset_lock_proof(&self) -> JsValue { + let asset_lock_proof = self.0.get_asset_lock_proof().to_owned(); + + match asset_lock_proof { + AssetLockProof::Chain(chain_asset_lock_proof) => { + ChainAssetLockProofWasm::from(chain_asset_lock_proof).into() + } + AssetLockProof::Instant(instant_asset_lock_proof) => { + InstantAssetLockProofWasm::from(instant_asset_lock_proof).into() + } + } + } + + #[wasm_bindgen(js_name=setPublicKeys)] + pub fn set_public_keys(&mut self, public_keys: Vec) -> Result<(), JsValue> { + let public_keys = public_keys + .iter() + .map(|value| { + let public_key: Ref = + generic_of_js_val::(value, "IdentityPublicKey")?; + Ok(public_key.clone().into()) + }) + .collect::, JsValue>>()?; + + self.0.set_public_keys(public_keys); + + Ok(()) + } + + #[wasm_bindgen(js_name=addPublicKeys)] + pub fn add_public_keys(&mut self, public_keys: Vec) -> Result<(), JsValue> { + let mut public_keys = public_keys + .iter() + .map(|value| { + let public_key: Ref = + generic_of_js_val::(value, "IdentityPublicKey")?; + Ok(public_key.clone().into()) + }) + .collect::, JsValue>>()?; + + self.0.add_public_keys(&mut public_keys); + + Ok(()) + } + + #[wasm_bindgen(js_name=getPublicKeys)] + pub fn get_public_keys(&self) -> Vec { + self.0 + .get_public_keys() + .iter() + .map(IdentityPublicKey::to_owned) + .map(IdentityPublicKeyWasm::from) + .map(JsValue::from) + .collect() + } + + #[wasm_bindgen(getter, js_name=publicKeys)] + pub fn public_keys(&self) -> Vec { + self.get_public_keys() + } + + #[wasm_bindgen(js_name=getType)] + pub fn get_type(&self) -> u8 { + self.0.get_type() as u8 + } + + #[wasm_bindgen(getter, js_name=identityId)] + pub fn identity_id(&self) -> IdentifierWrapper { + self.get_identity_id() + } + + #[wasm_bindgen(js_name=getIdentityId)] + pub fn get_identity_id(&self) -> IdentifierWrapper { + self.0.get_identity_id().clone().into() + } + + #[wasm_bindgen(js_name=getOwnerId)] + pub fn get_owner_id(&self) -> IdentifierWrapper { + self.0.get_owner_id().clone().into() + } + + #[wasm_bindgen(js_name=toObject)] + pub fn to_object(&self, options: JsValue) -> Result { + let opts: super::to_object::ToObjectOptions = if options.is_object() { + with_js_error!(serde_wasm_bindgen::from_value(options))? + } else { + Default::default() + }; + + let skip_signature = opts.skip_signature; + let object = super::to_object::to_object_struct(&self.0, opts); + let js_object = js_sys::Object::new(); + + js_sys::Reflect::set( + &js_object, + &"type".to_owned().into(), + &object.transition_type.into(), + )?; + + js_sys::Reflect::set( + &js_object, + &"protocolVersion".to_owned().into(), + &object.protocol_version.into(), + )?; + + if let Some(signature) = object.signature { + let signature_value: JsValue = if signature.is_empty() { + JsValue::undefined() + } else { + Buffer::from_bytes(&signature).into() + }; + + js_sys::Reflect::set(&js_object, &"signature".to_owned().into(), &signature_value)?; + } + + let asset_lock_proof_object = match object.asset_lock_proof { + AssetLockProof::Instant(instant_asset_lock_proof) => { + InstantAssetLockProofWasm::from(instant_asset_lock_proof).to_object()? + } + AssetLockProof::Chain(chain_asset_lock_proof) => { + ChainAssetLockProofWasm::from(chain_asset_lock_proof).to_object()? + } + }; + + js_sys::Reflect::set( + &js_object, + &"assetLockProof".to_owned().into(), + &asset_lock_proof_object, + )?; + + let keys_objects = object + .public_keys + .into_iter() + .map(IdentityPublicKeyWasm::from) + .map(|key| key.to_object(skip_signature)) + .collect::>()?; + + js_sys::Reflect::set(&js_object, &"publicKeys".to_owned().into(), &keys_objects)?; + + Ok(js_object.into()) + } + + #[wasm_bindgen(js_name=toJSON)] + pub fn to_json(&self) -> Result { + let object = super::to_object::to_object_struct(&self.0, Default::default()); + let js_object = js_sys::Object::new(); + + js_sys::Reflect::set( + &js_object, + &"type".to_owned().into(), + &object.transition_type.into(), + )?; + + js_sys::Reflect::set( + &js_object, + &"protocolVersion".to_owned().into(), + &object.protocol_version.into(), + )?; + + if let Some(signature) = object.signature { + let signature_value: JsValue = if signature.is_empty() { + JsValue::undefined() + } else { + string_encoding::encode(signature.as_slice(), Encoding::Base64).into() + }; + + js_sys::Reflect::set(&js_object, &"signature".to_owned().into(), &signature_value)?; + } + + let asset_lock_proof_json = match object.asset_lock_proof { + AssetLockProof::Instant(instant_asset_lock_proof) => { + InstantAssetLockProofWasm::from(instant_asset_lock_proof).to_json()? + } + AssetLockProof::Chain(chain_asset_lock_proof) => { + ChainAssetLockProofWasm::from(chain_asset_lock_proof).to_json()? + } + }; + + js_sys::Reflect::set( + &js_object, + &"assetLockProof".to_owned().into(), + &asset_lock_proof_json, + )?; + + let keys_objects = object + .public_keys + .into_iter() + .map(IdentityPublicKeyWasm::from) + .map(|key| key.to_json()) + .collect::>()?; + + js_sys::Reflect::set(&js_object, &"publicKeys".to_owned().into(), &keys_objects)?; + + Ok(js_object.into()) + } + + #[wasm_bindgen(js_name=getModifiedDataIds)] + pub fn get_modified_data_ids(&self) -> Vec { + let ids = self.0.get_modified_data_ids(); + + ids.into_iter() + .map(|id| { + >::from(id.clone()).into() + }) + .collect() + } + + #[wasm_bindgen(js_name=isDataContractStateTransition)] + pub fn is_data_contract_state_transition(&self) -> bool { + self.0.is_data_contract_state_transition() + } + + #[wasm_bindgen(js_name=isDocumentStateTransition)] + pub fn is_document_state_transition(&self) -> bool { + self.0.is_document_state_transition() + } + + #[wasm_bindgen(js_name=isIdentityStateTransition)] + pub fn is_identity_state_transition(&self) -> bool { + self.0.is_identity_state_transition() + } + + #[wasm_bindgen(js_name=setExecutionContext)] + pub fn set_execution_context(&mut self, context: &StateTransitionExecutionContextWasm) { + self.0.set_execution_context(context.into()) + } + + #[wasm_bindgen(js_name=getExecutionContext)] + pub fn get_execution_context(&mut self) -> StateTransitionExecutionContextWasm { + self.0.get_execution_context().into() + } + + #[wasm_bindgen(js_name=signByPrivateKey)] + pub fn sign_by_private_key( + &mut self, + private_key: Vec, + key_type: u8, + bls: JsBlsAdapter, + ) -> Result<(), JsValue> { + let bls_adapter = BlsAdapter(bls); + let key_type = key_type + .try_into() + .map_err(|e: anyhow::Error| e.to_string())?; + + self.0 + .sign_by_private_key(private_key.as_slice(), key_type, &bls_adapter) + .map_err(from_dpp_err) + } + + #[wasm_bindgen(js_name=getSignature)] + pub fn get_signature(&self) -> Buffer { + Buffer::from_bytes(self.0.get_signature()) + } +} diff --git a/packages/wasm-dpp/src/identity/state_transition/identity_create_transition/identity_create_transition_basic_validator.rs b/packages/wasm-dpp/src/identity/state_transition/identity_create_transition/identity_create_transition_basic_validator.rs new file mode 100644 index 00000000000..9d276588d5d --- /dev/null +++ b/packages/wasm-dpp/src/identity/state_transition/identity_create_transition/identity_create_transition_basic_validator.rs @@ -0,0 +1,153 @@ +use dpp::identity::state_transition::asset_lock_proof::{ + AssetLockProofValidator, AssetLockTransactionValidator, ChainAssetLockProofStructureValidator, + InstantAssetLockProofStructureValidator, +}; +use dpp::identity::state_transition::identity_create_transition::validation::basic::IdentityCreateTransitionBasicValidator; +use dpp::identity::state_transition::validate_public_key_signatures::PublicKeysSignaturesValidator; +use dpp::identity::validation::{ + PublicKeysValidator, RequiredPurposeAndSecurityLevelValidator, PUBLIC_KEY_SCHEMA_FOR_TRANSITION, +}; +use dpp::state_transition::state_transition_execution_context::StateTransitionExecutionContext; + +use dpp::version::{ProtocolVersionValidator, COMPATIBILITY_MAP, LATEST_VERSION}; +use dpp::ProtocolError; + +use std::sync::Arc; +use wasm_bindgen::prelude::*; + +use crate::bls_adapter::{BlsAdapter, JsBlsAdapter}; +use crate::errors::from_dpp_err; +use crate::utils::ToSerdeJSONExt; +use crate::{ + state_repository::{ExternalStateRepositoryLike, ExternalStateRepositoryLikeWrapper}, + validation::ValidationResultWasm, + StateTransitionExecutionContextWasm, +}; + +#[wasm_bindgen(js_name=IdentityCreateTransitionBasicValidator)] +pub struct IdentityCreateTransitionBasicValidatorWasm( + IdentityCreateTransitionBasicValidator< + PublicKeysValidator, + RequiredPurposeAndSecurityLevelValidator, + ExternalStateRepositoryLikeWrapper, + PublicKeysSignaturesValidator, + BlsAdapter, + >, +); + +impl + From< + IdentityCreateTransitionBasicValidator< + PublicKeysValidator, + RequiredPurposeAndSecurityLevelValidator, + ExternalStateRepositoryLikeWrapper, + PublicKeysSignaturesValidator, + BlsAdapter, + >, + > for IdentityCreateTransitionBasicValidatorWasm +{ + fn from( + validator: IdentityCreateTransitionBasicValidator< + PublicKeysValidator, + RequiredPurposeAndSecurityLevelValidator, + ExternalStateRepositoryLikeWrapper, + PublicKeysSignaturesValidator, + BlsAdapter, + >, + ) -> Self { + Self(validator) + } +} + +#[wasm_bindgen(js_class=IdentityCreateTransitionBasicValidator)] +impl IdentityCreateTransitionBasicValidatorWasm { + #[wasm_bindgen(constructor)] + pub fn new( + state_repository: ExternalStateRepositoryLike, + js_bls: JsBlsAdapter, + ) -> Result { + let state_repository_wrapper = + Arc::new(ExternalStateRepositoryLikeWrapper::new(state_repository)); + + let protocol_version_validator = ProtocolVersionValidator::new( + LATEST_VERSION, + LATEST_VERSION, + COMPATIBILITY_MAP.clone(), + ); + + let public_keys_validator = PublicKeysValidator::new_with_schema( + PUBLIC_KEY_SCHEMA_FOR_TRANSITION.clone(), + BlsAdapter(js_bls.clone()), + ) + .map_err(|e| from_dpp_err(ProtocolError::Generic(e.to_string())))?; + + let public_keys_transition_validator = RequiredPurposeAndSecurityLevelValidator::default(); + + let asset_lock_proof_validator: AssetLockProofValidator< + ExternalStateRepositoryLikeWrapper, + >; + { + let tx_validator = Arc::new(AssetLockTransactionValidator::new( + state_repository_wrapper.clone(), + )); + + let instant_asset_lock_proof_validator = InstantAssetLockProofStructureValidator::new( + state_repository_wrapper.clone(), + tx_validator.clone(), + ) + .map_err(|e| from_dpp_err(ProtocolError::Generic(e.to_string())))?; + + let chain_asset_lock_proof_validator = + ChainAssetLockProofStructureValidator::new(state_repository_wrapper, tx_validator) + .map_err(|e| from_dpp_err(ProtocolError::Generic(e.to_string())))?; + + asset_lock_proof_validator = AssetLockProofValidator::new( + instant_asset_lock_proof_validator, + chain_asset_lock_proof_validator, + ); + } + + let public_keys_signature_validator = + PublicKeysSignaturesValidator::new(BlsAdapter(js_bls.clone())); + + let validator = IdentityCreateTransitionBasicValidator::new( + protocol_version_validator, + Arc::new(public_keys_validator), + Arc::new(public_keys_transition_validator), + Arc::new(asset_lock_proof_validator), + BlsAdapter(js_bls), + public_keys_signature_validator, + ) + .map_err(|e| from_dpp_err(ProtocolError::Generic(e.to_string())))?; + + Ok(validator.into()) + } + + #[wasm_bindgen] + pub async fn validate( + mut self, + raw_state_transition: JsValue, + execution_context: &StateTransitionExecutionContextWasm, + ) -> Result { + let execution_context: &StateTransitionExecutionContext = execution_context.into(); + + let current_protocol_version = + js_sys::Reflect::get(&raw_state_transition, &"protocolVersion".into()) + .map(|v| v.as_f64().unwrap_or(LATEST_VERSION as f64) as u32) + .unwrap_or(LATEST_VERSION); + + self.0 + .protocol_version_validator() + .set_current_protocol_version(current_protocol_version); + + let state_transition_json = raw_state_transition.with_serde_to_json_value()?; + + let validation_result = self + .0 + .validate(&state_transition_json, execution_context) + .await + .map_err(|e| from_dpp_err(e.into()))?; + + Ok(validation_result.map(|_| JsValue::undefined()).into()) + } +} diff --git a/packages/wasm-dpp/src/identity/state_transition/identity_create_transition/identity_create_transition_state_validator.rs b/packages/wasm-dpp/src/identity/state_transition/identity_create_transition/identity_create_transition_state_validator.rs new file mode 100644 index 00000000000..6f240cb7775 --- /dev/null +++ b/packages/wasm-dpp/src/identity/state_transition/identity_create_transition/identity_create_transition_state_validator.rs @@ -0,0 +1,37 @@ +use crate::errors::from_dpp_err; +use crate::state_repository::{ExternalStateRepositoryLike, ExternalStateRepositoryLikeWrapper}; +use crate::validation::ValidationResultWasm; +use crate::IdentityCreateTransitionWasm; +use dpp::identity::state_transition::identity_create_transition::validation::state::validate_identity_create_transition_state; +use wasm_bindgen::prelude::*; +use wasm_bindgen::JsValue; + +#[wasm_bindgen(js_name=IdentityCreateTransitionStateValidator)] +pub struct IdentityCreateTransitionStateValidator { + state_repository: ExternalStateRepositoryLikeWrapper, +} + +#[wasm_bindgen(js_class=IdentityCreateTransitionStateValidator)] +impl IdentityCreateTransitionStateValidator { + #[wasm_bindgen(constructor)] + pub fn new(state_repository: ExternalStateRepositoryLike) -> Self { + Self { + state_repository: ExternalStateRepositoryLikeWrapper::new(state_repository), + } + } + + #[wasm_bindgen(js_name = validate)] + pub async fn validate( + &self, + state_transition: &IdentityCreateTransitionWasm, + ) -> Result { + let validation_result = validate_identity_create_transition_state( + &self.state_repository, + state_transition.to_owned().into(), + ) + .await + .map_err(|e| from_dpp_err(e.into()))?; + + Ok(validation_result.map(|_| JsValue::undefined()).into()) + } +} diff --git a/packages/wasm-dpp/src/identity/state_transition/identity_create_transition/mod.rs b/packages/wasm-dpp/src/identity/state_transition/identity_create_transition/mod.rs new file mode 100644 index 00000000000..e2c7d563d9f --- /dev/null +++ b/packages/wasm-dpp/src/identity/state_transition/identity_create_transition/mod.rs @@ -0,0 +1,8 @@ +pub use identity_create_transition::*; + +pub mod apply_identity_create_transition; +pub mod identity_create_transition; +pub mod identity_create_transition_basic_validator; +pub mod identity_create_transition_state_validator; + +mod to_object; diff --git a/packages/wasm-dpp/src/identity/state_transition/identity_create_transition/to_object.rs b/packages/wasm-dpp/src/identity/state_transition/identity_create_transition/to_object.rs new file mode 100644 index 00000000000..69b27bbfa0c --- /dev/null +++ b/packages/wasm-dpp/src/identity/state_transition/identity_create_transition/to_object.rs @@ -0,0 +1,46 @@ +use dpp::identity::state_transition::asset_lock_proof::AssetLockProof; +use dpp::{ + identifier::Identifier, + identity::{ + state_transition::identity_create_transition::IdentityCreateTransition, IdentityPublicKey, + }, + state_transition::StateTransitionLike, +}; +use serde::Deserialize; +use std::default::Default; + +#[derive(Deserialize, Default)] +#[serde(rename_all = "camelCase")] +pub struct ToObjectOptions { + pub skip_signature: Option, +} + +#[derive(Default)] +pub struct ToObject { + pub transition_type: u8, + pub protocol_version: u32, + pub identity_id: Identifier, + pub asset_lock_proof: AssetLockProof, + pub public_keys: Vec, + pub signature: Option>, +} + +pub fn to_object_struct( + transition: &IdentityCreateTransition, + options: ToObjectOptions, +) -> ToObject { + let mut to_object = ToObject { + transition_type: transition.get_type() as u8, + protocol_version: transition.get_protocol_version(), + public_keys: transition.get_public_keys().to_owned(), + asset_lock_proof: transition.get_asset_lock_proof().to_owned(), + identity_id: transition.get_identity_id().to_owned(), + signature: None, + }; + + if !options.skip_signature.unwrap_or(false) { + to_object.signature = Some(transition.get_signature().to_owned()); + } + + to_object +} diff --git a/packages/wasm-dpp/src/identity/state_transition/mod.rs b/packages/wasm-dpp/src/identity/state_transition/mod.rs new file mode 100644 index 00000000000..1413ed30422 --- /dev/null +++ b/packages/wasm-dpp/src/identity/state_transition/mod.rs @@ -0,0 +1,7 @@ +pub use asset_lock_proof::*; +pub use identity_create_transition::*; +pub use validate_public_key_signatures::*; + +mod asset_lock_proof; +mod identity_create_transition; +mod validate_public_key_signatures; diff --git a/packages/wasm-dpp/src/identity/state_transition/validate_public_key_signatures.rs b/packages/wasm-dpp/src/identity/state_transition/validate_public_key_signatures.rs new file mode 100644 index 00000000000..6d6231887f4 --- /dev/null +++ b/packages/wasm-dpp/src/identity/state_transition/validate_public_key_signatures.rs @@ -0,0 +1,59 @@ +use crate::bls_adapter::{BlsAdapter, JsBlsAdapter}; +use crate::errors::from_dpp_err; +use crate::utils::ToSerdeJSONExt; + +use crate::{ + identity::identity_public_key::IdentityPublicKeyWasm, validation::ValidationResultWasm, +}; + +use dpp::identity::state_transition::validate_public_key_signatures::{ + PublicKeysSignaturesValidator, TPublicKeysSignaturesValidator, +}; +use dpp::prelude::IdentityPublicKey; + +use serde_json::Value as JsonValue; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; + +#[wasm_bindgen(js_name = PublicKeysSignaturesValidator)] +pub struct PublicKeysSignaturesValidatorWasm(PublicKeysSignaturesValidator); + +impl From> for PublicKeysSignaturesValidatorWasm { + fn from(validator: PublicKeysSignaturesValidator) -> Self { + Self(validator) + } +} + +#[wasm_bindgen(js_class = PublicKeysSignaturesValidator)] +impl PublicKeysSignaturesValidatorWasm { + #[wasm_bindgen(constructor)] + pub fn new(bls: JsBlsAdapter) -> PublicKeysSignaturesValidatorWasm { + PublicKeysSignaturesValidator::new(BlsAdapter(bls)).into() + } + + #[wasm_bindgen(js_name=validate)] + pub fn validate_public_key_signatures( + &self, + raw_state_transition: JsValue, + raw_public_keys: Vec, + ) -> Result { + let state_transition_json = raw_state_transition.with_serde_to_json_value()?; + + let public_keys = raw_public_keys + .into_iter() + .map(|raw_key| { + let parsed_key: IdentityPublicKey = IdentityPublicKeyWasm::new(raw_key)?.into(); + parsed_key + .to_raw_json_object(false) + .map_err(|e| from_dpp_err(e.into())) + }) + .collect::, JsValue>>()?; + + let result = self + .0 + .validate_public_key_signatures(&state_transition_json, &public_keys) + .map_err(|e| from_dpp_err(e.into()))?; + + Ok(result.map(|_| JsValue::undefined()).into()) + } +} diff --git a/packages/wasm-dpp/src/identity/validation/public_keys_validator.rs b/packages/wasm-dpp/src/identity/validation/public_keys_validator.rs index 6f1b63e9c47..5465793825b 100644 --- a/packages/wasm-dpp/src/identity/validation/public_keys_validator.rs +++ b/packages/wasm-dpp/src/identity/validation/public_keys_validator.rs @@ -36,10 +36,12 @@ impl PublicKeysValidatorWasm { ) -> Result { let raw_public_keys = to_vec_of_serde_values(public_keys.iter())?; - self.public_key_validator + let validation_result = self + .public_key_validator .validate_keys(&raw_public_keys) - .map(ValidationResultWasm::from) - .map_err(|e| JsValue::from(e.to_string())) + .map_err(|e| JsValue::from(e.to_string()))?; + + Ok(validation_result.map(|_| JsValue::undefined()).into()) } #[wasm_bindgen(js_name=validatePublicKeyStructure)] @@ -49,10 +51,12 @@ impl PublicKeysValidatorWasm { ) -> Result { let pk_serde_json = public_key.with_serde_to_json_value()?; - self.public_key_validator + let validation_result = self + .public_key_validator .validate_public_key_structure(&pk_serde_json) - .map(ValidationResultWasm::from) - .map_err(|e| JsValue::from(e.to_string())) + .map_err(|e| JsValue::from(e.to_string()))?; + + Ok(validation_result.map(|_| JsValue::undefined()).into()) } #[wasm_bindgen(js_name=validateKeysInStateTransition)] @@ -62,9 +66,11 @@ impl PublicKeysValidatorWasm { ) -> Result { let raw_public_keys = to_vec_of_serde_values(public_keys.iter())?; - self.public_key_in_state_transition_validator + let validation_result = self + .public_key_in_state_transition_validator .validate_keys(&raw_public_keys) - .map(ValidationResultWasm::from) - .map_err(|e| JsValue::from(e.to_string())) + .map_err(|e| JsValue::from(e.to_string()))?; + + Ok(validation_result.map(|_| JsValue::undefined()).into()) } } diff --git a/packages/wasm-dpp/src/state_repository.rs b/packages/wasm-dpp/src/state_repository.rs index 072081b9352..8381ff272e4 100644 --- a/packages/wasm-dpp/src/state_repository.rs +++ b/packages/wasm-dpp/src/state_repository.rs @@ -1,19 +1,27 @@ //! Bindings for state repository -like objects coming from JS. +use serde::{Deserialize, Serialize}; use std::{convert::Infallible, pin::Pin, sync::Mutex}; use async_trait::async_trait; +use dpp::dashcore::consensus; use dpp::{ dashcore::InstantLock, data_contract::DataContract, document::Document, prelude::{Identifier, Identity}, - state_repository::StateRepositoryLike, + state_repository::{ + FetchTransactionResponse as FetchTransactionResponseDPP, StateRepositoryLike, + }, state_transition::state_transition_execution_context::StateTransitionExecutionContext, }; use wasm_bindgen::prelude::*; -use crate::{identifier::IdentifierWrapper, DataContractWasm, StateTransitionExecutionContextWasm}; +use crate::buffer::Buffer; +use crate::{ + identifier::IdentifierWrapper, DataContractWasm, IdentityWasm, + StateTransitionExecutionContextWasm, +}; #[wasm_bindgen] extern "C" { @@ -33,6 +41,60 @@ extern "C" { execution_context: StateTransitionExecutionContextWasm, ) -> JsValue; + #[wasm_bindgen(structural, method, js_name=fetchIdentity)] + pub fn fetch_identity( + this: &ExternalStateRepositoryLike, + id: IdentifierWrapper, + execution_context: StateTransitionExecutionContextWasm, + ) -> Option; + + #[wasm_bindgen(structural, method, js_name=fetchLatestPlatformCoreChainLockedHeight)] + pub fn fetch_latest_platform_core_chain_locked_height( + this: &ExternalStateRepositoryLike, + ) -> Option; + + #[wasm_bindgen(structural, method, js_name=fetchTransaction)] + pub fn fetch_transaction( + this: &ExternalStateRepositoryLike, + id: JsValue, + execution_context: StateTransitionExecutionContextWasm, + ) -> JsValue; + + #[wasm_bindgen(structural, method, js_name=isAssetLockTransactionOutPointAlreadyUsed)] + pub fn is_asset_lock_transaction_out_point_already_used( + this: &ExternalStateRepositoryLike, + out_point_buffer: Buffer, + execution_context: StateTransitionExecutionContextWasm, + ) -> bool; + + #[wasm_bindgen(structural, method, js_name=verifyInstantLock)] + pub fn verify_instant_lock( + this: &ExternalStateRepositoryLike, + instant_lock: Vec, + execution_context: StateTransitionExecutionContextWasm, + ) -> bool; + + #[wasm_bindgen(structural, method, js_name=createIdentity)] + pub fn create_identity( + this: &ExternalStateRepositoryLike, + identity: IdentityWasm, + execution_context: StateTransitionExecutionContextWasm, + ); + + #[wasm_bindgen(structural, method, js_name=storeIdentityPublicKeyHashes)] + pub fn store_identity_public_key_hashes( + this: &ExternalStateRepositoryLike, + identity_id: IdentifierWrapper, + public_key_hashes: Vec, + execution_context: StateTransitionExecutionContextWasm, + ); + + #[wasm_bindgen(structural, method, js_name=markAssetLockTransactionOutPointAsUsed)] + pub fn mark_asset_lock_transaction_out_point_as_used( + this: &ExternalStateRepositoryLike, + out_point_buffer: Buffer, + ); + // TODO add missing declarations } @@ -49,10 +111,40 @@ impl ExternalStateRepositoryLikeWrapper { } } +#[derive(Clone, Serialize, Deserialize)] +pub struct FetchTransactionResponse { + pub height: Option, + pub data: Option>, +} + +impl From for FetchTransactionResponse { + fn from(v: JsValue) -> Self { + if v.is_falsy() { + FetchTransactionResponse { + height: Some(0), + data: None, + } + } else { + serde_wasm_bindgen::from_value(v).unwrap() + } + } +} + +impl From for FetchTransactionResponseDPP { + fn from(v: FetchTransactionResponse) -> Self { + FetchTransactionResponseDPP { + data: v.data, + height: v.height, + } + } +} + #[async_trait] impl StateRepositoryLike for ExternalStateRepositoryLikeWrapper { type ConversionError = Infallible; type FetchDataContract = DataContractWasm; + type FetchIdentity = IdentityWasm; + type FetchTransaction = FetchTransactionResponse; async fn fetch_data_contract( &self, @@ -121,35 +213,52 @@ impl StateRepositoryLike for ExternalStateRepositoryLikeWrapper { todo!() } - async fn fetch_transaction( + async fn fetch_transaction( &self, - _id: &str, - _execution_context: &StateTransitionExecutionContext, - ) -> anyhow::Result> - where - T: for<'de> serde::de::Deserialize<'de> + 'static, - { - todo!() + id: &str, + execution_context: &StateTransitionExecutionContext, + ) -> anyhow::Result { + let response = self + .0 + .lock() + .expect("unexpected concurrency issue!") + .fetch_transaction(JsValue::from_str(id), execution_context.into()); + Ok(FetchTransactionResponse::from(response)) } - async fn fetch_identity( + async fn fetch_identity( &self, - _id: &Identifier, - _execution_context: &StateTransitionExecutionContext, - ) -> anyhow::Result> - where - T: for<'de> serde::de::Deserialize<'de> + 'static, - { - todo!() + id: &Identifier, + execution_context: &StateTransitionExecutionContext, + ) -> anyhow::Result> { + Ok(self + .0 + .lock() + .expect("unexpected concurrency issue!") + .fetch_identity(id.clone().into(), execution_context.clone().into()) + .map(Into::into)) } async fn store_identity_public_key_hashes( &self, - _identity_id: &Identifier, - _public_key_hashes: Vec>, - _execution_context: &StateTransitionExecutionContext, + identity_id: &Identifier, + public_key_hashes: Vec>, + execution_context: &StateTransitionExecutionContext, ) -> anyhow::Result<()> { - todo!() + let hashes = public_key_hashes + .iter() + .map(|hash| Buffer::from_bytes(hash)) + .collect(); + + Ok(self + .0 + .lock() + .expect("unexpected concurrency issue!") + .store_identity_public_key_hashes( + identity_id.clone().into(), + hashes, + execution_context.clone().into(), + )) } async fn fetch_identity_by_public_key_hashes( @@ -169,27 +278,55 @@ impl StateRepositoryLike for ExternalStateRepositoryLikeWrapper { todo!() } + async fn fetch_latest_platform_core_chain_locked_height(&self) -> anyhow::Result> { + Ok(self + .0 + .lock() + .expect("unexpected concurrency issue!") + .fetch_latest_platform_core_chain_locked_height() + .map(Into::into)) + } + async fn verify_instant_lock( &self, - _instant_lock: &InstantLock, - _execution_context: &StateTransitionExecutionContext, + instant_lock: &InstantLock, + execution_context: &StateTransitionExecutionContext, ) -> anyhow::Result { - todo!() + let raw_instant_lock = consensus::serialize(instant_lock); + + let verified = self + .0 + .lock() + .expect("unexpected concurrency issue!") + .verify_instant_lock(raw_instant_lock, execution_context.clone().into()); + + Ok(verified) } async fn is_asset_lock_transaction_out_point_already_used( &self, - _out_point_buffer: &[u8], - _execution_context: &StateTransitionExecutionContext, + out_point_buffer: &[u8], + execution_context: &StateTransitionExecutionContext, ) -> anyhow::Result { - todo!() + Ok(self + .0 + .lock() + .expect("unexpected concurrency issue!") + .is_asset_lock_transaction_out_point_already_used( + Buffer::from_bytes(out_point_buffer), + execution_context.clone().into(), + )) } async fn mark_asset_lock_transaction_out_point_as_used( &self, - _out_point_buffer: &[u8], + out_point_buffer: &[u8], ) -> anyhow::Result<()> { - todo!() + Ok(self + .0 + .lock() + .expect("unexpected concurrency issue!") + .mark_asset_lock_transaction_out_point_as_used(Buffer::from_bytes(out_point_buffer))) } async fn fetch_sml_store(&self) -> anyhow::Result @@ -201,10 +338,14 @@ impl StateRepositoryLike for ExternalStateRepositoryLikeWrapper { async fn create_identity( &self, - _identity: &Identity, - _execution_context: &StateTransitionExecutionContext, + identity: &Identity, + execution_context: &StateTransitionExecutionContext, ) -> anyhow::Result<()> { - todo!() + Ok(self + .0 + .lock() + .expect("unexpected concurrency issue!") + .create_identity(identity.clone().into(), execution_context.clone().into())) } async fn update_identity( diff --git a/packages/wasm-dpp/src/state_transition/mod.rs b/packages/wasm-dpp/src/state_transition/mod.rs index ff568cfa760..9abe0ceeb88 100644 --- a/packages/wasm-dpp/src/state_transition/mod.rs +++ b/packages/wasm-dpp/src/state_transition/mod.rs @@ -22,6 +22,18 @@ impl<'a> From<&'a StateTransitionExecutionContextWasm> for StateTransitionExecut } } +impl<'a> From<&'a StateTransitionExecutionContextWasm> for &'a StateTransitionExecutionContext { + fn from(wa: &'a StateTransitionExecutionContextWasm) -> Self { + &wa.0 + } +} + +impl<'a> From<&'a StateTransitionExecutionContext> for StateTransitionExecutionContextWasm { + fn from(rs: &'a StateTransitionExecutionContext) -> Self { + Self(rs.clone()) + } +} + #[wasm_bindgen(js_class=StateTransitionExecutionContext)] impl StateTransitionExecutionContextWasm { #[wasm_bindgen(constructor)] diff --git a/packages/wasm-dpp/src/utils.rs b/packages/wasm-dpp/src/utils.rs index 66f10f527ce..68cec372e6c 100644 --- a/packages/wasm-dpp/src/utils.rs +++ b/packages/wasm-dpp/src/utils.rs @@ -82,7 +82,7 @@ where pub fn stringify(data: &JsValue) -> Result { let replacer_func = Function::new_with_args( "key, value", - "return value.type=='Buffer' ? value.data : value ", + "return value && value.type=='Buffer' ? value.data : value ", ); let data_string: String = @@ -139,9 +139,9 @@ pub fn generic_of_js_val>( if ctor_name == class_name { let ptr = js_sys::Reflect::get(js_value, &JsValue::from_str("ptr"))?; - let ptr_u32: u32 = ptr - .as_f64() - .ok_or(JsValue::from("Invalid JS object pointer"))? as u32; + let ptr_u32: u32 = + ptr.as_f64() + .ok_or_else(|| JsValue::from("Invalid JS object pointer"))? as u32; let reference = unsafe { T::ref_from_abi(ptr_u32) }; Ok(reference) } else { diff --git a/packages/wasm-dpp/src/validation/validation_result.rs b/packages/wasm-dpp/src/validation/validation_result.rs index fa096239420..c220f725f80 100644 --- a/packages/wasm-dpp/src/validation/validation_result.rs +++ b/packages/wasm-dpp/src/validation/validation_result.rs @@ -4,7 +4,16 @@ use js_sys::JsString; use wasm_bindgen::prelude::*; #[wasm_bindgen(js_name=ValidationResult)] -pub struct ValidationResultWasm(ValidationResult<()>); +pub struct ValidationResultWasm(ValidationResult); + +impl From> for ValidationResultWasm +where + T: Into + Clone, +{ + fn from(validation_result: ValidationResult) -> Self { + ValidationResultWasm(validation_result.map(Into::into)) + } +} #[wasm_bindgen(js_class=ValidationResult)] impl ValidationResultWasm { @@ -32,10 +41,18 @@ impl ValidationResultWasm { .map(from_consensus_error_ref) .collect() } -} -impl From> for ValidationResultWasm { - fn from(validation_result: ValidationResult<()>) -> Self { - ValidationResultWasm(validation_result) + #[wasm_bindgen(js_name=getData)] + pub fn get_data(&self) -> JsValue { + self.0.data().unwrap_or(&JsValue::undefined()).to_owned() + } + + #[wasm_bindgen(js_name=getFirstError)] + pub fn get_first_error(&self) -> JsValue { + if !self.0.errors.is_empty() { + from_consensus_error_ref(&self.0.errors[0]) + } else { + JsValue::undefined() + } } } diff --git a/packages/wasm-dpp/test/integration/identity/stateTransition/IdentityCreateTransition/validation/basic/validateIdentityCreateTransitionBasicFactory.spec.js b/packages/wasm-dpp/test/integration/identity/stateTransition/IdentityCreateTransition/validation/basic/validateIdentityCreateTransitionBasicFactory.spec.js index b9b784c0c57..65d08c5ef2a 100644 --- a/packages/wasm-dpp/test/integration/identity/stateTransition/IdentityCreateTransition/validation/basic/validateIdentityCreateTransitionBasicFactory.spec.js +++ b/packages/wasm-dpp/test/integration/identity/stateTransition/IdentityCreateTransition/validation/basic/validateIdentityCreateTransitionBasicFactory.spec.js @@ -1,83 +1,86 @@ -const { getRE2Class } = require('@dashevo/wasm-re2'); - -const createAjv = require('@dashevo/dpp/lib/ajv/createAjv'); - -const JsonSchemaValidator = require('@dashevo/dpp/lib/validation/JsonSchemaValidator'); +const { PrivateKey } = require('@dashevo/dashcore-lib'); const getIdentityCreateTransitionFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityCreateTransitionFixture'); -const validateIdentityCreateTransitionBasicFactory = require( - '@dashevo/dpp/lib/identity/stateTransition/IdentityCreateTransition/validation/basic/validateIdentityCreateTransitionBasicFactory', -); - -const { - expectJsonSchemaError, - expectValidationError, -} = require('@dashevo/dpp/lib/test/expect/expectError'); - -const ValidationResult = require('@dashevo/dpp/lib/validation/ValidationResult'); -const InstantAssetLockProof = require('@dashevo/dpp/lib/identity/stateTransition/assetLockProof/instant/InstantAssetLockProof'); -const ChainAssetLockProof = require('@dashevo/dpp/lib/identity/stateTransition/assetLockProof/chain/ChainAssetLockProof'); -const SomeConsensusError = require('@dashevo/dpp/lib/test/mocks/SomeConsensusError'); -const IdentityPublicKey = require('@dashevo/dpp/lib/identity/IdentityPublicKey'); -const StateTransitionExecutionContext = require('@dashevo/dpp/lib/stateTransition/StateTransitionExecutionContext'); +const createStateRepositoryMock = require('@dashevo/dpp/lib/test/mocks/createStateRepositoryMock'); +const { expectValidationError, expectJsonSchemaError } = require('../../../../../../../lib/test/expect/expectError'); +const { default: loadWasmDpp } = require('../../../../../../../dist'); +const getBlsAdapterMock = require('../../../../../../../lib/test/mocks/getBlsAdapterMock'); describe('validateIdentityCreateTransitionBasicFactory', () => { - let validateIdentityCreateTransitionBasic; let rawStateTransition; let stateTransition; - let validatePublicKeysMock; - let validatePublicKeysInIdentityCreateTransition; - let assetLockPublicKeyHash; - let proofValidationFunctionsByTypeMock; - let validateProtocolVersionMock; - let validatePublicKeySignaturesMock; - beforeEach(async function beforeEach() { - validatePublicKeysMock = this.sinonSandbox.stub() - .returns(new ValidationResult()); + let stateRepositoryMock; + let mockIdentityPublicKey; + let executionContext; - validatePublicKeysInIdentityCreateTransition = this.sinonSandbox.stub() - .returns(new ValidationResult()); + let validateIdentityCreateTransitionBasic; - assetLockPublicKeyHash = Buffer.alloc(20, 1); + let StateTransitionExecutionContext; + let IdentityCreateTransition; + let IdentityPublicKey; + let UnsupportedProtocolVersionError; + let InvalidInstantAssetLockProofSignatureError; + let InvalidIdentityPublicKeySecurityLevelError; + let InvalidIdentityPublicKeyDataError; + let InvalidIdentityKeySignatureError; + let IdentityCreateTransitionBasicValidator; + + before(async () => { + ({ + IdentityCreateTransition, + StateTransitionExecutionContext, + UnsupportedProtocolVersionError, + InvalidInstantAssetLockProofSignatureError, + InvalidIdentityPublicKeySecurityLevelError, + InvalidIdentityPublicKeyDataError, + InvalidIdentityKeySignatureError, + IdentityPublicKey, + IdentityCreateTransitionBasicValidator, + } = await loadWasmDpp()); + + mockIdentityPublicKey = (publicKey, opts = {}) => new IdentityPublicKey({ + id: 0, + type: IdentityPublicKey.TYPES.ECDSA_SECP256K1, + data: publicKey, + purpose: IdentityPublicKey.PURPOSES.AUTHENTICATION, + securityLevel: IdentityPublicKey.SECURITY_LEVELS.MASTER, + readOnly: false, + ...opts, + }); + }); - const assetLockValidationResult = new ValidationResult(); + beforeEach(async function () { + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); + stateRepositoryMock.verifyInstantLock.returns(true); - assetLockValidationResult.setData(assetLockPublicKeyHash); + executionContext = new StateTransitionExecutionContext(); - const RE2 = await getRE2Class(); - const ajv = createAjv(RE2); + const blsAdapter = await getBlsAdapterMock(); - const jsonSchemaValidator = new JsonSchemaValidator(ajv); + const validator = new IdentityCreateTransitionBasicValidator(stateRepositoryMock, blsAdapter); + validateIdentityCreateTransitionBasic = (st, context) => validator.validate(st, context); - const proofValidationResult = new ValidationResult(); - proofValidationResult.setData(assetLockPublicKeyHash); + const stateTransitionJS = getIdentityCreateTransitionFixture(); + stateTransition = new IdentityCreateTransition(stateTransitionJS.toObject()); - proofValidationFunctionsByTypeMock = { - [InstantAssetLockProof.type]: this.sinonSandbox.stub().resolves(proofValidationResult), - [ChainAssetLockProof.type]: this.sinonSandbox.stub().resolves(proofValidationResult), - }; + const privateKey = new PrivateKey('9b67f852093bc61cea0eeca38599dbfba0de28574d2ed9b99d10d33dc1bde7b2'); + const publicKey = privateKey.toPublicKey(); - validateProtocolVersionMock = this.sinonSandbox.stub().returns(new ValidationResult()); + const identityPublicKey = mockIdentityPublicKey(publicKey.toBuffer()); - validatePublicKeySignaturesMock = this.sinonSandbox.stub() - .returns(new ValidationResult()); + stateTransition.setPublicKeys([identityPublicKey]); - validateIdentityCreateTransitionBasic = validateIdentityCreateTransitionBasicFactory( - jsonSchemaValidator, - validatePublicKeysMock, - validatePublicKeysInIdentityCreateTransition, - proofValidationFunctionsByTypeMock, - validateProtocolVersionMock, - validatePublicKeySignaturesMock, + await stateTransition.signByPrivateKey( + privateKey.toBuffer(), + identityPublicKey.type, ); - stateTransition = getIdentityCreateTransitionFixture(); - - const privateKey = '9b67f852093bc61cea0eeca38599dbfba0de28574d2ed9b99d10d33dc1bde7b2'; + const signature = stateTransition.getSignature(); + identityPublicKey.setSignature(signature); - await stateTransition.signByPrivateKey(privateKey, IdentityPublicKey.TYPES.ECDSA_SECP256K1); + stateTransition.setPublicKeys([identityPublicKey]); rawStateTransition = stateTransition.toObject(); }); @@ -86,9 +89,12 @@ describe('validateIdentityCreateTransitionBasicFactory', () => { it('should be present', async () => { delete rawStateTransition.protocolVersion; - const result = await validateIdentityCreateTransitionBasic(rawStateTransition); + const result = await validateIdentityCreateTransitionBasic( + rawStateTransition, + executionContext, + ); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -100,9 +106,12 @@ describe('validateIdentityCreateTransitionBasicFactory', () => { it('should be an integer', async () => { rawStateTransition.protocolVersion = '1'; - const result = await validateIdentityCreateTransitionBasic(rawStateTransition); + const result = await validateIdentityCreateTransitionBasic( + rawStateTransition, + executionContext, + ); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -111,26 +120,14 @@ describe('validateIdentityCreateTransitionBasicFactory', () => { }); it('should be valid', async () => { - rawStateTransition.protocolVersion = -1; - - const protocolVersionError = new SomeConsensusError('test'); - const protocolVersionResult = new ValidationResult([ - protocolVersionError, - ]); - - validateProtocolVersionMock.returns(protocolVersionResult); - - const result = await validateIdentityCreateTransitionBasic(rawStateTransition); - - expectValidationError(result, SomeConsensusError); - - const [error] = result.getErrors(); + rawStateTransition.protocolVersion = 1000; - expect(error).to.equal(protocolVersionError); - - expect(validateProtocolVersionMock).to.be.calledOnceWith( - rawStateTransition.protocolVersion, + const result = await validateIdentityCreateTransitionBasic( + rawStateTransition, + executionContext, ); + + await expectValidationError(result, UnsupportedProtocolVersionError); }); }); @@ -138,9 +135,12 @@ describe('validateIdentityCreateTransitionBasicFactory', () => { it('should be present', async () => { delete rawStateTransition.type; - const result = await validateIdentityCreateTransitionBasic(rawStateTransition); + const result = await validateIdentityCreateTransitionBasic( + rawStateTransition, + executionContext, + ); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -152,9 +152,12 @@ describe('validateIdentityCreateTransitionBasicFactory', () => { it('should be equal to 2', async () => { rawStateTransition.type = 666; - const result = await validateIdentityCreateTransitionBasic(rawStateTransition); + const result = await validateIdentityCreateTransitionBasic( + rawStateTransition, + executionContext, + ); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -170,9 +173,10 @@ describe('validateIdentityCreateTransitionBasicFactory', () => { const result = await validateIdentityCreateTransitionBasic( rawStateTransition, + executionContext, ); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -184,9 +188,12 @@ describe('validateIdentityCreateTransitionBasicFactory', () => { it('should be an object', async () => { rawStateTransition.assetLockProof = 1; - const result = await validateIdentityCreateTransitionBasic(rawStateTransition); + const result = await validateIdentityCreateTransitionBasic( + rawStateTransition, + executionContext, + ); - expectJsonSchemaError(result, 1); + await expectJsonSchemaError(result, 1); const [error] = result.getErrors(); @@ -195,31 +202,18 @@ describe('validateIdentityCreateTransitionBasicFactory', () => { }); it('should be valid', async () => { - const assetLockError = new SomeConsensusError('test'); - const assetLockResult = new ValidationResult([ - assetLockError, - ]); - - const executionContext = new StateTransitionExecutionContext(); - - proofValidationFunctionsByTypeMock[InstantAssetLockProof.type].resolves(assetLockResult); + stateRepositoryMock.verifyInstantLock.returns(false); const result = await validateIdentityCreateTransitionBasic( rawStateTransition, executionContext, ); - expectValidationError(result); + await expectValidationError(result); const [error] = result.getErrors(); - expect(error).to.equal(assetLockError); - - expect(proofValidationFunctionsByTypeMock[InstantAssetLockProof.type]) - .to.be.calledOnceWithExactly( - rawStateTransition.assetLockProof, - executionContext, - ); + expect(error).to.be.instanceOf(InvalidInstantAssetLockProofSignatureError); }); }); @@ -229,9 +223,10 @@ describe('validateIdentityCreateTransitionBasicFactory', () => { const result = await validateIdentityCreateTransitionBasic( rawStateTransition, + executionContext, ); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -245,9 +240,10 @@ describe('validateIdentityCreateTransitionBasicFactory', () => { const result = await validateIdentityCreateTransitionBasic( rawStateTransition, + executionContext, ); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -259,14 +255,15 @@ describe('validateIdentityCreateTransitionBasicFactory', () => { const [key] = rawStateTransition.publicKeys; for (let i = 0; i < 10; i++) { - rawStateTransition.publicKeys.push(key); + rawStateTransition.publicKeys.push({ ...key, id: i + 1 }); } const result = await validateIdentityCreateTransitionBasic( rawStateTransition, + executionContext, ); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -279,9 +276,10 @@ describe('validateIdentityCreateTransitionBasicFactory', () => { const result = await validateIdentityCreateTransitionBasic( rawStateTransition, + executionContext, ); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -290,66 +288,65 @@ describe('validateIdentityCreateTransitionBasicFactory', () => { }); it('should be valid', async () => { - const publicKeysError = new SomeConsensusError('test'); - const publicKeysResult = new ValidationResult([ - publicKeysError, - ]); + const privateKey = new PrivateKey(); - validatePublicKeysMock.returns(publicKeysResult); + // Mess up public key + const identityPublicKey = mockIdentityPublicKey(Buffer.alloc(33)); - const result = await validateIdentityCreateTransitionBasic( - rawStateTransition, + stateTransition.setPublicKeys([identityPublicKey]); + await stateTransition.signByPrivateKey( + privateKey.toBuffer(), + identityPublicKey.type, ); + identityPublicKey.setSignature(stateTransition.getSignature()); + stateTransition.setPublicKeys([identityPublicKey]); - expectValidationError(result); - - const [error] = result.getErrors(); + rawStateTransition = stateTransition.toObject(); - expect(error).to.equal(publicKeysError); + const result = await validateIdentityCreateTransitionBasic( + rawStateTransition, + executionContext, + ); - expect(validatePublicKeysMock) - .to.be.calledOnceWithExactly(rawStateTransition.publicKeys); + await expectValidationError(result, InvalidIdentityPublicKeyDataError); }); it('should have at least 1 master key', async () => { - const publicKeysError = new SomeConsensusError('test'); - const publicKeysResult = new ValidationResult([ - publicKeysError, - ]); + const privateKey = new PrivateKey(); - validatePublicKeysInIdentityCreateTransition.returns(publicKeysResult); + // Mess up public key's purpose + const identityPublicKey = mockIdentityPublicKey(privateKey.toPublicKey().toBuffer()); + identityPublicKey.setPurpose(2); - const result = await validateIdentityCreateTransitionBasic( - rawStateTransition, + stateTransition.setPublicKeys([identityPublicKey]); + await stateTransition.signByPrivateKey( + privateKey.toBuffer(), + identityPublicKey.type, ); + identityPublicKey.setSignature(stateTransition.getSignature()); + stateTransition.setPublicKeys([identityPublicKey]); - expectValidationError(result); - - const [error] = result.getErrors(); + rawStateTransition = stateTransition.toObject(); - expect(error).to.equal(publicKeysError); + const result = await validateIdentityCreateTransitionBasic( + rawStateTransition, + executionContext, + ); - expect(validatePublicKeysInIdentityCreateTransition) - .to.be.calledOnceWithExactly(rawStateTransition.publicKeys); + await expectValidationError(result, InvalidIdentityPublicKeySecurityLevelError); }); it('should have valid signatures', async () => { - const publicKeysError = new SomeConsensusError('test'); - const publicKeysResult = new ValidationResult([ - publicKeysError, - ]); - - validatePublicKeySignaturesMock.resolves(publicKeysResult); + const invalidSignature = Buffer.alloc(65); + rawStateTransition.signature = invalidSignature; + rawStateTransition.publicKeys[0].signature = invalidSignature; const result = await validateIdentityCreateTransitionBasic( rawStateTransition, + executionContext, ); - expectValidationError(result); - - const [error] = result.getErrors(); - - expect(error).to.equal(publicKeysError); + await expectValidationError(result, InvalidIdentityKeySignatureError); }); }); @@ -357,13 +354,16 @@ describe('validateIdentityCreateTransitionBasicFactory', () => { it('should be present', async () => { delete rawStateTransition.signature; - const result = await validateIdentityCreateTransitionBasic(rawStateTransition); + const result = await validateIdentityCreateTransitionBasic( + rawStateTransition, + executionContext, + ); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal(''); + expect(error.getInstancePath()).to.equal(''); expect(error.getKeyword()).to.equal('required'); expect(error.getParams().missingProperty).to.equal('signature'); }); @@ -371,52 +371,62 @@ describe('validateIdentityCreateTransitionBasicFactory', () => { it('should be a byte array', async () => { rawStateTransition.signature = new Array(65).fill('string'); - const result = await validateIdentityCreateTransitionBasic(rawStateTransition); + const result = await validateIdentityCreateTransitionBasic( + rawStateTransition, + executionContext, + ); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result, 65); - const [error, byteArrayError] = result.getErrors(); + const firstError = result.getErrors()[0]; + const lastError = result.getErrors()[64]; - expect(error.instancePath).to.equal('/signature/0'); - expect(error.getKeyword()).to.equal('type'); + expect(firstError.getInstancePath()).to.equal('/signature/0'); + expect(firstError.getKeyword()).to.equal('type'); - expect(byteArrayError.getKeyword()).to.equal('byteArray'); + expect(lastError.getInstancePath()).to.equal('/signature/64'); + expect(lastError.getKeyword()).to.equal('type'); }); it('should be not shorter than 65 bytes', async () => { rawStateTransition.signature = Buffer.alloc(64); - const result = await validateIdentityCreateTransitionBasic(rawStateTransition); + const result = await validateIdentityCreateTransitionBasic( + rawStateTransition, + executionContext, + ); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/signature'); + expect(error.getInstancePath()).to.equal('/signature'); expect(error.getKeyword()).to.equal('minItems'); }); it('should be not longer than 65 bytes', async () => { rawStateTransition.signature = Buffer.alloc(66); - const result = await validateIdentityCreateTransitionBasic(rawStateTransition); + const result = await validateIdentityCreateTransitionBasic( + rawStateTransition, + executionContext, + ); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/signature'); + expect(error.getInstancePath()).to.equal('/signature'); expect(error.getKeyword()).to.equal('maxItems'); }); }); it('should return valid result', async () => { - const result = await validateIdentityCreateTransitionBasic(rawStateTransition); + const result = await validateIdentityCreateTransitionBasic( + rawStateTransition, + executionContext, + ); expect(result.isValid()).to.be.true(); - - expect(validatePublicKeysMock).to.be.calledOnceWithExactly( - rawStateTransition.publicKeys, - ); }); }); diff --git a/packages/wasm-dpp/test/integration/identity/stateTransition/assetLockProof/chain/validateChainAssetLockProofStructureFactory.spec.js b/packages/wasm-dpp/test/integration/identity/stateTransition/assetLockProof/chain/validateChainAssetLockProofStructureFactory.spec.js index 61429b09702..46131521e36 100644 --- a/packages/wasm-dpp/test/integration/identity/stateTransition/assetLockProof/chain/validateChainAssetLockProofStructureFactory.spec.js +++ b/packages/wasm-dpp/test/integration/identity/stateTransition/assetLockProof/chain/validateChainAssetLockProofStructureFactory.spec.js @@ -1,53 +1,59 @@ -const { getRE2Class } = require('@dashevo/wasm-re2'); - -const createAjv = require('@dashevo/dpp/lib/ajv/createAjv'); +const { Transaction, Script, PrivateKey } = require('@dashevo/dashcore-lib'); const getChainAssetLockFixture = require('@dashevo/dpp/lib/test/fixtures/getChainAssetLockProofFixture'); -const JsonSchemaValidator = require('@dashevo/dpp/lib/validation/JsonSchemaValidator'); const createStateRepositoryMock = require('@dashevo/dpp/lib/test/mocks/createStateRepositoryMock'); -const { expectValidationError, expectJsonSchemaError } = require( - '@dashevo/dpp/lib/test/expect/expectError', -); +const { expect } = require('chai'); +const { expectJsonSchemaError, expectValidationError } = require('../../../../../../lib/test/expect/expectError'); -const validateChainAssetLockProofStructureFactory = require('@dashevo/dpp/lib/identity/stateTransition/assetLockProof/chain/validateChainAssetLockProofStructureFactory'); -const ValidationResult = require('@dashevo/dpp/lib/validation/ValidationResult'); -const IdentityAssetLockTransactionIsNotFoundError = require('@dashevo/dpp/lib/errors/consensus/basic/identity/IdentityAssetLockTransactionIsNotFoundError'); -const InvalidAssetLockProofCoreChainHeightError = require('@dashevo/dpp/lib/errors/consensus/basic/identity/InvalidAssetLockProofCoreChainHeightError'); -const InvalidAssetLockProofTransactionHeightError = require('@dashevo/dpp/lib/errors/consensus/basic/identity/InvalidAssetLockProofTransactionHeightError'); -const SomeConsensusError = require('@dashevo/dpp/lib/test/mocks/SomeConsensusError'); -const StateTransitionExecutionContext = require('@dashevo/dpp/lib/stateTransition/StateTransitionExecutionContext'); +const { default: loadWasmDpp } = require('../../../../../../dist'); describe('validateChainAssetLockProofStructureFactory', () => { let rawProof; let stateRepositoryMock; - let validateChainAssetLockProofStructure; - let jsonSchemaValidator; - let validateAssetLockTransactionMock; - let validateAssetLockTransactionResult; let publicKeyHash; let rawTransaction; let transactionHash; let executionContext; + let validateChainAssetLockProofStructure; + + let StateTransitionExecutionContext; + let ValidationResult; + let InvalidAssetLockProofCoreChainHeightError; + let IdentityAssetLockTransactionIsNotFoundError; + let InvalidIdentityAssetLockTransactionOutputError; + let InvalidAssetLockProofTransactionHeightError; + let ChainAssetLockProofStructureValidator; + + before(async () => { + ({ + StateTransitionExecutionContext, + ValidationResult, + InvalidAssetLockProofCoreChainHeightError, + IdentityAssetLockTransactionIsNotFoundError, + InvalidIdentityAssetLockTransactionOutputError, + InvalidAssetLockProofTransactionHeightError, + ChainAssetLockProofStructureValidator, + } = await loadWasmDpp()); + }); beforeEach(async function beforeEach() { rawTransaction = '030000000137feb5676d0851337ea3c9a992496aab7a0b3eee60aeeb9774000b7f4bababa5000000006b483045022100d91557de37645c641b948c6cd03b4ae3791a63a650db3e2fee1dcf5185d1b10402200e8bd410bf516ca61715867666d31e44495428ce5c1090bf2294a829ebcfa4ef0121025c3cc7fbfc52f710c941497fd01876c189171ea227458f501afcb38a297d65b4ffffffff021027000000000000166a14152073ca2300a86b510fa2f123d3ea7da3af68dcf77cb0090a0000001976a914152073ca2300a86b510fa2f123d3ea7da3af68dc88ac00000000'; transactionHash = '6e200d059fb567ba19e92f5c2dcd3dde522fd4e0a50af223752db16158dabb1d'; - const assetLock = getChainAssetLockFixture(); - - rawProof = assetLock.toObject(); - - const RE2 = await getRE2Class(); - const ajv = createAjv(RE2); + rawProof = getChainAssetLockFixture().toObject(); - jsonSchemaValidator = new JsonSchemaValidator(ajv); + // Change endianness of raw txId bytes in outPoint to match expectation of dashcore-rust + const txId = rawProof.outPoint.slice(0, 32); + const outputIndex = rawProof.outPoint.slice(32); + txId.reverse(); + rawProof.outPoint = Buffer.concat([txId, outputIndex]); stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); - stateRepositoryMock.fetchLatestPlatformCoreChainLockedHeight.resolves(42); + stateRepositoryMock.fetchLatestPlatformCoreChainLockedHeight.returns(42); - stateRepositoryMock.fetchTransaction.resolves({ + stateRepositoryMock.fetchTransaction.returns({ data: Buffer.from(rawTransaction, 'hex'), height: 42, }); @@ -56,18 +62,14 @@ describe('validateChainAssetLockProofStructureFactory', () => { publicKeyHash = Buffer.from('152073ca2300a86b510fa2f123d3ea7da3af68dc', 'hex'); - validateAssetLockTransactionResult = new ValidationResult(); - validateAssetLockTransactionResult.setData({ - publicKeyHash, - }); - validateAssetLockTransactionMock = this.sinonSandbox.stub().resolves( - validateAssetLockTransactionResult, + const validator = new ChainAssetLockProofStructureValidator( + stateRepositoryMock, ); - validateChainAssetLockProofStructure = validateChainAssetLockProofStructureFactory( - jsonSchemaValidator, - stateRepositoryMock, - validateAssetLockTransactionMock, + validateChainAssetLockProofStructure = ( + proof, context, + ) => validator.validate( + rawProof, context, ); }); @@ -80,7 +82,7 @@ describe('validateChainAssetLockProofStructureFactory', () => { executionContext, ); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -89,7 +91,6 @@ describe('validateChainAssetLockProofStructureFactory', () => { expect(error.getParams().missingProperty).to.equal('type'); expect(stateRepositoryMock.fetchTransaction).to.not.be.called(); - expect(validateAssetLockTransactionMock).to.not.be.called(); expect(stateRepositoryMock.fetchLatestPlatformCoreChainLockedHeight).to.not.be.called(); }); @@ -101,7 +102,7 @@ describe('validateChainAssetLockProofStructureFactory', () => { executionContext, ); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -122,7 +123,7 @@ describe('validateChainAssetLockProofStructureFactory', () => { executionContext, ); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -131,7 +132,6 @@ describe('validateChainAssetLockProofStructureFactory', () => { expect(error.getParams().missingProperty).to.equal('coreChainLockedHeight'); expect(stateRepositoryMock.fetchTransaction).to.not.be.called(); - expect(validateAssetLockTransactionMock).to.not.be.called(); expect(stateRepositoryMock.fetchLatestPlatformCoreChainLockedHeight).to.not.be.called(); }); @@ -143,7 +143,7 @@ describe('validateChainAssetLockProofStructureFactory', () => { executionContext, ); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -151,7 +151,6 @@ describe('validateChainAssetLockProofStructureFactory', () => { expect(error.getKeyword()).to.equal('type'); expect(stateRepositoryMock.fetchTransaction).to.not.be.called(); - expect(validateAssetLockTransactionMock).to.not.be.called(); expect(stateRepositoryMock.fetchLatestPlatformCoreChainLockedHeight).to.not.be.called(); }); @@ -163,7 +162,7 @@ describe('validateChainAssetLockProofStructureFactory', () => { executionContext, ); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -171,7 +170,6 @@ describe('validateChainAssetLockProofStructureFactory', () => { expect(error.getKeyword()).to.equal('type'); expect(stateRepositoryMock.fetchTransaction).to.not.be.called(); - expect(validateAssetLockTransactionMock).to.not.be.called(); expect(stateRepositoryMock.fetchLatestPlatformCoreChainLockedHeight).to.not.be.called(); }); @@ -183,7 +181,7 @@ describe('validateChainAssetLockProofStructureFactory', () => { executionContext, ); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -191,7 +189,6 @@ describe('validateChainAssetLockProofStructureFactory', () => { expect(error.getKeyword()).to.equal('minimum'); expect(stateRepositoryMock.fetchTransaction).to.not.be.called(); - expect(validateAssetLockTransactionMock).to.not.be.called(); expect(stateRepositoryMock.fetchLatestPlatformCoreChainLockedHeight).to.not.be.called(); }); @@ -203,20 +200,19 @@ describe('validateChainAssetLockProofStructureFactory', () => { executionContext, ); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/coreChainLockedHeight'); + expect(error.getInstancePath()).to.equal('/coreChainLockedHeight'); expect(error.getKeyword()).to.equal('maximum'); expect(stateRepositoryMock.fetchTransaction).to.not.be.called(); - expect(validateAssetLockTransactionMock).to.not.be.called(); expect(stateRepositoryMock.fetchLatestPlatformCoreChainLockedHeight).to.not.be.called(); }); it('should be less or equal to consensus core height', async () => { - stateRepositoryMock.fetchLatestPlatformCoreChainLockedHeight.resolves(41); + stateRepositoryMock.fetchLatestPlatformCoreChainLockedHeight.returns(41); const result = await validateChainAssetLockProofStructure( rawProof, @@ -242,16 +238,15 @@ describe('validateChainAssetLockProofStructureFactory', () => { executionContext, ); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal(''); + expect(error.getInstancePath()).to.equal(''); expect(error.getKeyword()).to.equal('required'); expect(error.getParams().missingProperty).to.equal('outPoint'); expect(stateRepositoryMock.fetchTransaction).to.not.be.called(); - expect(validateAssetLockTransactionMock).to.not.be.called(); expect(stateRepositoryMock.fetchLatestPlatformCoreChainLockedHeight).to.not.be.called(); }); @@ -263,17 +258,14 @@ describe('validateChainAssetLockProofStructureFactory', () => { executionContext, ); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result, 36); - const [error, byteArrayError] = result.getErrors(); + const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/outPoint/0'); + expect(error.getInstancePath()).to.equal('/outPoint/0'); expect(error.getKeyword()).to.equal('type'); - expect(byteArrayError.getKeyword()).to.equal('byteArray'); - expect(stateRepositoryMock.fetchTransaction).to.not.be.called(); - expect(validateAssetLockTransactionMock).to.not.be.called(); expect(stateRepositoryMock.fetchLatestPlatformCoreChainLockedHeight).to.not.be.called(); }); @@ -285,15 +277,14 @@ describe('validateChainAssetLockProofStructureFactory', () => { executionContext, ); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/outPoint'); + expect(error.getInstancePath()).to.equal('/outPoint'); expect(error.getKeyword()).to.equal('minItems'); expect(stateRepositoryMock.fetchTransaction).to.not.be.called(); - expect(validateAssetLockTransactionMock).to.not.be.called(); expect(stateRepositoryMock.fetchLatestPlatformCoreChainLockedHeight).to.not.be.called(); }); @@ -305,27 +296,26 @@ describe('validateChainAssetLockProofStructureFactory', () => { executionContext, ); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/outPoint'); + expect(error.getInstancePath()).to.equal('/outPoint'); expect(error.getKeyword()).to.equal('maxItems'); expect(stateRepositoryMock.fetchTransaction).to.not.be.called(); - expect(validateAssetLockTransactionMock).to.not.be.called(); expect(stateRepositoryMock.fetchLatestPlatformCoreChainLockedHeight).to.not.be.called(); }); - it('should point to existing transaction', async () => { - stateRepositoryMock.fetchTransaction.resolves(null); + it('should point to existing transaction', async function () { + stateRepositoryMock.fetchTransaction.returns(null); const result = await validateChainAssetLockProofStructure( rawProof, executionContext, ); - expectValidationError(result, IdentityAssetLockTransactionIsNotFoundError); + await expectValidationError(result, IdentityAssetLockTransactionIsNotFoundError); const [error] = result.getErrors(); @@ -336,39 +326,45 @@ describe('validateChainAssetLockProofStructureFactory', () => { expect(stateRepositoryMock.fetchTransaction).to.be.calledOnceWithExactly( transactionHash, - executionContext, + this.sinonSandbox.match.instanceOf(StateTransitionExecutionContext), ); - expect(validateAssetLockTransactionMock).to.not.be.called(); expect(stateRepositoryMock.fetchLatestPlatformCoreChainLockedHeight).to.be.calledOnce(); }); it('should point to valid transaction', async () => { - const consensusError = new SomeConsensusError('test'); + // Validator expects asset lock proof TX to have OP_RETURN in it's first output, break it + const parsedTx = new Transaction(Buffer.from(rawTransaction, 'hex')); + const fromAddress = new PrivateKey().toAddress(); + parsedTx.outputs[0].setScript(Script.buildPublicKeyHashOut(fromAddress).toString()); - validateAssetLockTransactionResult.addError(consensusError); + stateRepositoryMock.fetchTransaction.returns({ + data: parsedTx.toBuffer(), + height: 42, + }); const result = await validateChainAssetLockProofStructure( rawProof, executionContext, ); - expectValidationError(result, SomeConsensusError); - - const [error] = result.getErrors(); - - expect(error).to.deep.equal(consensusError); + await expectValidationError( + result, + InvalidIdentityAssetLockTransactionOutputError, + ); }); - it('should point to transaction from block lower than core chain locked height', async () => { + it('should point to transaction from block lower than core chain locked height', async function () { rawProof.coreChainLockedHeight = 41; - stateRepositoryMock.fetchLatestPlatformCoreChainLockedHeight.resolves(41); + stateRepositoryMock.fetchLatestPlatformCoreChainLockedHeight.returns(41); const result = await validateChainAssetLockProofStructure( rawProof, executionContext, ); - expectValidationError(result, InvalidAssetLockProofTransactionHeightError); + await expectValidationError( + result, InvalidAssetLockProofTransactionHeightError, + ); const [error] = result.getErrors(); @@ -378,14 +374,13 @@ describe('validateChainAssetLockProofStructureFactory', () => { expect(stateRepositoryMock.fetchTransaction).to.be.calledOnceWithExactly( transactionHash, - executionContext, + this.sinonSandbox.match.instanceOf(StateTransitionExecutionContext), ); - expect(validateAssetLockTransactionMock).to.not.be.called(); expect(stateRepositoryMock.fetchLatestPlatformCoreChainLockedHeight).to.be.calledOnce(); }); }); - it('should return valid result', async () => { + it('should return valid result', async function () { const result = await validateChainAssetLockProofStructure( rawProof, executionContext, @@ -397,13 +392,9 @@ describe('validateChainAssetLockProofStructureFactory', () => { expect(stateRepositoryMock.fetchTransaction).to.be.calledOnceWithExactly( transactionHash, - executionContext, - ); - expect(validateAssetLockTransactionMock).to.be.calledOnceWithExactly( - Buffer.from(rawTransaction, 'hex'), - 0, - executionContext, + this.sinonSandbox.match.instanceOf(StateTransitionExecutionContext), ); + expect(stateRepositoryMock.fetchLatestPlatformCoreChainLockedHeight).to.be.calledOnce(); }); }); diff --git a/packages/wasm-dpp/test/integration/identity/stateTransition/assetLockProof/fetchAssetLockTransactionOutputFactory.spec.js b/packages/wasm-dpp/test/integration/identity/stateTransition/assetLockProof/fetchAssetLockTransactionOutputFactory.spec.js index b83cafc6407..e2a3459c18a 100644 --- a/packages/wasm-dpp/test/integration/identity/stateTransition/assetLockProof/fetchAssetLockTransactionOutputFactory.spec.js +++ b/packages/wasm-dpp/test/integration/identity/stateTransition/assetLockProof/fetchAssetLockTransactionOutputFactory.spec.js @@ -1,32 +1,52 @@ -const { Transaction, Script } = require('@dashevo/dashcore-lib'); -const Output = require('@dashevo/dashcore-lib/lib/transaction/output'); +const { Transaction } = require('@dashevo/dashcore-lib'); -const fetchAssetLockTransactionOutputFactory = require('@dashevo/dpp/lib/identity/stateTransition/assetLockProof/fetchAssetLockTransactionOutputFactory'); const getChainAssetLockFixture = require('@dashevo/dpp/lib/test/fixtures/getChainAssetLockProofFixture'); const getInstantAssetLockProofFixture = require('@dashevo/dpp/lib/test/fixtures/getInstantAssetLockProofFixture'); const createStateRepositoryMock = require('@dashevo/dpp/lib/test/mocks/createStateRepositoryMock'); - -const UnknownAssetLockProofError = require('@dashevo/dpp/lib/identity/errors/UnknownAssetLockProofTypeError'); -const AssetLockTransactionIsNotFoundError = require('@dashevo/dpp/lib/identity/errors/AssetLockTransactionIsNotFoundError'); -const StateTransitionExecutionContext = require('@dashevo/dpp/lib/stateTransition/StateTransitionExecutionContext'); +const { default: loadWasmDpp } = require('../../../../../dist'); describe('fetchAssetLockTransactionOutputFactory', () => { let fetchAssetLockTransactionOutput; let stateRepositoryMock; let executionContext; + let StateTransitionExecutionContext; + let InstantAssetLockProof; + let ChainAssetLockProof; + let AssetLockTransactionIsNotFoundError; + let UnknownAssetLockProofTypeError; + + let fetchAssetLockTransactionOutputDPP; + + before(async () => { + ({ + StateTransitionExecutionContext, + fetchAssetLockTransactionOutput: fetchAssetLockTransactionOutputDPP, + InstantAssetLockProof, + ChainAssetLockProof, + AssetLockTransactionIsNotFoundError, + UnknownAssetLockProofTypeError, + } = await loadWasmDpp()); + }); + beforeEach(function beforeEach() { stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); - fetchAssetLockTransactionOutput = fetchAssetLockTransactionOutputFactory(stateRepositoryMock); executionContext = new StateTransitionExecutionContext(); + + fetchAssetLockTransactionOutput = (proof, context) => fetchAssetLockTransactionOutputDPP( + stateRepositoryMock, + proof, + context, + ); }); describe('InstantAssetLockProof', () => { let assetLockProofFixture; beforeEach(() => { - assetLockProofFixture = getInstantAssetLockProofFixture(); + const assetLockProofFixtureJS = getInstantAssetLockProofFixture(); + assetLockProofFixture = new InstantAssetLockProof(assetLockProofFixtureJS.toObject()); }); it('should return asset lock output', async () => { @@ -47,34 +67,46 @@ describe('fetchAssetLockTransactionOutputFactory', () => { beforeEach(() => { const rawTransaction = '030000000137feb5676d0851337ea3c9a992496aab7a0b3eee60aeeb9774000b7f4bababa5000000006b483045022100d91557de37645c641b948c6cd03b4ae3791a63a650db3e2fee1dcf5185d1b10402200e8bd410bf516ca61715867666d31e44495428ce5c1090bf2294a829ebcfa4ef0121025c3cc7fbfc52f710c941497fd01876c189171ea227458f501afcb38a297d65b4ffffffff021027000000000000166a14152073ca2300a86b510fa2f123d3ea7da3af68dcf77cb0090a0000001976a914152073ca2300a86b510fa2f123d3ea7da3af68dc88ac00000000'; - assetLockProofFixture = getChainAssetLockFixture(); - stateRepositoryMock.fetchTransaction.resolves({ + const assetLockProofFixtureJS = getChainAssetLockFixture(); + + const outPoint = Transaction + .parseOutPointBuffer(Buffer.from(assetLockProofFixtureJS.getOutPoint())); + ({ transactionHash } = outPoint); + + const rawProof = assetLockProofFixtureJS.toObject(); + + // Change endianness of raw txId bytes in outPoint to match expectation of dashcore-rust + const txId = rawProof.outPoint.slice(0, 32); + const outputIndex = rawProof.outPoint.slice(32); + txId.reverse(); + rawProof.outPoint = Buffer.concat([txId, outputIndex]); + + assetLockProofFixture = new ChainAssetLockProof(rawProof); + + stateRepositoryMock.fetchTransaction.returns({ data: Buffer.from(rawTransaction, 'hex'), height: 42, }); const transaction = new Transaction(rawTransaction); ([output] = transaction.outputs); - - const outPoint = Transaction.parseOutPointBuffer(assetLockProofFixture.getOutPoint()); - ({ transactionHash } = outPoint); }); - it('should fetch output from state repository', async () => { + it('should fetch output from state repository', async function () { const assetLockTransactionOutput = await fetchAssetLockTransactionOutput( assetLockProofFixture, executionContext, ); - expect(assetLockTransactionOutput).to.deep.equal(output); + expect(assetLockTransactionOutput).to.deep.equal(output.toObject()); expect(stateRepositoryMock.fetchTransaction).to.be.calledOnceWithExactly( transactionHash, - executionContext, + this.sinonSandbox.match.instanceOf(StateTransitionExecutionContext), ); }); - it('should throw IdentityAssetLockTransactionIsNotFoundError when transaction is not found', async () => { + it('should throw AssetLockTransactionIsNotFoundError when transaction is not found', async () => { stateRepositoryMock.fetchTransaction.resolves(null); try { @@ -83,14 +115,14 @@ describe('fetchAssetLockTransactionOutputFactory', () => { executionContext, ); - expect.fail('should throw IdentityAssetLockTransactionIsNotFoundError'); + expect.fail('should throw AssetLockTransactionIsNotFoundError'); } catch (e) { expect(e).to.be.an.instanceOf(AssetLockTransactionIsNotFoundError); expect(e.getTransactionId()).to.deep.equal(transactionHash); } }); - it('should return mocked output on dry run', async () => { + it('should return mocked output on dry run', async function () { executionContext.enableDryRun(); const result = await fetchAssetLockTransactionOutput( @@ -100,24 +132,36 @@ describe('fetchAssetLockTransactionOutputFactory', () => { executionContext.disableDryRun(); - expect(result).to.deep.equal(new Output({ + expect(result).to.deep.equal({ satoshis: 1000, - script: new Script(), - })); + script: '', + }); expect(stateRepositoryMock.fetchTransaction).to.be.calledOnceWithExactly( transactionHash, - executionContext, + this.sinonSandbox.match.instanceOf(StateTransitionExecutionContext), ); }); }); - it('should throw UnknownAssetLockProofError for unknown assetLockProof', async function it() { - const type = 666; + it('should throw UnknownAssetLockProofTypeError for unknown assetLockProof', async () => { + try { + await fetchAssetLockTransactionOutput( + {}, + executionContext, + ); + + expect.fail('should throw UnknownAssetLockProofTypeError'); + } catch (e) { + expect(e).to.be.an.instanceOf(UnknownAssetLockProofTypeError); + } + }); + + it('should throw UnknownAssetLockProofTypeError for unknown assetLockProof type', async () => { + const type = 100; - const assetLockProofFixture = { - getType: this.sinonSandbox.stub().returns(type), - }; + const assetLockProofFixture = new ChainAssetLockProof(getChainAssetLockFixture().toObject()); + assetLockProofFixture.getType = () => type; try { await fetchAssetLockTransactionOutput( @@ -125,9 +169,9 @@ describe('fetchAssetLockTransactionOutputFactory', () => { executionContext, ); - expect.fail('should throw UnknownAssetLockProofError'); + expect.fail('should throw UnknownAssetLockProofTypeError'); } catch (e) { - expect(e).to.be.an.instanceOf(UnknownAssetLockProofError); + expect(e).to.be.an.instanceOf(UnknownAssetLockProofTypeError); expect(e.getType()).to.equal(type); } }); diff --git a/packages/wasm-dpp/test/integration/identity/stateTransition/assetLockProof/instant/validateInstantAssetLockProofStructureFactory.spec.js b/packages/wasm-dpp/test/integration/identity/stateTransition/assetLockProof/instant/validateInstantAssetLockProofStructureFactory.spec.js index 6cc4aae5ccd..6fe624a59a3 100644 --- a/packages/wasm-dpp/test/integration/identity/stateTransition/assetLockProof/instant/validateInstantAssetLockProofStructureFactory.spec.js +++ b/packages/wasm-dpp/test/integration/identity/stateTransition/assetLockProof/instant/validateInstantAssetLockProofStructureFactory.spec.js @@ -1,35 +1,40 @@ -const { getRE2Class } = require('@dashevo/wasm-re2'); - const DashCoreLib = require('@dashevo/dashcore-lib'); -const createAjv = require('@dashevo/dpp/lib/ajv/createAjv'); - const getInstantAssetLockFixture = require('@dashevo/dpp/lib/test/fixtures/getInstantAssetLockProofFixture'); -const JsonSchemaValidator = require('@dashevo/dpp/lib/validation/JsonSchemaValidator'); const createStateRepositoryMock = require('@dashevo/dpp/lib/test/mocks/createStateRepositoryMock'); -const InvalidIdentityAssetLockProofError = require('@dashevo/dpp/lib/errors/consensus/basic/identity/InvalidInstantAssetLockProofError'); -const IdentityAssetLockProofLockedTransactionMismatchError = require('@dashevo/dpp/lib/errors/consensus/basic/identity/IdentityAssetLockProofLockedTransactionMismatchError'); -const InvalidIdentityAssetLockProofSignatureError = require('@dashevo/dpp/lib/errors/consensus/basic/identity/InvalidInstantAssetLockProofSignatureError'); -const { expectValidationError, expectJsonSchemaError } = require( - '@dashevo/dpp/lib/test/expect/expectError', -); +const { expect } = require('chai'); +const { expectJsonSchemaError, expectValidationError } = require('../../../../../../lib/test/expect/expectError'); -const ValidationResult = require('@dashevo/dpp/lib/validation/ValidationResult'); -const InvalidIdentityAssetLockTransactionError = require('@dashevo/dpp/lib/errors/consensus/basic/identity/InvalidIdentityAssetLockTransactionError'); -const validateInstantAssetLockProofStructureFactory = require('@dashevo/dpp/lib/identity/stateTransition/assetLockProof/instant/validateInstantAssetLockProofStructureFactory'); +const { default: loadWasmDpp } = require('../../../../../../dist'); describe('validateInstantAssetLockProofStructureFactory', () => { let rawProof; let transaction; let stateRepositoryMock; - let instantLockFromBufferMock; - let instantLockMock; + let executionContext; + + let StateTransitionExecutionContext; + let ValidationResult; + let InvalidInstantAssetLockProofError; + let IdentityAssetLockProofLockedTransactionMismatchError; + let InvalidInstantAssetLockProofSignatureError; + let InvalidIdentityAssetLockTransactionError; + let InstantAssetLockProofStructureValidator; + let validateInstantAssetLockProofStructure; - let jsonSchemaValidator; - let validateAssetLockTransactionResult; - let publicKeyHash; - let validateAssetLockTransactionMock; + + before(async () => { + ({ + StateTransitionExecutionContext, + ValidationResult, + InvalidInstantAssetLockProofError, + IdentityAssetLockProofLockedTransactionMismatchError, + InvalidInstantAssetLockProofSignatureError, + InvalidIdentityAssetLockTransactionError, + InstantAssetLockProofStructureValidator, + } = await loadWasmDpp()); + }); beforeEach(async function beforeEach() { const assetLock = getInstantAssetLockFixture(); @@ -37,50 +42,25 @@ describe('validateInstantAssetLockProofStructureFactory', () => { rawProof = assetLock.toObject(); - const RE2 = await getRE2Class(); - const ajv = createAjv(RE2); - - jsonSchemaValidator = new JsonSchemaValidator(ajv); - stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); - stateRepositoryMock.verifyInstantLock.resolves(true); + stateRepositoryMock.verifyInstantLock.returns(true); - instantLockMock = { - txid: transaction.id, - verify: this.sinonSandbox.stub().resolves(true), - }; + executionContext = new StateTransitionExecutionContext(); - instantLockFromBufferMock = this.sinonSandbox.stub(DashCoreLib.InstantLock, 'fromBuffer').returns(instantLockMock); - - publicKeyHash = Buffer.from('152073ca2300a86b510fa2f123d3ea7da3af68dc', 'hex'); - - validateAssetLockTransactionResult = new ValidationResult(); - validateAssetLockTransactionResult.setData({ - publicKeyHash, - transaction, - }); - validateAssetLockTransactionMock = this.sinonSandbox.stub().resolves( - validateAssetLockTransactionResult, - ); - - validateInstantAssetLockProofStructure = validateInstantAssetLockProofStructureFactory( - jsonSchemaValidator, - stateRepositoryMock, - validateAssetLockTransactionMock, + const validator = new InstantAssetLockProofStructureValidator(stateRepositoryMock); + validateInstantAssetLockProofStructure = (proof, context) => validator.validate( + proof, + context, ); }); - afterEach(() => { - instantLockFromBufferMock.restore(); - }); - describe('type', () => { it('should be present', async () => { delete rawProof.type; - const result = await validateInstantAssetLockProofStructure(rawProof); + const result = await validateInstantAssetLockProofStructure(rawProof, executionContext); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -89,16 +69,14 @@ describe('validateInstantAssetLockProofStructureFactory', () => { expect(error.getParams().missingProperty).to.equal('type'); expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); - expect(instantLockMock.verify).to.not.be.called(); - expect(validateAssetLockTransactionMock).to.not.be.called(); }); it('should be equal to 0', async () => { rawProof.type = -1; - const result = await validateInstantAssetLockProofStructure(rawProof); + const result = await validateInstantAssetLockProofStructure(rawProof, executionContext); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -106,8 +84,6 @@ describe('validateInstantAssetLockProofStructureFactory', () => { expect(error.getKeyword()).to.equal('const'); expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); - expect(instantLockMock.verify).to.not.be.called(); - expect(validateAssetLockTransactionMock).to.not.be.called(); }); }); @@ -115,9 +91,9 @@ describe('validateInstantAssetLockProofStructureFactory', () => { it('should be present', async () => { delete rawProof.instantLock; - const result = await validateInstantAssetLockProofStructure(rawProof); + const result = await validateInstantAssetLockProofStructure(rawProof, executionContext); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -126,35 +102,29 @@ describe('validateInstantAssetLockProofStructureFactory', () => { expect(error.getParams().missingProperty).to.equal('instantLock'); expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); - expect(instantLockMock.verify).to.not.be.called(); - expect(validateAssetLockTransactionMock).to.not.be.called(); }); it('should be a byte array', async () => { rawProof.instantLock = new Array(165).fill('string'); - const result = await validateInstantAssetLockProofStructure(rawProof); + const result = await validateInstantAssetLockProofStructure(rawProof, executionContext); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result, 165); - const [error, byteArrayError] = result.getErrors(); + const [error] = result.getErrors(); expect(error.getInstancePath()).to.equal('/instantLock/0'); expect(error.getKeyword()).to.equal('type'); - expect(byteArrayError.getKeyword()).to.equal('byteArray'); - expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); - expect(instantLockMock.verify).to.not.be.called(); - expect(validateAssetLockTransactionMock).to.not.be.called(); }); it('should not be shorter than 160 bytes', async () => { rawProof.instantLock = Buffer.alloc(159); - const result = await validateInstantAssetLockProofStructure(rawProof); + const result = await validateInstantAssetLockProofStructure(rawProof, executionContext); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -162,16 +132,14 @@ describe('validateInstantAssetLockProofStructureFactory', () => { expect(error.getKeyword()).to.equal('minItems'); expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); - expect(instantLockMock.verify).to.not.be.called(); - expect(validateAssetLockTransactionMock).to.not.be.called(); }); it('should not be longer than 100 Kb', async () => { rawProof.instantLock = Buffer.alloc(100001); - const result = await validateInstantAssetLockProofStructure(rawProof); + const result = await validateInstantAssetLockProofStructure(rawProof, executionContext); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -179,37 +147,34 @@ describe('validateInstantAssetLockProofStructureFactory', () => { expect(error.getKeyword()).to.equal('maxItems'); expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); - expect(instantLockMock.verify).to.not.be.called(); - expect(validateAssetLockTransactionMock).to.not.be.called(); }); it('should be valid', async () => { - const instantLockError = new Error('something is wrong'); - - instantLockFromBufferMock.throws(instantLockError); - rawProof.instantLock = Buffer.alloc(200); - const result = await validateInstantAssetLockProofStructure(rawProof); - expectValidationError(result, InvalidIdentityAssetLockProofError); + const result = await validateInstantAssetLockProofStructure(rawProof, executionContext); + await expectValidationError(result, InvalidInstantAssetLockProofError); const [error] = result.getErrors(); expect(error.getCode()).to.equal(1041); - expect(error.getValidationError()).to.equal(instantLockError); expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); - expect(instantLockMock.verify).to.not.be.called(); - expect(validateAssetLockTransactionMock).to.not.be.called(); }); it('should lock the same transaction', async () => { const txId = Buffer.alloc(32); - instantLockMock.txid = txId.toString('hex'); - const result = await validateInstantAssetLockProofStructure(rawProof); + const instantLockParsed = new DashCoreLib.InstantLock(rawProof.instantLock); + instantLockParsed.txid = txId.toString('hex'); + rawProof.instantLock = instantLockParsed.toBuffer(); + + const result = await validateInstantAssetLockProofStructure(rawProof, executionContext); - expectValidationError(result, IdentityAssetLockProofLockedTransactionMismatchError); + await expectValidationError( + result, + IdentityAssetLockProofLockedTransactionMismatchError, + ); const [error] = result.getErrors(); @@ -217,24 +182,24 @@ describe('validateInstantAssetLockProofStructureFactory', () => { expect(error.getInstantLockTransactionId()).to.deep.equal(txId); expect(error.getAssetLockTransactionId()).to.deep.equal(Buffer.from(transaction.id, 'hex')); - // expect(stateRepositoryMock.verifyInstantLock).to.be.calledOnce(); - expect(instantLockMock.verify).to.not.be.called(); - expect(validateAssetLockTransactionMock).to.be.calledOnce(); + expect(stateRepositoryMock.verifyInstantLock).to.be.calledOnce(); }); it('should have valid signature', async () => { stateRepositoryMock.verifyInstantLock.resolves(false); - const result = await validateInstantAssetLockProofStructure(rawProof); + const result = await validateInstantAssetLockProofStructure(rawProof, executionContext); - expectValidationError(result, InvalidIdentityAssetLockProofSignatureError); + await expectValidationError( + result, + InvalidInstantAssetLockProofSignatureError, + ); const [error] = result.getErrors(); expect(error.getCode()).to.equal(1042); expect(stateRepositoryMock.verifyInstantLock).to.be.calledOnce(); - expect(validateAssetLockTransactionMock).to.not.be.called(); }); }); @@ -242,9 +207,9 @@ describe('validateInstantAssetLockProofStructureFactory', () => { it('should be present', async () => { delete rawProof.transaction; - const result = await validateInstantAssetLockProofStructure(rawProof); + const result = await validateInstantAssetLockProofStructure(rawProof, executionContext); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); @@ -253,88 +218,66 @@ describe('validateInstantAssetLockProofStructureFactory', () => { expect(error.getParams().missingProperty).to.equal('transaction'); expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); - expect(instantLockMock.verify).to.not.be.called(); - expect(validateAssetLockTransactionMock).to.not.be.called(); }); it('should be a byte array', async () => { rawProof.transaction = new Array(65).fill('string'); - const result = await validateInstantAssetLockProofStructure(rawProof); + const result = await validateInstantAssetLockProofStructure(rawProof, executionContext); - expectJsonSchemaError(result, 2); + await expectJsonSchemaError(result, 65); - const [error, byteArrayError] = result.getErrors(); + const [error] = result.getErrors(); expect(error.getInstancePath()).to.equal('/transaction/0'); expect(error.getKeyword()).to.equal('type'); - expect(byteArrayError.getKeyword()).to.equal('byteArray'); - expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); - expect(instantLockMock.verify).to.not.be.called(); - expect(validateAssetLockTransactionMock).to.not.be.called(); }); it('should not be shorter than 1 byte', async () => { rawProof.transaction = Buffer.alloc(0); - const result = await validateInstantAssetLockProofStructure(rawProof); + const result = await validateInstantAssetLockProofStructure(rawProof, executionContext); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/transaction'); + expect(error.getInstancePath()).to.equal('/transaction'); expect(error.getKeyword()).to.equal('minItems'); expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); - expect(instantLockMock.verify).to.not.be.called(); - expect(validateAssetLockTransactionMock).to.not.be.called(); }); it('should not be longer than 100 Kb', async () => { rawProof.transaction = Buffer.alloc(100001); - const result = await validateInstantAssetLockProofStructure(rawProof); + const result = await validateInstantAssetLockProofStructure(rawProof, executionContext); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/transaction'); + expect(error.getInstancePath()).to.equal('/transaction'); expect(error.getKeyword()).to.equal('maxItems'); expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); - expect(instantLockMock.verify).to.not.be.called(); - expect(validateAssetLockTransactionMock).to.not.be.called(); }); it('should should be valid', async () => { - const validationError = new Error('parsing failed'); - - const consensusError = new InvalidIdentityAssetLockTransactionError(validationError.message); + rawProof.transaction = Buffer.alloc(1000); - consensusError.setValidationError(validationError); + const result = await validateInstantAssetLockProofStructure(rawProof, executionContext); - validateAssetLockTransactionResult.addError(consensusError); - validateAssetLockTransactionMock.resolves(validateAssetLockTransactionResult); - - const result = await validateInstantAssetLockProofStructure(rawProof); - - expectValidationError(result, InvalidIdentityAssetLockTransactionError); + await expectValidationError(result, InvalidIdentityAssetLockTransactionError); const [error] = result.getErrors(); expect(error.getCode()).to.equal(1038); + expect(error.getValidationError()).to.exist(); - expect(error).to.equal(consensusError); - - expect(error.getValidationError()).to.equal(validationError); - - // expect(stateRepositoryMock.verifyInstantLock).to.be.calledOnce(); - expect(instantLockMock.verify).to.not.be.called(); - expect(validateAssetLockTransactionMock).to.be.calledOnce(); + expect(stateRepositoryMock.verifyInstantLock).to.be.calledOnce(); }); }); @@ -342,60 +285,62 @@ describe('validateInstantAssetLockProofStructureFactory', () => { it('should be present', async () => { delete rawProof.outputIndex; - const result = await validateInstantAssetLockProofStructure(rawProof); + const result = await validateInstantAssetLockProofStructure(rawProof, executionContext); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal(''); + expect(error.getInstancePath()).to.equal(''); expect(error.getKeyword()).to.equal('required'); expect(error.getParams().missingProperty).to.equal('outputIndex'); expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); - expect(instantLockMock.verify).to.not.be.called(); }); it('should be an integer', async () => { rawProof.outputIndex = 1.1; - const result = await validateInstantAssetLockProofStructure(rawProof); + const result = await validateInstantAssetLockProofStructure(rawProof, executionContext); - expectJsonSchemaError(result, 1); + await expectJsonSchemaError(result, 1); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/outputIndex'); + expect(error.getInstancePath()).to.equal('/outputIndex'); expect(error.getKeyword()).to.equal('type'); expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); - expect(instantLockMock.verify).to.not.be.called(); }); it('should not be less than 0', async () => { rawProof.outputIndex = -1; - const result = await validateInstantAssetLockProofStructure(rawProof); + const result = await validateInstantAssetLockProofStructure(rawProof, executionContext); - expectJsonSchemaError(result); + await expectJsonSchemaError(result); const [error] = result.getErrors(); - expect(error.instancePath).to.equal('/outputIndex'); + expect(error.getInstancePath()).to.equal('/outputIndex'); expect(error.getKeyword()).to.equal('minimum'); expect(stateRepositoryMock.verifyInstantLock).to.not.be.called(); - expect(instantLockMock.verify).to.not.be.called(); }); }); it('should return valid result', async () => { - const result = await validateInstantAssetLockProofStructure(rawProof); + const result = await validateInstantAssetLockProofStructure( + rawProof, + executionContext, + ); expect(result).to.be.an.instanceOf(ValidationResult); expect(result.isValid()).to.be.true(); + + const publicKeyHash = transaction.outputs[0].script.getData(); expect(result.getData()).to.deep.equal(publicKeyHash); - // expect(stateRepositoryMock.verifyInstantLock).to.be.calledOnce(); + expect(stateRepositoryMock.verifyInstantLock).to.be.calledOnce(); }); }); diff --git a/packages/wasm-dpp/test/integration/identity/stateTransition/assetLockProof/validateAssetLockTransactionFactory.spec.js b/packages/wasm-dpp/test/integration/identity/stateTransition/assetLockProof/validateAssetLockTransactionFactory.spec.js index 653349db883..99b3b76b8f8 100644 --- a/packages/wasm-dpp/test/integration/identity/stateTransition/assetLockProof/validateAssetLockTransactionFactory.spec.js +++ b/packages/wasm-dpp/test/integration/identity/stateTransition/assetLockProof/validateAssetLockTransactionFactory.spec.js @@ -1,33 +1,46 @@ -const { Transaction } = require('@dashevo/dashcore-lib'); +const { Transaction, PrivateKey, Script } = require('@dashevo/dashcore-lib'); -const DashCoreLib = require('@dashevo/dashcore-lib'); -const validateAssetLockTransactionFactory = require('@dashevo/dpp/lib/identity/stateTransition/assetLockProof/validateAssetLockTransactionFactory'); const createStateRepositoryMock = require('@dashevo/dpp/lib/test/mocks/createStateRepositoryMock'); -const ValidationResult = require('@dashevo/dpp/lib/validation/ValidationResult'); - -const { expectValidationError } = require('@dashevo/dpp/lib/test/expect/expectError'); - -const InvalidIdentityAssetLockTransactionError = require('@dashevo/dpp/lib/errors/consensus/basic/identity/InvalidIdentityAssetLockTransactionError'); -const IdentityAssetLockTransactionOutputNotFoundError = require('@dashevo/dpp/lib/errors/consensus/basic/identity/IdentityAssetLockTransactionOutputNotFoundError'); -const IdentityAssetLockTransactionOutPointAlreadyExistsError = require('@dashevo/dpp/lib/errors/consensus/basic/identity/IdentityAssetLockTransactionOutPointAlreadyExistsError'); -const InvalidIdentityAssetLockTransactionOutputError = require('@dashevo/dpp/lib/errors/consensus/basic/identity/InvalidIdentityAssetLockTransactionOutputError'); -const InvalidAssetLockTransactionOutputReturnSizeError = require('@dashevo/dpp/lib/errors/consensus/basic/identity/InvalidAssetLockTransactionOutputReturnSizeError'); -const StateTransitionExecutionContext = require('@dashevo/dpp/lib/stateTransition/StateTransitionExecutionContext'); +const { expect } = require('chai'); +const { default: loadWasmDpp } = require('../../../../../dist'); +const { expectValidationError } = require('../../../../../lib/test/expect/expectError'); describe('validateAssetLockTransactionFactory', () => { let stateRepositoryMock; - let validateAssetLockTransaction; let rawTransaction; let outputIndex; - let transactionInstance; - let transactionMock; let executionContext; + let StateTransitionExecutionContext; + let InvalidIdentityAssetLockTransactionError; + let IdentityAssetLockTransactionOutputNotFoundError; + let InvalidIdentityAssetLockTransactionOutputError; + let InvalidAssetLockTransactionOutputReturnSizeError; + let IdentityAssetLockTransactionOutPointAlreadyExistsError; + let TransactionDecodeError; + let ValidationResult; + + let validateAssetLockTransaction; + let validateAssetLockTransactionDPP; + + before(async () => { + ({ + StateTransitionExecutionContext, + ValidationResult, + TransactionDecodeError, + InvalidIdentityAssetLockTransactionError, + IdentityAssetLockTransactionOutputNotFoundError, + InvalidIdentityAssetLockTransactionOutputError, + InvalidAssetLockTransactionOutputReturnSizeError, + IdentityAssetLockTransactionOutPointAlreadyExistsError, + validateAssetLockTransaction: validateAssetLockTransactionDPP, + } = await loadWasmDpp()); + }); + beforeEach(function beforeEach() { rawTransaction = '030000000137feb5676d0851337ea3c9a992496aab7a0b3eee60aeeb9774000b7f4bababa5000000006b483045022100d91557de37645c641b948c6cd03b4ae3791a63a650db3e2fee1dcf5185d1b10402200e8bd410bf516ca61715867666d31e44495428ce5c1090bf2294a829ebcfa4ef0121025c3cc7fbfc52f710c941497fd01876c189171ea227458f501afcb38a297d65b4ffffffff021027000000000000166a14152073ca2300a86b510fa2f123d3ea7da3af68dcf77cb0090a0000001976a914152073ca2300a86b510fa2f123d3ea7da3af68dc88ac00000000'; outputIndex = 0; - transactionInstance = new Transaction(rawTransaction); executionContext = new StateTransitionExecutionContext(); @@ -35,32 +48,29 @@ describe('validateAssetLockTransactionFactory', () => { stateRepositoryMock.isAssetLockTransactionOutPointAlreadyUsed.returns(false); - validateAssetLockTransaction = validateAssetLockTransactionFactory(stateRepositoryMock); - }); - - afterEach(() => { - if (transactionMock) { - transactionMock.restore(); - } + validateAssetLockTransaction = (tx, index, context) => validateAssetLockTransactionDPP( + stateRepositoryMock, + tx, + index, + context, + ); }); it('should be valid transaction', async () => { rawTransaction = '030000000137feb5676d085133'; - validateAssetLockTransaction = validateAssetLockTransactionFactory(stateRepositoryMock); - const result = await validateAssetLockTransaction( rawTransaction, outputIndex, executionContext, ); - expectValidationError(result, InvalidIdentityAssetLockTransactionError); + await expectValidationError(result, InvalidIdentityAssetLockTransactionError); const [error] = result.getErrors(); expect(error.getCode()).to.equal(1038); - expect(error.getValidationError()).to.be.instanceOf(Error); + expect(error.getValidationError()).to.be.instanceOf(TransactionDecodeError); expect(result.getData()).to.be.undefined(); expect(stateRepositoryMock.isAssetLockTransactionOutPointAlreadyUsed).to.not.be.called(); @@ -75,7 +85,7 @@ describe('validateAssetLockTransactionFactory', () => { executionContext, ); - expectValidationError(result, IdentityAssetLockTransactionOutputNotFoundError); + await expectValidationError(result, IdentityAssetLockTransactionOutputNotFoundError); const [error] = result.getErrors(); @@ -86,30 +96,19 @@ describe('validateAssetLockTransactionFactory', () => { expect(stateRepositoryMock.isAssetLockTransactionOutPointAlreadyUsed).to.not.be.called(); }); - it('should point to output with OR_RETURN', async function it() { - const isDataOut = this.sinonSandbox.stub().returns(false); - - const stubInstance = this.sinonSandbox.createStubInstance(Transaction); - - stubInstance.outputs = [{ - script: { - isDataOut, - }, - }]; - - stubInstance.getOutPointBuffer.returns(transactionInstance.getOutPointBuffer(outputIndex)); - - transactionMock = this.sinonSandbox.stub(DashCoreLib, 'Transaction').returns( - stubInstance, - ); + it('should point to output with OR_RETURN', async () => { + const regularTransaction = new Transaction(Buffer.from(rawTransaction, 'hex')); + // Mess up expected TX output script + regularTransaction.outputs[0] + .setScript(Script.buildPublicKeyHashOut(new PrivateKey().toAddress()).toString()); const result = await validateAssetLockTransaction( - rawTransaction, + regularTransaction.toBuffer().toString('hex'), outputIndex, executionContext, ); - expectValidationError(result, InvalidIdentityAssetLockTransactionOutputError); + await expectValidationError(result, InvalidIdentityAssetLockTransactionOutputError); const [error] = result.getErrors(); @@ -117,30 +116,18 @@ describe('validateAssetLockTransactionFactory', () => { expect(error.getOutputIndex()).to.equal(outputIndex); }); - it('should contain valid public key hash', async function it() { - const isDataOut = this.sinonSandbox.stub().returns(true); - const getData = this.sinonSandbox.stub().returns(Buffer.alloc(0)); - - const stubInstance = this.sinonSandbox.createStubInstance(Transaction); - stubInstance.outputs = [{ - script: { - isDataOut, - getData, - }, - }]; - stubInstance.getOutPointBuffer.returns(transactionInstance.getOutPointBuffer(outputIndex)); - - transactionMock = this.sinonSandbox.stub(DashCoreLib, 'Transaction').returns( - stubInstance, - ); + it('should contain valid public key hash', async () => { + const regularTransaction = new Transaction(Buffer.from(rawTransaction, 'hex')); + regularTransaction.outputs[0] + .setScript(Script.buildDataOut(Buffer.alloc(0))); const result = await validateAssetLockTransaction( - rawTransaction, + regularTransaction.toBuffer().toString('hex'), outputIndex, executionContext, ); - expectValidationError(result, InvalidAssetLockTransactionOutputReturnSizeError); + await expectValidationError(result, InvalidAssetLockTransactionOutputReturnSizeError); const [error] = result.getErrors(); @@ -148,7 +135,7 @@ describe('validateAssetLockTransactionFactory', () => { expect(error.getOutputIndex()).to.equal(outputIndex); }); - it('should return IdentityAssetLockTransactionOutPointAlreadyExistsError if outPoint was already used', async () => { + it('should return IdentityAssetLockTransactionOutPointAlreadyExistsError if outPoint was already used', async function () { stateRepositoryMock.isAssetLockTransactionOutPointAlreadyUsed.returns(true); const result = await validateAssetLockTransaction( @@ -157,7 +144,7 @@ describe('validateAssetLockTransactionFactory', () => { executionContext, ); - expectValidationError(result, IdentityAssetLockTransactionOutPointAlreadyExistsError); + await expectValidationError(result, IdentityAssetLockTransactionOutPointAlreadyExistsError); const [error] = result.getErrors(); @@ -165,19 +152,19 @@ describe('validateAssetLockTransactionFactory', () => { const transaction = new Transaction(rawTransaction); - expect(Buffer.isBuffer(error.getTransactionId())).to.be.true(); expect(error.getTransactionId()).to.deep.equal(Buffer.from(transaction.id, 'hex')); expect(error.getOutputIndex()).to.deep.equal(outputIndex); expect(result.getData()).to.be.undefined(); expect(stateRepositoryMock.isAssetLockTransactionOutPointAlreadyUsed) .to.be.calledOnceWithExactly( - transaction.getOutPointBuffer(outputIndex), - executionContext, + this.sinonSandbox.match((val) => Buffer.from(val) + .equals(transaction.getOutPointBuffer(outputIndex))), + this.sinonSandbox.match.instanceOf(StateTransitionExecutionContext), ); }); - it('should return valid result', async () => { + it('should return valid result', async function () { const result = await validateAssetLockTransaction( rawTransaction, outputIndex, @@ -193,13 +180,14 @@ describe('validateAssetLockTransactionFactory', () => { expect(stateRepositoryMock.isAssetLockTransactionOutPointAlreadyUsed) .to.be.calledOnceWithExactly( - initialTransaction.getOutPointBuffer(outputIndex), - executionContext, + this.sinonSandbox.match((val) => Buffer.from(val) + .equals(initialTransaction.getOutPointBuffer(outputIndex))), + this.sinonSandbox.match.instanceOf(StateTransitionExecutionContext), ); const { transaction, publicKeyHash } = result.getData(); expect(publicKeyHash).to.deep.equal(initialPublicKeyHash); - expect(transaction).to.be.an.instanceOf(Transaction); - expect(transaction.toJSON()).to.deep.equal(initialTransaction.toJSON()); + expect(new Transaction(Buffer.from(transaction)).toJSON()) + .to.deep.equal(initialTransaction.toJSON()); }); }); diff --git a/packages/wasm-dpp/test/integration/identity/stateTransition/validatePublicKeySignaturesFactory.spec.js b/packages/wasm-dpp/test/integration/identity/stateTransition/validatePublicKeySignaturesFactory.spec.js index e793c150aaa..9d7abd0c0a1 100644 --- a/packages/wasm-dpp/test/integration/identity/stateTransition/validatePublicKeySignaturesFactory.spec.js +++ b/packages/wasm-dpp/test/integration/identity/stateTransition/validatePublicKeySignaturesFactory.spec.js @@ -3,20 +3,40 @@ const { PrivateKey, crypto: { Hash } } = require('@dashevo/dashcore-lib'); const crypto = require('crypto'); const getIdentityCreateTransitionFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityCreateTransitionFixture'); -const IdentityPublicKey = require('@dashevo/dpp/lib/identity/IdentityPublicKey'); const BlsSignatures = require('@dashevo/dpp/lib/bls/bls'); -const validatePublicKeySignaturesFactory = require('@dashevo/dpp/lib/identity/stateTransition/validatePublicKeySignaturesFactory'); -const ValidationResult = require('@dashevo/dpp/lib/validation/ValidationResult'); -const { expectValidationError } = require('@dashevo/dpp/lib/test/expect/expectError'); -const InvalidIdentityKeySignatureError = require('@dashevo/dpp/lib/errors/consensus/basic/identity/InvalidIdentityKeySignatureError'); + +const { expect } = require('chai'); +const { expectValidationError } = require('../../../../lib/test/expect/expectError'); +const getBlsAdapterMock = require('../../../../lib/test/mocks/getBlsAdapterMock'); + +const { default: loadWasmDpp } = require('../../../../dist'); describe('validatePublicKeySignaturesFactory', () => { let identityCreateTransition; let rawIdentityCreateTransition; let validatePublicKeySignatures; - beforeEach(async function beforeEach() { - identityCreateTransition = getIdentityCreateTransitionFixture(); + let IdentityCreateTransition; + let IdentityPublicKey; + let InvalidIdentityKeySignatureError; + let ValidationResult; + let PublicKeysSignaturesValidator; + let blsAdapter; + + before(async () => { + ({ + IdentityCreateTransition, + IdentityPublicKey, + InvalidIdentityKeySignatureError, + PublicKeysSignaturesValidator, + ValidationResult, + } = await loadWasmDpp()); + }); + + beforeEach(async () => { + identityCreateTransition = new IdentityCreateTransition( + getIdentityCreateTransitionFixture().toObject(), + ); const privateKey1 = new PrivateKey(); const publicKey1 = privateKey1.toPublicKey(); @@ -42,11 +62,13 @@ describe('validatePublicKeySignaturesFactory', () => { readOnly: false, }); - const { PrivateKey: BlsPrivateKey } = await BlsSignatures.getInstance(); + const blsModule = await BlsSignatures.getInstance(); + blsAdapter = await getBlsAdapterMock(); + + const { PrivateKey: BlsPrivateKey } = blsModule; const randomBytes = new Uint8Array(crypto.randomBytes(256)); const privateKey3 = BlsPrivateKey.fromBytes(randomBytes, true); - // blsPrivateKeyHex = Buffer.from(blsPrivateKey.serialize()).toString('hex'); const publicKey3 = privateKey3.getPublicKey(); const identityPublicKey3 = new IdentityPublicKey({ @@ -65,14 +87,14 @@ describe('validatePublicKeySignaturesFactory', () => { ]); await identityCreateTransition.signByPrivateKey( - privateKey1, + privateKey1.toBuffer(), IdentityPublicKey.TYPES.ECDSA_SECP256K1, ); const signature1 = identityCreateTransition.getSignature(); await identityCreateTransition.signByPrivateKey( - privateKey2, + privateKey2.toBuffer(), IdentityPublicKey.TYPES.ECDSA_HASH160, ); @@ -81,6 +103,7 @@ describe('validatePublicKeySignaturesFactory', () => { await identityCreateTransition.signByPrivateKey( Buffer.from(privateKey3.serialize()), IdentityPublicKey.TYPES.BLS12_381, + blsAdapter, ); const signature3 = identityCreateTransition.getSignature(); @@ -89,11 +112,18 @@ describe('validatePublicKeySignaturesFactory', () => { identityPublicKey2.setSignature(signature2); identityPublicKey3.setSignature(signature3); - rawIdentityCreateTransition = identityCreateTransition.toObject(); + identityCreateTransition.setPublicKeys([ + identityPublicKey1, + identityPublicKey2, + identityPublicKey3, + ]); - const createStateTransitionMock = this.sinonSandbox.stub().resolves(identityCreateTransition); + rawIdentityCreateTransition = identityCreateTransition.toObject(); - validatePublicKeySignatures = validatePublicKeySignaturesFactory(createStateTransitionMock); + const validator = new PublicKeysSignaturesValidator(blsAdapter); + validatePublicKeySignatures = ( + stateTransition, keys, + ) => validator.validate(stateTransition, keys); }); it('should return InvalidIdentityKeySignatureError if signature is not valid', async () => { @@ -104,10 +134,9 @@ describe('validatePublicKeySignaturesFactory', () => { const result = await validatePublicKeySignatures( rawIdentityCreateTransition, rawIdentityCreateTransition.publicKeys, - identityCreateTransition.getExecutionContext(), ); - expectValidationError(result, InvalidIdentityKeySignatureError); + await expectValidationError(result, InvalidIdentityKeySignatureError); const error = result.getFirstError(); @@ -118,7 +147,7 @@ describe('validatePublicKeySignaturesFactory', () => { const result = await validatePublicKeySignatures( rawIdentityCreateTransition, rawIdentityCreateTransition.publicKeys, - identityCreateTransition.getExecutionContext(), + blsAdapter, ); expect(result).to.be.instanceOf(ValidationResult); diff --git a/packages/wasm-dpp/test/unit/identity/stateTransition/IdentityCreateTransition/IdentityCreateTransition.spec.js b/packages/wasm-dpp/test/unit/identity/stateTransition/IdentityCreateTransition/IdentityCreateTransition.spec.js index 0194e57acdc..562088cd433 100644 --- a/packages/wasm-dpp/test/unit/identity/stateTransition/IdentityCreateTransition/IdentityCreateTransition.spec.js +++ b/packages/wasm-dpp/test/unit/identity/stateTransition/IdentityCreateTransition/IdentityCreateTransition.spec.js @@ -1,35 +1,65 @@ -const IdentityPublicKey = require('@dashevo/dpp/lib/identity/IdentityPublicKey'); - -const stateTransitionTypes = require( - '@dashevo/dpp/lib/stateTransition/stateTransitionTypes', -); +const getIdentityCreateTransitionFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityCreateTransitionFixture'); +const getChainAssetLockProofFixture = require('@dashevo/dpp/lib/test/fixtures/getChainAssetLockProofFixture'); +const stateTransitionTypes = require('@dashevo/dpp/lib/stateTransition/stateTransitionTypes'); const protocolVersion = require('@dashevo/dpp/lib/version/protocolVersion'); -const IdentityCreateTransition = require('@dashevo/dpp/lib/identity/stateTransition/IdentityCreateTransition/IdentityCreateTransition'); -const Identifier = require('@dashevo/dpp/lib/identifier/Identifier'); - -const getIdentityCreateTransitionFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityCreateTransitionFixture'); -const InstantAssetLockProof = require('@dashevo/dpp/lib/identity/stateTransition/assetLockProof/instant/InstantAssetLockProof'); +const { default: loadWasmDpp } = require('../../../../../dist'); describe('IdentityCreateTransition', () => { let rawStateTransition; let stateTransition; + let IdentityCreateTransition; + let InstantAssetLockProof; + let KeyType; + let KeyPurpose; + let KeySecurityLevel; + let IdentityPublicKey; + let Identifier; + + const mockRawPublicKey = (params = {}) => ({ + id: 0, + type: KeyType.ECDSA_SECP256K1, + data: Buffer.from('AkVuTKyF3YgKLAQlLEtaUL2HTditwGILfWUVqjzYnIgH', 'base64'), + purpose: KeyPurpose.AUTHENTICATION, + securityLevel: KeySecurityLevel.MASTER, + signature: Buffer.alloc(32).fill(1), + readOnly: false, + ...params, + }); + + before(async () => { + ({ + IdentityCreateTransition, IdentityPublicKey, KeyType, KeyPurpose, KeySecurityLevel, + InstantAssetLockProof, Identifier, + } = await loadWasmDpp()); + }); + beforeEach(() => { - stateTransition = getIdentityCreateTransitionFixture(); - rawStateTransition = stateTransition.toObject(); + rawStateTransition = getIdentityCreateTransitionFixture().toObject(); + stateTransition = new IdentityCreateTransition( + rawStateTransition, + ); }); describe('#constructor', () => { - it('should create an instance with specified data', () => { + it('should create instance with specified data', () => { expect(stateTransition.getAssetLockProof().toObject()).to.deep.equal( rawStateTransition.assetLockProof, ); - expect(stateTransition.publicKeys).to.deep.equal([ - new IdentityPublicKey(rawStateTransition.publicKeys[0]), + expect(stateTransition.publicKeys.map((key) => key.toObject())).to.deep.equal([ + new IdentityPublicKey(rawStateTransition.publicKeys[0]).toObject(), ]); }); + + it('should create instance with chain asset lock proof', () => { + const stObject = stateTransition.toObject(); + stObject.assetLockProof = getChainAssetLockProofFixture().toObject(); + stateTransition = new IdentityCreateTransition(stObject); + expect(stateTransition.getAssetLockProof().toObject()) + .to.deep.equal(stObject.assetLockProof); + }); }); describe('#getType', () => { @@ -53,8 +83,8 @@ describe('IdentityCreateTransition', () => { new InstantAssetLockProof(rawStateTransition.assetLockProof), ); - expect(stateTransition.identityId).to.deep.equal( - stateTransition.getAssetLockProof().createIdentifier(), + expect(stateTransition.identityId.toBuffer()).to.deep.equal( + stateTransition.getAssetLockProof().createIdentifier().toBuffer(), ); }); }); @@ -69,44 +99,53 @@ describe('IdentityCreateTransition', () => { describe('#setPublicKeys', () => { it('should set public keys', () => { - const publicKeys = [new IdentityPublicKey(), new IdentityPublicKey()]; + const publicKeys = [ + new IdentityPublicKey(mockRawPublicKey({ id: 0 })), + new IdentityPublicKey(mockRawPublicKey({ id: 1 })), + ]; stateTransition.setPublicKeys(publicKeys); - expect(stateTransition.publicKeys).to.have.deep.members(publicKeys); + expect(stateTransition.publicKeys.map((key) => key.toObject())) + .to.have.deep.members(publicKeys.map((key) => key.toObject())); }); }); describe('#getPublicKeys', () => { it('should return set public keys', () => { - expect(stateTransition.getPublicKeys()).to.deep.equal( - rawStateTransition.publicKeys.map((rawPublicKey) => new IdentityPublicKey(rawPublicKey)), + expect(stateTransition.getPublicKeys().map((key) => key.toObject())).to.deep.equal( + rawStateTransition.publicKeys + .map((rawPublicKey) => new IdentityPublicKey(rawPublicKey).toObject()), ); }); }); describe('#addPublicKeys', () => { it('should add more public keys', () => { - const publicKeys = [new IdentityPublicKey(), new IdentityPublicKey()]; + const publicKeys = [ + new IdentityPublicKey(mockRawPublicKey({ id: 0 })), + new IdentityPublicKey(mockRawPublicKey({ id: 1 })), + ]; - stateTransition.publicKeys = []; + stateTransition.setPublicKeys([]); stateTransition.addPublicKeys(publicKeys); - expect(stateTransition.getPublicKeys()).to.have.deep.members(publicKeys); + expect(stateTransition.getPublicKeys().map((key) => key.toObject())) + .to.have.deep.members(publicKeys.map((key) => key.toObject())); }); }); describe('#getIdentityId', () => { it('should return identity id', () => { - expect(stateTransition.getIdentityId()).to.deep.equal( - stateTransition.getAssetLockProof().createIdentifier(), + expect(stateTransition.getIdentityId().toBuffer()).to.deep.equal( + stateTransition.getAssetLockProof().createIdentifier().toBuffer(), ); }); }); describe('#getOwnerId', () => { it('should return owner id', () => { - expect(stateTransition.getOwnerId()).to.equal( - stateTransition.getIdentityId(), + expect(stateTransition.getOwnerId().toBuffer()).to.deep.equal( + stateTransition.getIdentityId().toBuffer(), ); }); }); @@ -158,8 +197,8 @@ describe('IdentityCreateTransition', () => { const identityId = result[0]; expect(identityId).to.be.an.instanceOf(Identifier); - expect(identityId).to.be.deep.equal( - new IdentityCreateTransition(rawStateTransition).getIdentityId(), + expect(identityId.toBuffer()).to.be.deep.equal( + new IdentityCreateTransition(rawStateTransition).getIdentityId().toBuffer(), ); }); }); diff --git a/packages/wasm-dpp/test/unit/identity/stateTransition/IdentityCreateTransition/applyIdentityCreateTransitionFactory.spec.js b/packages/wasm-dpp/test/unit/identity/stateTransition/IdentityCreateTransition/applyIdentityCreateTransitionFactory.spec.js index ec34c9936ab..6e78c7f2945 100644 --- a/packages/wasm-dpp/test/unit/identity/stateTransition/IdentityCreateTransition/applyIdentityCreateTransitionFactory.spec.js +++ b/packages/wasm-dpp/test/unit/identity/stateTransition/IdentityCreateTransition/applyIdentityCreateTransitionFactory.spec.js @@ -1,9 +1,3 @@ -const Identity = require('@dashevo/dpp/lib/identity/Identity'); - -const applyIdentityCreateTransitionFactory = require( - '@dashevo/dpp/lib/identity/stateTransition/IdentityCreateTransition/applyIdentityCreateTransitionFactory', -); - const getIdentityCreateTransitionFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityCreateTransitionFixture'); const { convertSatoshiToCredits } = require('@dashevo/dpp/lib/identity/creditsConverter'); @@ -11,21 +5,37 @@ const { convertSatoshiToCredits } = require('@dashevo/dpp/lib/identity/creditsCo const createStateRepositoryMock = require('@dashevo/dpp/lib/test/mocks/createStateRepositoryMock'); const protocolVersion = require('@dashevo/dpp/lib/version/protocolVersion'); -const StateTransitionExecutionContext = require('@dashevo/dpp/lib/stateTransition/StateTransitionExecutionContext'); -const ReadOperation = require('@dashevo/dpp/lib/stateTransition/fee/operations/ReadOperation'); + +const { default: loadWasmDpp } = require('../../../../../dist'); describe('applyIdentityCreateTransitionFactory', () => { let stateTransition; let applyIdentityCreateTransition; let stateRepositoryMock; - let fetchAssetLockTransactionOutputMock; let output; let executionContext; + let StateTransitionExecutionContext; + let IdentityCreateTransition; + let Identity; + + let applyIdentityCreateTransitionDPP; + + before(async () => { + ({ + StateTransitionExecutionContext, + IdentityCreateTransition, + applyIdentityCreateTransition: applyIdentityCreateTransitionDPP, + Identity, + } = await loadWasmDpp()); + }); + beforeEach(function beforeEach() { stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); - stateTransition = getIdentityCreateTransitionFixture(); + stateTransition = new IdentityCreateTransition( + getIdentityCreateTransitionFixture().toObject(), + ); executionContext = new StateTransitionExecutionContext(); @@ -33,19 +43,13 @@ describe('applyIdentityCreateTransitionFactory', () => { output = stateTransition.getAssetLockProof().getOutput(); - fetchAssetLockTransactionOutputMock = this.sinonSandbox.stub().resolves(output); - - applyIdentityCreateTransition = applyIdentityCreateTransitionFactory( + applyIdentityCreateTransition = (st) => applyIdentityCreateTransitionDPP( stateRepositoryMock, - fetchAssetLockTransactionOutputMock, + st, ); }); - it('should store identity created from state transition', async () => { - executionContext.addOperation( - new ReadOperation(1), - ); - + it('should store identity created from state transition', async function () { await applyIdentityCreateTransition(stateTransition); const balance = convertSatoshiToCredits( @@ -60,9 +64,11 @@ describe('applyIdentityCreateTransitionFactory', () => { revision: 0, }); + const { match } = this.sinonSandbox; + expect(stateRepositoryMock.createIdentity).to.have.been.calledOnceWithExactly( - identity, - executionContext, + match((arg) => expect(arg.toObject()).to.deep.equal(identity.toObject())), + match.instanceOf(StateTransitionExecutionContext), ); const publicKeyHashes = identity @@ -70,21 +76,15 @@ describe('applyIdentityCreateTransitionFactory', () => { .map((publicKey) => publicKey.hash()); expect(stateRepositoryMock.storeIdentityPublicKeyHashes).to.have.been.calledOnceWithExactly( - identity.getId(), - publicKeyHashes, - executionContext, + match((arg) => expect(arg.toBuffer()).to.deep.equal(identity.getId().toBuffer())), + match((arg) => expect(arg).to.deep.equal(publicKeyHashes)), + match.instanceOf(StateTransitionExecutionContext), ); + const outPoint = stateTransition.getAssetLockProof().getOutPoint(); expect(stateRepositoryMock.markAssetLockTransactionOutPointAsUsed).to.have.been .calledOnceWithExactly( - stateTransition.getAssetLockProof().getOutPoint(), - executionContext, - ); - - expect(fetchAssetLockTransactionOutputMock) - .to.be.calledOnceWithExactly( - stateTransition.getAssetLockProof(), - executionContext, + match((arg) => Buffer.from(arg).equals(outPoint)), ); }); }); diff --git a/packages/wasm-dpp/test/unit/identity/stateTransition/IdentityCreateTransition/validation/state/validateIdentityCreateTransitionStateFactory.spec.js b/packages/wasm-dpp/test/unit/identity/stateTransition/IdentityCreateTransition/validation/state/validateIdentityCreateTransitionStateFactory.spec.js index 83b8d6b3162..d5db06e4794 100644 --- a/packages/wasm-dpp/test/unit/identity/stateTransition/IdentityCreateTransition/validation/state/validateIdentityCreateTransitionStateFactory.spec.js +++ b/packages/wasm-dpp/test/unit/identity/stateTransition/IdentityCreateTransition/validation/state/validateIdentityCreateTransitionStateFactory.spec.js @@ -1,54 +1,85 @@ -const { expectValidationError } = require( - '@dashevo/dpp/lib/test/expect/expectError', -); - -const validateIdentityCreateTransitionStateFactory = require( - '@dashevo/dpp/lib/identity/stateTransition/IdentityCreateTransition/validation/state/validateIdentityCreateTransitionStateFactory', -); - const getIdentityCreateTransitionFixture = require('@dashevo/dpp/lib/test/fixtures/getIdentityCreateTransitionFixture'); -const IdentityAlreadyExistsError = require( - '@dashevo/dpp/lib/errors/consensus/state/identity/IdentityAlreadyExistsError', -); - const createStateRepositoryMock = require('@dashevo/dpp/lib/test/mocks/createStateRepositoryMock'); -const IdentityPublicKey = require('@dashevo/dpp/lib/identity/IdentityPublicKey'); + +const protocolVersion = require('@dashevo/dpp/lib/version/protocolVersion'); +const { default: loadWasmDpp } = require('../../../../../../../dist'); +const generateRandomIdentifierAsync = require('../../../../../../../lib/test/utils/generateRandomIdentifierAsync'); +const { expectValidationError } = require('../../../../../../../lib/test/expect/expectError'); describe('validateIdentityCreateTransitionStateFactory', () => { let validateIdentityCreateTransitionState; let stateTransition; let stateRepositoryMock; + let identity; + + let IdentityCreateTransition; + let IdentityPublicKey; + let IdentityAlreadyExistsError; + let Identity; + let IdentityCreateTransitionStateValidator; + + before(async () => { + ({ + IdentityCreateTransition, + IdentityAlreadyExistsError, + IdentityPublicKey, + Identity, + IdentityCreateTransitionStateValidator, + } = await loadWasmDpp()); + }); - beforeEach(async function beforeEach() { - const privateKey = 'af432c476f65211f45f48f1d42c9c0b497e56696aa1736b40544ef1a496af837'; + beforeEach(async function () { + const privateKey = Buffer.from('af432c476f65211f45f48f1d42c9c0b497e56696aa1736b40544ef1a496af837', 'hex'); stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); - validateIdentityCreateTransitionState = validateIdentityCreateTransitionStateFactory( - stateRepositoryMock, - ); + const validator = new IdentityCreateTransitionStateValidator(stateRepositoryMock); + validateIdentityCreateTransitionState = (st) => validator.validate(st); - stateTransition = getIdentityCreateTransitionFixture(); + stateTransition = new IdentityCreateTransition( + getIdentityCreateTransitionFixture().toObject(), + ); await stateTransition.signByPrivateKey(privateKey, IdentityPublicKey.TYPES.ECDSA_SECP256K1); const rawTransaction = '030000000137feb5676d0851337ea3c9a992496aab7a0b3eee60aeeb9774000b7f4bababa5000000006b483045022100d91557de37645c641b948c6cd03b4ae3791a63a650db3e2fee1dcf5185d1b10402200e8bd410bf516ca61715867666d31e44495428ce5c1090bf2294a829ebcfa4ef0121025c3cc7fbfc52f710c941497fd01876c189171ea227458f501afcb38a297d65b4ffffffff021027000000000000166a14152073ca2300a86b510fa2f123d3ea7da3af68dcf77cb0090a0000001976a914152073ca2300a86b510fa2f123d3ea7da3af68dc88ac00000000'; - stateRepositoryMock.fetchTransaction.resolves(rawTransaction); + stateRepositoryMock.fetchTransaction.returns({ + data: Buffer.from(rawTransaction, 'hex'), + height: 42, + }); + + identity = new Identity({ + protocolVersion: protocolVersion.latestVersion, + id: await generateRandomIdentifierAsync(), + publicKeys: [ + { + id: 0, + type: 0, + data: Buffer.alloc(36).fill('a'), + purpose: 0, + securityLevel: 0, + readOnly: false, + signature: Buffer.alloc(36).fill('a'), + }, + ], + balance: 0, + revision: 0, + }); }); it('should return invalid result if identity already exists', async () => { - stateRepositoryMock.fetchIdentity.resolves({}); + stateRepositoryMock.fetchIdentity.returns(identity); const result = await validateIdentityCreateTransitionState(stateTransition); - expectValidationError(result, IdentityAlreadyExistsError, 1); + await expectValidationError(result, IdentityAlreadyExistsError); const [error] = result.getErrors(); expect(error.getCode()).to.equal(4011); - expect(Buffer.isBuffer(error.getIdentityId())).to.be.true(); - expect(error.getIdentityId()).to.deep.equal(stateTransition.getIdentityId()); + expect(error.getIdentityId()).to.exist(); + expect(error.getIdentityId()).to.deep.equal(stateTransition.getIdentityId().toBuffer()); }); it('should return valid result if state transition is valid', async () => { @@ -58,7 +89,7 @@ describe('validateIdentityCreateTransitionStateFactory', () => { }); it('should return valid result on dry run', async () => { - stateRepositoryMock.fetchIdentity.resolves({}); + stateRepositoryMock.fetchIdentity.returns(identity); stateTransition.getExecutionContext().enableDryRun(); diff --git a/packages/wasm-dpp/test/unit/identity/stateTransition/assetLockProof/ChainAssetLockProof.spec.js b/packages/wasm-dpp/test/unit/identity/stateTransition/assetLockProof/ChainAssetLockProof.spec.js new file mode 100644 index 00000000000..c00a78608c2 --- /dev/null +++ b/packages/wasm-dpp/test/unit/identity/stateTransition/assetLockProof/ChainAssetLockProof.spec.js @@ -0,0 +1,63 @@ +const getChainAssetLockFixture = require('@dashevo/dpp/lib/test/fixtures/getChainAssetLockProofFixture'); + +const { default: loadWasmDpp } = require('../../../../../dist'); + +describe('ChainAssetLockProof', () => { + let ChainAssetLockProof; + let chainAssetLockProof; + let chainAssetLockProofJS; + + before(async () => { + ({ ChainAssetLockProof } = await loadWasmDpp()); + + chainAssetLockProofJS = getChainAssetLockFixture(); + chainAssetLockProof = new ChainAssetLockProof( + chainAssetLockProofJS.toObject(), + ); + }); + + describe('#getType', () => { + it('should return correct type', () => { + expect(chainAssetLockProof.getType()) + .to.equal(chainAssetLockProofJS.getType()); + }); + }); + + describe('#getCoreChainLockedHeight', () => { + it('should return correct coreChainLockedHeight', () => { + expect(chainAssetLockProof.getCoreChainLockedHeight()) + .to.equal(chainAssetLockProofJS.getCoreChainLockedHeight()); + }); + }); + + describe('#getOutPoint', () => { + it('should return correct outPoint', () => { + expect(chainAssetLockProof.getOutPoint()) + .to.deep.equal(chainAssetLockProofJS.getOutPoint()); + }); + }); + + describe('#toJSON', () => { + it('should return correct JSON', () => { + expect(chainAssetLockProof.toJSON()) + .to.deep.equal(chainAssetLockProofJS.toJSON()); + }); + }); + + describe('#toObject', () => { + it('should return correct object', () => { + expect(chainAssetLockProof.toObject()) + .to.deep.equal(chainAssetLockProofJS.toObject()); + }); + }); + + describe('#createIdentifier', () => { + it('should return correct identifier', () => { + const identifier = chainAssetLockProof.createIdentifier(); + const identifierJS = chainAssetLockProofJS.createIdentifier(); + + expect(identifier.toBuffer()) + .to.deep.equal(identifierJS.toBuffer()); + }); + }); +}); diff --git a/packages/wasm-dpp/test/unit/identity/stateTransition/assetLockProof/InstantAssetLockProof.spec.js b/packages/wasm-dpp/test/unit/identity/stateTransition/assetLockProof/InstantAssetLockProof.spec.js new file mode 100644 index 00000000000..0b8f7b6551d --- /dev/null +++ b/packages/wasm-dpp/test/unit/identity/stateTransition/assetLockProof/InstantAssetLockProof.spec.js @@ -0,0 +1,90 @@ +const getInstantAssetLockProofFixture = require('@dashevo/dpp/lib/test/fixtures/getInstantAssetLockProofFixture'); + +const { default: loadWasmDpp } = require('../../../../../dist'); + +describe('InstantAssetLockProof', () => { + let InstantAssetLockProof; + let instantAssetLockProof; + let instantAssetLockProofJS; + + before(async () => { + ({ InstantAssetLockProof } = await loadWasmDpp()); + + instantAssetLockProofJS = getInstantAssetLockProofFixture(); + instantAssetLockProof = new InstantAssetLockProof( + instantAssetLockProofJS.toObject(), + ); + }); + + describe('#getType', () => { + it('should return correct type', () => { + expect(instantAssetLockProof.getType()) + .to.equal(instantAssetLockProofJS.getType()); + }); + }); + + describe('#getOutputIndex', () => { + it('should return correct type', () => { + expect(instantAssetLockProof.getOutputIndex()) + .to.equal(instantAssetLockProofJS.getOutputIndex()); + }); + }); + + describe('#getOutPoint', () => { + it('should return correct outPoint', () => { + expect(instantAssetLockProof.getOutPoint()) + .to.deep.equal(instantAssetLockProofJS.getOutPoint()); + }); + }); + + describe('#getOutput', () => { + it('should return correct output', () => { + expect(instantAssetLockProof.getOutput()) + .to.deep.equal(instantAssetLockProofJS.getOutput().toObject()); + }); + }); + + describe('#createIdentifier', () => { + it('should return correct identifier', () => { + const identifier = instantAssetLockProof.createIdentifier(); + const identifierJS = instantAssetLockProofJS.createIdentifier(); + + expect(identifier.toBuffer()) + .to.deep.equal(identifierJS.toBuffer()); + }); + }); + + describe('#getInstantLock', () => { + it('should return correct instant lock', () => { + const instantLock = instantAssetLockProof.getInstantLock(); + const instantLockJS = instantAssetLockProofJS.getInstantLock(); + + expect(instantLock) + .to.deep.equal(instantLockJS.toBuffer()); + }); + }); + + describe('#getTransaction', () => { + it('should return correct transaction', () => { + const transaction = instantAssetLockProof.getTransaction(); + const transactionJS = instantAssetLockProofJS.getTransaction(); + + expect(transaction) + .to.deep.equal(transactionJS.toBuffer()); + }); + }); + + describe('#toObject', () => { + it('should return correct object', () => { + expect(instantAssetLockProof.toObject()) + .to.deep.equal(instantAssetLockProofJS.toObject()); + }); + }); + + describe('#toJSON', () => { + it('should return correct JSON', () => { + expect(instantAssetLockProof.toJSON()) + .to.deep.equal(instantAssetLockProofJS.toJSON()); + }); + }); +}); diff --git a/packages/wasm-dpp/test/unit/identity/stateTransition/assetLockProof/createAssetLockProofInstance.spec.js b/packages/wasm-dpp/test/unit/identity/stateTransition/assetLockProof/createAssetLockProofInstance.spec.js index b5bdea509e8..d597396c93d 100644 --- a/packages/wasm-dpp/test/unit/identity/stateTransition/assetLockProof/createAssetLockProofInstance.spec.js +++ b/packages/wasm-dpp/test/unit/identity/stateTransition/assetLockProof/createAssetLockProofInstance.spec.js @@ -1,21 +1,36 @@ -const createAssetLockProofInstance = require('@dashevo/dpp/lib/identity/stateTransition/assetLockProof/createAssetLockProofInstance'); +const createAssetLockProofInstanceJS = require('@dashevo/dpp/lib/identity/stateTransition/assetLockProof/createAssetLockProofInstance'); const getChainAssetLockFixture = require('@dashevo/dpp/lib/test/fixtures/getChainAssetLockProofFixture'); const getInstantAssetLockProofFixture = require('@dashevo/dpp/lib/test/fixtures/getInstantAssetLockProofFixture'); -const ChainAssetLockProof = require('@dashevo/dpp/lib/identity/stateTransition/assetLockProof/chain/ChainAssetLockProof'); -const InstantAssetLockProof = require('@dashevo/dpp/lib/identity/stateTransition/assetLockProof/instant/InstantAssetLockProof'); +const { default: loadWasmDpp } = require('../../../../../dist'); describe('createAssetLockProofInstance', () => { + let createAssetLockProofInstance; + let InstantAssetLockProof; + let ChainAssetLockProof; + + before(async () => { + ({ + createAssetLockProofInstance, + InstantAssetLockProof, + ChainAssetLockProof, + } = await loadWasmDpp()); + }); + it('should create an instance of InstantAssetLockProof', () => { const assetLockProofFixture = getInstantAssetLockProofFixture(); const instance = createAssetLockProofInstance(assetLockProofFixture.toObject()); + const instanceJS = createAssetLockProofInstanceJS(assetLockProofFixture.toObject()); + expect(instance.toObject()).to.deep.equal(instanceJS.toObject()); expect(instance).to.be.an.instanceOf(InstantAssetLockProof); }); it('should create an instance of ChainAssetLockProof', () => { const assetLockProofFixture = getChainAssetLockFixture(); const instance = createAssetLockProofInstance(assetLockProofFixture.toObject()); + const instanceJS = createAssetLockProofInstanceJS(assetLockProofFixture.toObject()); + expect(instance.toObject()).to.deep.equal(instanceJS.toObject()); expect(instance).to.be.an.instanceOf(ChainAssetLockProof); }); }); diff --git a/packages/wasm-dpp/test/unit/identity/stateTransition/assetLockProof/fetchAssetLockPublicKeyHashFactory.spec.js b/packages/wasm-dpp/test/unit/identity/stateTransition/assetLockProof/fetchAssetLockPublicKeyHashFactory.spec.js index 6d39228f671..48cba2d46cb 100644 --- a/packages/wasm-dpp/test/unit/identity/stateTransition/assetLockProof/fetchAssetLockPublicKeyHashFactory.spec.js +++ b/packages/wasm-dpp/test/unit/identity/stateTransition/assetLockProof/fetchAssetLockPublicKeyHashFactory.spec.js @@ -1,32 +1,59 @@ -const fetchAssetLockPublicKeyHashFactory = require('@dashevo/dpp/lib/identity/stateTransition/assetLockProof/fetchAssetLockPublicKeyHashFactory'); +const { Script } = require('@dashevo/dashcore-lib'); + const getInstantAssetLockProofFixture = require('@dashevo/dpp/lib/test/fixtures/getInstantAssetLockProofFixture'); -const AssetLockOutputNotFoundError = require('@dashevo/dpp/lib/identity/errors/AssetLockOutputNotFoundError'); + +const createStateRepositoryMock = require('@dashevo/dpp/lib/test/mocks/createStateRepositoryMock'); +const { default: loadWasmpDpp } = require('../../../../../dist'); describe('fetchAssetLockPublicKeyHashFactory', () => { let fetchAssetLockPublicKeyHash; - let fetchAssetLockTransactionOutputMock; - let assetLockProof; + let stateRepositoryMock; + let executionContext; + + let StateTransitionExecutionContext; + let InstantAssetLockProof; + let AssetLockOutputNotFoundError; + let fetchAssetLockPublicKeyHashDPP; + + before(async () => { + ({ + StateTransitionExecutionContext, + InstantAssetLockProof, + AssetLockOutputNotFoundError, + fetchAssetLockPublicKeyHash: fetchAssetLockPublicKeyHashDPP, + } = await loadWasmpDpp()); + }); beforeEach(function beforeEach() { - fetchAssetLockTransactionOutputMock = this.sinonSandbox.stub(); + stateRepositoryMock = createStateRepositoryMock(this.sinonSandbox); - fetchAssetLockPublicKeyHash = fetchAssetLockPublicKeyHashFactory( - fetchAssetLockTransactionOutputMock, - ); + executionContext = new StateTransitionExecutionContext(); - assetLockProof = getInstantAssetLockProofFixture(); + fetchAssetLockPublicKeyHash = (proof) => fetchAssetLockPublicKeyHashDPP( + stateRepositoryMock, + proof, + executionContext, + ); }); it('should return public key hash for specified asset lock proof', async () => { - fetchAssetLockTransactionOutputMock.resolves(assetLockProof.getOutput()); + const assetLockProof = new InstantAssetLockProof( + getInstantAssetLockProofFixture().toObject(), + ); const result = await fetchAssetLockPublicKeyHash(assetLockProof); - expect(result).to.deep.equal(assetLockProof.getOutput().script.getData()); + expect(result).to + .deep.equal(new Script(assetLockProof.getOutput().script).getData()); }); it('should throw AssetLockOutputNotFoundError if output is not found', async () => { try { + const assetLockProofJS = getInstantAssetLockProofFixture(); + // Mess up TX outputs + assetLockProofJS.transaction.outputs = []; + + const assetLockProof = new InstantAssetLockProof(assetLockProofJS.toObject()); await fetchAssetLockPublicKeyHash(assetLockProof); expect.fail('should throw AssetLockOutputNotFoundError'); } catch (e) {