diff --git a/packages/platform-test-suite/test/e2e/withdrawals.spec.js b/packages/platform-test-suite/test/e2e/withdrawals.spec.js index 9c5dbee62b..f2a80ee433 100644 --- a/packages/platform-test-suite/test/e2e/withdrawals.spec.js +++ b/packages/platform-test-suite/test/e2e/withdrawals.spec.js @@ -183,7 +183,7 @@ describe('Withdrawals', function withdrawalsTest() { )).to.be.rejectedWith(`Withdrawal amount "${amountToWithdraw}" is bigger that identity balance "${identityBalanceBefore}"`); }); - it('should not allow to create withdrawal with wrong security key type', async () => { + it('should not allow to create withdrawal with authentication key purpose', async () => { const account = await client.getWalletAccount(); const identityBalanceBefore = identity.getBalance(); const withdrawTo = await account.getUnusedAddress(); @@ -196,7 +196,7 @@ describe('Withdrawals', function withdrawalsTest() { toAddress: withdrawTo.address, signingKeyIndex: 1, }, - )).to.be.rejectedWith('Error conversion not implemented: Invalid public key security level HIGH. The state transition requires one of MASTER'); + )).to.be.rejectedWith('Error conversion not implemented: Invalid identity key purpose AUTHENTICATION. This state transition requires TRANSFER | OWNER'); }); // TODO: Figure out how to overcome client-side validation and implement diff --git a/packages/rs-dpp/src/core_types/validator/v0/mod.rs b/packages/rs-dpp/src/core_types/validator/v0/mod.rs index d472b4a145..daadc197fe 100644 --- a/packages/rs-dpp/src/core_types/validator/v0/mod.rs +++ b/packages/rs-dpp/src/core_types/validator/v0/mod.rs @@ -1,6 +1,5 @@ use dashcore::{ProTxHash, PubkeyHash}; -use std::fmt; -use std::fmt::{Debug, Display, Formatter}; +use std::fmt::{Debug, Formatter}; use crate::bls_signatures::PublicKey as BlsPublicKey; #[cfg(feature = "core-types-serde-conversion")] 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 ea33077b76..2b24a1e280 100644 --- a/packages/rs-dpp/src/errors/consensus/basic/basic_error.rs +++ b/packages/rs-dpp/src/errors/consensus/basic/basic_error.rs @@ -52,7 +52,7 @@ use crate::consensus::basic::identity::{ InvalidIdentityUpdateTransitionDisableKeysError, InvalidIdentityUpdateTransitionEmptyError, InvalidInstantAssetLockProofError, InvalidInstantAssetLockProofSignatureError, MissingMasterPublicKeyError, NotImplementedIdentityCreditWithdrawalTransitionPoolingError, - TooManyMasterPublicKeyError, + TooManyMasterPublicKeyError, WithdrawalOutputScriptNotAllowedWhenSigningWithOwnerKeyError, }; use crate::consensus::basic::invalid_identifier_error::InvalidIdentifierError; use crate::consensus::basic::state_transition::{ @@ -335,6 +335,11 @@ pub enum BasicError { InvalidIdentityCreditWithdrawalTransitionOutputScriptError, ), + #[error(transparent)] + WithdrawalOutputScriptNotAllowedWhenSigningWithOwnerKeyError( + WithdrawalOutputScriptNotAllowedWhenSigningWithOwnerKeyError, + ), + #[error(transparent)] InvalidIdentityCreditWithdrawalTransitionCoreFeeError( InvalidIdentityCreditWithdrawalTransitionCoreFeeError, diff --git a/packages/rs-dpp/src/errors/consensus/basic/identity/mod.rs b/packages/rs-dpp/src/errors/consensus/basic/identity/mod.rs index aafc111b85..3b411457fa 100644 --- a/packages/rs-dpp/src/errors/consensus/basic/identity/mod.rs +++ b/packages/rs-dpp/src/errors/consensus/basic/identity/mod.rs @@ -29,6 +29,7 @@ pub use invalid_instant_asset_lock_proof_signature_error::*; pub use missing_master_public_key_error::*; pub use not_implemented_identity_credit_withdrawal_transition_pooling_error::*; pub use too_many_master_public_key_error::*; +pub use withdrawal_output_script_not_allowed_when_signing_with_owner_key::*; mod data_contract_bounds_not_present_error; mod disabling_key_id_also_being_added_in_same_transition_error; @@ -62,3 +63,4 @@ mod invalid_instant_asset_lock_proof_signature_error; mod missing_master_public_key_error; mod not_implemented_identity_credit_withdrawal_transition_pooling_error; mod too_many_master_public_key_error; +mod withdrawal_output_script_not_allowed_when_signing_with_owner_key; diff --git a/packages/rs-dpp/src/errors/consensus/basic/identity/withdrawal_output_script_not_allowed_when_signing_with_owner_key.rs b/packages/rs-dpp/src/errors/consensus/basic/identity/withdrawal_output_script_not_allowed_when_signing_with_owner_key.rs new file mode 100644 index 0000000000..70f4c68b5d --- /dev/null +++ b/packages/rs-dpp/src/errors/consensus/basic/identity/withdrawal_output_script_not_allowed_when_signing_with_owner_key.rs @@ -0,0 +1,50 @@ +use crate::errors::ProtocolError; +use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize}; +use thiserror::Error; + +use crate::consensus::basic::BasicError; +use crate::consensus::ConsensusError; +use crate::identity::core_script::CoreScript; + +use crate::identity::KeyID; +use bincode::{Decode, Encode}; + +#[derive( + Error, Debug, Clone, PartialEq, Eq, Encode, Decode, PlatformSerialize, PlatformDeserialize, +)] +#[error("Withdrawal output script not allowed when signing with owner key {key_id}")] +#[platform_serialize(unversioned)] +pub struct WithdrawalOutputScriptNotAllowedWhenSigningWithOwnerKeyError { + output_script: CoreScript, + key_id: KeyID, +} + +/* + +DO NOT CHANGE ORDER OF FIELDS WITHOUT INTRODUCING OF NEW VERSION + +*/ + +impl WithdrawalOutputScriptNotAllowedWhenSigningWithOwnerKeyError { + pub fn new(output_script: CoreScript, key_id: KeyID) -> Self { + Self { + output_script, + key_id, + } + } + + pub fn output_script(&self) -> &CoreScript { + &self.output_script + } + + pub fn key_id(&self) -> KeyID { + self.key_id + } +} +impl From for ConsensusError { + fn from(err: WithdrawalOutputScriptNotAllowedWhenSigningWithOwnerKeyError) -> Self { + Self::BasicError( + BasicError::WithdrawalOutputScriptNotAllowedWhenSigningWithOwnerKeyError(err), + ) + } +} diff --git a/packages/rs-dpp/src/errors/consensus/codes.rs b/packages/rs-dpp/src/errors/consensus/codes.rs index ca00dc1c2b..aa05a541bf 100644 --- a/packages/rs-dpp/src/errors/consensus/codes.rs +++ b/packages/rs-dpp/src/errors/consensus/codes.rs @@ -153,6 +153,7 @@ impl ErrorWithCode for BasicError { Self::MasterPublicKeyUpdateError(_) => 10529, Self::IdentityAssetLockTransactionOutPointNotEnoughBalanceError(_) => 10530, Self::IdentityAssetLockStateTransitionReplayError(_) => 10531, + Self::WithdrawalOutputScriptNotAllowedWhenSigningWithOwnerKeyError(_) => 10532, // State Transition Errors: 10600-10699 Self::InvalidStateTransitionTypeError { .. } => 10600, diff --git a/packages/rs-dpp/src/identity/identity_public_key/purpose.rs b/packages/rs-dpp/src/identity/identity_public_key/purpose.rs index dbadfa5748..36255fdc45 100644 --- a/packages/rs-dpp/src/identity/identity_public_key/purpose.rs +++ b/packages/rs-dpp/src/identity/identity_public_key/purpose.rs @@ -1,4 +1,6 @@ -use crate::identity::Purpose::{AUTHENTICATION, DECRYPTION, ENCRYPTION, SYSTEM, TRANSFER, VOTING}; +use crate::identity::Purpose::{ + AUTHENTICATION, DECRYPTION, ENCRYPTION, OWNER, SYSTEM, TRANSFER, VOTING, +}; use anyhow::bail; use bincode::{Decode, Encode}; #[cfg(feature = "cbor")] @@ -37,6 +39,8 @@ pub enum Purpose { SYSTEM = 4, /// this key cannot be used for signing documents VOTING = 5, + /// this key is used to prove ownership of a masternode or evonode + OWNER = 6, } impl From for [u8; 1] { @@ -54,6 +58,7 @@ impl From for &'static [u8; 1] { TRANSFER => &[3], SYSTEM => &[4], VOTING => &[5], + OWNER => &[6], } } } @@ -68,6 +73,7 @@ impl TryFrom for Purpose { 3 => Ok(TRANSFER), 4 => Ok(SYSTEM), 5 => Ok(VOTING), + 6 => Ok(OWNER), value => bail!("unrecognized purpose: {}", value), } } @@ -83,6 +89,7 @@ impl TryFrom for Purpose { 3 => Ok(TRANSFER), 4 => Ok(SYSTEM), 5 => Ok(VOTING), + 6 => Ok(OWNER), value => bail!("unrecognized purpose: {}", value), } } @@ -102,8 +109,15 @@ impl std::fmt::Display for Purpose { impl Purpose { /// The full range of purposes - pub fn full_range() -> [Purpose; 5] { - [AUTHENTICATION, ENCRYPTION, DECRYPTION, TRANSFER, VOTING] + pub fn full_range() -> [Purpose; 6] { + [ + AUTHENTICATION, + ENCRYPTION, + DECRYPTION, + TRANSFER, + VOTING, + OWNER, + ] } /// Just the authentication and withdraw purposes pub fn searchable_purposes() -> [Purpose; 3] { @@ -113,8 +127,4 @@ impl Purpose { pub fn encryption_decryption() -> [Purpose; 2] { [ENCRYPTION, DECRYPTION] } - /// The last purpose - pub fn last() -> Purpose { - Self::TRANSFER - } } diff --git a/packages/rs-dpp/src/identity/identity_public_key/random.rs b/packages/rs-dpp/src/identity/identity_public_key/random.rs index 0dea7a5dee..dec286b927 100644 --- a/packages/rs-dpp/src/identity/identity_public_key/random.rs +++ b/packages/rs-dpp/src/identity/identity_public_key/random.rs @@ -467,6 +467,149 @@ impl IdentityPublicKey { } } + /// Generates a random ECDSA critical-level authentication key for a masternode owner. + /// + /// This function generates a random key that can be used for owner authentication in a masternode context. + /// The function accepts an optional seed for deterministic key generation, or uses entropy-based randomness if no seed is provided. + /// + /// # Parameters + /// + /// * `id`: The identifier (`KeyID`) for the masternode owner key. + /// * `seed`: An optional `u64` value used to seed the random number generator. If `None`, the RNG will be seeded from entropy. + /// * `platform_version`: A reference to the `PlatformVersion` struct, which is used to determine the correct key structure version. + /// + /// # Returns + /// + /// Returns a tuple containing the generated `IdentityPublicKey` for the masternode owner and the corresponding private key as a byte vector. + /// + /// # Errors + /// + /// Returns a `ProtocolError` if the platform version is not supported. + pub fn random_masternode_owner_key( + id: KeyID, + seed: Option, + platform_version: &PlatformVersion, + ) -> Result<(Self, Vec), ProtocolError> { + let mut rng = match seed { + None => StdRng::from_entropy(), + Some(seed_value) => StdRng::seed_from_u64(seed_value), + }; + Self::random_masternode_owner_key_with_rng(id, &mut rng, platform_version) + } + + /// Generates a random ECDSA critical-level authentication key for a masternode owner using a custom RNG. + /// + /// This function generates a random key using a given random number generator (RNG). This is useful when specific control over the randomness is needed. + /// + /// # Parameters + /// + /// * `id`: The identifier (`KeyID`) for the masternode owner key. + /// * `rng`: A mutable reference to a `StdRng` instance used to generate randomness. + /// * `platform_version`: A reference to the `PlatformVersion` struct, which is used to determine the correct key structure version. + /// + /// # Returns + /// + /// Returns a tuple containing the generated `IdentityPublicKey` for the masternode owner and the corresponding private key as a byte vector. + /// + /// # Errors + /// + /// Returns a `ProtocolError` if the platform version is not supported. + pub fn random_masternode_owner_key_with_rng( + id: KeyID, + rng: &mut StdRng, + platform_version: &PlatformVersion, + ) -> Result<(Self, Vec), ProtocolError> { + match platform_version + .dpp + .identity_versions + .identity_key_structure_version + { + 0 => { + let (key, private_key) = + IdentityPublicKeyV0::random_owner_key_with_rng(id, rng, platform_version)?; + Ok((key.into(), private_key)) + } + version => Err(ProtocolError::UnknownVersionMismatch { + method: "IdentityPublicKey::random_masternode_owner_key_with_rng".to_string(), + known_versions: vec![0], + received: version, + }), + } + } + + /// Generates a random ECDSA critical-level transfer key for a masternode. + /// + /// This function generates a random key for use in transferring ownership of a masternode. An optional seed can be provided for deterministic key generation, or entropy-based randomness is used if no seed is given. + /// + /// # Parameters + /// + /// * `id`: The identifier (`KeyID`) for the masternode transfer key. + /// * `seed`: An optional `u64` value used to seed the random number generator. If `None`, the RNG will be seeded from entropy. + /// * `platform_version`: A reference to the `PlatformVersion` struct, which is used to determine the correct key structure version. + /// + /// # Returns + /// + /// Returns a tuple containing the generated `IdentityPublicKey` for the masternode transfer key and the corresponding private key as a byte vector. + /// + /// # Errors + /// + /// Returns a `ProtocolError` if the platform version is not supported. + pub fn random_masternode_transfer_key( + id: KeyID, + seed: Option, + platform_version: &PlatformVersion, + ) -> Result<(Self, Vec), ProtocolError> { + let mut rng = match seed { + None => StdRng::from_entropy(), + Some(seed_value) => StdRng::seed_from_u64(seed_value), + }; + Self::random_masternode_transfer_key_with_rng(id, &mut rng, platform_version) + } + + /// Generates a random ECDSA critical-level transfer key for a masternode using a custom RNG. + /// + /// This function generates a random key for masternode transfers using a given random number generator (RNG). + /// + /// # Parameters + /// + /// * `id`: The identifier (`KeyID`) for the masternode transfer key. + /// * `rng`: A mutable reference to a `StdRng` instance used to generate randomness. + /// * `platform_version`: A reference to the `PlatformVersion` struct, which is used to determine the correct key structure version. + /// + /// # Returns + /// + /// Returns a tuple containing the generated `IdentityPublicKey` for the masternode transfer key and the corresponding private key as a byte vector. + /// + /// # Errors + /// + /// Returns a `ProtocolError` if the platform version is not supported. + pub fn random_masternode_transfer_key_with_rng( + id: KeyID, + rng: &mut StdRng, + platform_version: &PlatformVersion, + ) -> Result<(Self, Vec), ProtocolError> { + match platform_version + .dpp + .identity_versions + .identity_key_structure_version + { + 0 => { + let (key, private_key) = + IdentityPublicKeyV0::random_masternode_transfer_key_with_rng( + id, + rng, + platform_version, + )?; + Ok((key.into(), private_key)) + } + version => Err(ProtocolError::UnknownVersionMismatch { + method: "IdentityPublicKey::random_masternode_transfer_key_with_rng".to_string(), + known_versions: vec![0], + received: version, + }), + } + } + /// Generates a random ECDSA high-level authentication public key along with its corresponding private key. /// /// This method constructs a random ECDSA (using the secp256k1 curve) high-level authentication public key diff --git a/packages/rs-dpp/src/identity/identity_public_key/v0/random.rs b/packages/rs-dpp/src/identity/identity_public_key/v0/random.rs index 13afdd673f..162e836e1b 100644 --- a/packages/rs-dpp/src/identity/identity_public_key/v0/random.rs +++ b/packages/rs-dpp/src/identity/identity_public_key/v0/random.rs @@ -1,7 +1,7 @@ use crate::identity::contract_bounds::ContractBounds; use crate::identity::identity_public_key::v0::IdentityPublicKeyV0; use crate::identity::KeyType::{ECDSA_HASH160, ECDSA_SECP256K1}; -use crate::identity::Purpose::{AUTHENTICATION, VOTING}; +use crate::identity::Purpose::{AUTHENTICATION, OWNER, TRANSFER, VOTING}; use crate::identity::SecurityLevel::{CRITICAL, HIGH, MASTER, MEDIUM}; use crate::identity::{KeyCount, KeyID, KeyType, Purpose, SecurityLevel}; use crate::version::PlatformVersion; @@ -245,6 +245,58 @@ impl IdentityPublicKeyV0 { )) } + pub fn random_owner_key_with_rng( + id: KeyID, + rng: &mut StdRng, + platform_version: &PlatformVersion, + ) -> Result<(Self, Vec), ProtocolError> { + let key_type = ECDSA_HASH160; + let purpose = OWNER; + let security_level = CRITICAL; + let read_only = true; + let (data, private_data) = + key_type.random_public_and_private_key_data(rng, platform_version)?; + Ok(( + IdentityPublicKeyV0 { + id, + key_type, + purpose, + security_level, + read_only, + disabled_at: None, + data: data.into(), + contract_bounds: None, + }, + private_data, + )) + } + + pub fn random_masternode_transfer_key_with_rng( + id: KeyID, + rng: &mut StdRng, + platform_version: &PlatformVersion, + ) -> Result<(Self, Vec), ProtocolError> { + let key_type = ECDSA_HASH160; + let purpose = TRANSFER; + let security_level = CRITICAL; + let read_only = true; + let (data, private_data) = + key_type.random_public_and_private_key_data(rng, platform_version)?; + Ok(( + IdentityPublicKeyV0 { + id, + key_type, + purpose, + security_level, + read_only, + disabled_at: None, + data: data.into(), + contract_bounds: None, + }, + private_data, + )) + } + pub fn random_ecdsa_critical_level_authentication_key_with_rng( id: KeyID, rng: &mut StdRng, diff --git a/packages/rs-dpp/src/state_transition/mod.rs b/packages/rs-dpp/src/state_transition/mod.rs index bc20c6ab0a..cd97cae4ae 100644 --- a/packages/rs-dpp/src/state_transition/mod.rs +++ b/packages/rs-dpp/src/state_transition/mod.rs @@ -152,8 +152,8 @@ macro_rules! call_getter_method_identity_signed { StateTransition::DataContractCreate(st) => Some(st.$method($args)), StateTransition::DataContractUpdate(st) => Some(st.$method($args)), StateTransition::DocumentsBatch(st) => Some(st.$method($args)), - StateTransition::IdentityCreate(st) => None, - StateTransition::IdentityTopUp(st) => None, + StateTransition::IdentityCreate(_) => None, + StateTransition::IdentityTopUp(_) => None, StateTransition::IdentityCreditWithdrawal(st) => Some(st.$method($args)), StateTransition::IdentityUpdate(st) => Some(st.$method($args)), StateTransition::IdentityCreditTransfer(st) => Some(st.$method($args)), @@ -165,8 +165,8 @@ macro_rules! call_getter_method_identity_signed { StateTransition::DataContractCreate(st) => Some(st.$method()), StateTransition::DataContractUpdate(st) => Some(st.$method()), StateTransition::DocumentsBatch(st) => Some(st.$method()), - StateTransition::IdentityCreate(_st) => None, - StateTransition::IdentityTopUp(_st) => None, + StateTransition::IdentityCreate(_) => None, + StateTransition::IdentityTopUp(_) => None, StateTransition::IdentityCreditWithdrawal(st) => Some(st.$method()), StateTransition::IdentityUpdate(st) => Some(st.$method()), StateTransition::IdentityCreditTransfer(st) => Some(st.$method()), 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 d143eaae31..2836b96cc6 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 @@ -16,12 +16,12 @@ use platform_version::version::{FeatureVersion, PlatformVersion}; pub enum PreferredKeyPurposeForSigningWithdrawal { /// Use any key Any, - /// Use the master key, then the transfer key - MasterPreferred, - /// Use the transfer key, then the master key + /// Use the owner key, then the transfer key + OwnerPreferred, + /// Use the transfer key, then the owner key TransferPreferred, - /// Only use the master key - MasterOnly, + /// Only use the owner key + OwnerOnly, /// Only use the transfer key TransferOnly, } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/v1/identity_signed.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/v1/identity_signed.rs index 045bab7b27..bc541c4b3c 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/v1/identity_signed.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/v1/identity_signed.rs @@ -1,4 +1,4 @@ -use crate::identity::SecurityLevel::{CRITICAL, MASTER}; +use crate::identity::SecurityLevel::CRITICAL; use crate::identity::{KeyID, Purpose, SecurityLevel}; use crate::state_transition::identity_credit_withdrawal_transition::v1::IdentityCreditWithdrawalTransitionV1; use crate::state_transition::StateTransitionIdentitySigned; @@ -12,16 +12,12 @@ impl StateTransitionIdentitySigned for IdentityCreditWithdrawalTransitionV1 { self.signature_public_key_id = key_id } - fn security_level_requirement(&self, purpose: Purpose) -> Vec { - if purpose == Purpose::AUTHENTICATION { - vec![MASTER] - } else { - // for transfer - vec![CRITICAL] - } + fn security_level_requirement(&self, _purpose: Purpose) -> Vec { + // critical is used for both transfer and owner + vec![CRITICAL] } fn purpose_requirement(&self) -> Vec { - vec![Purpose::TRANSFER, Purpose::AUTHENTICATION] + vec![Purpose::TRANSFER, Purpose::OWNER] } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/v1/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/v1/v0_methods.rs index de4817bca4..8f20f27494 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/v1/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/identity_credit_withdrawal_transition/v1/v0_methods.rs @@ -65,9 +65,9 @@ impl IdentityCreditWithdrawalTransitionMethodsV0 for IdentityCreditWithdrawalTra let mut key: Option<&IdentityPublicKey>; match preferred_key_purpose_for_signing_withdrawal { - PreferredKeyPurposeForSigningWithdrawal::MasterPreferred => { + PreferredKeyPurposeForSigningWithdrawal::OwnerPreferred => { key = identity.get_first_public_key_matching( - Purpose::AUTHENTICATION, + Purpose::OWNER, SecurityLevel::full_range().into(), KeyType::all_key_types().into(), true, @@ -93,16 +93,16 @@ impl IdentityCreditWithdrawalTransitionMethodsV0 for IdentityCreditWithdrawalTra if key.is_none() || !signer.can_sign_with(key.unwrap()) { key = identity.get_first_public_key_matching( - Purpose::AUTHENTICATION, + Purpose::OWNER, SecurityLevel::full_range().into(), KeyType::all_key_types().into(), true, ); } } - PreferredKeyPurposeForSigningWithdrawal::MasterOnly => { + PreferredKeyPurposeForSigningWithdrawal::OwnerOnly => { key = identity.get_first_public_key_matching( - Purpose::AUTHENTICATION, + Purpose::OWNER, SecurityLevel::full_range().into(), KeyType::all_key_types().into(), true, diff --git a/packages/rs-dpp/src/state_transition/state_transitions/identity/public_key_in_creation/methods/validate_identity_public_keys_structure/v0/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/identity/public_key_in_creation/methods/validate_identity_public_keys_structure/v0/mod.rs index 81b489092b..6b02b2e645 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/identity/public_key_in_creation/methods/validate_identity_public_keys_structure/v0/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/identity/public_key_in_creation/methods/validate_identity_public_keys_structure/v0/mod.rs @@ -19,7 +19,7 @@ use crate::ProtocolError; use platform_version::version::PlatformVersion; lazy_static! { - static ref ALLOWED_SECURITY_LEVELS: HashMap> = { + static ref ALLOWED_SECURITY_LEVELS_FOR_EXTERNALLY_ADDED_KEYS: HashMap> = { let mut m = HashMap::new(); m.insert( Purpose::AUTHENTICATION, @@ -119,8 +119,8 @@ impl IdentityPublicKeyInCreation { let validation_errors = identity_public_keys_with_witness .iter() .filter_map(|identity_public_key| { - let allowed_security_levels = - ALLOWED_SECURITY_LEVELS.get(&identity_public_key.purpose()); + let allowed_security_levels = ALLOWED_SECURITY_LEVELS_FOR_EXTERNALLY_ADDED_KEYS + .get(&identity_public_key.purpose()); if let Some(levels) = allowed_security_levels { if !levels.contains(&identity_public_key.security_level()) { Some( diff --git a/packages/rs-drive-abci/src/execution/engine/run_block_proposal/mod.rs b/packages/rs-drive-abci/src/execution/engine/run_block_proposal/mod.rs index e2149678b3..43712c7feb 100644 --- a/packages/rs-drive-abci/src/execution/engine/run_block_proposal/mod.rs +++ b/packages/rs-drive-abci/src/execution/engine/run_block_proposal/mod.rs @@ -1,12 +1,15 @@ use crate::error::execution::ExecutionError; use crate::error::Error; +use crate::execution::types::block_state_info; +use crate::execution::types::block_state_info::v0::BlockStateInfoV0Methods; use crate::metrics::HistogramTiming; -use crate::platform_types::epoch_info::v0::EpochInfoV0Methods; +use crate::platform_types::epoch_info::v0::{EpochInfoV0Getters, EpochInfoV0Methods}; use crate::platform_types::platform::Platform; use crate::platform_types::platform_state::v0::PlatformStateV0Methods; use crate::platform_types::platform_state::PlatformState; use crate::platform_types::{block_execution_outcome, block_proposal}; use crate::rpc::core::CoreRPCLike; +use dpp::block::epoch::Epoch; use dpp::validation::ValidationResult; use dpp::version::PlatformVersion; use drive::grovedb::Transaction; @@ -97,8 +100,24 @@ Your software version: {}, latest supported protocol version: {}."#, // Set current protocol version to the block platform state block_platform_state .set_current_protocol_version_in_consensus(next_protocol_version); + + let last_block_time_ms = platform_state.last_committed_block_time_ms(); + + // Init block execution context + let block_state_info = block_state_info::v0::BlockStateInfoV0::from_block_proposal( + &block_proposal, + last_block_time_ms, + ); + + let block_info = block_state_info.to_block_info( + Epoch::new(epoch_info.current_epoch_index()) + .expect("current epoch index should be in range"), + ); + // This is for events like adding stuff to the root tree, or making structural changes/fixes self.perform_events_on_first_block_of_protocol_change( + platform_state, + &block_info, transaction, old_protocol_version, next_platform_version, diff --git a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/create_owner_identity/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/create_owner_identity/mod.rs index 042b35c5ca..048ea1266a 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/create_owner_identity/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/create_owner_identity/mod.rs @@ -1,4 +1,5 @@ mod v0; +mod v1; use crate::error::execution::ExecutionError; use crate::error::Error; @@ -38,9 +39,10 @@ where .create_owner_identity { 0 => Self::create_owner_identity_v0(masternode, platform_version), + 1 => Self::create_owner_identity_v1(masternode, platform_version), version => Err(Error::Execution(ExecutionError::UnknownVersionMismatch { method: "create_owner_identity".to_string(), - known_versions: vec![0], + known_versions: vec![0, 1], received: version, })), } diff --git a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/create_owner_identity/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/create_owner_identity/v0/mod.rs index 18028f7a31..176b6d90db 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/create_owner_identity/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/create_owner_identity/v0/mod.rs @@ -25,7 +25,9 @@ where Ok(identity) } - fn get_owner_identifier(masternode: &MasternodeListItem) -> Result { + pub(crate) fn get_owner_identifier( + masternode: &MasternodeListItem, + ) -> Result { let masternode_identifier: [u8; 32] = masternode.pro_tx_hash.into(); Ok(masternode_identifier.into()) } diff --git a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/create_owner_identity/v1/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/create_owner_identity/v1/mod.rs new file mode 100644 index 0000000000..da89aaafc7 --- /dev/null +++ b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/create_owner_identity/v1/mod.rs @@ -0,0 +1,33 @@ +use crate::error::Error; +use crate::platform_types::platform::Platform; +use crate::rpc::core::CoreRPCLike; +use dashcore_rpc::dashcore_rpc_json::MasternodeListItem; +use dpp::identity::accessors::IdentityGettersV0; +use dpp::identity::Identity; +use dpp::version::PlatformVersion; + +impl Platform +where + C: CoreRPCLike, +{ + pub(super) fn create_owner_identity_v1( + masternode: &MasternodeListItem, + platform_version: &PlatformVersion, + ) -> Result { + let owner_identifier = Self::get_owner_identifier(masternode)?; + let mut identity = Identity::create_basic_identity(owner_identifier, platform_version)?; + identity.add_public_keys([ + Self::get_owner_identity_withdrawal_key( + masternode.state.payout_address, + 0, + platform_version, + )?, + Self::get_owner_identity_owner_key( + masternode.state.owner_address, + 1, + platform_version, + )?, + ]); + Ok(identity) + } +} diff --git a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/get_owner_identity_owner_key/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/get_owner_identity_owner_key/mod.rs new file mode 100644 index 0000000000..8b76f02566 --- /dev/null +++ b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/get_owner_identity_owner_key/mod.rs @@ -0,0 +1,45 @@ +mod v0; + +use crate::error::execution::ExecutionError; +use crate::error::Error; +use crate::platform_types::platform::Platform; + +use dpp::identity::{IdentityPublicKey, KeyID}; + +use dpp::version::PlatformVersion; + +impl Platform { + /// Retrieves an identity public key using the provided owner key and key ID. + /// + /// This function derives the identity public key and delegates to a version-specific method depending on the platform version. + /// + /// # Arguments + /// + /// * owner_public_key_address - The public key address of the owner. + /// * key_id - The KeyID for the identity public key. + /// * platform_version - The version of the platform to determine which method to delegate to. + /// + /// # Returns + /// + /// * Result - Returns the derived identity public key if successful. Otherwise, returns an error. + pub(crate) fn get_owner_identity_owner_key( + owner_public_key_address: [u8; 20], + key_id: KeyID, + platform_version: &PlatformVersion, + ) -> Result { + match platform_version + .drive_abci + .methods + .core_based_updates + .masternode_updates + .get_owner_identity_owner_key + { + 0 => Self::get_owner_identity_owner_key_v0(owner_public_key_address, key_id), + version => Err(Error::Execution(ExecutionError::UnknownVersionMismatch { + method: "get_owner_identity_owner_key".to_string(), + known_versions: vec![0], + received: version, + })), + } + } +} diff --git a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/get_owner_identity_owner_key/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/get_owner_identity_owner_key/v0/mod.rs new file mode 100644 index 0000000000..ba099e99ed --- /dev/null +++ b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/get_owner_identity_owner_key/v0/mod.rs @@ -0,0 +1,24 @@ +use crate::error::Error; +use crate::platform_types::platform::Platform; +use dpp::identity::identity_public_key::v0::IdentityPublicKeyV0; +use dpp::identity::{IdentityPublicKey, KeyID, KeyType, Purpose, SecurityLevel}; +use dpp::platform_value::BinaryData; + +impl Platform { + pub(super) fn get_owner_identity_owner_key_v0( + owner_public_key_address: [u8; 20], + key_id: KeyID, + ) -> Result { + Ok(IdentityPublicKeyV0 { + id: key_id, + key_type: KeyType::ECDSA_HASH160, + purpose: Purpose::OWNER, + security_level: SecurityLevel::CRITICAL, + read_only: true, + data: BinaryData::new(owner_public_key_address.to_vec()), + disabled_at: None, + contract_bounds: None, + } + .into()) + } +} diff --git a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/get_owner_identity_key/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/get_owner_identity_withdrawal_key/mod.rs similarity index 100% rename from packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/get_owner_identity_key/mod.rs rename to packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/get_owner_identity_withdrawal_key/mod.rs diff --git a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/get_owner_identity_key/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/get_owner_identity_withdrawal_key/v0/mod.rs similarity index 100% rename from packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/get_owner_identity_key/v0/mod.rs rename to packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/get_owner_identity_withdrawal_key/v0/mod.rs diff --git a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/mod.rs index e3168a64e7..e2a9176d3c 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/mod.rs @@ -4,14 +4,14 @@ mod create_voter_identity; mod disable_identity_keys; mod get_operator_identifier; mod get_operator_identity_keys; -mod get_owner_identity_key; +mod get_owner_identity_owner_key; +mod get_owner_identity_withdrawal_key; mod get_voter_identifier; mod get_voter_identity_key; mod update_masternode_identities; mod update_operator_identity; mod update_owner_withdrawal_address; mod update_voter_identity; - // // // #[cfg(test)] diff --git a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/update_owner_withdrawal_address/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/update_owner_withdrawal_address/mod.rs index f2f5b5c708..1f81d17c0b 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/update_owner_withdrawal_address/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/update_owner_withdrawal_address/mod.rs @@ -11,6 +11,7 @@ use drive::util::batch::DriveOperation; use drive::grovedb::Transaction; mod v0; +mod v1; impl Platform where @@ -61,11 +62,286 @@ where drive_operations, platform_version, ), + 1 => self.update_owner_withdrawal_address_v1( + owner_identifier, + new_withdrawal_address, + block_info, + transaction, + drive_operations, + platform_version, + ), version => Err(Error::Execution(ExecutionError::UnknownVersionMismatch { method: "update_owner_withdrawal_address".to_string(), - known_versions: vec![0], + known_versions: vec![0, 1], received: version, })), } } } + +#[cfg(test)] +mod tests { + use crate::test::helpers::setup::TestPlatformBuilder; + use dpp::block::block_info::BlockInfo; + use dpp::identity::accessors::IdentityGettersV0; + use dpp::identity::identity_public_key::v0::IdentityPublicKeyV0; + use dpp::identity::{Identity, IdentityV0, KeyType, Purpose, SecurityLevel}; + use dpp::platform_value::BinaryData; + use dpp::prelude::{Identifier, IdentityPublicKey}; + use platform_version::version::PlatformVersion; + use rand::prelude::StdRng; + use rand::Rng; + use rand::SeedableRng; + use std::collections::BTreeMap; + + #[test] + fn test_update_withdrawal_address() { + let platform_version = PlatformVersion::latest(); + let platform = TestPlatformBuilder::new() + .build_with_mock_rpc() + .set_genesis_state(); + + let block_info = BlockInfo::default(); + + let mut rng = StdRng::seed_from_u64(5); + + let payout_address: [u8; 20] = rng.gen(); + + let withdrawal_key: IdentityPublicKey = IdentityPublicKeyV0 { + id: 0, + key_type: KeyType::ECDSA_HASH160, + purpose: Purpose::TRANSFER, + security_level: SecurityLevel::CRITICAL, + read_only: true, + data: BinaryData::new(payout_address.to_vec()), + disabled_at: None, + contract_bounds: None, + } + .into(); + + let identity: Identity = IdentityV0 { + id: Identifier::random_with_rng(&mut rng), + public_keys: BTreeMap::from([(0, withdrawal_key.clone())]), + balance: 0, + revision: 0, + } + .into(); + + // We just add this identity to the system first + + platform + .drive + .add_new_identity( + identity.clone(), + true, + &block_info, + true, + None, + platform_version, + ) + .expect("expected to add a new identity"); + + let transaction = platform.drive.grove.start_transaction(); + + let mut drive_operations = vec![]; + + platform + .update_owner_withdrawal_address( + identity.id().to_buffer(), + [0; 20], + &block_info, + &transaction, + &mut drive_operations, + platform_version, + ) + .expect("expected to update owner withdrawal address"); + + platform + .drive + .apply_drive_operations( + drive_operations, + true, + &block_info, + Some(&transaction), + platform_version, + None, + ) + .expect("expected to apply drive operations"); + } + + #[test] + fn test_update_to_same_withdrawal_address() { + let platform_version = PlatformVersion::latest(); + let platform = TestPlatformBuilder::new() + .build_with_mock_rpc() + .set_genesis_state(); + + let block_info = BlockInfo::default(); + + let mut rng = StdRng::seed_from_u64(5); + + let payout_address: [u8; 20] = rng.gen(); + + let withdrawal_key: IdentityPublicKey = IdentityPublicKeyV0 { + id: 0, + key_type: KeyType::ECDSA_HASH160, + purpose: Purpose::TRANSFER, + security_level: SecurityLevel::CRITICAL, + read_only: true, + data: BinaryData::new(payout_address.to_vec()), + disabled_at: None, + contract_bounds: None, + } + .into(); + + let identity: Identity = IdentityV0 { + id: Identifier::random_with_rng(&mut rng), + public_keys: BTreeMap::from([(0, withdrawal_key.clone())]), + balance: 0, + revision: 0, + } + .into(); + + // We just add this identity to the system first + + platform + .drive + .add_new_identity( + identity.clone(), + true, + &block_info, + true, + None, + platform_version, + ) + .expect("expected to add a new identity"); + + let transaction = platform.drive.grove.start_transaction(); + + let mut drive_operations = vec![]; + + platform + .update_owner_withdrawal_address( + identity.id().to_buffer(), + payout_address, + &block_info, + &transaction, + &mut drive_operations, + platform_version, + ) + .expect("expected to update owner withdrawal address"); + + platform + .drive + .apply_drive_operations( + drive_operations, + true, + &block_info, + Some(&transaction), + platform_version, + None, + ) + .expect("expected to apply drive operations"); + } + #[test] + fn test_update_to_previously_disabled_withdrawal_address() { + let platform_version = PlatformVersion::latest(); + let platform = TestPlatformBuilder::new() + .build_with_mock_rpc() + .set_genesis_state(); + + let block_info = BlockInfo::default(); + + let mut rng = StdRng::seed_from_u64(5); + + let payout_address: [u8; 20] = rng.gen(); + + let withdrawal_key: IdentityPublicKey = IdentityPublicKeyV0 { + id: 0, + key_type: KeyType::ECDSA_HASH160, + purpose: Purpose::TRANSFER, + security_level: SecurityLevel::CRITICAL, + read_only: true, + data: BinaryData::new(payout_address.to_vec()), + disabled_at: None, + contract_bounds: None, + } + .into(); + + let identity: Identity = IdentityV0 { + id: Identifier::random_with_rng(&mut rng), + public_keys: BTreeMap::from([(0, withdrawal_key.clone())]), + balance: 0, + revision: 0, + } + .into(); + + // We just add this identity to the system first + + platform + .drive + .add_new_identity( + identity.clone(), + true, + &block_info, + true, + None, + platform_version, + ) + .expect("expected to add a new identity"); + + let transaction = platform.drive.grove.start_transaction(); + + let mut drive_operations = vec![]; + + platform + .update_owner_withdrawal_address( + identity.id().to_buffer(), + [0; 20], + &block_info, + &transaction, + &mut drive_operations, + platform_version, + ) + .expect("expected to update owner withdrawal address"); + + platform + .drive + .apply_drive_operations( + drive_operations, + true, + &block_info, + Some(&transaction), + platform_version, + None, + ) + .expect("expected to apply drive operations"); + + let transaction = platform.drive.grove.start_transaction(); + + let mut drive_operations = vec![]; + + platform + .update_owner_withdrawal_address( + identity.id().to_buffer(), + payout_address, + &block_info, + &transaction, + &mut drive_operations, + platform_version, + ) + .expect("expected to update owner withdrawal address"); + + platform + .drive + .apply_drive_operations( + drive_operations, + true, + &block_info, + Some(&transaction), + platform_version, + None, + ) + .expect("expected to apply drive operations"); + } +} diff --git a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/update_owner_withdrawal_address/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/update_owner_withdrawal_address/v0/mod.rs index 8484f4436d..640a038160 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/update_owner_withdrawal_address/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/update_owner_withdrawal_address/v0/mod.rs @@ -124,270 +124,3 @@ where Ok(()) } } - -#[cfg(test)] -mod tests { - use crate::test::helpers::setup::TestPlatformBuilder; - use dpp::block::block_info::BlockInfo; - use dpp::identity::accessors::IdentityGettersV0; - use dpp::identity::identity_public_key::v0::IdentityPublicKeyV0; - use dpp::identity::{Identity, IdentityV0, KeyType, Purpose, SecurityLevel}; - use dpp::platform_value::BinaryData; - use dpp::prelude::{Identifier, IdentityPublicKey}; - use platform_version::version::PlatformVersion; - use rand::prelude::StdRng; - use rand::Rng; - use rand::SeedableRng; - use std::collections::BTreeMap; - - #[test] - fn test_update_withdrawal_address() { - let platform_version = PlatformVersion::latest(); - let platform = TestPlatformBuilder::new() - .build_with_mock_rpc() - .set_genesis_state(); - - let block_info = BlockInfo::default(); - - let mut rng = StdRng::seed_from_u64(5); - - let payout_address: [u8; 20] = rng.gen(); - - let withdrawal_key: IdentityPublicKey = IdentityPublicKeyV0 { - id: 0, - key_type: KeyType::ECDSA_HASH160, - purpose: Purpose::TRANSFER, - security_level: SecurityLevel::CRITICAL, - read_only: true, - data: BinaryData::new(payout_address.to_vec()), - disabled_at: None, - contract_bounds: None, - } - .into(); - - let identity: Identity = IdentityV0 { - id: Identifier::random_with_rng(&mut rng), - public_keys: BTreeMap::from([(0, withdrawal_key.clone())]), - balance: 0, - revision: 0, - } - .into(); - - // We just add this identity to the system first - - platform - .drive - .add_new_identity( - identity.clone(), - true, - &block_info, - true, - None, - platform_version, - ) - .expect("expected to add a new identity"); - - let transaction = platform.drive.grove.start_transaction(); - - let mut drive_operations = vec![]; - - platform - .update_owner_withdrawal_address( - identity.id().to_buffer(), - [0; 20], - &block_info, - &transaction, - &mut drive_operations, - platform_version, - ) - .expect("expected to update owner withdrawal address"); - - platform - .drive - .apply_drive_operations( - drive_operations, - true, - &block_info, - Some(&transaction), - platform_version, - None, - ) - .expect("expected to apply drive operations"); - } - - #[test] - fn test_update_to_same_withdrawal_address() { - let platform_version = PlatformVersion::latest(); - let platform = TestPlatformBuilder::new() - .build_with_mock_rpc() - .set_genesis_state(); - - let block_info = BlockInfo::default(); - - let mut rng = StdRng::seed_from_u64(5); - - let payout_address: [u8; 20] = rng.gen(); - - let withdrawal_key: IdentityPublicKey = IdentityPublicKeyV0 { - id: 0, - key_type: KeyType::ECDSA_HASH160, - purpose: Purpose::TRANSFER, - security_level: SecurityLevel::CRITICAL, - read_only: true, - data: BinaryData::new(payout_address.to_vec()), - disabled_at: None, - contract_bounds: None, - } - .into(); - - let identity: Identity = IdentityV0 { - id: Identifier::random_with_rng(&mut rng), - public_keys: BTreeMap::from([(0, withdrawal_key.clone())]), - balance: 0, - revision: 0, - } - .into(); - - // We just add this identity to the system first - - platform - .drive - .add_new_identity( - identity.clone(), - true, - &block_info, - true, - None, - platform_version, - ) - .expect("expected to add a new identity"); - - let transaction = platform.drive.grove.start_transaction(); - - let mut drive_operations = vec![]; - - platform - .update_owner_withdrawal_address( - identity.id().to_buffer(), - payout_address, - &block_info, - &transaction, - &mut drive_operations, - platform_version, - ) - .expect("expected to update owner withdrawal address"); - - platform - .drive - .apply_drive_operations( - drive_operations, - true, - &block_info, - Some(&transaction), - platform_version, - None, - ) - .expect("expected to apply drive operations"); - } - #[test] - fn test_update_to_previously_disabled_withdrawal_address() { - let platform_version = PlatformVersion::latest(); - let platform = TestPlatformBuilder::new() - .build_with_mock_rpc() - .set_genesis_state(); - - let block_info = BlockInfo::default(); - - let mut rng = StdRng::seed_from_u64(5); - - let payout_address: [u8; 20] = rng.gen(); - - let withdrawal_key: IdentityPublicKey = IdentityPublicKeyV0 { - id: 0, - key_type: KeyType::ECDSA_HASH160, - purpose: Purpose::TRANSFER, - security_level: SecurityLevel::CRITICAL, - read_only: true, - data: BinaryData::new(payout_address.to_vec()), - disabled_at: None, - contract_bounds: None, - } - .into(); - - let identity: Identity = IdentityV0 { - id: Identifier::random_with_rng(&mut rng), - public_keys: BTreeMap::from([(0, withdrawal_key.clone())]), - balance: 0, - revision: 0, - } - .into(); - - // We just add this identity to the system first - - platform - .drive - .add_new_identity( - identity.clone(), - true, - &block_info, - true, - None, - platform_version, - ) - .expect("expected to add a new identity"); - - let transaction = platform.drive.grove.start_transaction(); - - let mut drive_operations = vec![]; - - platform - .update_owner_withdrawal_address( - identity.id().to_buffer(), - [0; 20], - &block_info, - &transaction, - &mut drive_operations, - platform_version, - ) - .expect("expected to update owner withdrawal address"); - - platform - .drive - .apply_drive_operations( - drive_operations, - true, - &block_info, - Some(&transaction), - platform_version, - None, - ) - .expect("expected to apply drive operations"); - - let transaction = platform.drive.grove.start_transaction(); - - let mut drive_operations = vec![]; - - platform - .update_owner_withdrawal_address( - identity.id().to_buffer(), - payout_address, - &block_info, - &transaction, - &mut drive_operations, - platform_version, - ) - .expect("expected to update owner withdrawal address"); - - platform - .drive - .apply_drive_operations( - drive_operations, - true, - &block_info, - Some(&transaction), - platform_version, - None, - ) - .expect("expected to apply drive operations"); - } -} diff --git a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/update_owner_withdrawal_address/v1/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/update_owner_withdrawal_address/v1/mod.rs new file mode 100644 index 0000000000..40e074193a --- /dev/null +++ b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/update_owner_withdrawal_address/v1/mod.rs @@ -0,0 +1,136 @@ +use crate::error::execution::ExecutionError; +use crate::error::Error; +use crate::platform_types::platform::Platform; + +use crate::rpc::core::CoreRPCLike; +use dpp::block::block_info::BlockInfo; + +use dpp::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0; +use dpp::identity::{KeyID, Purpose}; + +use dpp::version::PlatformVersion; +use drive::drive::identity::key::fetch::{ + IdentityKeysRequest, KeyIDIdentityPublicKeyPairBTreeMap, KeyRequestType, +}; +use drive::grovedb::Transaction; +use drive::util::batch::DriveOperation; +use drive::util::batch::DriveOperation::IdentityOperation; +use drive::util::batch::IdentityOperationType::{ + AddNewKeysToIdentity, DisableIdentityKeys, ReEnableIdentityKeys, +}; +impl Platform +where + C: CoreRPCLike, +{ + /// In this version we change how the new key_id is found, as there might also be an owner key + pub(super) fn update_owner_withdrawal_address_v1( + &self, + owner_identifier: [u8; 32], + new_withdrawal_address: [u8; 20], + block_info: &BlockInfo, + transaction: &Transaction, + drive_operations: &mut Vec, + platform_version: &PlatformVersion, + ) -> Result<(), Error> { + let key_request = IdentityKeysRequest { + identity_id: owner_identifier, + request_type: KeyRequestType::AllKeys, + limit: None, + offset: None, + }; + + let old_withdrawal_identity_keys = self + .drive + .fetch_identity_keys::( + key_request, + Some(transaction), + platform_version, + )?; + + if old_withdrawal_identity_keys.is_empty() { + return Err(Error::Execution(ExecutionError::DriveMissingData( + "expected masternode owner identity to be in state".to_string(), + ))); + } + + let key_ids_to_disable: Vec = old_withdrawal_identity_keys + .iter() + .filter_map(|(key_id, key)| { + if key.disabled_at().is_some() + || key.data().as_slice() == &new_withdrawal_address + || key.purpose() == Purpose::OWNER + { + // We should not disable the owner key + // Also no need to disable withdrawal keys again + // Or if we are adding the same key we already had + None + } else { + Some(*key_id) + } + }) + .collect(); + + if !key_ids_to_disable.is_empty() { + tracing::trace!( + identity_id = ?owner_identifier, + keys_ids = ?key_ids_to_disable, + disable_at = ?block_info.time_ms, + method = "update_owner_withdrawal_address_v1", + "disable old withdrawal keys in owner identity" + ); + + drive_operations.push(IdentityOperation(DisableIdentityKeys { + identity_id: owner_identifier, + keys_ids: key_ids_to_disable, + })); + } + + if let Some((key_id, previously_disabled_old_key)) = old_withdrawal_identity_keys + .iter() + .find(|(_, key)| key.data().as_slice() == new_withdrawal_address) + { + // there might be a situation where we should do nothing as well + if previously_disabled_old_key.is_disabled() { + // We need to re-enable the withdrawal key + tracing::trace!( + identity_id = ?owner_identifier, + withdrawal_key = ?previously_disabled_old_key, + method = "update_owner_withdrawal_address_v0", + "re-enabled withdrawal key to owner identity" + ); + + drive_operations.push(IdentityOperation(ReEnableIdentityKeys { + identity_id: owner_identifier, + keys_ids: vec![*key_id], + })); + } + } else { + let last_key_id = *old_withdrawal_identity_keys + .keys() + .max() + .expect("there must be keys, we already checked"); + + // add the new key + let new_owner_withdrawal_key = Self::get_owner_identity_withdrawal_key( + new_withdrawal_address, + last_key_id + 1, + platform_version, + )?; + + tracing::trace!( + identity_id = ?owner_identifier, + withdrawal_key = ?new_owner_withdrawal_key, + method = "update_owner_withdrawal_address_v1", + "add new withdrawal key to owner identity" + ); + + drive_operations.push(IdentityOperation(AddNewKeysToIdentity { + identity_id: owner_identifier, + unique_keys_to_add: vec![], + non_unique_keys_to_add: vec![new_owner_withdrawal_key], + })); + } + + Ok(()) + } +} diff --git a/packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/mod.rs index 659a7e94aa..56c1e17c6a 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/mod.rs @@ -3,6 +3,8 @@ mod v0; use crate::error::execution::ExecutionError; use crate::error::Error; use crate::platform_types::platform::Platform; +use crate::platform_types::platform_state::PlatformState; +use dpp::block::block_info::BlockInfo; use dpp::version::PlatformVersion; use dpp::version::ProtocolVersion; use drive::grovedb::Transaction; @@ -40,6 +42,8 @@ impl Platform { /// pub fn perform_events_on_first_block_of_protocol_change( &self, + platform_state: &PlatformState, + block_info: &BlockInfo, transaction: &Transaction, previous_protocol_version: ProtocolVersion, platform_version: &PlatformVersion, @@ -51,6 +55,8 @@ impl Platform { .perform_events_on_first_block_of_protocol_change { Some(0) => self.perform_events_on_first_block_of_protocol_change_v0( + platform_state, + block_info, transaction, previous_protocol_version, platform_version, diff --git a/packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs index 62a0d5a46f..f5cf202665 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs @@ -1,7 +1,14 @@ use crate::error::Error; use crate::platform_types::platform::Platform; +use crate::platform_types::platform_state::v0::PlatformStateV0Methods; +use crate::platform_types::platform_state::PlatformState; +use dpp::block::block_info::BlockInfo; +use dpp::dashcore::hashes::Hash; use dpp::version::PlatformVersion; use dpp::version::ProtocolVersion; +use drive::drive::identity::key::fetch::{ + IdentityKeysRequest, KeyIDIdentityPublicKeyPairBTreeMap, KeyRequestType, +}; use drive::drive::identity::withdrawals::paths::{ get_withdrawal_root_path, WITHDRAWAL_TRANSACTIONS_BROADCASTED_KEY, WITHDRAWAL_TRANSACTIONS_SUM_AMOUNT_TREE_KEY, @@ -30,12 +37,19 @@ impl Platform { /// * `Err(Error)`: If there was an issue executing the protocol-specific events. pub(super) fn perform_events_on_first_block_of_protocol_change_v0( &self, + platform_state: &PlatformState, + block_info: &BlockInfo, transaction: &Transaction, previous_protocol_version: ProtocolVersion, platform_version: &PlatformVersion, ) -> Result<(), Error> { if previous_protocol_version < 4 && platform_version.protocol_version >= 4 { - self.transition_to_version_4(transaction, platform_version)?; + self.transition_to_version_4( + platform_state, + block_info, + transaction, + platform_version, + )?; } Ok(()) @@ -57,9 +71,12 @@ impl Platform { /// * `Err(Error)`: If there was an issue creating or updating the necessary data structures. fn transition_to_version_4( &self, + platform_state: &PlatformState, + block_info: &BlockInfo, transaction: &Transaction, platform_version: &PlatformVersion, ) -> Result<(), Error> { + // We are adding the withdrawal transactions sum amount tree let path = get_withdrawal_root_path(); self.drive.grove_insert_if_not_exists( (&path).into(), @@ -69,6 +86,7 @@ impl Platform { None, &platform_version.drive, )?; + // We are adding a tree to store broadcasted transactions that might expire self.drive.grove_insert_if_not_exists( (&path).into(), &WITHDRAWAL_TRANSACTIONS_BROADCASTED_KEY, @@ -77,6 +95,57 @@ impl Platform { None, &platform_version.drive, )?; + // We need to add all masternode owner keys + // This is because owner identities only had a withdrawal key + // But no owner key + for masternode in platform_state.full_masternode_list().values() { + let masternode_id = masternode.pro_tx_hash.to_byte_array(); + let key_request = IdentityKeysRequest { + identity_id: masternode_id, + request_type: KeyRequestType::AllKeys, + limit: None, + offset: None, + }; + + let old_owner_identity_keys = self + .drive + .fetch_identity_keys::( + key_request, + Some(transaction), + platform_version, + )?; + + if old_owner_identity_keys.is_empty() { + continue; + } + + let last_key_id = *old_owner_identity_keys + .keys() + .max() + .expect("there must be keys, we already checked"); + + let new_owner_key = Self::get_owner_identity_owner_key( + masternode.state.owner_address, + last_key_id + 1, + platform_version, + )?; + + tracing::trace!( + identity_id = ?masternode_id, + withdrawal_key = ?new_owner_key, + method = "transition_to_version_4", + "add new owner key to owner identity" + ); + + self.drive.add_new_non_unique_keys_to_identity( + masternode_id, + vec![new_owner_key], + block_info, + true, + Some(transaction), + platform_version, + )?; + } Ok(()) } } 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 46ab19c2fd..bea2dcdccf 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 @@ -29,6 +29,7 @@ use crate::execution::validation::state_transition::identity_top_up::StateTransi use crate::execution::validation::state_transition::state_transitions::identity_update::advanced_structure::v0::IdentityUpdateStateTransitionIdentityAndSignaturesValidationV0; use crate::execution::validation::state_transition::state_transitions::identity_top_up::identity_retrieval::v0::IdentityTopUpStateTransitionIdentityRetrievalV0; use crate::execution::validation::state_transition::ValidationMode; +use crate::execution::validation::state_transition::state_transitions::identity_credit_withdrawal::signature_purpose_matches_requirements::IdentityCreditWithdrawalStateTransitionSignaturePurposeMatchesRequirementsValidation; pub(super) fn process_state_transition_v0<'a, C: CoreRPCLike>( platform: &'a PlatformRef, block_info: &BlockInfo, @@ -810,7 +811,6 @@ impl StateTransitionIdentityBasedSignatureValidationV0 for StateTransition { match self { StateTransition::DataContractCreate(_) | StateTransition::DataContractUpdate(_) - | StateTransition::IdentityCreditWithdrawal(_) | StateTransition::IdentityCreditTransfer(_) | StateTransition::DocumentsBatch(_) => { //Basic signature verification @@ -823,6 +823,29 @@ impl StateTransitionIdentityBasedSignatureValidationV0 for StateTransition { platform_version, )?) } + StateTransition::IdentityCreditWithdrawal(credit_withdrawal) => { + let mut consensus_validation_result = self + .validate_state_transition_identity_signed( + drive, + true, + false, + tx, + execution_context, + platform_version, + )?; + + if consensus_validation_result.is_valid_with_data() { + let validation_result = credit_withdrawal + .validate_signature_purpose_matches_requirements( + consensus_validation_result.data.as_ref().unwrap(), + platform_version, + )?; + if !validation_result.is_valid() { + consensus_validation_result.add_errors(validation_result.errors); + } + } + Ok(consensus_validation_result) + } StateTransition::IdentityUpdate(_) => { //Basic signature verification Ok(self.validate_state_transition_identity_signed( 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 c2eaee7f71..3b42e8e744 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 @@ -1,5 +1,6 @@ mod balance; mod nonce; +pub(crate) mod signature_purpose_matches_requirements; mod state; mod structure; @@ -131,7 +132,9 @@ impl StateTransitionStateValidationV0 for IdentityCreditWithdrawalTransition { #[cfg(test)] mod tests { use crate::config::{PlatformConfig, PlatformTestConfig}; - use crate::execution::validation::state_transition::tests::setup_identity_with_withdrawal_key_and_system_credits; + use crate::execution::validation::state_transition::tests::{ + setup_identity_with_withdrawal_key_and_system_credits, setup_masternode_owner_identity, + }; use crate::platform_types::state_transitions_processing_result::StateTransitionExecutionResult; use crate::test::helpers::fast_forward_to_block::fast_forward_to_block; use crate::test::helpers::setup::TestPlatformBuilder; @@ -378,6 +381,154 @@ mod tests { ); } + #[test] + fn test_masternode_credit_withdrawal_without_withdrawal_address_creates_withdrawal_document_when_signing_with_withdrawal_key( + ) { + let platform_version = PlatformVersion::latest(); + let platform_config = PlatformConfig { + testing_configs: PlatformTestConfig { + disable_instant_lock_signature_verification: true, + ..Default::default() + }, + ..Default::default() + }; + + let mut rng = StdRng::seed_from_u64(529); + + let mut platform = TestPlatformBuilder::new() + .with_config(platform_config) + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + fast_forward_to_block(&platform, 1_200_000_000, 900, 42, 1, false); //next epoch + + let (identity, signer, _, _) = setup_masternode_owner_identity( + &mut platform, + rng.gen(), + dash_to_credits!(0.5), + platform_version, + ); + + let platform_state = platform.state.load(); + + let withdrawal_amount = dash_to_credits!(0.1); + + let credit_withdrawal_transition = IdentityCreditWithdrawalTransition::try_from_identity( + &identity, + None, + withdrawal_amount, + Pooling::Never, + 1, + 0, + signer, + None, + PreferredKeyPurposeForSigningWithdrawal::TransferOnly, + 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_with_time(1_200_001_000), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(..)] + ); + } + + #[test] + fn test_masternode_credit_withdrawal_without_withdrawal_address_creates_withdrawal_document_when_signing_with_owner_key( + ) { + let platform_version = PlatformVersion::latest(); + let platform_config = PlatformConfig { + testing_configs: PlatformTestConfig { + disable_instant_lock_signature_verification: true, + ..Default::default() + }, + ..Default::default() + }; + + let mut rng = StdRng::seed_from_u64(529); + + let mut platform = TestPlatformBuilder::new() + .with_config(platform_config) + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + fast_forward_to_block(&platform, 1_200_000_000, 900, 42, 1, false); //next epoch + + let (identity, signer, _, _) = setup_masternode_owner_identity( + &mut platform, + rng.gen(), + dash_to_credits!(0.5), + platform_version, + ); + + let platform_state = platform.state.load(); + + let withdrawal_amount = dash_to_credits!(0.1); + + let credit_withdrawal_transition = IdentityCreditWithdrawalTransition::try_from_identity( + &identity, + None, + withdrawal_amount, + Pooling::Never, + 1, + 0, + signer, + None, + PreferredKeyPurposeForSigningWithdrawal::OwnerOnly, + 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_with_time(1_200_001_000), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(..)] + ); + } + mod errors { use super::*; use dpp::consensus::state::state_error::StateError; @@ -458,5 +609,84 @@ mod tests { )] ); } + + #[test] + fn test_masternode_credit_withdrawal_with_withdrawal_address_creates_when_signing_with_owner_key_should_fail( + ) { + let platform_version = PlatformVersion::latest(); + let platform_config = PlatformConfig { + testing_configs: PlatformTestConfig { + disable_instant_lock_signature_verification: true, + ..Default::default() + }, + ..Default::default() + }; + + let mut rng = StdRng::seed_from_u64(529); + + let mut platform = TestPlatformBuilder::new() + .with_config(platform_config) + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + fast_forward_to_block(&platform, 1_200_000_000, 900, 42, 1, false); //next epoch + + let (identity, signer, _, _) = setup_masternode_owner_identity( + &mut platform, + rng.gen(), + dash_to_credits!(0.5), + platform_version, + ); + + let platform_state = platform.state.load(); + + let withdrawal_amount = dash_to_credits!(0.1); + + let credit_withdrawal_transition = + IdentityCreditWithdrawalTransition::try_from_identity( + &identity, + Some(CoreScript::random_p2pkh(&mut rng)), + withdrawal_amount, + Pooling::Never, + 1, + 0, + signer, + None, + PreferredKeyPurposeForSigningWithdrawal::OwnerOnly, + 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_with_time(1_200_001_000), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::UnpaidConsensusError( + ConsensusError::BasicError( + BasicError::WithdrawalOutputScriptNotAllowedWhenSigningWithOwnerKeyError(_) + ) + )] + ); + } } } diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_withdrawal/signature_purpose_matches_requirements/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_withdrawal/signature_purpose_matches_requirements/mod.rs new file mode 100644 index 0000000000..d82846f84e --- /dev/null +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_withdrawal/signature_purpose_matches_requirements/mod.rs @@ -0,0 +1,45 @@ +pub(crate) mod v0; + +use crate::error::Error; +use dpp::identity::PartialIdentity; +use dpp::state_transition::identity_credit_withdrawal_transition::IdentityCreditWithdrawalTransition; +use dpp::validation::SimpleConsensusValidationResult; +use dpp::version::PlatformVersion; +use crate::error::execution::ExecutionError; +use crate::execution::validation::state_transition::identity_credit_withdrawal::signature_purpose_matches_requirements::v0::IdentityCreditWithdrawalStateTransitionSignaturePurposeMatchesRequirementsValidationV0; + +pub(in crate::execution::validation::state_transition) trait IdentityCreditWithdrawalStateTransitionSignaturePurposeMatchesRequirementsValidation +{ + fn validate_signature_purpose_matches_requirements( + &self, + identity: &PartialIdentity, + platform_version: &PlatformVersion, + ) -> Result; +} + +impl IdentityCreditWithdrawalStateTransitionSignaturePurposeMatchesRequirementsValidation + for IdentityCreditWithdrawalTransition +{ + fn validate_signature_purpose_matches_requirements( + &self, + identity: &PartialIdentity, + platform_version: &PlatformVersion, + ) -> Result { + match platform_version + .drive_abci + .validation_and_processing + .state_transitions + .identity_credit_withdrawal_state_transition_purpose_matches_requirements + { + 0 => self.validate_signature_purpose_matches_requirements_v0( + identity, + platform_version, + ), + version => Err(Error::Execution(ExecutionError::UnknownVersionMismatch { + method: "identity credit withdrawal transition: validate_signature_purpose_matches_requirements".to_string(), + known_versions: vec![0], + received: version, + })), + } + } +} diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_withdrawal/signature_purpose_matches_requirements/v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_withdrawal/signature_purpose_matches_requirements/v0/mod.rs new file mode 100644 index 0000000000..4bdd1ff1a2 --- /dev/null +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_credit_withdrawal/signature_purpose_matches_requirements/v0/mod.rs @@ -0,0 +1,53 @@ +use crate::error::execution::ExecutionError; +use crate::error::Error; +use dpp::consensus::basic::identity::WithdrawalOutputScriptNotAllowedWhenSigningWithOwnerKeyError; +use dpp::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0; +use dpp::identity::{PartialIdentity, Purpose}; +use dpp::state_transition::identity_credit_withdrawal_transition::accessors::IdentityCreditWithdrawalTransitionAccessorsV0; +use dpp::state_transition::identity_credit_withdrawal_transition::IdentityCreditWithdrawalTransition; +use dpp::state_transition::StateTransitionIdentitySigned; +use dpp::validation::SimpleConsensusValidationResult; +use dpp::version::PlatformVersion; + +pub(super) trait IdentityCreditWithdrawalStateTransitionSignaturePurposeMatchesRequirementsValidationV0 +{ + fn validate_signature_purpose_matches_requirements_v0( + &self, + identity: &PartialIdentity, + platform_version: &PlatformVersion, + ) -> Result; +} + +impl IdentityCreditWithdrawalStateTransitionSignaturePurposeMatchesRequirementsValidationV0 + for IdentityCreditWithdrawalTransition +{ + fn validate_signature_purpose_matches_requirements_v0( + &self, + identity: &PartialIdentity, + _platform_version: &PlatformVersion, + ) -> Result { + let mut result = SimpleConsensusValidationResult::default(); + + if let Some(output_script) = self.output_script() { + let Some(signing_key) = identity + .loaded_public_keys + .get(&self.signature_public_key_id()) + else { + return Err(Error::Execution(ExecutionError::CorruptedCodeExecution( + "we should have a loaded key at this point", + ))); + }; + + if signing_key.purpose() == Purpose::OWNER { + result.add_error( + WithdrawalOutputScriptNotAllowedWhenSigningWithOwnerKeyError::new( + output_script.clone(), + signing_key.id(), + ), + ); + } + } + + Ok(result) + } +} diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_update/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_update/mod.rs index 0b6adcdf0d..629240407a 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_update/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_update/mod.rs @@ -116,9 +116,13 @@ mod tests { use crate::execution::validation::state_transition::tests::{ setup_add_key_to_identity, setup_identity_return_master_key, }; + 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::ConsensusError; use dpp::dash_to_credits; + use dpp::dashcore::key::{KeyPair, Secp256k1}; use dpp::data_contract::accessors::v0::DataContractV0Getters; use dpp::identity::accessors::IdentityGettersV0; use dpp::identity::contract_bounds::ContractBounds; @@ -128,8 +132,11 @@ mod tests { use dpp::serialization::{PlatformSerializable, Signable}; use dpp::state_transition::identity_update_transition::v0::IdentityUpdateTransitionV0; use dpp::state_transition::identity_update_transition::IdentityUpdateTransition; + use dpp::state_transition::public_key_in_creation::v0::IdentityPublicKeyInCreationV0; use dpp::state_transition::StateTransition; use platform_version::version::PlatformVersion; + use rand::rngs::StdRng; + use rand::SeedableRng; #[test] fn test_identity_update_that_disables_an_authentication_key() { @@ -337,4 +344,109 @@ mod tests { .join(" | ") ); } + + #[test] + fn test_identity_update_adding_owner_key_not_allowed() { + let platform_config = PlatformConfig { + testing_configs: PlatformTestConfig { + disable_instant_lock_signature_verification: true, + ..Default::default() + }, + ..Default::default() + }; + + let platform_version = PlatformVersion::latest(); + + let mut platform = TestPlatformBuilder::new() + .with_config(platform_config) + .build_with_mock_rpc() + .set_genesis_state(); + + let (identity, signer, key) = + setup_identity_return_master_key(&mut platform, 958, dash_to_credits!(0.1)); + + let platform_state = platform.state.load(); + + let secp = Secp256k1::new(); + + let mut rng = StdRng::seed_from_u64(1292); + + let new_key_pair = KeyPair::new(&secp, &mut rng); + + let new_key = IdentityPublicKeyInCreationV0 { + id: 2, + purpose: Purpose::OWNER, + security_level: SecurityLevel::HIGH, + key_type: KeyType::ECDSA_SECP256K1, + read_only: false, + data: new_key_pair.public_key().serialize().to_vec().into(), + signature: Default::default(), + contract_bounds: None, + }; + + let update_transition: IdentityUpdateTransition = IdentityUpdateTransitionV0 { + identity_id: identity.id(), + revision: 1, + nonce: 1, + add_public_keys: vec![new_key.into()], + disable_public_keys: vec![], + user_fee_increase: 0, + signature_public_key_id: key.id(), + signature: Default::default(), + } + .into(); + + let mut update_transition: StateTransition = update_transition.into(); + + let data = update_transition + .signable_bytes() + .expect("expected signable bytes"); + update_transition.set_signature( + signer + .sign(&key, data.as_slice()) + .expect("expected to sign"), + ); + + let update_transition_bytes = update_transition + .serialize_to_bytes() + .expect("expected to serialize"); + + let transaction = platform.drive.grove.start_transaction(); + + let processing_result = platform + .platform + .process_raw_state_transitions( + &vec![update_transition_bytes.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + true, + None, + ) + .expect("expected to process state transition"); + + // We expect there to be an error because you should not be able to add owner keys + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::UnpaidConsensusError( + ConsensusError::BasicError(_) + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit"); + + let issues = platform + .drive + .grove + .visualize_verify_grovedb(None, true, false, &platform_version.drive.grove_version) + .expect("expected to have no issues"); + + assert_eq!(issues.len(), 0); + } } 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 51df74cde5..b0986e026d 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 @@ -512,6 +512,111 @@ pub(in crate::execution) mod tests { ); } + pub(in crate::execution) fn setup_masternode_owner_identity( + platform: &mut TempPlatform, + seed: u64, + credits: Credits, + platform_version: &PlatformVersion, + ) -> (Identity, SimpleSigner, IdentityPublicKey, IdentityPublicKey) { + let mut signer = SimpleSigner::default(); + + platform + .drive + .add_to_system_credits(credits, None, platform_version) + .expect("expected to add to system credits"); + + let mut rng = StdRng::seed_from_u64(seed); + + let (transfer_key, transfer_private_key) = + IdentityPublicKey::random_masternode_transfer_key_with_rng( + 0, + &mut rng, + platform_version, + ) + .expect("expected to get key pair"); + + let (owner_key, owner_private_key) = + IdentityPublicKey::random_masternode_owner_key_with_rng(1, &mut rng, platform_version) + .expect("expected to get key pair"); + + let owner_address = owner_key + .public_key_hash() + .expect("expected a public key hash"); + + let payout_address = transfer_key + .public_key_hash() + .expect("expected a public key hash"); + + signer.add_key(transfer_key.clone(), transfer_private_key.clone()); + signer.add_key(owner_key.clone(), owner_private_key.clone()); + + let pro_tx_hash_bytes: [u8; 32] = rng.gen(); + + let identity: Identity = IdentityV0 { + id: pro_tx_hash_bytes.into(), + public_keys: BTreeMap::from([(0, transfer_key.clone()), (1, owner_key.clone())]), + balance: credits, + revision: 0, + } + .into(); + + // We just add this identity to the system first + + platform + .drive + .add_new_identity( + identity.clone(), + true, + &BlockInfo::default(), + true, + None, + platform_version, + ) + .expect("expected to add a new identity"); + + let mut platform_state = platform.state.load().clone().deref().clone(); + + let pro_tx_hash = ProTxHash::from_byte_array(pro_tx_hash_bytes); + + let random_ip = Ipv4Addr::new( + rng.gen_range(0..255), + rng.gen_range(0..255), + rng.gen_range(0..255), + rng.gen_range(0..255), + ); + + platform_state.full_masternode_list_mut().insert( + pro_tx_hash, + MasternodeListItem { + node_type: MasternodeType::Regular, + pro_tx_hash, + collateral_hash: Txid::from_byte_array(rng.gen()), + collateral_index: 0, + collateral_address: rng.gen(), + operator_reward: 0.0, + state: DMNState { + service: SocketAddr::new(IpAddr::V4(random_ip), 19999), + registered_height: 0, + pose_revived_height: None, + pose_ban_height: None, + revocation_reason: 0, + owner_address, + voting_address: rng.gen(), + payout_address, + pub_key_operator: vec![], + operator_payout_address: None, + platform_node_id: None, + platform_p2p_port: None, + platform_http_port: None, + }, + }, + ); + + platform.state.store(Arc::new(platform_state)); + + (identity, signer, owner_key, transfer_key) + } + pub(in crate::execution) fn setup_masternode_voting_identity( platform: &mut TempPlatform, seed: u64, diff --git a/packages/rs-drive/src/drive/identity/estimation_costs/for_purpose_in_key_reference_tree/v0/mod.rs b/packages/rs-drive/src/drive/identity/estimation_costs/for_purpose_in_key_reference_tree/v0/mod.rs index f7a49a2bd7..68d4852ad3 100644 --- a/packages/rs-drive/src/drive/identity/estimation_costs/for_purpose_in_key_reference_tree/v0/mod.rs +++ b/packages/rs-drive/src/drive/identity/estimation_costs/for_purpose_in_key_reference_tree/v0/mod.rs @@ -45,6 +45,7 @@ impl Drive { Purpose::TRANSFER => ApproximateElements(1), Purpose::SYSTEM => ApproximateElements(1), Purpose::VOTING => ApproximateElements(1), + Purpose::OWNER => ApproximateElements(1), }; let estimated_layer_sizes = match purpose { @@ -58,6 +59,7 @@ impl Drive { Purpose::TRANSFER => AllReference(1, KEY_REFERENCE_SIZE, None), Purpose::SYSTEM => AllReference(1, KEY_REFERENCE_SIZE, None), Purpose::VOTING => AllReference(1, KEY_REFERENCE_SIZE, None), + Purpose::OWNER => AllReference(1, KEY_REFERENCE_SIZE, None), }; // we then need to insert the identity keys layer estimated_costs_only_with_layer_info.insert( diff --git a/packages/rs-drive/src/drive/identity/key/fetch/mod.rs b/packages/rs-drive/src/drive/identity/key/fetch/mod.rs index 248e7cd904..75466736bd 100644 --- a/packages/rs-drive/src/drive/identity/key/fetch/mod.rs +++ b/packages/rs-drive/src/drive/identity/key/fetch/mod.rs @@ -691,8 +691,8 @@ impl IdentityKeysRequest { sec_btree_map.insert(security_level, CurrentKeyOfKindRequest); } let mut purpose_btree_map = BTreeMap::new(); - for purpose in 0..=Purpose::last() as u8 { - purpose_btree_map.insert(purpose, sec_btree_map.clone()); + for purpose in Purpose::searchable_purposes() { + purpose_btree_map.insert(purpose as u8, sec_btree_map.clone()); } IdentityKeysRequest { identity_id, diff --git a/packages/rs-drive/src/util/batch/drive_op_batch/withdrawals.rs b/packages/rs-drive/src/util/batch/drive_op_batch/withdrawals.rs index 2a95fc39a7..26bf6617a7 100644 --- a/packages/rs-drive/src/util/batch/drive_op_batch/withdrawals.rs +++ b/packages/rs-drive/src/util/batch/drive_op_batch/withdrawals.rs @@ -1,8 +1,7 @@ use std::collections::HashMap; use crate::drive::identity::withdrawals::paths::{ - get_withdrawal_root_path_vec, get_withdrawal_transactions_broadcasted_path, - get_withdrawal_transactions_broadcasted_path_vec, get_withdrawal_transactions_queue_path, + get_withdrawal_root_path_vec, get_withdrawal_transactions_broadcasted_path_vec, get_withdrawal_transactions_queue_path_vec, get_withdrawal_transactions_sum_tree_path_vec, WITHDRAWAL_TRANSACTIONS_NEXT_INDEX_KEY, }; 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 f7d5dda710..bb74c9d433 100644 --- a/packages/rs-platform-version/src/version/drive_abci_versions.rs +++ b/packages/rs-platform-version/src/version/drive_abci_versions.rs @@ -200,6 +200,7 @@ pub struct DriveAbciStateTransitionValidationVersions { pub identity_update_state_transition: DriveAbciStateTransitionValidationVersion, pub identity_top_up_state_transition: DriveAbciStateTransitionValidationVersion, pub identity_credit_withdrawal_state_transition: DriveAbciStateTransitionValidationVersion, + pub identity_credit_withdrawal_state_transition_purpose_matches_requirements: FeatureVersion, pub identity_credit_transfer_state_transition: DriveAbciStateTransitionValidationVersion, pub masternode_vote_state_transition: DriveAbciStateTransitionValidationVersion, pub contract_create_state_transition: DriveAbciStateTransitionValidationVersion, @@ -247,6 +248,7 @@ pub struct DriveAbciMasternodeIdentitiesUpdatesMethodVersions { pub get_voter_identity_key: FeatureVersion, pub get_operator_identity_keys: FeatureVersion, pub get_owner_identity_withdrawal_key: FeatureVersion, + pub get_owner_identity_owner_key: FeatureVersion, pub get_voter_identifier_from_masternode_list_item: FeatureVersion, pub get_operator_identifier_from_masternode_list_item: FeatureVersion, pub create_operator_identity: FeatureVersion, 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 28db2e2a7b..380e9ae88e 100644 --- a/packages/rs-platform-version/src/version/mocks/v2_test.rs +++ b/packages/rs-platform-version/src/version/mocks/v2_test.rs @@ -629,6 +629,7 @@ pub const TEST_PLATFORM_V2: PlatformVersion = PlatformVersion { get_voter_identity_key: 0, get_operator_identity_keys: 0, get_owner_identity_withdrawal_key: 0, + get_owner_identity_owner_key: 0, get_voter_identifier_from_masternode_list_item: 0, get_operator_identifier_from_masternode_list_item: 0, create_operator_identity: 0, @@ -769,6 +770,7 @@ pub const TEST_PLATFORM_V2: PlatformVersion = PlatformVersion { state: 0, transform_into_action: 0, }, + identity_credit_withdrawal_state_transition_purpose_matches_requirements: 0, identity_credit_transfer_state_transition: DriveAbciStateTransitionValidationVersion { basic_structure: Some(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 1f9c5eb579..436b1d9b89 100644 --- a/packages/rs-platform-version/src/version/mocks/v3_test.rs +++ b/packages/rs-platform-version/src/version/mocks/v3_test.rs @@ -629,6 +629,7 @@ pub const TEST_PLATFORM_V3: PlatformVersion = PlatformVersion { get_voter_identity_key: 0, get_operator_identity_keys: 0, get_owner_identity_withdrawal_key: 0, + get_owner_identity_owner_key: 0, get_voter_identifier_from_masternode_list_item: 0, get_operator_identifier_from_masternode_list_item: 0, create_operator_identity: 0, @@ -769,6 +770,7 @@ pub const TEST_PLATFORM_V3: PlatformVersion = PlatformVersion { state: 0, transform_into_action: 0, }, + identity_credit_withdrawal_state_transition_purpose_matches_requirements: 0, identity_credit_transfer_state_transition: DriveAbciStateTransitionValidationVersion { basic_structure: Some(0), diff --git a/packages/rs-platform-version/src/version/v1.rs b/packages/rs-platform-version/src/version/v1.rs index 8104775a5e..c762c864b2 100644 --- a/packages/rs-platform-version/src/version/v1.rs +++ b/packages/rs-platform-version/src/version/v1.rs @@ -628,6 +628,7 @@ pub const PLATFORM_V1: PlatformVersion = PlatformVersion { get_voter_identity_key: 0, get_operator_identity_keys: 0, get_owner_identity_withdrawal_key: 0, + get_owner_identity_owner_key: 0, get_voter_identifier_from_masternode_list_item: 0, get_operator_identifier_from_masternode_list_item: 0, create_operator_identity: 0, @@ -768,6 +769,7 @@ pub const PLATFORM_V1: PlatformVersion = PlatformVersion { state: 0, transform_into_action: 0, }, + identity_credit_withdrawal_state_transition_purpose_matches_requirements: 0, identity_credit_transfer_state_transition: DriveAbciStateTransitionValidationVersion { basic_structure: Some(0), diff --git a/packages/rs-platform-version/src/version/v2.rs b/packages/rs-platform-version/src/version/v2.rs index 3f9931df8a..7df825fb32 100644 --- a/packages/rs-platform-version/src/version/v2.rs +++ b/packages/rs-platform-version/src/version/v2.rs @@ -628,6 +628,7 @@ pub const PLATFORM_V2: PlatformVersion = PlatformVersion { get_voter_identity_key: 0, get_operator_identity_keys: 0, get_owner_identity_withdrawal_key: 0, + get_owner_identity_owner_key: 0, get_voter_identifier_from_masternode_list_item: 0, get_operator_identifier_from_masternode_list_item: 0, create_operator_identity: 0, @@ -768,6 +769,7 @@ pub const PLATFORM_V2: PlatformVersion = PlatformVersion { state: 0, transform_into_action: 0, }, + identity_credit_withdrawal_state_transition_purpose_matches_requirements: 0, identity_credit_transfer_state_transition: DriveAbciStateTransitionValidationVersion { basic_structure: Some(0), diff --git a/packages/rs-platform-version/src/version/v3.rs b/packages/rs-platform-version/src/version/v3.rs index b3c7cf3cc4..609a9a9015 100644 --- a/packages/rs-platform-version/src/version/v3.rs +++ b/packages/rs-platform-version/src/version/v3.rs @@ -636,6 +636,7 @@ pub const PLATFORM_V3: PlatformVersion = PlatformVersion { get_voter_identity_key: 0, get_operator_identity_keys: 0, get_owner_identity_withdrawal_key: 0, + get_owner_identity_owner_key: 0, get_voter_identifier_from_masternode_list_item: 0, get_operator_identifier_from_masternode_list_item: 0, create_operator_identity: 0, @@ -776,6 +777,7 @@ pub const PLATFORM_V3: PlatformVersion = PlatformVersion { state: 0, transform_into_action: 0, }, + identity_credit_withdrawal_state_transition_purpose_matches_requirements: 0, identity_credit_transfer_state_transition: DriveAbciStateTransitionValidationVersion { basic_structure: Some(0), diff --git a/packages/rs-platform-version/src/version/v4.rs b/packages/rs-platform-version/src/version/v4.rs index c607170dec..dd693aa2f8 100644 --- a/packages/rs-platform-version/src/version/v4.rs +++ b/packages/rs-platform-version/src/version/v4.rs @@ -630,15 +630,16 @@ pub const PLATFORM_V4: PlatformVersion = PlatformVersion { get_voter_identity_key: 0, get_operator_identity_keys: 0, get_owner_identity_withdrawal_key: 0, + get_owner_identity_owner_key: 0, get_voter_identifier_from_masternode_list_item: 0, get_operator_identifier_from_masternode_list_item: 0, create_operator_identity: 0, - create_owner_identity: 0, + create_owner_identity: 1, create_voter_identity: 0, disable_identity_keys: 0, update_masternode_identities: 0, update_operator_identity: 0, - update_owner_withdrawal_address: 0, + update_owner_withdrawal_address: 1, update_voter_identity: 0, }, }, @@ -770,6 +771,7 @@ pub const PLATFORM_V4: PlatformVersion = PlatformVersion { state: 0, transform_into_action: 0, }, + identity_credit_withdrawal_state_transition_purpose_matches_requirements: 0, identity_credit_transfer_state_transition: DriveAbciStateTransitionValidationVersion { basic_structure: Some(0), diff --git a/packages/rs-sdk/src/platform/fetch_many.rs b/packages/rs-sdk/src/platform/fetch_many.rs index 897be764ae..eede165d26 100644 --- a/packages/rs-sdk/src/platform/fetch_many.rs +++ b/packages/rs-sdk/src/platform/fetch_many.rs @@ -31,14 +31,13 @@ use dpp::{ block::extended_epoch_info::ExtendedEpochInfo, voting::votes::resource_vote::ResourceVote, }; use dpp::{document::Document, voting::contender_structs::ContenderWithSerializedDocument}; -use drive::grovedb::query_result_type::{Key, Path}; +use drive::grovedb::query_result_type::Key; use drive::grovedb::Element; use drive_proof_verifier::types::{ Contenders, ContestedResource, ContestedResources, DataContracts, Elements, ExtendedEpochInfos, - IdentityBalances, IdentityPublicKeys, KeysInPath, MasternodeProtocolVote, - MasternodeProtocolVotes, ProposerBlockCountById, ProposerBlockCountByRange, - ProposerBlockCounts, ProtocolVersionUpgrades, ResourceVotesByIdentity, - VotePollsGroupedByTimestamp, Voter, Voters, + IdentityBalances, IdentityPublicKeys, MasternodeProtocolVote, MasternodeProtocolVotes, + ProposerBlockCountById, ProposerBlockCountByRange, ProposerBlockCounts, + ProtocolVersionUpgrades, ResourceVotesByIdentity, VotePollsGroupedByTimestamp, Voter, Voters, }; use drive_proof_verifier::{types::Documents, FromProof}; use rs_dapi_client::{transport::TransportRequest, DapiRequest, RequestSettings}; diff --git a/packages/wasm-dpp/src/errors/consensus/consensus_error.rs b/packages/wasm-dpp/src/errors/consensus/consensus_error.rs index 642dce93fc..ba828e674e 100644 --- a/packages/wasm-dpp/src/errors/consensus/consensus_error.rs +++ b/packages/wasm-dpp/src/errors/consensus/consensus_error.rs @@ -63,7 +63,7 @@ use dpp::consensus::state::data_trigger::DataTriggerError::{ use wasm_bindgen::{JsError, JsValue}; use dpp::consensus::basic::data_contract::{ContestedUniqueIndexOnMutableDocumentTypeError, ContestedUniqueIndexWithUniqueIndexError, InvalidDocumentTypeRequiredSecurityLevelError, UnknownDocumentCreationRestrictionModeError, UnknownSecurityLevelError, UnknownStorageKeyRequirementsError, UnknownTradeModeError, UnknownTransferableTypeError}; use dpp::consensus::basic::document::{ContestedDocumentsTemporarilyNotAllowedError, DocumentCreationNotAllowedError, DocumentFieldMaxSizeExceededError, MaxDocumentsTransitionsExceededError, MissingPositionsInDocumentTypePropertiesError}; -use dpp::consensus::basic::identity::{DataContractBoundsNotPresentError, DisablingKeyIdAlsoBeingAddedInSameTransitionError, InvalidIdentityCreditWithdrawalTransitionAmountError, InvalidIdentityUpdateTransitionDisableKeysError, InvalidIdentityUpdateTransitionEmptyError, TooManyMasterPublicKeyError}; +use dpp::consensus::basic::identity::{DataContractBoundsNotPresentError, DisablingKeyIdAlsoBeingAddedInSameTransitionError, InvalidIdentityCreditWithdrawalTransitionAmountError, InvalidIdentityUpdateTransitionDisableKeysError, InvalidIdentityUpdateTransitionEmptyError, TooManyMasterPublicKeyError, WithdrawalOutputScriptNotAllowedWhenSigningWithOwnerKeyError}; use dpp::consensus::basic::overflow_error::OverflowError; use dpp::consensus::state::data_contract::document_type_update_error::DocumentTypeUpdateError; use dpp::consensus::state::document::document_contest_currently_locked_error::DocumentContestCurrentlyLockedError; @@ -560,6 +560,13 @@ fn from_basic_error(basic_error: &BasicError) -> JsValue { BasicError::ContestedDocumentsTemporarilyNotAllowedError(e) => { generic_consensus_error!(ContestedDocumentsTemporarilyNotAllowedError, e).into() } + BasicError::WithdrawalOutputScriptNotAllowedWhenSigningWithOwnerKeyError(e) => { + generic_consensus_error!( + WithdrawalOutputScriptNotAllowedWhenSigningWithOwnerKeyError, + e + ) + .into() + } } } diff --git a/packages/wasm-dpp/src/identity/identity_public_key/purpose.rs b/packages/wasm-dpp/src/identity/identity_public_key/purpose.rs index 5e598ffa52..33503d6179 100644 --- a/packages/wasm-dpp/src/identity/identity_public_key/purpose.rs +++ b/packages/wasm-dpp/src/identity/identity_public_key/purpose.rs @@ -15,6 +15,8 @@ pub enum PurposeWasm { SYSTEM = 4, /// this key cannot be used for signing documents VOTING = 5, + /// this key is only for masternode owners + OWNER = 6, } impl From for PurposeWasm { @@ -26,6 +28,7 @@ impl From for PurposeWasm { Purpose::TRANSFER => PurposeWasm::TRANSFER, Purpose::SYSTEM => PurposeWasm::SYSTEM, Purpose::VOTING => PurposeWasm::VOTING, + Purpose::OWNER => PurposeWasm::OWNER, } } }