diff --git a/packages/rs-dpp/src/identity/state_transition/mod.rs b/packages/rs-dpp/src/identity/state_transition/mod.rs index a2b4256eb68..874928f8c58 100644 --- a/packages/rs-dpp/src/identity/state_transition/mod.rs +++ b/packages/rs-dpp/src/identity/state_transition/mod.rs @@ -16,3 +16,11 @@ pub trait AssetLockProved { /// Get asset lock proof fn asset_lock_proof(&self) -> &AssetLockProof; } + +/// Only state transitions funded with Asset Lock Transactions have Asset Lock Proofs +pub trait OptionallyAssetLockProved { + /// Returns asset lock proof if state transition implements it + fn optional_asset_lock_proof(&self) -> Option<&AssetLockProof> { + None + } +} diff --git a/packages/rs-dpp/src/state_transition/mod.rs b/packages/rs-dpp/src/state_transition/mod.rs index 73518cf73ed..b874db79903 100644 --- a/packages/rs-dpp/src/state_transition/mod.rs +++ b/packages/rs-dpp/src/state_transition/mod.rs @@ -36,7 +36,9 @@ use crate::consensus::ConsensusError; use crate::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0; use crate::identity::signer::Signer; +use crate::identity::state_transition::OptionallyAssetLockProved; use crate::identity::{IdentityPublicKey, KeyID, KeyType, Purpose, SecurityLevel}; +use crate::prelude::AssetLockProof; pub use state_transitions::*; use crate::serialization::Signable; @@ -243,16 +245,13 @@ pub enum StateTransition { IdentityCreditTransfer(IdentityCreditTransferTransition), } -impl StateTransition { - /// This means we should transform into the action before validation of the structure - pub fn requires_state_to_validate_structure(&self) -> bool { - matches!(self, StateTransition::DocumentsBatch(_)) - } - /// This means we should transform into the action before validation of the identity and signatures - pub fn requires_state_to_validate_identity_and_signatures(&self) -> bool { - matches!(self, StateTransition::DocumentsBatch(_)) +impl OptionallyAssetLockProved for StateTransition { + fn optional_asset_lock_proof(&self) -> Option<&AssetLockProof> { + call_method!(self, optional_asset_lock_proof) } +} +impl StateTransition { pub fn is_identity_signed(&self) -> bool { !matches!( self, diff --git a/packages/rs-dpp/src/state_transition/state_transitions/contract/data_contract_create_transition/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/contract/data_contract_create_transition/mod.rs index 0d3e8ae0f78..929946cda53 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/contract/data_contract_create_transition/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/contract/data_contract_create_transition/mod.rs @@ -27,6 +27,7 @@ use platform_versioning::PlatformVersioned; use serde::{Deserialize, Serialize}; use crate::data_contract::created_data_contract::CreatedDataContract; +use crate::identity::state_transition::OptionallyAssetLockProved; pub use v0::*; pub type DataContractCreateTransitionLatest = DataContractCreateTransitionV0; @@ -146,6 +147,8 @@ impl DataContractCreateTransition { } } +impl OptionallyAssetLockProved for DataContractCreateTransition {} + #[cfg(test)] mod test { use crate::data_contract::conversion::json::DataContractJsonConversionMethodsV0; @@ -155,7 +158,6 @@ mod test { use crate::data_contract::accessors::v0::DataContractV0Getters; use crate::data_contract::conversion::value::v0::DataContractValueConversionMethodsV0; use crate::state_transition::data_contract_create_transition::accessors::DataContractCreateTransitionAccessorsV0; - use crate::state_transition::state_transitions::common_fields::property_names; use crate::state_transition::traits::StateTransitionLike; use crate::state_transition::{StateTransitionType, StateTransitionValueConvert}; use crate::tests::fixtures::get_data_contract_fixture; @@ -185,7 +187,7 @@ mod test { ), ), ( - property_names::ENTROPY, + ENTROPY, Value::Bytes32(created_data_contract.entropy_used().to_buffer()), ), ( diff --git a/packages/rs-dpp/src/state_transition/state_transitions/contract/data_contract_update_transition/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/contract/data_contract_update_transition/mod.rs index 56b7003ac0c..7d95c8f68e7 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/contract/data_contract_update_transition/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/contract/data_contract_update_transition/mod.rs @@ -27,6 +27,7 @@ use platform_version::{TryFromPlatformVersioned, TryIntoPlatformVersioned}; use crate::data_contract::DataContract; +use crate::identity::state_transition::OptionallyAssetLockProved; pub use v0::*; pub type DataContractUpdateTransitionLatest = DataContractUpdateTransitionV0; @@ -99,6 +100,8 @@ impl StateTransitionFieldTypes for DataContractUpdateTransition { } } +impl OptionallyAssetLockProved for DataContractUpdateTransition {} + #[cfg(test)] mod test { use crate::data_contract::DataContract; diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/documents_batch_transition/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/documents_batch_transition/mod.rs index 51678da4b61..d3c905890c9 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/documents_batch_transition/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/documents_batch_transition/mod.rs @@ -41,6 +41,7 @@ use crate::state_transition::data_contract_update_transition::{ use crate::state_transition::documents_batch_transition::fields::property_names; +use crate::identity::state_transition::OptionallyAssetLockProved; pub use v0::*; #[derive( @@ -593,3 +594,4 @@ pub fn get_security_level_requirement(v: &Value, default: SecurityLevel) -> Secu // assert_eq!(hex::encode(expected_bytes), hex::encode(bytes)); // } // } +impl OptionallyAssetLockProved for DocumentsBatchTransition {} diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_create_transition/proved.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_create_transition/proved.rs index 79954d54f93..cee34c9d4be 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_create_transition/proved.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_create_transition/proved.rs @@ -1,8 +1,14 @@ -use crate::identity::state_transition::AssetLockProved; +use crate::identity::state_transition::{AssetLockProved, OptionallyAssetLockProved}; use crate::prelude::AssetLockProof; use crate::state_transition::identity_create_transition::IdentityCreateTransition; use crate::ProtocolError; +impl OptionallyAssetLockProved for IdentityCreateTransition { + fn optional_asset_lock_proof(&self) -> Option<&AssetLockProof> { + Some(self.asset_lock_proof()) + } +} + impl AssetLockProved for IdentityCreateTransition { fn set_asset_lock_proof( &mut self, diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_transfer_transition/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_transfer_transition/mod.rs index ebe10ef16a9..eee8925ca7f 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_transfer_transition/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_transfer_transition/mod.rs @@ -15,6 +15,7 @@ use crate::state_transition::identity_credit_transfer_transition::v0::IdentityCr use crate::state_transition::identity_credit_transfer_transition::v0::IdentityCreditTransferTransitionV0Signable; use crate::state_transition::StateTransitionFieldTypes; +use crate::identity::state_transition::OptionallyAssetLockProved; use crate::ProtocolError; use bincode::{Decode, Encode}; use derive_more::From; @@ -71,6 +72,8 @@ impl IdentityCreditTransferTransition { } } +impl OptionallyAssetLockProved for IdentityCreditTransferTransition {} + impl StateTransitionFieldTypes for IdentityCreditTransferTransition { fn signature_property_paths() -> Vec<&'static str> { vec![SIGNATURE] diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/mod.rs index 7dc6a29030f..c7b052160e2 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/mod.rs @@ -15,6 +15,7 @@ mod version; use crate::state_transition::identity_credit_withdrawal_transition::v0::IdentityCreditWithdrawalTransitionV0Signable; use crate::state_transition::StateTransitionFieldTypes; +use crate::identity::state_transition::OptionallyAssetLockProved; use crate::ProtocolError; use bincode::{Decode, Encode}; use data_contracts::withdrawals_contract::document_types::withdrawal::properties::OUTPUT_SCRIPT; @@ -65,3 +66,5 @@ impl StateTransitionFieldTypes for IdentityCreditWithdrawalTransition { vec![SIGNATURE, OUTPUT_SCRIPT] } } + +impl OptionallyAssetLockProved for IdentityCreditWithdrawalTransition {} diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_topup_transition/proved.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_topup_transition/proved.rs index 3d992128085..d664ee5ab91 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_topup_transition/proved.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_topup_transition/proved.rs @@ -1,8 +1,14 @@ -use crate::identity::state_transition::AssetLockProved; +use crate::identity::state_transition::{AssetLockProved, OptionallyAssetLockProved}; use crate::prelude::AssetLockProof; use crate::state_transition::identity_topup_transition::IdentityTopUpTransition; use crate::ProtocolError; +impl OptionallyAssetLockProved for IdentityTopUpTransition { + fn optional_asset_lock_proof(&self) -> Option<&AssetLockProof> { + Some(self.asset_lock_proof()) + } +} + impl AssetLockProved for IdentityTopUpTransition { fn set_asset_lock_proof( &mut self, diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_update_transition/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_update_transition/mod.rs index 0ef9d4c5d7e..f1649265804 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_update_transition/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_update_transition/mod.rs @@ -17,6 +17,7 @@ use crate::state_transition::identity_update_transition::v0::IdentityUpdateTrans use crate::state_transition::StateTransitionFieldTypes; use fields::*; +use crate::identity::state_transition::OptionallyAssetLockProved; use crate::ProtocolError; use bincode::{Decode, Encode}; use derive_more::From; @@ -70,6 +71,8 @@ impl IdentityUpdateTransition { } } +impl OptionallyAssetLockProved for IdentityUpdateTransition {} + impl StateTransitionFieldTypes for IdentityUpdateTransition { fn binary_property_paths() -> Vec<&'static str> { vec![SIGNATURE, ADD_PUBLIC_KEYS_SIGNATURE] diff --git a/packages/rs-drive-abci/src/abci/handler/mod.rs b/packages/rs-drive-abci/src/abci/handler/mod.rs index dc8649ac9ad..68f709864fe 100644 --- a/packages/rs-drive-abci/src/abci/handler/mod.rs +++ b/packages/rs-drive-abci/src/abci/handler/mod.rs @@ -753,8 +753,8 @@ where ) -> Result { let _timer = crate::metrics::abci_request_duration("check_tx"); - let RequestCheckTx { tx, .. } = request; - match self.platform.check_tx(tx.as_slice()) { + let RequestCheckTx { tx, r#type } = request; + match self.platform.check_tx(tx.as_slice(), r#type.try_into()?) { Ok(validation_result) => { let platform_state = self.platform.state.read().unwrap(); let platform_version = platform_state.current_platform_version()?; @@ -774,7 +774,11 @@ where let gas_wanted = validation_result .data - .map(|fee_result| fee_result.total_base_fee()) + .map(|fee_result| { + fee_result + .map(|fee_result| fee_result.total_base_fee()) + .unwrap_or_default() + }) .unwrap_or_default(); Ok(ResponseCheckTx { diff --git a/packages/rs-drive-abci/src/execution/check_tx/mod.rs b/packages/rs-drive-abci/src/execution/check_tx/mod.rs index e6c5b7d3f35..64434b669ae 100644 --- a/packages/rs-drive-abci/src/execution/check_tx/mod.rs +++ b/packages/rs-drive-abci/src/execution/check_tx/mod.rs @@ -2,6 +2,7 @@ use crate::error::execution::ExecutionError; use crate::error::Error; use crate::platform_types::platform::Platform; +use crate::abci::AbciError; use crate::rpc::core::CoreRPCLike; use dpp::consensus::ConsensusError; use dpp::fee::fee_result::FeeResult; @@ -9,6 +10,44 @@ use dpp::validation::ValidationResult; mod v0; +// @append_only +#[repr(u8)] +#[derive(Copy, Clone, Debug)] +pub enum CheckTxLevel { + FirstTimeCheck, + Recheck, +} + +impl TryFrom for CheckTxLevel { + type Error = Error; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(CheckTxLevel::FirstTimeCheck), + 1 => Ok(CheckTxLevel::Recheck), + value => Err(Error::Abci(AbciError::BadRequest(format!( + "Invalid value for CheckTxLevel {}", + value + )))), + } + } +} + +impl TryFrom for CheckTxLevel { + type Error = Error; + + fn try_from(value: i32) -> Result { + match value { + 0 => Ok(CheckTxLevel::FirstTimeCheck), + 1 => Ok(CheckTxLevel::Recheck), + value => Err(Error::Abci(AbciError::BadRequest(format!( + "Invalid value for CheckTxLevel {}", + value + )))), + } + } +} + impl Platform where C: CoreRPCLike, @@ -30,11 +69,12 @@ where pub fn check_tx( &self, raw_tx: &[u8], - ) -> Result, Error> { + check_tx_level: CheckTxLevel, + ) -> Result, ConsensusError>, Error> { let state = self.state.read().expect("expected to get state"); let platform_version = state.current_platform_version()?; match platform_version.drive_abci.methods.engine.check_tx { - 0 => self.check_tx_v0(raw_tx), + 0 => self.check_tx_v0(raw_tx, check_tx_level), version => Err(Error::Execution(ExecutionError::UnknownVersionMismatch { method: "check_tx".to_string(), known_versions: vec![0], diff --git a/packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs b/packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs index 7868028ca8e..cafa016da34 100644 --- a/packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs @@ -1,4 +1,6 @@ use crate::error::Error; +use crate::execution::check_tx::CheckTxLevel; +use crate::execution::validation::state_transition::check_tx_verification::state_transition_to_execution_event_for_check_tx; use crate::execution::validation::state_transition::processor::process_state_transition; #[cfg(test)] use crate::platform_types::event_execution_result::EventExecutionResult; @@ -76,7 +78,8 @@ where pub(super) fn check_tx_v0( &self, raw_tx: &[u8], - ) -> Result, Error> { + check_tx_level: CheckTxLevel, + ) -> Result, ConsensusError>, Error> { let state_transition = match StateTransition::deserialize_from_bytes(raw_tx) { Ok(state_transition) => state_transition, Err(err) => { @@ -101,13 +104,22 @@ where block_info, }; - let execution_event = process_state_transition(&platform_ref, state_transition, None)?; - let platform_version = platform_ref.state.current_platform_version()?; + let execution_event = state_transition_to_execution_event_for_check_tx( + &platform_ref, + state_transition, + check_tx_level, + )?; + // We should run the execution event in dry run to see if we would have enough fees for the transition execution_event.and_then_borrowed_validation(|execution_event| { - self.validate_fees_of_event(execution_event, block_info, None, platform_version) + if let Some(execution_event) = execution_event { + self.validate_fees_of_event(execution_event, block_info, None, platform_version) + .map(|validation_result| validation_result.map(Some)) + } else { + Ok(ValidationResult::new_with_data(None)) + } }) } } @@ -157,6 +169,7 @@ mod tests { use dpp::version::PlatformVersion; use dpp::NativeBlsModule; + use crate::execution::check_tx::CheckTxLevel::{FirstTimeCheck, Recheck}; use dpp::identity::contract_bounds::ContractBounds::SingleContractDocumentType; use dpp::platform_value::Bytes32; use dpp::system_data_contracts::dashpay_contract; @@ -166,7 +179,7 @@ mod tests { use rand::SeedableRng; use std::collections::BTreeMap; - // This test needs to be finished, but is still useful for debugging + // This test needs to be redone with new contract bytes, but is still useful for debugging #[test] #[ignore] fn verify_check_tx_on_data_contract_create() { @@ -232,19 +245,34 @@ mod tests { let transaction = platform.drive.grove.start_transaction(); - let check_result = platform.check_tx(&tx).expect("expected to check tx"); + let check_result = platform + .check_tx(&tx, FirstTimeCheck) + .expect("expected to check tx"); + assert!(check_result.is_valid()); - let _result = platform + let check_result = platform + .check_tx(&tx, Recheck) + .expect("expected to check tx"); + + assert!(check_result.is_valid()); + + platform .platform .process_raw_state_transitions( - &vec![tx], + &vec![tx.clone()], &state, &BlockInfo::default(), &transaction, platform_version, ) .expect("expected to process state transition"); + + let check_result = platform + .check_tx(&tx, Recheck) + .expect("expected to check tx"); + + assert!(!check_result.is_valid()); } #[test] @@ -307,10 +335,152 @@ mod tests { .expect("expected to insert identity"); let validation_result = platform - .check_tx(serialized.as_slice()) + .check_tx(serialized.as_slice(), FirstTimeCheck) .expect("expected to check tx"); assert!(validation_result.errors.is_empty()); + + let check_result = platform + .check_tx(serialized.as_slice(), Recheck) + .expect("expected to check tx"); + + assert!(check_result.is_valid()); + + let transaction = platform.drive.grove.start_transaction(); + + platform + .platform + .process_raw_state_transitions( + &vec![serialized.clone()], + &state, + &BlockInfo::default(), + &transaction, + platform_version, + ) + .expect("expected to process state transition"); + + let check_result = platform + .check_tx(serialized.as_slice(), Recheck) + .expect("expected to check tx"); + + assert!(check_result.is_valid()); // it should still be valid, because we didn't commit the transaction + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit"); + + let check_result = platform + .check_tx(serialized.as_slice(), Recheck) + .expect("expected to check tx"); + + assert!(check_result.is_valid()); // it should still be valid, because we don't validate state + } + + #[test] + fn data_contract_create_check_tx_after_identity_balance_used_up() { + let mut platform = TestPlatformBuilder::new() + .with_config(PlatformConfig::default()) + .build_with_mock_rpc(); + + platform + .core_rpc + .expect_verify_instant_lock() + .returning(|_, _| Ok(true)); + + let state = platform.state.read().unwrap(); + let protocol_version = state.current_protocol_version_in_consensus(); + let platform_version = PlatformVersion::get(protocol_version).unwrap(); + + let (key, private_key) = IdentityPublicKey::random_ecdsa_critical_level_authentication_key( + 1, + Some(1), + platform_version, + ) + .expect("expected to get key pair"); + + platform + .drive + .create_initial_state_structure(None, platform_version) + .expect("expected to create state structure"); + let identity: Identity = IdentityV0 { + id: Identifier::new([ + 158, 113, 180, 126, 91, 83, 62, 44, 83, 54, 97, 88, 240, 215, 84, 139, 167, 156, + 166, 203, 222, 4, 64, 31, 215, 199, 149, 151, 190, 246, 251, 44, + ]), + public_keys: BTreeMap::from([(1, key.clone())]), + balance: 200000000, // we have enough balance only for 1 insertion (this is where this test is different) + revision: 0, + } + .into(); + + let dashpay = get_dashpay_contract_fixture(Some(identity.id()), protocol_version); + let mut create_contract_state_transition: StateTransition = dashpay + .try_into_platform_versioned(platform_version) + .expect("expected a state transition"); + create_contract_state_transition + .sign(&key, private_key.as_slice(), &NativeBlsModule) + .expect("expected to sign transition"); + let serialized = create_contract_state_transition + .serialize_to_bytes() + .expect("serialized state transition"); + platform + .drive + .add_new_identity( + identity, + false, + &BlockInfo::default(), + true, + None, + platform_version, + ) + .expect("expected to insert identity"); + + let validation_result = platform + .check_tx(serialized.as_slice(), FirstTimeCheck) + .expect("expected to check tx"); + + assert!(validation_result.errors.is_empty()); + + let check_result = platform + .check_tx(serialized.as_slice(), Recheck) + .expect("expected to check tx"); + + assert!(check_result.is_valid()); + + let transaction = platform.drive.grove.start_transaction(); + + platform + .platform + .process_raw_state_transitions( + &vec![serialized.clone()], + &state, + &BlockInfo::default(), + &transaction, + platform_version, + ) + .expect("expected to process state transition"); + + let check_result = platform + .check_tx(serialized.as_slice(), Recheck) + .expect("expected to check tx"); + + assert!(check_result.is_valid()); // it should still be valid, because we didn't commit the transaction + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit"); + + let check_result = platform + .check_tx(serialized.as_slice(), Recheck) + .expect("expected to check tx"); + + assert!(!check_result.is_valid()); // the identity shouldn't have enough balance anymore } #[test] @@ -494,7 +664,10 @@ mod tests { .expect("expected to commit transaction"); let validation_result = platform - .check_tx(documents_batch_update_serialized_transition.as_slice()) + .check_tx( + documents_batch_update_serialized_transition.as_slice(), + FirstTimeCheck, + ) .expect("expected to check tx"); assert!(validation_result.errors.is_empty()); @@ -609,7 +782,10 @@ mod tests { .expect("serialized state transition"); let validation_result = platform - .check_tx(identity_top_up_serialized_transition.as_slice()) + .check_tx( + identity_top_up_serialized_transition.as_slice(), + FirstTimeCheck, + ) .expect("expected to check tx"); assert!(validation_result.errors.is_empty()); @@ -742,7 +918,10 @@ mod tests { .expect("serialized state transition"); let validation_result = platform - .check_tx(identity_top_up_serialized_transition.as_slice()) + .check_tx( + identity_top_up_serialized_transition.as_slice(), + FirstTimeCheck, + ) .expect("expected to check tx"); assert!(validation_result.errors.is_empty()); @@ -766,7 +945,21 @@ mod tests { .expect("expected to commit transaction"); let validation_result = platform - .check_tx(identity_top_up_serialized_transition.as_slice()) + .check_tx( + identity_top_up_serialized_transition.as_slice(), + FirstTimeCheck, + ) + .expect("expected to check tx"); + + assert!(matches!( + validation_result.errors.first().expect("expected an error"), + ConsensusError::BasicError( + BasicError::IdentityAssetLockTransactionOutPointAlreadyExistsError(_) + ) + )); + + let validation_result = platform + .check_tx(identity_top_up_serialized_transition.as_slice(), Recheck) .expect("expected to check tx"); assert!(matches!( @@ -853,7 +1046,10 @@ mod tests { .expect("serialized state transition"); let validation_result = platform - .check_tx(identity_top_up_serialized_transition.as_slice()) + .check_tx( + identity_top_up_serialized_transition.as_slice(), + FirstTimeCheck, + ) .expect("expected to check tx"); // This errors because we never created the identity @@ -973,7 +1169,10 @@ mod tests { .expect("serialized state transition"); let validation_result = platform - .check_tx(identity_top_up_serialized_transition.as_slice()) + .check_tx( + identity_top_up_serialized_transition.as_slice(), + FirstTimeCheck, + ) .expect("expected to check tx"); assert!(validation_result.errors.is_empty()); @@ -1037,7 +1236,21 @@ mod tests { .expect("serialized state transition"); let validation_result = platform - .check_tx(identity_create_serialized_transition.as_slice()) + .check_tx( + identity_create_serialized_transition.as_slice(), + FirstTimeCheck, + ) + .expect("expected to check tx"); + + assert!(matches!( + validation_result.errors.first().expect("expected an error"), + ConsensusError::BasicError( + BasicError::IdentityAssetLockTransactionOutPointAlreadyExistsError(_) + ) + )); + + let validation_result = platform + .check_tx(identity_create_serialized_transition.as_slice(), Recheck) .expect("expected to check tx"); assert!(matches!( @@ -1148,7 +1361,7 @@ mod tests { .expect("expected to serialize"); let validation_result = platform - .check_tx(update_transition_bytes.as_slice()) + .check_tx(update_transition_bytes.as_slice(), FirstTimeCheck) .expect("expected to execute identity top up tx"); // Only master keys can sign an update @@ -1272,7 +1485,7 @@ mod tests { .expect("expected to serialize"); let validation_result = platform - .check_tx(update_transition_bytes.as_slice()) + .check_tx(update_transition_bytes.as_slice(), FirstTimeCheck) .expect("expected to execute identity top up tx"); // we won't have enough funds diff --git a/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/process_raw_state_transitions/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/process_raw_state_transitions/v0/mod.rs index d056cf0710c..21d180fd139 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/process_raw_state_transitions/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/process_raw_state_transitions/v0/mod.rs @@ -7,6 +7,7 @@ use dpp::block::block_info::BlockInfo; use dpp::consensus::basic::decode::SerializedObjectParsingError; use dpp::dashcore::hashes::Hash; use dpp::fee::fee_result::FeeResult; +use dpp::identity::state_transition::OptionallyAssetLockProved; use dpp::serialization::PlatformDeserializable; use dpp::state_transition::StateTransition; use dpp::{dashcore, ProtocolError}; @@ -158,10 +159,7 @@ where tracing::trace!(?state_transition, "Processing state transition"); let state_transition_name = state_transition.name(); - let is_st_asset_lock_funded = matches!( - state_transition, - StateTransition::IdentityCreate(_) | StateTransition::IdentityTopUp(_), - ); + let is_st_asset_lock_funded = state_transition.optional_asset_lock_proof().is_some(); // Validate state transition and produce an execution event let mut st_validation_result = @@ -195,7 +193,6 @@ where // enough to cover processing fees // TODO: process_state_transition should return fees for invalid state transitions as well so we can // deduct the fees from balance if identity is valid - // TODO: Replace with state_transition.optional_asset_lock_proof().is_some() in check tx PR let state_transition_execution_result = if is_st_asset_lock_funded { StateTransitionExecutionResult::UnpaidConsensusError(first_consensus_error) } else { diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/check_tx_verification/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/check_tx_verification/mod.rs new file mode 100644 index 00000000000..bd7eb14d372 --- /dev/null +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/check_tx_verification/mod.rs @@ -0,0 +1,47 @@ +pub(crate) mod v0; + +use crate::error::execution::ExecutionError; +use crate::error::Error; +use crate::execution::types::execution_event::ExecutionEvent; +use crate::platform_types::platform::PlatformRef; +use crate::rpc::core::CoreRPCLike; +use dpp::prelude::ConsensusValidationResult; +use dpp::state_transition::StateTransition; + +use crate::execution::check_tx::CheckTxLevel; + +/// === CHECK TX: NEW ==== +/// Full validation for identity create and identity top up +/// Otherwise only validate: +/// * identity has enough balance for fee +/// * identity signature on tx is valid +/// * ST structure is valid +/// +/// === CHECK TX: RECHECK === +/// For identity create and identity top up, make sure asset lock has not been used up +/// For other state transitions verify that the user still has enough balance +/// +pub(in crate::execution) fn state_transition_to_execution_event_for_check_tx<'a, C: CoreRPCLike>( + platform: &'a PlatformRef, + state_transition: StateTransition, + check_tx_level: CheckTxLevel, +) -> Result>>, Error> { + let platform_version = platform.state.current_platform_version()?; + match platform_version + .drive_abci + .validation_and_processing + .state_transition_to_execution_event_for_check_tx + { + 0 => v0::state_transition_to_execution_event_for_check_tx_v0( + platform, + state_transition, + check_tx_level, + platform_version, + ), + version => Err(Error::Execution(ExecutionError::UnknownVersionMismatch { + method: "state_transition_to_execution_event_for_check_tx".to_string(), + known_versions: vec![0], + received: version, + })), + } +} diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/check_tx_verification/v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/check_tx_verification/v0/mod.rs new file mode 100644 index 00000000000..20d35ea0b23 --- /dev/null +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/check_tx_verification/v0/mod.rs @@ -0,0 +1,222 @@ +use crate::error::Error; +use crate::execution::types::execution_event::ExecutionEvent; +use crate::execution::validation::state_transition::transformer::StateTransitionActionTransformerV0; +use crate::platform_types::platform::PlatformRef; +use crate::platform_types::platform_state::v0::PlatformStateV0Methods; +use crate::rpc::core::CoreRPCLike; +use dpp::identity::state_transition::OptionallyAssetLockProved; +use dpp::prelude::ConsensusValidationResult; + +use dpp::state_transition::{StateTransition}; +use dpp::version::{DefaultForPlatformVersion, PlatformVersion}; +use crate::execution::check_tx::CheckTxLevel; +use crate::execution::types::state_transition_execution_context::StateTransitionExecutionContext; +use crate::execution::validation::state_transition::common::asset_lock::proof::verify_is_not_spent::AssetLockProofVerifyIsNotSpent; +use crate::execution::validation::state_transition::processor::process_state_transition; +use crate::execution::validation::state_transition::processor::v0::{StateTransitionSignatureValidationV0, StateTransitionStructureValidationV0}; + +/// A trait for validating state transitions within a blockchain. +pub(crate) trait StateTransitionCheckTxValidationV0 { + /// This means we should do the full validation on check_tx + fn requires_check_tx_full_validation(&self) -> bool; +} + +impl StateTransitionCheckTxValidationV0 for StateTransition { + fn requires_check_tx_full_validation(&self) -> bool { + matches!( + self, + StateTransition::IdentityCreate(_) | StateTransition::IdentityTopUp(_) + ) + } +} + +pub(super) fn state_transition_to_execution_event_for_check_tx_v0<'a, C: CoreRPCLike>( + platform: &'a PlatformRef, + state_transition: StateTransition, + check_tx_level: CheckTxLevel, + platform_version: &PlatformVersion, +) -> Result>>, Error> { + match check_tx_level { + CheckTxLevel::FirstTimeCheck => { + if state_transition.requires_check_tx_full_validation() { + Ok(process_state_transition(platform, state_transition, None)?.map(Some)) + } else { + // we need to validate the structure, the fees, and the signature + let mut state_transition_execution_context = + StateTransitionExecutionContext::default_for_platform_version( + platform_version, + )?; + + let action = if state_transition.requires_state_to_validate_structure() { + let state_transition_action_result = state_transition.transform_into_action( + platform, + true, + &mut state_transition_execution_context, + None, + )?; + if !state_transition_action_result.is_valid_with_data() { + return Ok( + ConsensusValidationResult::>::new_with_errors( + state_transition_action_result.errors, + ), + ); + } + Some(state_transition_action_result.into_data()?) + } else { + None + }; + + // Validating structure + let result = state_transition.validate_structure( + &platform.into(), + action.as_ref(), + platform.state.current_protocol_version_in_consensus(), + )?; + if !result.is_valid() { + return Ok( + ConsensusValidationResult::>::new_with_errors( + result.errors, + ), + ); + } + + let action = if state_transition + .requires_state_to_validate_identity_and_signatures() + { + if let Some(action) = action { + Some(action) + } else { + let state_transition_action_result = state_transition + .transform_into_action( + platform, + true, + &mut state_transition_execution_context, + None, + )?; + if !state_transition_action_result.is_valid_with_data() { + return Ok( + ConsensusValidationResult::>::new_with_errors( + state_transition_action_result.errors, + ), + ); + } + Some(state_transition_action_result.into_data()?) + } + } else { + None + }; + + // + let result = state_transition.validate_identity_and_signatures( + platform.drive, + action.as_ref(), + None, + &mut state_transition_execution_context, + platform_version, + )?; + // Validating signatures + if !result.is_valid() { + return Ok( + ConsensusValidationResult::>::new_with_errors( + result.errors, + ), + ); + } + let maybe_identity = result.into_data()?; + + let action = if let Some(action) = action { + action + } else { + let state_transition_action_result = state_transition.transform_into_action( + platform, + true, + &mut state_transition_execution_context, + None, + )?; + if !state_transition_action_result.is_valid_with_data() { + return Ok( + ConsensusValidationResult::>::new_with_errors( + state_transition_action_result.errors, + ), + ); + } + state_transition_action_result.into_data()? + }; + + let execution_event = ExecutionEvent::create_from_state_transition_action( + action, + maybe_identity, + platform.state.epoch_ref(), + state_transition_execution_context, + platform_version, + )?; + + Ok( + ConsensusValidationResult::>::new_with_data(Some( + execution_event, + )), + ) + } + } + CheckTxLevel::Recheck => { + if let Some(asset_lock_proof) = state_transition.optional_asset_lock_proof() { + // we should check that the asset lock is still valid + let validation_result = + asset_lock_proof.verify_is_not_spent(platform, None, platform_version)?; + + if validation_result.is_valid() { + Ok(ConsensusValidationResult::>::new_with_data(None)) + } else { + Ok( + ConsensusValidationResult::>::new_with_errors( + validation_result.errors, + ), + ) + } + } else { + // TODO: We aren't calculating processing fees atm. We probably should reconsider this + + let mut state_transition_execution_context = + StateTransitionExecutionContext::default_for_platform_version( + platform_version, + )?; + + let state_transition_action_result = state_transition.transform_into_action( + platform, + true, + &mut state_transition_execution_context, + None, + )?; + + if !state_transition_action_result.is_valid_with_data() { + return Ok( + ConsensusValidationResult::>::new_with_errors( + state_transition_action_result.errors, + ), + ); + } + let action = state_transition_action_result.into_data()?; + + let maybe_identity = platform.drive.fetch_identity_with_balance( + state_transition.owner_id().to_buffer(), + None, + platform_version, + )?; + + let execution_event = ExecutionEvent::create_from_state_transition_action( + action, + maybe_identity, + platform.state.epoch_ref(), + state_transition_execution_context, + platform_version, + )?; + + Ok( + ConsensusValidationResult::>::new_with_data(Some( + execution_event, + )), + ) + } + } + } +} diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/mod.rs index d498e0f5de5..c1e44b07b05 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/mod.rs @@ -1,52 +1,2 @@ -mod chain; -mod instant; - -use crate::error::Error; -use crate::platform_types::platform::PlatformRef; -use crate::rpc::core::CoreRPCLike; -use dpp::prelude::AssetLockProof; -use dpp::validation::SimpleConsensusValidationResult; -use dpp::version::PlatformVersion; -use drive::grovedb::TransactionArg; - -/// A trait for validating state transitions within a blockchain. -pub trait AssetLockProofStateValidation { - /// Validates the state transition by analyzing the changes in the platform state after applying the transaction. - /// - /// # Arguments - /// - /// * `platform` - A reference to the platform containing the state data. - /// * `tx` - The transaction argument to be applied. - /// - /// # Type Parameters - /// - /// * `C: CoreRPCLike` - A type constraint indicating that C should implement `CoreRPCLike`. - /// - /// # Returns - /// - /// * `Result` - A result with either a SimpleConsensusValidationResult or an Error. - fn validate_state( - &self, - platform_ref: &PlatformRef, - transaction: TransactionArg, - platform_version: &PlatformVersion, - ) -> Result; -} - -impl AssetLockProofStateValidation for AssetLockProof { - fn validate_state( - &self, - platform_ref: &PlatformRef, - transaction: TransactionArg, - platform_version: &PlatformVersion, - ) -> Result { - match self { - AssetLockProof::Instant(proof) => { - proof.validate_state(platform_ref, transaction, platform_version) - } - AssetLockProof::Chain(proof) => { - proof.validate_state(platform_ref, transaction, platform_version) - } - } - } -} +pub mod validate; +pub mod verify_is_not_spent; diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/validate/chain/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/validate/chain/mod.rs new file mode 100644 index 00000000000..619c9b7d814 --- /dev/null +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/validate/chain/mod.rs @@ -0,0 +1,35 @@ +use crate::error::Error; +use crate::platform_types::platform::PlatformRef; +use crate::rpc::core::CoreRPCLike; +use dpp::consensus::basic::identity::{ + InvalidAssetLockProofCoreChainHeightError, +}; +use dpp::identity::state_transition::asset_lock_proof::chain::ChainAssetLockProof; +use dpp::validation::SimpleConsensusValidationResult; +use dpp::version::PlatformVersion; +use drive::grovedb::TransactionArg; +use crate::execution::validation::state_transition::common::asset_lock::proof::validate::AssetLockProofValidation; +use crate::execution::validation::state_transition::common::asset_lock::proof::verify_is_not_spent::AssetLockProofVerifyIsNotSpent; + +// TODO: Versioning +impl AssetLockProofValidation for ChainAssetLockProof { + fn validate( + &self, + platform_ref: &PlatformRef, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result { + let mut result = SimpleConsensusValidationResult::default(); + + if platform_ref.block_info.core_height < self.core_chain_locked_height { + result.add_error(InvalidAssetLockProofCoreChainHeightError::new( + self.core_chain_locked_height, + platform_ref.block_info.core_height, + )); + + return Ok(result); + } + + self.verify_is_not_spent(platform_ref, transaction, platform_version) + } +} diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/validate/instant/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/validate/instant/mod.rs new file mode 100644 index 00000000000..ce09c729f6e --- /dev/null +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/validate/instant/mod.rs @@ -0,0 +1,42 @@ +use crate::error::execution::ExecutionError; +use crate::error::Error; +use crate::platform_types::platform::PlatformRef; +use crate::rpc::core::CoreRPCLike; +use crate::rpc::signature::CoreSignatureVerification; +use dpp::consensus::basic::identity::{ + IdentityAssetLockTransactionOutPointAlreadyExistsError, + InvalidInstantAssetLockProofSignatureError, +}; +use dpp::identity::state_transition::asset_lock_proof::InstantAssetLockProof; +use dpp::platform_value::Bytes36; +use dpp::validation::SimpleConsensusValidationResult; +use dpp::version::PlatformVersion; +use drive::grovedb::TransactionArg; +use crate::execution::validation::state_transition::common::asset_lock::proof::validate::AssetLockProofValidation; +use crate::execution::validation::state_transition::common::asset_lock::proof::verify_is_not_spent::AssetLockProofVerifyIsNotSpent; + +// TODO: Versioning +impl AssetLockProofValidation for InstantAssetLockProof { + fn validate( + &self, + platform_ref: &PlatformRef, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result { + let mut result = SimpleConsensusValidationResult::default(); + + // Verify instant lock signature with Core + + let is_instant_lock_signature_valid = self + .instant_lock() + .verify_signature(platform_ref.core_rpc, platform_ref.block_info.core_height)?; + + if !is_instant_lock_signature_valid { + result.add_error(InvalidInstantAssetLockProofSignatureError::new()); + + return Ok(result); + } + + self.verify_is_not_spent(platform_ref, transaction, platform_version) + } +} diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/validate/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/validate/mod.rs new file mode 100644 index 00000000000..596657faccd --- /dev/null +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/validate/mod.rs @@ -0,0 +1,52 @@ +mod chain; +mod instant; + +use crate::error::Error; +use crate::platform_types::platform::PlatformRef; +use crate::rpc::core::CoreRPCLike; +use dpp::prelude::AssetLockProof; +use dpp::validation::SimpleConsensusValidationResult; +use dpp::version::PlatformVersion; +use drive::grovedb::TransactionArg; + +/// A trait for validating state transitions within a blockchain. +pub trait AssetLockProofValidation { + /// Validates the state transition by analyzing the changes in the platform state after applying the transaction. + /// + /// # Arguments + /// + /// * `platform` - A reference to the platform containing the state data. + /// * `tx` - The transaction argument to be applied. + /// + /// # Type Parameters + /// + /// * `C: CoreRPCLike` - A type constraint indicating that C should implement `CoreRPCLike`. + /// + /// # Returns + /// + /// * `Result` - A result with either a SimpleConsensusValidationResult or an Error. + fn validate( + &self, + platform_ref: &PlatformRef, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result; +} + +impl AssetLockProofValidation for AssetLockProof { + fn validate( + &self, + platform_ref: &PlatformRef, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result { + match self { + AssetLockProof::Instant(proof) => { + proof.validate(platform_ref, transaction, platform_version) + } + AssetLockProof::Chain(proof) => { + proof.validate(platform_ref, transaction, platform_version) + } + } + } +} diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/chain/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/verify_is_not_spent/chain/mod.rs similarity index 73% rename from packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/chain/mod.rs rename to packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/verify_is_not_spent/chain/mod.rs index d9a4f553836..ee988de1722 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/chain/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/verify_is_not_spent/chain/mod.rs @@ -1,21 +1,19 @@ use crate::error::execution::ExecutionError; use crate::error::Error; -use crate::execution::validation::state_transition::common::asset_lock::proof::AssetLockProofStateValidation; use crate::platform_types::platform::PlatformRef; -use crate::rpc::core::CoreRPCLike; use dpp::consensus::basic::identity::{ IdentityAssetLockTransactionOutPointAlreadyExistsError, - InvalidAssetLockProofCoreChainHeightError, }; use dpp::identity::state_transition::asset_lock_proof::chain::ChainAssetLockProof; use dpp::platform_value::Bytes36; use dpp::validation::SimpleConsensusValidationResult; use dpp::version::PlatformVersion; use drive::grovedb::TransactionArg; +use crate::execution::validation::state_transition::common::asset_lock::proof::verify_is_not_spent::AssetLockProofVerifyIsNotSpent; // TODO: Versioning -impl AssetLockProofStateValidation for ChainAssetLockProof { - fn validate_state( +impl AssetLockProofVerifyIsNotSpent for ChainAssetLockProof { + fn verify_is_not_spent( &self, platform_ref: &PlatformRef, transaction: TransactionArg, @@ -23,15 +21,6 @@ impl AssetLockProofStateValidation for ChainAssetLockProof { ) -> Result { let mut result = SimpleConsensusValidationResult::default(); - if platform_ref.block_info.core_height < self.core_chain_locked_height { - result.add_error(InvalidAssetLockProofCoreChainHeightError::new( - self.core_chain_locked_height, - platform_ref.block_info.core_height, - )); - - return Ok(result); - } - // Make sure that asset lock isn't spent yet let outpoint_bytes = self.out_point.try_into().map_err(|_e| { diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/instant/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/verify_is_not_spent/instant/mod.rs similarity index 73% rename from packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/instant/mod.rs rename to packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/verify_is_not_spent/instant/mod.rs index 4f193c2c33b..cff397074bb 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/instant/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/verify_is_not_spent/instant/mod.rs @@ -1,22 +1,20 @@ use crate::error::execution::ExecutionError; use crate::error::Error; -use crate::execution::validation::state_transition::common::asset_lock::proof::AssetLockProofStateValidation; use crate::platform_types::platform::PlatformRef; use crate::rpc::core::CoreRPCLike; -use crate::rpc::signature::CoreSignatureVerification; use dpp::consensus::basic::identity::{ IdentityAssetLockTransactionOutPointAlreadyExistsError, - InvalidInstantAssetLockProofSignatureError, }; use dpp::identity::state_transition::asset_lock_proof::InstantAssetLockProof; use dpp::platform_value::Bytes36; use dpp::validation::SimpleConsensusValidationResult; use dpp::version::PlatformVersion; use drive::grovedb::TransactionArg; +use crate::execution::validation::state_transition::common::asset_lock::proof::verify_is_not_spent::AssetLockProofVerifyIsNotSpent; // TODO: Versioning -impl AssetLockProofStateValidation for InstantAssetLockProof { - fn validate_state( +impl AssetLockProofVerifyIsNotSpent for InstantAssetLockProof { + fn verify_is_not_spent( &self, platform_ref: &PlatformRef, transaction: TransactionArg, @@ -51,18 +49,6 @@ impl AssetLockProofStateValidation for InstantAssetLockProof { )) } - // Verify instant lock signature with Core - - let is_instant_lock_signature_valid = self - .instant_lock() - .verify_signature(platform_ref.core_rpc, platform_ref.block_info.core_height)?; - - if !is_instant_lock_signature_valid { - result.add_error(InvalidInstantAssetLockProofSignatureError::new()); - - return Ok(result); - } - Ok(result) } } diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/verify_is_not_spent/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/verify_is_not_spent/mod.rs new file mode 100644 index 00000000000..9a864e4492f --- /dev/null +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/common/asset_lock/proof/verify_is_not_spent/mod.rs @@ -0,0 +1,53 @@ +mod chain; +mod instant; + +use crate::error::Error; +use crate::platform_types::platform::PlatformRef; +use crate::rpc::core::CoreRPCLike; +use dpp::prelude::AssetLockProof; +use dpp::validation::SimpleConsensusValidationResult; +use dpp::version::PlatformVersion; +use drive::grovedb::TransactionArg; + +/// A trait for validating that an asset lock is not spent +pub trait AssetLockProofVerifyIsNotSpent { + /// Validates that the asset lock was not spent + /// + /// # Arguments + /// + /// * `platform` - A reference to the platform containing the state data. + /// * `transaction` - The database transaction to check on, can be None. + /// * `platform_version` - The platform version that we are using + /// + /// # Type Parameters + /// + /// * `C: CoreRPCLike` - A type constraint indicating that C should implement `CoreRPCLike`. + /// + /// # Returns + /// + /// * `Result` - A result with either a SimpleConsensusValidationResult or an Error. + fn verify_is_not_spent( + &self, + platform_ref: &PlatformRef, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result; +} + +impl AssetLockProofVerifyIsNotSpent for AssetLockProof { + fn verify_is_not_spent( + &self, + platform_ref: &PlatformRef, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result { + match self { + AssetLockProof::Instant(proof) => { + proof.verify_is_not_spent(platform_ref, transaction, platform_version) + } + AssetLockProof::Chain(proof) => { + proof.verify_is_not_spent(platform_ref, transaction, platform_version) + } + } + } +} diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/mod.rs index 6e781129222..0ef51bf5856 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/mod.rs @@ -1,3 +1,4 @@ +pub(crate) mod check_tx_verification; mod common; pub(crate) mod processor; mod state_transitions; diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/processor/v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/processor/v0/mod.rs index 3d100cb4943..ac909f52e09 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/processor/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/processor/v0/mod.rs @@ -143,6 +143,11 @@ pub(crate) trait StateTransitionSignatureValidationV0 { execution_context: &mut StateTransitionExecutionContext, platform_version: &PlatformVersion, ) -> Result>, Error>; + + /// This means we should transform into the action before validation of the identity and signatures + fn requires_state_to_validate_identity_and_signatures(&self) -> bool { + false + } } /// A trait for validating state transitions within a blockchain. @@ -163,6 +168,11 @@ pub(crate) trait StateTransitionStructureValidationV0 { action: Option<&StateTransitionAction>, protocol_version: u32, ) -> Result; + + /// This means we should transform into the action before validation of the structure + fn requires_state_to_validate_structure(&self) -> bool { + false + } } /// A trait for validating state transitions within a blockchain. @@ -226,6 +236,11 @@ impl StateTransitionStructureValidationV0 for StateTransition { } } } + + /// This means we should transform into the action before validation of the structure + fn requires_state_to_validate_structure(&self) -> bool { + matches!(self, StateTransition::DocumentsBatch(_)) + } } impl StateTransitionSignatureValidationV0 for StateTransition { @@ -371,6 +386,11 @@ impl StateTransitionSignatureValidationV0 for StateTransition { } } } + + /// This means we should transform into the action before validation of the identity and signatures + fn requires_state_to_validate_identity_and_signatures(&self) -> bool { + matches!(self, StateTransition::DocumentsBatch(_)) + } } impl StateTransitionStateValidationV0 for StateTransition { diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/documents_batch/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/documents_batch/mod.rs index 1158428f2ee..379d2a995e2 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/documents_batch/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/documents_batch/mod.rs @@ -95,6 +95,10 @@ impl StateTransitionStructureValidationV0 for DocumentsBatchTransition { })), } } + + fn requires_state_to_validate_structure(&self) -> bool { + true + } } impl StateTransitionStateValidationV0 for DocumentsBatchTransition { diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_create/state/v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_create/state/v0/mod.rs index 1de5fa9aebb..ef090ac682b 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_create/state/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_create/state/v0/mod.rs @@ -19,13 +19,16 @@ use dpp::version::PlatformVersion; use drive::state_transition_action::identity::identity_create::IdentityCreateTransitionAction; use drive::state_transition_action::StateTransitionAction; -use drive::grovedb::TransactionArg; -use dpp::version::DefaultForPlatformVersion; use crate::error::execution::ExecutionError; -use crate::execution::types::execution_operation::ExecutionOperation; use crate::execution::types::execution_operation::signature_verification_operation::SignatureVerificationOperation; -use crate::execution::types::state_transition_execution_context::{StateTransitionExecutionContext, StateTransitionExecutionContextMethodsV0}; -use crate::execution::validation::state_transition::common::asset_lock::proof::AssetLockProofStateValidation; +use crate::execution::types::execution_operation::ExecutionOperation; +use crate::execution::types::state_transition_execution_context::{ + StateTransitionExecutionContext, StateTransitionExecutionContextMethodsV0, +}; +use crate::execution::validation::state_transition::common::asset_lock::proof::validate::AssetLockProofValidation; +use dpp::version::DefaultForPlatformVersion; +use drive::grovedb::TransactionArg; + use crate::execution::validation::state_transition::common::asset_lock::transaction::fetch_asset_lock_transaction_output_sync::fetch_asset_lock_transaction_output_sync; use crate::execution::validation::state_transition::common::validate_unique_identity_public_key_hashes_in_state::validate_unique_identity_public_key_hashes_in_state; @@ -72,8 +75,7 @@ impl IdentityCreateStateTransitionStateValidationV0 for IdentityCreateTransition } // Validate asset lock proof state - //todo: add costs for state validation - validation_result.merge(self.asset_lock_proof().validate_state( + validation_result.merge(self.asset_lock_proof().validate( platform, tx, platform_version, diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_top_up/state/v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_top_up/state/v0/mod.rs index 9e7caca1555..66ece6b365d 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_top_up/state/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_top_up/state/v0/mod.rs @@ -17,12 +17,13 @@ use dpp::version::PlatformVersion; use drive::state_transition_action::identity::identity_topup::IdentityTopUpTransitionAction; use drive::state_transition_action::StateTransitionAction; -use drive::grovedb::TransactionArg; use crate::error::execution::ExecutionError; +use drive::grovedb::TransactionArg; + use crate::execution::types::execution_operation::ExecutionOperation; use crate::execution::types::execution_operation::signature_verification_operation::SignatureVerificationOperation; use crate::execution::types::state_transition_execution_context::{StateTransitionExecutionContext, StateTransitionExecutionContextMethodsV0}; -use crate::execution::validation::state_transition::common::asset_lock::proof::AssetLockProofStateValidation; +use crate::execution::validation::state_transition::common::asset_lock::proof::validate::AssetLockProofValidation; use crate::execution::validation::state_transition::common::asset_lock::transaction::fetch_asset_lock_transaction_output_sync::fetch_asset_lock_transaction_output_sync; pub(in crate::execution::validation::state_transition::state_transitions::identity_top_up) trait IdentityTopUpStateTransitionStateValidationV0 @@ -53,7 +54,7 @@ impl IdentityTopUpStateTransitionStateValidationV0 for IdentityTopUpTransition { ) -> Result, Error> { let mut validation_result = ConsensusValidationResult::::default(); - validation_result.merge(self.asset_lock_proof().validate_state( + validation_result.merge(self.asset_lock_proof().validate( platform, tx, platform_version, diff --git a/packages/rs-drive/src/drive/batch/transitions/document/document_create_transition.rs b/packages/rs-drive/src/drive/batch/transitions/document/document_create_transition.rs index 08e1e360bb9..68892e8eef5 100644 --- a/packages/rs-drive/src/drive/batch/transitions/document/document_create_transition.rs +++ b/packages/rs-drive/src/drive/batch/transitions/document/document_create_transition.rs @@ -30,17 +30,16 @@ impl DriveHighLevelDocumentOperationConverter for DocumentCreateTransitionAction let storage_flags = StorageFlags::new_single_epoch(epoch.index, Some(owner_id.to_buffer())); - let mut drive_operations = vec![]; - drive_operations.push(DocumentOperation(DocumentOperationType::AddDocument { - owned_document_info: OwnedDocumentInfo { - document_info: DocumentOwnedInfo((document, Some(Cow::Owned(storage_flags)))), - owner_id: Some(owner_id.into_buffer()), + Ok(vec![DocumentOperation( + DocumentOperationType::AddDocument { + owned_document_info: OwnedDocumentInfo { + document_info: DocumentOwnedInfo((document, Some(Cow::Owned(storage_flags)))), + owner_id: Some(owner_id.into_buffer()), + }, + contract_id: data_contract_id, + document_type_name: Cow::Owned(document_type_name), + override_document: false, }, - contract_id: data_contract_id, - document_type_name: Cow::Owned(document_type_name), - override_document: false, - })); - - Ok(drive_operations) + )]) } } diff --git a/packages/rs-platform-version/src/version/drive_abci_versions.rs b/packages/rs-platform-version/src/version/drive_abci_versions.rs index a3e9d9a4420..36c5af53083 100644 --- a/packages/rs-platform-version/src/version/drive_abci_versions.rs +++ b/packages/rs-platform-version/src/version/drive_abci_versions.rs @@ -75,6 +75,7 @@ pub struct DriveAbciMethodVersions { pub struct DriveAbciValidationVersions { pub state_transitions: DriveAbciStateTransitionValidationVersions, pub process_state_transition: FeatureVersion, + pub state_transition_to_execution_event_for_check_tx: FeatureVersion, } #[derive(Clone, Debug, Default)] diff --git a/packages/rs-platform-version/src/version/mocks/v2_test.rs b/packages/rs-platform-version/src/version/mocks/v2_test.rs index 481b2b0a6a3..a1a8d821439 100644 --- a/packages/rs-platform-version/src/version/mocks/v2_test.rs +++ b/packages/rs-platform-version/src/version/mocks/v2_test.rs @@ -619,6 +619,7 @@ pub(crate) const TEST_PLATFORM_V2: PlatformVersion = PlatformVersion { }, }, process_state_transition: 0, + state_transition_to_execution_event_for_check_tx: 0, }, query: DriveAbciQueryVersions { response_metadata: 0, diff --git a/packages/rs-platform-version/src/version/mocks/v3_test.rs b/packages/rs-platform-version/src/version/mocks/v3_test.rs index 846fbf9b013..4a87ecde428 100644 --- a/packages/rs-platform-version/src/version/mocks/v3_test.rs +++ b/packages/rs-platform-version/src/version/mocks/v3_test.rs @@ -619,6 +619,7 @@ pub(crate) const TEST_PLATFORM_V3: PlatformVersion = PlatformVersion { }, }, process_state_transition: 0, + state_transition_to_execution_event_for_check_tx: 0, }, query: DriveAbciQueryVersions { response_metadata: 0, diff --git a/packages/rs-platform-version/src/version/v1.rs b/packages/rs-platform-version/src/version/v1.rs index f53927ca66f..cc0fc782c6a 100644 --- a/packages/rs-platform-version/src/version/v1.rs +++ b/packages/rs-platform-version/src/version/v1.rs @@ -616,6 +616,7 @@ pub(super) const PLATFORM_V1: PlatformVersion = PlatformVersion { }, }, process_state_transition: 0, + state_transition_to_execution_event_for_check_tx: 0, }, query: DriveAbciQueryVersions { response_metadata: 0,