From 1739623b141bd78f12141310504ade3a10a2ff59 Mon Sep 17 00:00:00 2001 From: QuantumExplorer Date: Tue, 16 Jul 2024 18:18:32 +0700 Subject: [PATCH] chore(platform)!: disable credit withdrawals in V1 (#1961) --- .../src/errors/consensus/basic/basic_error.rs | 7 +- .../rs-dpp/src/errors/consensus/basic/mod.rs | 2 + .../basic/unsupported_feature_error.rs | 45 ++++++++ packages/rs-dpp/src/errors/consensus/codes.rs | 5 +- .../methods/mod.rs | 3 + .../methods/v0/mod.rs | 6 +- .../v0/v0_methods.rs | 36 ++++--- .../identity_credit_withdrawal/mod.rs | 102 +++++++++++++++++- .../structure/mod.rs | 1 + .../structure/v0/mod.rs | 77 ++++--------- .../structure/v1/mod.rs | 71 ++++++++++++ .../state_transition/state_transitions/mod.rs | 82 +++++++++++++- .../tests/strategy_tests/main.rs | 3 +- .../v0/mod.rs | 2 +- .../transition/withdraw_from_identity.rs | 1 + .../src/errors/consensus/consensus_error.rs | 5 +- 16 files changed, 361 insertions(+), 87 deletions(-) create mode 100644 packages/rs-dpp/src/errors/consensus/basic/unsupported_feature_error.rs create mode 100644 packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_withdrawal/structure/v1/mod.rs diff --git a/packages/rs-dpp/src/errors/consensus/basic/basic_error.rs b/packages/rs-dpp/src/errors/consensus/basic/basic_error.rs index 68c782268c6..4a8a29951d8 100644 --- a/packages/rs-dpp/src/errors/consensus/basic/basic_error.rs +++ b/packages/rs-dpp/src/errors/consensus/basic/basic_error.rs @@ -58,7 +58,9 @@ use crate::consensus::basic::state_transition::{ InvalidStateTransitionTypeError, MissingStateTransitionTypeError, StateTransitionMaxSizeExceededError, }; -use crate::consensus::basic::{IncompatibleProtocolVersionError, UnsupportedProtocolVersionError}; +use crate::consensus::basic::{ + IncompatibleProtocolVersionError, UnsupportedFeatureError, UnsupportedProtocolVersionError, +}; use crate::consensus::ConsensusError; use crate::consensus::basic::overflow_error::OverflowError; @@ -382,6 +384,9 @@ pub enum BasicError { #[error(transparent)] OverflowError(OverflowError), + + #[error(transparent)] + UnsupportedFeatureError(UnsupportedFeatureError), } impl From for ConsensusError { diff --git a/packages/rs-dpp/src/errors/consensus/basic/mod.rs b/packages/rs-dpp/src/errors/consensus/basic/mod.rs index f0553e49ba5..b96be4629b0 100644 --- a/packages/rs-dpp/src/errors/consensus/basic/mod.rs +++ b/packages/rs-dpp/src/errors/consensus/basic/mod.rs @@ -1,5 +1,6 @@ pub use basic_error::*; pub use incompatible_protocol_version_error::*; +pub use unsupported_feature_error::*; pub use unsupported_protocol_version_error::*; pub use unsupported_version_error::*; @@ -18,5 +19,6 @@ pub mod json_schema_compilation_error; pub mod json_schema_error; pub mod overflow_error; pub mod state_transition; +pub mod unsupported_feature_error; pub mod unsupported_version_error; pub mod value_error; diff --git a/packages/rs-dpp/src/errors/consensus/basic/unsupported_feature_error.rs b/packages/rs-dpp/src/errors/consensus/basic/unsupported_feature_error.rs new file mode 100644 index 00000000000..40c7dc217b7 --- /dev/null +++ b/packages/rs-dpp/src/errors/consensus/basic/unsupported_feature_error.rs @@ -0,0 +1,45 @@ +use crate::consensus::basic::BasicError; +use crate::consensus::ConsensusError; +use crate::errors::ProtocolError; +use bincode::{Decode, Encode}; +use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize}; + +use thiserror::Error; + +#[derive( + Error, Debug, Clone, PartialEq, Eq, Encode, Decode, PlatformSerialize, PlatformDeserialize, +)] +#[error("feature {feature_name} is not supported in version {current_protocol_version}")] +#[platform_serialize(unversioned)] +pub struct UnsupportedFeatureError { + /* + + DO NOT CHANGE ORDER OF FIELDS WITHOUT INTRODUCING OF NEW VERSION + + */ + feature_name: String, + current_protocol_version: u32, +} + +impl UnsupportedFeatureError { + pub fn new(feature_name: String, current_protocol_version: u32) -> Self { + Self { + feature_name, + current_protocol_version, + } + } + + pub fn feature(&self) -> &String { + &self.feature_name + } + + pub fn current_protocol_version(&self) -> u32 { + self.current_protocol_version + } +} + +impl From for ConsensusError { + fn from(err: UnsupportedFeatureError) -> Self { + Self::BasicError(BasicError::UnsupportedFeatureError(err)) + } +} diff --git a/packages/rs-dpp/src/errors/consensus/codes.rs b/packages/rs-dpp/src/errors/consensus/codes.rs index 4e271ebadd4..793a366e119 100644 --- a/packages/rs-dpp/src/errors/consensus/codes.rs +++ b/packages/rs-dpp/src/errors/consensus/codes.rs @@ -36,6 +36,7 @@ impl ErrorWithCode for BasicError { Self::UnsupportedProtocolVersionError(_) => 10003, Self::IncompatibleProtocolVersionError(_) => 10004, Self::VersionError(_) => 10005, + Self::UnsupportedFeatureError(_) => 10006, // Structure Errors: 10100-10199 #[cfg(feature = "json-schema-validation")] @@ -93,8 +94,9 @@ impl ErrorWithCode for BasicError { Self::UnknownTransferableTypeError { .. } => 10243, Self::UnknownTradeModeError { .. } => 10244, Self::UnknownDocumentCreationRestrictionModeError { .. } => 10245, - Self::ContractError(DataContractError::RegexError(_)) => 10247, Self::IncompatibleDocumentTypeSchemaError { .. } => 10246, + Self::ContractError(DataContractError::RegexError(_)) => 10247, + Self::ContestedUniqueIndexOnMutableDocumentTypeError(_) => 10248, // Document Errors: 10400-10499 Self::DataContractNotPresentError { .. } => 10400, @@ -156,7 +158,6 @@ impl ErrorWithCode for BasicError { // General Errors 10700-10799 Self::OverflowError(_) => 10700, - Self::ContestedUniqueIndexOnMutableDocumentTypeError(_) => 10701, } } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/methods/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/methods/mod.rs index 3aaef91f3c4..a149b7d0512 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/methods/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/methods/mod.rs @@ -12,6 +12,7 @@ use crate::identity::Identity; #[cfg(feature = "state-transition-signing")] use crate::identity::core_script::CoreScript; +use crate::identity::IdentityPublicKey; #[cfg(feature = "state-transition-signing")] use crate::prelude::{IdentityNonce, UserFeeIncrease}; #[cfg(feature = "state-transition-signing")] @@ -29,6 +30,7 @@ impl IdentityCreditWithdrawalTransitionMethodsV0 for IdentityCreditWithdrawalTra #[cfg(feature = "state-transition-signing")] fn try_from_identity( identity: &Identity, + withdrawal_key_to_use: Option<&IdentityPublicKey>, output_script: CoreScript, amount: u64, pooling: Pooling, @@ -47,6 +49,7 @@ impl IdentityCreditWithdrawalTransitionMethodsV0 for IdentityCreditWithdrawalTra ) { 0 => Ok(IdentityCreditWithdrawalTransitionV0::try_from_identity( identity, + withdrawal_key_to_use, output_script, amount, pooling, diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/methods/v0/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/methods/v0/mod.rs index c739eadb0eb..6fd9bbeccec 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/methods/v0/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/methods/v0/mod.rs @@ -4,6 +4,7 @@ use crate::identity::core_script::CoreScript; use crate::identity::signer::Signer; #[cfg(feature = "state-transition-signing")] use crate::identity::Identity; +use crate::identity::IdentityPublicKey; #[cfg(feature = "state-transition-signing")] use crate::prelude::{IdentityNonce, UserFeeIncrease}; #[cfg(feature = "state-transition-signing")] @@ -20,6 +21,7 @@ pub trait IdentityCreditWithdrawalTransitionMethodsV0 { #[cfg(feature = "state-transition-signing")] fn try_from_identity( identity: &Identity, + withdrawal_key_to_use: Option<&IdentityPublicKey>, output_script: CoreScript, amount: u64, pooling: Pooling, @@ -27,8 +29,8 @@ pub trait IdentityCreditWithdrawalTransitionMethodsV0 { user_fee_increase: UserFeeIncrease, signer: S, nonce: IdentityNonce, - _platform_version: &PlatformVersion, - _version: Option, + platform_version: &PlatformVersion, + version: Option, ) -> Result; /// Get State Transition Type diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/v0/v0_methods.rs index 53200c82e9a..65e63b3bd8f 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/v0/v0_methods.rs @@ -4,26 +4,27 @@ use crate::identity::accessors::IdentityGettersV0; use crate::identity::core_script::CoreScript; #[cfg(feature = "state-transition-signing")] use crate::identity::signer::Signer; +use crate::identity::IdentityPublicKey; #[cfg(feature = "state-transition-signing")] use crate::identity::{Identity, KeyType, Purpose, SecurityLevel}; #[cfg(feature = "state-transition-signing")] use crate::prelude::{IdentityNonce, UserFeeIncrease}; -#[cfg(feature = "state-transition-signing")] -use crate::ProtocolError; -#[cfg(feature = "state-transition-signing")] -use platform_version::version::{FeatureVersion, PlatformVersion}; - use crate::state_transition::identity_credit_withdrawal_transition::methods::IdentityCreditWithdrawalTransitionMethodsV0; use crate::state_transition::identity_credit_withdrawal_transition::v0::IdentityCreditWithdrawalTransitionV0; #[cfg(feature = "state-transition-signing")] use crate::state_transition::{GetDataContractSecurityLevelRequirementFn, StateTransition}; #[cfg(feature = "state-transition-signing")] use crate::withdrawal::Pooling; +#[cfg(feature = "state-transition-signing")] +use crate::ProtocolError; +#[cfg(feature = "state-transition-signing")] +use platform_version::version::{FeatureVersion, PlatformVersion}; impl IdentityCreditWithdrawalTransitionMethodsV0 for IdentityCreditWithdrawalTransitionV0 { #[cfg(feature = "state-transition-signing")] fn try_from_identity( identity: &Identity, + withdrawal_key_to_use: Option<&IdentityPublicKey>, output_script: CoreScript, amount: u64, pooling: Pooling, @@ -47,17 +48,20 @@ impl IdentityCreditWithdrawalTransitionMethodsV0 for IdentityCreditWithdrawalTra } .into(); - let identity_public_key = identity - .get_first_public_key_matching( - Purpose::TRANSFER, - SecurityLevel::full_range().into(), - KeyType::all_key_types().into(), - ) - .ok_or( - ProtocolError::DesiredKeyWithTypePurposeSecurityLevelMissing( - "no withdrawal public key".to_string(), - ), - )?; + let identity_public_key = match withdrawal_key_to_use { + Some(key) => key, + None => identity + .get_first_public_key_matching( + Purpose::TRANSFER, + SecurityLevel::full_range().into(), + KeyType::all_key_types().into(), + ) + .ok_or_else(|| { + ProtocolError::DesiredKeyWithTypePurposeSecurityLevelMissing( + "no withdrawal public key".to_string(), + ) + })?, + }; transition.sign_external( identity_public_key, diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_withdrawal/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_withdrawal/mod.rs index bc0da27d448..6ea4eafed4a 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_withdrawal/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_withdrawal/mod.rs @@ -19,7 +19,7 @@ use crate::rpc::core::CoreRPCLike; use crate::execution::validation::state_transition::identity_credit_withdrawal::state::v0::IdentityCreditWithdrawalStateTransitionStateValidationV0; use crate::execution::validation::state_transition::identity_credit_withdrawal::structure::v0::IdentityCreditWithdrawalStateTransitionStructureValidationV0; - +use crate::execution::validation::state_transition::identity_credit_withdrawal::structure::v1::IdentityCreditWithdrawalStateTransitionStructureValidationV1; use crate::execution::validation::state_transition::processor::v0::{ StateTransitionBasicStructureValidationV0, StateTransitionStateValidationV0, }; @@ -67,9 +67,10 @@ impl StateTransitionBasicStructureValidationV0 for IdentityCreditWithdrawalTrans .basic_structure { Some(0) => { - // There is nothing expensive here - self.validate_basic_structure_v0() + // Returns not supported + self.validate_basic_structure_v0(platform_version) } + Some(1) => self.validate_basic_structure_v1(), Some(version) => Err(Error::Execution(ExecutionError::UnknownVersionMismatch { method: "identity credit withdrawal transition: validate_basic_structure" .to_string(), @@ -113,3 +114,98 @@ impl StateTransitionStateValidationV0 for IdentityCreditWithdrawalTransition { } } } + +#[cfg(test)] +mod tests { + use crate::config::{PlatformConfig, PlatformTestConfig}; + use crate::execution::validation::state_transition::tests::{ + fast_forward_to_block, setup_identity, + setup_identity_with_withdrawal_key_and_system_credits, + }; + use crate::platform_types::state_transitions_processing_result::StateTransitionExecutionResult; + use crate::test::helpers::setup::TestPlatformBuilder; + use assert_matches::assert_matches; + use dpp::block::block_info::BlockInfo; + use dpp::consensus::basic::BasicError; + use dpp::consensus::ConsensusError; + use dpp::dash_to_credits; + use dpp::identity::core_script::CoreScript; + use dpp::serialization::PlatformSerializable; + use dpp::state_transition::identity_credit_withdrawal_transition::methods::IdentityCreditWithdrawalTransitionMethodsV0; + use dpp::state_transition::identity_credit_withdrawal_transition::IdentityCreditWithdrawalTransition; + use dpp::withdrawal::Pooling; + use platform_version::version::PlatformVersion; + use rand::prelude::StdRng; + use rand::{Rng, SeedableRng}; + + #[test] + fn test_identity_credit_withdrawal_is_disabled_on_release() { + let platform_version = PlatformVersion::first(); + let platform_config = PlatformConfig { + testing_configs: PlatformTestConfig { + disable_instant_lock_signature_verification: true, + ..Default::default() + }, + ..Default::default() + }; + + let mut rng = StdRng::seed_from_u64(567); + + let mut platform = TestPlatformBuilder::new() + .with_config(platform_config) + .build_with_mock_rpc() + .set_initial_state_structure(); + + fast_forward_to_block(&platform, 1_200_000_000, 900, 1); //next epoch + + let (identity, signer, _, withdrawal_key) = + setup_identity_with_withdrawal_key_and_system_credits( + &mut platform, + rng.gen(), + dash_to_credits!(0.5), + ); + + let platform_state = platform.state.load(); + + let withdrawal_amount = dash_to_credits!(0.1); + + let credit_withdrawal_transition = IdentityCreditWithdrawalTransition::try_from_identity( + &identity, + Some(&withdrawal_key), + CoreScript::random_p2pkh(&mut rng), + withdrawal_amount, + Pooling::Never, + 1, + 0, + signer, + 2, + platform_version, + None, + ) + .expect("expected a credit withdrawal transition"); + + let credit_withdrawal_transition_serialized_transition = credit_withdrawal_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![credit_withdrawal_transition_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::UnpaidConsensusError( + ConsensusError::BasicError(BasicError::UnsupportedFeatureError(_)) + )] + ); + } +} diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_withdrawal/structure/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_withdrawal/structure/mod.rs index 9a1925de7fc..008be12cc67 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_withdrawal/structure/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_withdrawal/structure/mod.rs @@ -1 +1,2 @@ pub(crate) mod v0; +pub(crate) mod v1; diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_withdrawal/structure/v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_withdrawal/structure/v0/mod.rs index e06dbfd8288..d69627cf842 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_withdrawal/structure/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_withdrawal/structure/v0/mod.rs @@ -1,71 +1,30 @@ -use dpp::consensus::basic::identity::{ - InvalidIdentityCreditWithdrawalTransitionAmountError, - InvalidIdentityCreditWithdrawalTransitionCoreFeeError, - InvalidIdentityCreditWithdrawalTransitionOutputScriptError, - NotImplementedIdentityCreditWithdrawalTransitionPoolingError, -}; -use dpp::consensus::ConsensusError; - use crate::error::Error; -use dpp::state_transition::identity_credit_withdrawal_transition::accessors::IdentityCreditWithdrawalTransitionAccessorsV0; -use dpp::state_transition::identity_credit_withdrawal_transition::v0::{ - MIN_CORE_FEE_PER_BYTE, MIN_WITHDRAWAL_AMOUNT, -}; +use dpp::consensus::basic::{BasicError, UnsupportedFeatureError}; +use dpp::consensus::ConsensusError; use dpp::state_transition::identity_credit_withdrawal_transition::IdentityCreditWithdrawalTransition; -use dpp::util::is_fibonacci_number::is_fibonacci_number; use dpp::validation::SimpleConsensusValidationResult; -use dpp::withdrawal::Pooling; +use dpp::version::PlatformVersion; pub(in crate::execution::validation::state_transition::state_transitions::identity_credit_withdrawal) trait IdentityCreditWithdrawalStateTransitionStructureValidationV0 { - fn validate_basic_structure_v0(&self) -> Result; + fn validate_basic_structure_v0(&self, platform_version: &PlatformVersion) -> Result; } impl IdentityCreditWithdrawalStateTransitionStructureValidationV0 for IdentityCreditWithdrawalTransition { - fn validate_basic_structure_v0(&self) -> Result { - let mut result = SimpleConsensusValidationResult::default(); - - if self.amount() < MIN_WITHDRAWAL_AMOUNT { - result.add_error(ConsensusError::from( - InvalidIdentityCreditWithdrawalTransitionAmountError::new( - self.amount(), - MIN_WITHDRAWAL_AMOUNT, - ), - )); - } - - // currently we do not support pooling, so we must validate that pooling is `Never` - - if self.pooling() != Pooling::Never { - result.add_error( - NotImplementedIdentityCreditWithdrawalTransitionPoolingError::new( - self.pooling() as u8 - ), - ); - - return Ok(result); - } - - // validate core_fee is in fibonacci sequence - if !is_fibonacci_number(self.core_fee_per_byte() as u64) { - result.add_error(InvalidIdentityCreditWithdrawalTransitionCoreFeeError::new( - self.core_fee_per_byte(), - MIN_CORE_FEE_PER_BYTE, - )); - - return Ok(result); - } - - // validate output_script types - if !self.output_script().is_p2pkh() && !self.output_script().is_p2sh() { - result.add_error( - InvalidIdentityCreditWithdrawalTransitionOutputScriptError::new( - self.output_script().clone(), - ), - ); - } - - Ok(result) + fn validate_basic_structure_v0( + &self, + platform_version: &PlatformVersion, + ) -> Result { + // This is basically disabled, return that it is not enabled + + let error = SimpleConsensusValidationResult::new_with_error(ConsensusError::BasicError( + BasicError::UnsupportedFeatureError(UnsupportedFeatureError::new( + "identity credit withdrawals".to_string(), + platform_version.protocol_version, + )), + )); + + Ok(error) } } diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_withdrawal/structure/v1/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_withdrawal/structure/v1/mod.rs new file mode 100644 index 00000000000..b89b35655c9 --- /dev/null +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_withdrawal/structure/v1/mod.rs @@ -0,0 +1,71 @@ +use dpp::consensus::basic::identity::{ + InvalidIdentityCreditWithdrawalTransitionAmountError, + InvalidIdentityCreditWithdrawalTransitionCoreFeeError, + InvalidIdentityCreditWithdrawalTransitionOutputScriptError, + NotImplementedIdentityCreditWithdrawalTransitionPoolingError, +}; +use dpp::consensus::ConsensusError; + +use crate::error::Error; +use dpp::state_transition::identity_credit_withdrawal_transition::accessors::IdentityCreditWithdrawalTransitionAccessorsV0; +use dpp::state_transition::identity_credit_withdrawal_transition::v0::{ + MIN_CORE_FEE_PER_BYTE, MIN_WITHDRAWAL_AMOUNT, +}; +use dpp::state_transition::identity_credit_withdrawal_transition::IdentityCreditWithdrawalTransition; +use dpp::util::is_fibonacci_number::is_fibonacci_number; +use dpp::validation::SimpleConsensusValidationResult; +use dpp::withdrawal::Pooling; + +pub(in crate::execution::validation::state_transition::state_transitions::identity_credit_withdrawal) trait IdentityCreditWithdrawalStateTransitionStructureValidationV1 { + fn validate_basic_structure_v1(&self) -> Result; +} + +impl IdentityCreditWithdrawalStateTransitionStructureValidationV1 + for IdentityCreditWithdrawalTransition +{ + fn validate_basic_structure_v1(&self) -> Result { + let mut result = SimpleConsensusValidationResult::default(); + + if self.amount() < MIN_WITHDRAWAL_AMOUNT { + result.add_error(ConsensusError::from( + InvalidIdentityCreditWithdrawalTransitionAmountError::new( + self.amount(), + MIN_WITHDRAWAL_AMOUNT, + ), + )); + } + + // currently we do not support pooling, so we must validate that pooling is `Never` + + if self.pooling() != Pooling::Never { + result.add_error( + NotImplementedIdentityCreditWithdrawalTransitionPoolingError::new( + self.pooling() as u8 + ), + ); + + return Ok(result); + } + + // validate core_fee is in fibonacci sequence + if !is_fibonacci_number(self.core_fee_per_byte() as u64) { + result.add_error(InvalidIdentityCreditWithdrawalTransitionCoreFeeError::new( + self.core_fee_per_byte(), + MIN_CORE_FEE_PER_BYTE, + )); + + return Ok(result); + } + + // validate output_script types + if !self.output_script().is_p2pkh() && !self.output_script().is_p2sh() { + result.add_error( + InvalidIdentityCreditWithdrawalTransitionOutputScriptError::new( + self.output_script().clone(), + ), + ); + } + + Ok(result) + } +} diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/mod.rs index 948bde51140..a37f1fe65e8 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/mod.rs @@ -56,7 +56,7 @@ pub(crate) mod tests { use crate::test::helpers::setup::TempPlatform; use dpp::block::block_info::BlockInfo; use dpp::fee::Credits; - use dpp::identity::{Identity, IdentityPublicKey, IdentityV0}; + use dpp::identity::{Identity, IdentityPublicKey, IdentityV0, KeyType, Purpose, SecurityLevel}; use dpp::prelude::{Identifier, IdentityNonce}; use platform_version::version::PlatformVersion; use rand::prelude::StdRng; @@ -196,6 +196,86 @@ pub(crate) mod tests { (identity, signer, critical_public_key) } + pub(crate) fn setup_identity_with_withdrawal_key_and_system_credits( + platform: &mut TempPlatform, + seed: u64, + credits: Credits, + ) -> (Identity, SimpleSigner, IdentityPublicKey, IdentityPublicKey) { + let platform_version = PlatformVersion::latest(); + platform + .drive + .add_to_system_credits(credits, None, platform_version) + .expect("expected to add to system credits"); + let mut signer = SimpleSigner::default(); + + let mut rng = StdRng::seed_from_u64(seed); + + let (master_key, master_private_key) = + IdentityPublicKey::random_ecdsa_master_authentication_key_with_rng( + 0, + &mut rng, + platform_version, + ) + .expect("expected to get key pair"); + + signer.add_key(master_key.clone(), master_private_key.clone()); + + let (critical_public_key, private_key) = + IdentityPublicKey::random_ecdsa_critical_level_authentication_key_with_rng( + 1, + &mut rng, + platform_version, + ) + .expect("expected to get key pair"); + + signer.add_key(critical_public_key.clone(), private_key.clone()); + + let (withdrawal_public_key, withdrawal_private_key) = + IdentityPublicKey::random_key_with_known_attributes( + 2, + &mut rng, + Purpose::TRANSFER, + SecurityLevel::CRITICAL, + KeyType::ECDSA_SECP256K1, + None, + platform_version, + ) + .expect("expected to get key pair"); + + signer.add_key( + withdrawal_public_key.clone(), + withdrawal_private_key.clone(), + ); + + let identity: Identity = IdentityV0 { + id: Identifier::random_with_rng(&mut rng), + public_keys: BTreeMap::from([ + (0, master_key.clone()), + (1, critical_public_key.clone()), + (2, withdrawal_public_key.clone()), + ]), + balance: credits, + revision: 0, + } + .into(); + + // We just add this identity to the system first + + platform + .drive + .add_new_identity( + identity.clone(), + false, + &BlockInfo::default(), + true, + None, + platform_version, + ) + .expect("expected to add a new identity"); + + (identity, signer, critical_public_key, withdrawal_public_key) + } + pub(crate) fn process_state_transitions( platform: &TempPlatform, state_transitions: &[StateTransition], diff --git a/packages/rs-drive-abci/tests/strategy_tests/main.rs b/packages/rs-drive-abci/tests/strategy_tests/main.rs index f1deb92d5e7..6050a080321 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/main.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/main.rs @@ -2950,6 +2950,7 @@ mod tests { } #[test] + #[ignore] fn run_chain_top_up_and_withdraw_from_identities() { let platform_version = PlatformVersion::latest(); let strategy = NetworkStrategy { @@ -2998,7 +2999,7 @@ mod tests { failure_testing: None, query_testing: None, // because we can add an identity and withdraw from it in the same block - // the result would be different then expected + // the result would be different from what would be expected verify_state_transition_results: false, ..Default::default() }; diff --git a/packages/rs-drive/src/drive/balances/remove_from_system_credits_operations/v0/mod.rs b/packages/rs-drive/src/drive/balances/remove_from_system_credits_operations/v0/mod.rs index 5d7effa5539..ebdda43ad23 100644 --- a/packages/rs-drive/src/drive/balances/remove_from_system_credits_operations/v0/mod.rs +++ b/packages/rs-drive/src/drive/balances/remove_from_system_credits_operations/v0/mod.rs @@ -50,7 +50,7 @@ impl Drive { let new_total = total_credits_in_platform .checked_sub(amount) .ok_or(Error::Drive(DriveError::CriticalCorruptedState( - "trying to remove an amount that would underflow credits", + "trying to remove an amount that would underflow total system credits", )))?; let path_holding_total_credits_vec = misc_path_vec(); let replace_op = GroveDbOp::replace_op( diff --git a/packages/rs-sdk/src/platform/transition/withdraw_from_identity.rs b/packages/rs-sdk/src/platform/transition/withdraw_from_identity.rs index 8e9020a5f3d..1dbff8c3ead 100644 --- a/packages/rs-sdk/src/platform/transition/withdraw_from_identity.rs +++ b/packages/rs-sdk/src/platform/transition/withdraw_from_identity.rs @@ -51,6 +51,7 @@ impl WithdrawFromIdentity for Identity { let new_identity_nonce = sdk.get_identity_nonce(self.id(), true, settings).await?; let state_transition = IdentityCreditWithdrawalTransition::try_from_identity( self, + None, CoreScript::new(address.script_pubkey()), amount, Pooling::Never, diff --git a/packages/wasm-dpp/src/errors/consensus/consensus_error.rs b/packages/wasm-dpp/src/errors/consensus/consensus_error.rs index 43ce0566371..14e02526dbf 100644 --- a/packages/wasm-dpp/src/errors/consensus/consensus_error.rs +++ b/packages/wasm-dpp/src/errors/consensus/consensus_error.rs @@ -33,7 +33,6 @@ use crate::errors::consensus::state::identity::{ InvalidIdentityNonceErrorWasm, MissingIdentityPublicKeyIdsErrorWasm, }; use dpp::consensus::basic::decode::VersionError; -use dpp::consensus::basic::BasicError; use dpp::consensus::basic::BasicError::{ DuplicatedIdentityPublicKeyBasicError, DuplicatedIdentityPublicKeyIdBasicError, IdentityAssetLockProofLockedTransactionMismatchError, @@ -53,6 +52,7 @@ use dpp::consensus::basic::BasicError::{ NotImplementedIdentityCreditWithdrawalTransitionPoolingError, ProtocolVersionParsingError, UnsupportedProtocolVersionError, UnsupportedVersionError, }; +use dpp::consensus::basic::{BasicError, UnsupportedFeatureError}; use dpp::consensus::fee::fee_error::FeeError; use dpp::consensus::signature::SignatureError; use dpp::consensus::state::state_error::StateError; @@ -536,6 +536,9 @@ fn from_basic_error(basic_error: &BasicError) -> JsValue { BasicError::ContestedUniqueIndexOnMutableDocumentTypeError(e) => { generic_consensus_error!(ContestedUniqueIndexOnMutableDocumentTypeError, e).into() } + BasicError::UnsupportedFeatureError(e) => { + generic_consensus_error!(UnsupportedFeatureError, e).into() + } } }