From 4bfc573b029d5c493519a734ffd97ec7e38fc596 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Wed, 22 Jan 2025 17:03:04 +0700 Subject: [PATCH 1/6] more tests for token update --- .../document/batch_transition/methods/mod.rs | 62 ++++++++++ .../batch_transition/methods/v1/mod.rs | 21 ++++ .../batch_transition/v1/v0_methods.rs | 93 ++++++++++++++- .../state_transitions/batch/mod.rs | 108 ++++++++++++++++++ .../rs-drive/src/drive/group/fetch/mod.rs | 1 - packages/rs-drive/src/drive/group/mod.rs | 1 + .../src/drive/group/{fetch => }/queries.rs | 0 packages/rs-drive/src/drive/tokens/mod.rs | 3 - .../v0/mod.rs | 5 +- 9 files changed, 286 insertions(+), 8 deletions(-) rename packages/rs-drive/src/drive/group/{fetch => }/queries.rs (100%) diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/methods/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/methods/mod.rs index a92bc453fb2..7324529d841 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/methods/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/methods/mod.rs @@ -35,6 +35,7 @@ use crate::ProtocolError; use platform_value::Identifier; #[cfg(feature = "state-transition-signing")] use platform_version::version::{FeatureVersion, PlatformVersion}; +use crate::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; pub mod v0; pub mod v1; @@ -860,4 +861,65 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransition { }), } } + + #[cfg(feature = "state-transition-signing")] + fn new_token_config_update_transition( + token_id: Identifier, + owner_id: Identifier, + data_contract_id: Identifier, + token_contract_position: u16, + update_token_configuration_item: TokenConfigurationChangeItem, + public_note: Option, + using_group_info: Option, + identity_public_key: &IdentityPublicKey, + identity_contract_nonce: IdentityNonce, + user_fee_increase: UserFeeIncrease, + signer: &S, + platform_version: &PlatformVersion, + batch_feature_version: Option, + config_update_feature_version: Option, + base_feature_version: Option, + ) -> Result { + match batch_feature_version.unwrap_or( + platform_version + .dpp + .state_transition_serialization_versions + .batch_state_transition + .default_current_version, + ) { + 1 | 0 + if platform_version + .dpp + .state_transition_serialization_versions + .batch_state_transition + .max_version + >= 1 => + { + // Create the emergency action transition for batch version 1 + BatchTransitionV1::new_token_config_update_transition( + token_id, + owner_id, + data_contract_id, + token_contract_position, + update_token_configuration_item, + public_note, + using_group_info, + identity_public_key, + identity_contract_nonce, + user_fee_increase, + signer, + platform_version, + batch_feature_version, + config_update_feature_version, + base_feature_version, + ) + } + version => Err(ProtocolError::UnknownVersionMismatch { + method: "DocumentsBatchTransition::new_token_config_update_transition" + .to_string(), + known_versions: vec![1], + received: version, + }), + } + } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/methods/v1/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/methods/v1/mod.rs index ea9c43e7d4f..67c4dfa7d6d 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/methods/v1/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/methods/v1/mod.rs @@ -24,6 +24,8 @@ use crate::ProtocolError; use platform_value::Identifier; #[cfg(feature = "state-transition-signing")] use platform_version::version::PlatformVersion; +#[cfg(feature = "state-transition-signing")] +use crate::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; pub trait DocumentsBatchTransitionMethodsV1: DocumentsBatchTransitionAccessorsV0 { #[cfg(feature = "state-transition-signing")] @@ -165,4 +167,23 @@ pub trait DocumentsBatchTransitionMethodsV1: DocumentsBatchTransitionAccessorsV0 delete_feature_version: Option, base_feature_version: Option, ) -> Result; + + #[cfg(feature = "state-transition-signing")] + fn new_token_config_update_transition( + token_id: Identifier, + owner_id: Identifier, + data_contract_id: Identifier, + token_contract_position: u16, + update_token_configuration_item: TokenConfigurationChangeItem, + public_note: Option, + using_group_info: Option, + identity_public_key: &IdentityPublicKey, + identity_contract_nonce: IdentityNonce, + user_fee_increase: UserFeeIncrease, + signer: &S, + platform_version: &PlatformVersion, + batch_feature_version: Option, + config_update_feature_version: Option, + base_feature_version: Option, + ) -> Result; } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v0_methods.rs index 0ae1f0abcc9..da329aeccb6 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v0_methods.rs @@ -31,7 +31,7 @@ use crate::state_transition::batch_transition::methods::v0::DocumentsBatchTransi use std::iter::Map; use std::slice::Iter; -use crate::state_transition::batch_transition::{BatchTransitionV1, TokenBurnTransition, TokenDestroyFrozenFundsTransition, TokenEmergencyActionTransition, TokenFreezeTransition, TokenMintTransition, TokenTransferTransition, TokenUnfreezeTransition}; +use crate::state_transition::batch_transition::{BatchTransitionV1, TokenBurnTransition, TokenConfigUpdateTransition, TokenDestroyFrozenFundsTransition, TokenEmergencyActionTransition, TokenFreezeTransition, TokenMintTransition, TokenTransferTransition, TokenUnfreezeTransition}; #[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::{ BatchTransition, DocumentDeleteTransition, @@ -44,22 +44,43 @@ use platform_value::Identifier; #[cfg(feature = "state-transition-signing")] use platform_version::version::{FeatureVersion, PlatformVersion}; use crate::balances::credits::TokenAmount; +#[cfg(feature = "state-transition-signing")] +use crate::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; +#[cfg(feature = "state-transition-signing")] use crate::group::{GroupStateTransitionInfo, GroupStateTransitionInfoStatus}; +#[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::document_create_transition::v0::v0_methods::DocumentCreateTransitionV0Methods; +#[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::batched_transition::document_purchase_transition::v0::v0_methods::DocumentPurchaseTransitionV0Methods; +#[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; +#[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::methods::v1::DocumentsBatchTransitionMethodsV1; +#[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::resolvers::v0::BatchTransitionResolversV0; +#[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; +#[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; +#[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::token_base_transition::v0::TokenBaseTransitionV0; +#[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::token_base_transition::v0::v0_methods::TokenBaseTransitionV0Methods; +#[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::token_burn_transition::TokenBurnTransitionV0; +#[cfg(feature = "state-transition-signing")] +use crate::state_transition::batch_transition::token_config_update_transition::TokenConfigUpdateTransitionV0; +#[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::token_destroy_frozen_funds_transition::TokenDestroyFrozenFundsTransitionV0; +#[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::token_emergency_action_transition::TokenEmergencyActionTransitionV0; +#[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::token_freeze_transition::TokenFreezeTransitionV0; +#[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::token_mint_transition::TokenMintTransitionV0; +#[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::token_transfer_transition::TokenTransferTransitionV0; +#[cfg(feature = "state-transition-signing")] use crate::state_transition::batch_transition::token_unfreeze_transition::TokenUnfreezeTransitionV0; #[cfg(feature = "state-transition-signing")] use crate::tokens::emergency_action::TokenEmergencyAction; @@ -892,4 +913,74 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { )?; Ok(state_transition) } + + #[cfg(feature = "state-transition-signing")] + fn new_token_config_update_transition( + token_id: Identifier, + owner_id: Identifier, + data_contract_id: Identifier, + token_contract_position: u16, + update_token_configuration_item: TokenConfigurationChangeItem, + public_note: Option, + using_group_info: Option, + identity_public_key: &IdentityPublicKey, + identity_contract_nonce: IdentityNonce, + user_fee_increase: UserFeeIncrease, + signer: &S, + _platform_version: &PlatformVersion, + _batch_feature_version: Option, + _config_update_feature_version: Option, + _base_feature_version: Option, + ) -> Result { + let mut config_update_transition = + TokenConfigUpdateTransition::V0(TokenConfigUpdateTransitionV0 { + base: TokenBaseTransition::V0(TokenBaseTransitionV0 { + identity_contract_nonce, + token_contract_position, + data_contract_id, + token_id, + using_group_info: None, + }), + update_token_configuration_item, + public_note, + }); + + if let Some(using_group_info_status) = using_group_info { + match using_group_info_status { + GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer( + group_contract_position, + ) => { + let action_id = config_update_transition.calculate_action_id(owner_id); + config_update_transition + .base_mut() + .set_using_group_info(Some(GroupStateTransitionInfo { + group_contract_position, + action_id, + action_is_proposer: true, + })) + } + GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner(info) => { + config_update_transition + .base_mut() + .set_using_group_info(Some(info)) + } + } + } + + let batch_transition: BatchTransition = BatchTransitionV1 { + owner_id, + transitions: vec![BatchedTransition::Token(config_update_transition.into())], + user_fee_increase, + signature_public_key_id: 0, + signature: Default::default(), + } + .into(); + let mut state_transition: StateTransition = batch_transition.into(); + state_transition.sign_external( + identity_public_key, + signer, + Some(|_, _| Ok(SecurityLevel::CRITICAL)), + )?; + Ok(state_transition) + } } diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/mod.rs index f20e61f03bc..1cbb0cbfbab 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/mod.rs @@ -10200,6 +10200,7 @@ mod tests { use dpp::data_contract::change_control_rules::v0::ChangeControlRulesV0; use dpp::data_contract::change_control_rules::ChangeControlRules; use dpp::state_transition::batch_transition::methods::v1::DocumentsBatchTransitionMethodsV1; + use dpp::data_contract::associated_token::token_configuration::accessors::v0::TokenConfigurationV0Getters; mod token_mint_tests { use super::*; @@ -14413,5 +14414,112 @@ mod tests { assert_eq!(token_balance, Some(expected_amount)); } } + + mod token_config_update_tests { + use super::*; + mod non_group { + use dpp::data_contract::accessors::v1::DataContractV1Getters; + use dpp::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; + use super::*; + #[test] + fn test_token_config_update_by_owner_changing_total_max_supply() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_max_supply_change_rules(ChangeControlRules::V0( + ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::ContractOwner, + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }, + )); + }), + None, + platform_version, + ); + + let config_update_transition = + BatchTransition::new_token_config_update_transition( + token_id, + identity.id(), + contract.id(), + 0, + TokenConfigurationChangeItem::MaxSupply(Some(1000000)), + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let config_update_transition_serialized_transition = + config_update_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![config_update_transition_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let contract = platform + .drive + .fetch_contract( + contract.id().to_buffer(), + None, + None, + None, + platform_version, + ).unwrap() + .expect("expected to fetch token balance").expect("expected contract"); + let updated_token_config = contract.contract.expected_token_configuration(0).expect("expected token configuration"); + assert_eq!(updated_token_config.max_supply(), Some(1000000)); + } + } + } } } diff --git a/packages/rs-drive/src/drive/group/fetch/mod.rs b/packages/rs-drive/src/drive/group/fetch/mod.rs index 32186f8a6e2..473e9dfa488 100644 --- a/packages/rs-drive/src/drive/group/fetch/mod.rs +++ b/packages/rs-drive/src/drive/group/fetch/mod.rs @@ -6,4 +6,3 @@ mod fetch_action_signers; mod fetch_active_action_info; mod fetch_group_info; mod fetch_group_infos; -mod queries; diff --git a/packages/rs-drive/src/drive/group/mod.rs b/packages/rs-drive/src/drive/group/mod.rs index a5fec1b5dea..156fc42f0cf 100644 --- a/packages/rs-drive/src/drive/group/mod.rs +++ b/packages/rs-drive/src/drive/group/mod.rs @@ -8,3 +8,4 @@ mod insert; pub mod paths; #[cfg(feature = "server")] mod prove; +mod queries; diff --git a/packages/rs-drive/src/drive/group/fetch/queries.rs b/packages/rs-drive/src/drive/group/queries.rs similarity index 100% rename from packages/rs-drive/src/drive/group/fetch/queries.rs rename to packages/rs-drive/src/drive/group/queries.rs diff --git a/packages/rs-drive/src/drive/tokens/mod.rs b/packages/rs-drive/src/drive/tokens/mod.rs index c1f8f4fa90c..146fd9b29b3 100644 --- a/packages/rs-drive/src/drive/tokens/mod.rs +++ b/packages/rs-drive/src/drive/tokens/mod.rs @@ -7,7 +7,6 @@ mod add_transaction_history_operations; pub mod apply_status; /// Manages operations related to balance handling. -#[cfg(any(feature = "server", feature = "verify"))] pub mod balance; /// Implements functionality for burning tokens. @@ -23,7 +22,6 @@ pub mod estimated_costs; pub mod freeze; /// Identity token info module, like if someone is frozen -#[cfg(any(feature = "server", feature = "verify"))] pub mod info; /// Implements minting operations for creating new tokens. @@ -47,7 +45,6 @@ pub mod unfreeze; pub mod calculate_total_tokens_balance; /// Token status module, like if the token is paused -#[cfg(feature = "server")] pub mod status; /// Token paths diff --git a/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs b/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs index 507ebc81a59..d579d94dc15 100644 --- a/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs +++ b/packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs @@ -32,7 +32,7 @@ use dpp::state_transition::batch_transition::document_replace_transition::Docume use dpp::state_transition::batch_transition::batched_transition::document_transfer_transition::v0::v0_methods::DocumentTransferTransitionV0Methods; use dpp::state_transition::batch_transition::batched_transition::document_transition::{DocumentTransition, DocumentTransitionV0Methods}; use dpp::state_transition::batch_transition::batched_transition::document_update_price_transition::v0::v0_methods::DocumentUpdatePriceTransitionV0Methods; -use dpp::state_transition::batch_transition::batched_transition::token_transition::{TokenTransition, TokenTransitionV0Methods}; +use dpp::state_transition::batch_transition::batched_transition::token_transition::{TokenTransition, TokenTransitionV0Methods, TOKEN_HISTORY_ID_BYTES}; use dpp::state_transition::batch_transition::token_base_transition::v0::v0_methods::TokenBaseTransitionV0Methods; use dpp::state_transition::batch_transition::token_config_update_transition::v0::v0_methods::TokenConfigUpdateTransitionV0Methods; use dpp::state_transition::batch_transition::token_destroy_frozen_funds_transition::v0::v0_methods::TokenDestroyFrozenFundsTransitionV0Methods; @@ -44,7 +44,6 @@ use dpp::state_transition::batch_transition::token_unfreeze_transition::v0::v0_m use dpp::state_transition::masternode_vote_transition::accessors::MasternodeVoteTransitionAccessorsV0; use dpp::state_transition::proof_result::StateTransitionProofResult; use dpp::state_transition::proof_result::StateTransitionProofResult::{VerifiedBalanceTransfer, VerifiedDataContract, VerifiedDocuments, VerifiedIdentity, VerifiedMasternodeVote, VerifiedPartialIdentity, VerifiedTokenActionWithDocument, VerifiedTokenBalance, VerifiedTokenBalanceAbsence, VerifiedTokenIdentitiesBalances, VerifiedTokenIdentityInfo, VerifiedTokenStatus}; -use dpp::system_data_contracts::SystemDataContract; use dpp::tokens::info::v0::IdentityTokenInfoV0Accessors; use dpp::tokens::status::v0::TokenStatusV0Accessors; use dpp::voting::vote_polls::VotePoll; @@ -319,7 +318,7 @@ impl Drive { let keeps_historical_document = token_config.keeps_history(); if keeps_historical_document { let query = SingleDocumentDriveQuery { - contract_id: SystemDataContract::TokenHistory.id().to_buffer(), + contract_id: TOKEN_HISTORY_ID_BYTES, document_type_name, document_type_keeps_history: false, document_id: token_transition From 7e4cd57f1ed2c4f8cd8b4e9fa5a6547997a4e29c Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Wed, 22 Jan 2025 23:22:22 +0700 Subject: [PATCH 2/6] more work --- .../token_configuration/accessors/mod.rs | 2 +- .../src/errors/consensus/basic/basic_error.rs | 5 +- .../src/errors/consensus/basic/token/mod.rs | 2 - packages/rs-dpp/src/errors/consensus/codes.rs | 19 +- .../src/errors/consensus/state/state_error.rs | 14 +- .../token/invalid_group_position_error.rs | 18 +- .../src/errors/consensus/state/token/mod.rs | 8 + ...stination_identity_does_not_exist_error.rs | 32 ++ .../token/token_mint_past_max_supply_error.rs | 55 ++ ...upply_to_less_than_current_supply_error.rs | 48 ++ .../state_v0/mod.rs | 72 ++- .../state_v0/mod.rs | 42 +- .../state_transitions/batch/mod.rs | 497 +++++++++++++++++- .../system/fetch_token_total_supply/mod.rs | 25 + .../system/fetch_token_total_supply/v0/mod.rs | 29 + 15 files changed, 835 insertions(+), 33 deletions(-) rename packages/rs-dpp/src/errors/consensus/{basic => state}/token/invalid_group_position_error.rs (68%) create mode 100644 packages/rs-dpp/src/errors/consensus/state/token/new_tokens_destination_identity_does_not_exist_error.rs create mode 100644 packages/rs-dpp/src/errors/consensus/state/token/token_mint_past_max_supply_error.rs create mode 100644 packages/rs-dpp/src/errors/consensus/state/token/token_setting_max_supply_to_less_than_current_supply_error.rs diff --git a/packages/rs-dpp/src/data_contract/associated_token/token_configuration/accessors/mod.rs b/packages/rs-dpp/src/data_contract/associated_token/token_configuration/accessors/mod.rs index 31e816a0b1f..0429831203a 100644 --- a/packages/rs-dpp/src/data_contract/associated_token/token_configuration/accessors/mod.rs +++ b/packages/rs-dpp/src/data_contract/associated_token/token_configuration/accessors/mod.rs @@ -56,7 +56,7 @@ impl TokenConfigurationV0Getters for TokenConfiguration { } /// Returns the maximum supply. - fn max_supply(&self) -> Option { + fn max_supply(&self) -> Option { match self { TokenConfiguration::V0(v0) => v0.max_supply(), } 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 e89758dde71..2b2127d0dcf 100644 --- a/packages/rs-dpp/src/errors/consensus/basic/basic_error.rs +++ b/packages/rs-dpp/src/errors/consensus/basic/basic_error.rs @@ -73,7 +73,7 @@ use crate::consensus::basic::group::GroupActionNotAllowedOnTransitionError; use crate::consensus::basic::overflow_error::OverflowError; use crate::consensus::basic::token::{ ChoosingTokenMintRecipientNotAllowedError, ContractHasNoTokensError, - DestinationIdentityForTokenMintingNotSetError, InvalidActionIdError, InvalidGroupPositionError, + DestinationIdentityForTokenMintingNotSetError, InvalidActionIdError, InvalidTokenIdError, InvalidTokenPositionError, TokenTransferToOurselfError, }; use crate::consensus::basic::unsupported_version_error::UnsupportedVersionError; @@ -438,9 +438,6 @@ pub enum BasicError { #[error(transparent)] ContractHasNoTokensError(ContractHasNoTokensError), - #[error(transparent)] - InvalidGroupPositionError(InvalidGroupPositionError), - #[error(transparent)] GroupPositionDoesNotExistError(GroupPositionDoesNotExistError), diff --git a/packages/rs-dpp/src/errors/consensus/basic/token/mod.rs b/packages/rs-dpp/src/errors/consensus/basic/token/mod.rs index efdcadc77af..d77dfff6fab 100644 --- a/packages/rs-dpp/src/errors/consensus/basic/token/mod.rs +++ b/packages/rs-dpp/src/errors/consensus/basic/token/mod.rs @@ -2,7 +2,6 @@ mod choosing_token_mint_recipient_not_allowed_error; mod contract_has_no_tokens_error; mod destination_identity_for_token_minting_not_set_error; mod invalid_action_id_error; -mod invalid_group_position_error; mod invalid_token_id_error; mod invalid_token_position_error; mod token_transfer_to_ourselves_error; @@ -11,7 +10,6 @@ pub use choosing_token_mint_recipient_not_allowed_error::*; pub use contract_has_no_tokens_error::*; pub use destination_identity_for_token_minting_not_set_error::*; pub use invalid_action_id_error::*; -pub use invalid_group_position_error::*; pub use invalid_token_id_error::*; pub use invalid_token_position_error::*; pub use token_transfer_to_ourselves_error::*; diff --git a/packages/rs-dpp/src/errors/consensus/codes.rs b/packages/rs-dpp/src/errors/consensus/codes.rs index 00ee6d4da59..00cb785991f 100644 --- a/packages/rs-dpp/src/errors/consensus/codes.rs +++ b/packages/rs-dpp/src/errors/consensus/codes.rs @@ -104,14 +104,13 @@ impl ErrorWithCode for BasicError { Self::NonContiguousContractTokenPositionsError(_) => 10253, // Group Errors: 10350-10399 - Self::InvalidGroupPositionError(_) => 10350, - Self::GroupPositionDoesNotExistError(_) => 10351, - Self::GroupActionNotAllowedOnTransitionError(_) => 10352, - Self::GroupTotalPowerLessThanRequiredError(_) => 10353, - Self::GroupNonUnilateralMemberPowerHasLessThanRequiredPowerError(_) => 10354, - Self::GroupExceedsMaxMembersError(_) => 10355, - Self::GroupMemberHasPowerOfZeroError(_) => 10356, - Self::GroupMemberHasPowerOverLimitError(_) => 10357, + Self::GroupPositionDoesNotExistError(_) => 10350, + Self::GroupActionNotAllowedOnTransitionError(_) => 10351, + Self::GroupTotalPowerLessThanRequiredError(_) => 10352, + Self::GroupNonUnilateralMemberPowerHasLessThanRequiredPowerError(_) => 10353, + Self::GroupExceedsMaxMembersError(_) => 10354, + Self::GroupMemberHasPowerOfZeroError(_) => 10355, + Self::GroupMemberHasPowerOverLimitError(_) => 10356, // Document Errors: 10400-10449 Self::DataContractNotPresentError { .. } => 10400, @@ -248,6 +247,10 @@ impl ErrorWithCode for StateError { Self::UnauthorizedTokenActionError(_) => 40151, Self::IdentityTokenAccountFrozenError(_) => 40152, Self::IdentityTokenAccountNotFrozenError(_) => 40153, + Self::TokenSettingMaxSupplyToLessThanCurrentSupplyError(_) => 40154, + Self::TokenMintPastMaxSupplyError(_) => 40155, + Self::NewTokensDestinationIdentityDoesNotExistError(_) => 40156, + Self::InvalidGroupPositionError(_) => 40157, // Identity Errors: 40200-40299 Self::IdentityAlreadyExistsError(_) => 40200, diff --git a/packages/rs-dpp/src/errors/consensus/state/state_error.rs b/packages/rs-dpp/src/errors/consensus/state/state_error.rs index 91eaf396ccc..279021f5dbd 100644 --- a/packages/rs-dpp/src/errors/consensus/state/state_error.rs +++ b/packages/rs-dpp/src/errors/consensus/state/state_error.rs @@ -42,7 +42,7 @@ use crate::consensus::state::identity::missing_transfer_key_error::MissingTransf use crate::consensus::state::identity::no_transfer_key_for_core_withdrawal_available_error::NoTransferKeyForCoreWithdrawalAvailableError; use crate::consensus::state::prefunded_specialized_balances::prefunded_specialized_balance_insufficient_error::PrefundedSpecializedBalanceInsufficientError; use crate::consensus::state::prefunded_specialized_balances::prefunded_specialized_balance_not_found_error::PrefundedSpecializedBalanceNotFoundError; -use crate::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountFrozenError, IdentityTokenAccountNotFrozenError, UnauthorizedTokenActionError}; +use crate::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountFrozenError, IdentityTokenAccountNotFrozenError, InvalidGroupPositionError, NewTokensDestinationIdentityDoesNotExistError, TokenMintPastMaxSupplyError, TokenSettingMaxSupplyToLessThanCurrentSupplyError, UnauthorizedTokenActionError}; use crate::consensus::state::voting::masternode_incorrect_voter_identity_id_error::MasternodeIncorrectVoterIdentityIdError; use crate::consensus::state::voting::masternode_incorrect_voting_address_error::MasternodeIncorrectVotingAddressError; use crate::consensus::state::voting::masternode_not_found_error::MasternodeNotFoundError; @@ -230,6 +230,18 @@ pub enum StateError { #[error(transparent)] DataContractUpdateActionNotAllowedError(DataContractUpdateActionNotAllowedError), + + #[error(transparent)] + TokenSettingMaxSupplyToLessThanCurrentSupplyError(TokenSettingMaxSupplyToLessThanCurrentSupplyError), + + #[error(transparent)] + TokenMintPastMaxSupplyError(TokenMintPastMaxSupplyError), + + #[error(transparent)] + NewTokensDestinationIdentityDoesNotExistError(NewTokensDestinationIdentityDoesNotExistError), + + #[error(transparent)] + InvalidGroupPositionError(InvalidGroupPositionError), } impl From for ConsensusError { diff --git a/packages/rs-dpp/src/errors/consensus/basic/token/invalid_group_position_error.rs b/packages/rs-dpp/src/errors/consensus/state/token/invalid_group_position_error.rs similarity index 68% rename from packages/rs-dpp/src/errors/consensus/basic/token/invalid_group_position_error.rs rename to packages/rs-dpp/src/errors/consensus/state/token/invalid_group_position_error.rs index 8464f1064aa..1f2c746fd61 100644 --- a/packages/rs-dpp/src/errors/consensus/basic/token/invalid_group_position_error.rs +++ b/packages/rs-dpp/src/errors/consensus/state/token/invalid_group_position_error.rs @@ -1,38 +1,38 @@ -use crate::consensus::basic::BasicError; use crate::consensus::ConsensusError; use crate::data_contract::GroupContractPosition; use crate::ProtocolError; use bincode::{Decode, Encode}; use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize}; use thiserror::Error; +use crate::consensus::state::state_error::StateError; #[derive( Error, Debug, Clone, PartialEq, Eq, Encode, Decode, PlatformSerialize, PlatformDeserialize, )] #[error( - "Invalid group position {}, expected {}", + "Invalid group position {}, max {}", invalid_group_position, - expected_group_position + max_group_position )] #[platform_serialize(unversioned)] pub struct InvalidGroupPositionError { - expected_group_position: GroupContractPosition, + max_group_position: GroupContractPosition, invalid_group_position: GroupContractPosition, } impl InvalidGroupPositionError { pub fn new( - expected_group_position: GroupContractPosition, + max_group_position: GroupContractPosition, invalid_group_position: GroupContractPosition, ) -> Self { Self { - expected_group_position, + max_group_position, invalid_group_position, } } - pub fn expected_group_position(&self) -> GroupContractPosition { - self.expected_group_position + pub fn max_group_position(&self) -> GroupContractPosition { + self.max_group_position } pub fn invalid_group_position(&self) -> GroupContractPosition { @@ -42,6 +42,6 @@ impl InvalidGroupPositionError { impl From for ConsensusError { fn from(err: InvalidGroupPositionError) -> Self { - Self::BasicError(BasicError::InvalidGroupPositionError(err)) + Self::StateError(StateError::InvalidGroupPositionError(err)) } } diff --git a/packages/rs-dpp/src/errors/consensus/state/token/mod.rs b/packages/rs-dpp/src/errors/consensus/state/token/mod.rs index 6838bc261c7..6d419607e81 100644 --- a/packages/rs-dpp/src/errors/consensus/state/token/mod.rs +++ b/packages/rs-dpp/src/errors/consensus/state/token/mod.rs @@ -2,8 +2,16 @@ mod identity_does_not_have_enough_token_balance_error; mod identity_token_account_frozen_error; mod identity_token_account_not_frozen_error; mod unauthorized_token_action_error; +mod token_setting_max_supply_to_less_than_current_supply_error; +mod token_mint_past_max_supply_error; +mod new_tokens_destination_identity_does_not_exist_error; +mod invalid_group_position_error; pub use identity_does_not_have_enough_token_balance_error::*; pub use identity_token_account_frozen_error::*; pub use identity_token_account_not_frozen_error::*; pub use unauthorized_token_action_error::*; +pub use token_setting_max_supply_to_less_than_current_supply_error::*; +pub use token_mint_past_max_supply_error::*; +pub use new_tokens_destination_identity_does_not_exist_error::*; +pub use invalid_group_position_error::*; diff --git a/packages/rs-dpp/src/errors/consensus/state/token/new_tokens_destination_identity_does_not_exist_error.rs b/packages/rs-dpp/src/errors/consensus/state/token/new_tokens_destination_identity_does_not_exist_error.rs new file mode 100644 index 00000000000..99132ad070b --- /dev/null +++ b/packages/rs-dpp/src/errors/consensus/state/token/new_tokens_destination_identity_does_not_exist_error.rs @@ -0,0 +1,32 @@ +use crate::consensus::state::state_error::StateError; +use crate::consensus::ConsensusError; +use crate::ProtocolError; +use bincode::{Decode, Encode}; +use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize}; +use platform_value::Identifier; +use thiserror::Error; + +#[derive( + Error, Debug, Clone, PartialEq, Eq, Encode, Decode, PlatformSerialize, PlatformDeserialize, +)] +#[error("The specified new tokens destination identity {identity_id} does not exist")] +#[platform_serialize(unversioned)] +pub struct NewTokensDestinationIdentityDoesNotExistError { + identity_id: Identifier, +} + +impl NewTokensDestinationIdentityDoesNotExistError { + pub fn new(identity_id: Identifier) -> Self { + Self { identity_id } + } + + pub fn identity_id(&self) -> &Identifier { + &self.identity_id + } +} + +impl From for ConsensusError { + fn from(err: NewTokensDestinationIdentityDoesNotExistError) -> Self { + Self::StateError(StateError::NewTokensDestinationIdentityDoesNotExistError(err)) + } +} \ No newline at end of file diff --git a/packages/rs-dpp/src/errors/consensus/state/token/token_mint_past_max_supply_error.rs b/packages/rs-dpp/src/errors/consensus/state/token/token_mint_past_max_supply_error.rs new file mode 100644 index 00000000000..e6d3817699a --- /dev/null +++ b/packages/rs-dpp/src/errors/consensus/state/token/token_mint_past_max_supply_error.rs @@ -0,0 +1,55 @@ +use crate::consensus::state::state_error::StateError; +use crate::consensus::ConsensusError; +use crate::ProtocolError; +use bincode::{Decode, Encode}; +use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize}; +use platform_value::Identifier; +use thiserror::Error; +use crate::balances::credits::TokenAmount; + +#[derive( + Error, Debug, Clone, PartialEq, Eq, Encode, Decode, PlatformSerialize, PlatformDeserialize, +)] +#[error( + "Token {token_id} attempted to mint {amount}, which exceeds the max supply {max_supply}, current supply is {current_supply}" +)] +#[platform_serialize(unversioned)] +pub struct TokenMintPastMaxSupplyError { + token_id: Identifier, + amount: TokenAmount, + current_supply: TokenAmount, + max_supply: TokenAmount, +} + +impl TokenMintPastMaxSupplyError { + pub fn new(token_id: Identifier, amount: TokenAmount, current_supply: TokenAmount, max_supply: TokenAmount) -> Self { + Self { + token_id, + amount, + current_supply, + max_supply, + } + } + + pub fn token_id(&self) -> &Identifier { + &self.token_id + } + + pub fn amount(&self) -> TokenAmount { + self.amount + } + + pub fn current_supply(&self) -> TokenAmount { + self.current_supply + } + + pub fn max_supply(&self) -> TokenAmount { + self.max_supply + } +} + +impl From for ConsensusError { + fn from(err: TokenMintPastMaxSupplyError) -> Self { + Self::StateError(StateError::TokenMintPastMaxSupplyError(err)) + } +} \ No newline at end of file diff --git a/packages/rs-dpp/src/errors/consensus/state/token/token_setting_max_supply_to_less_than_current_supply_error.rs b/packages/rs-dpp/src/errors/consensus/state/token/token_setting_max_supply_to_less_than_current_supply_error.rs new file mode 100644 index 00000000000..a24be7dcdaf --- /dev/null +++ b/packages/rs-dpp/src/errors/consensus/state/token/token_setting_max_supply_to_less_than_current_supply_error.rs @@ -0,0 +1,48 @@ +use crate::consensus::state::state_error::StateError; +use crate::consensus::ConsensusError; +use crate::ProtocolError; +use bincode::{Decode, Encode}; +use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize}; +use platform_value::Identifier; +use thiserror::Error; + +#[derive( + Error, Debug, Clone, PartialEq, Eq, Encode, Decode, PlatformSerialize, PlatformDeserialize, +)] +#[error( + "Token {token_id} attempted to set max supply to {max_supply}, which is less than the current supply {current_supply}" +)] +#[platform_serialize(unversioned)] +pub struct TokenSettingMaxSupplyToLessThanCurrentSupplyError { + token_id: Identifier, + max_supply: u64, + current_supply: u64, +} + +impl TokenSettingMaxSupplyToLessThanCurrentSupplyError { + pub fn new(token_id: Identifier, max_supply: u64, current_supply: u64) -> Self { + Self { + token_id, + max_supply, + current_supply, + } + } + + pub fn token_id(&self) -> &Identifier { + &self.token_id + } + + pub fn max_supply(&self) -> u64 { + self.max_supply + } + + pub fn current_supply(&self) -> u64 { + self.current_supply + } +} + +impl From for ConsensusError { + fn from(err: TokenSettingMaxSupplyToLessThanCurrentSupplyError) -> Self { + Self::StateError(StateError::TokenSettingMaxSupplyToLessThanCurrentSupplyError(err)) + } +} \ No newline at end of file diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_config_update_transition_action/state_v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_config_update_transition_action/state_v0/mod.rs index c4aec5e98f8..6bfa6e5f0ab 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_config_update_transition_action/state_v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_config_update_transition_action/state_v0/mod.rs @@ -1,18 +1,22 @@ use dpp::block::block_info::BlockInfo; use dpp::consensus::ConsensusError; use dpp::consensus::state::state_error::StateError; -use dpp::consensus::state::token::UnauthorizedTokenActionError; +use dpp::consensus::state::token::{InvalidGroupPositionError, NewTokensDestinationIdentityDoesNotExistError, TokenSettingMaxSupplyToLessThanCurrentSupplyError, UnauthorizedTokenActionError}; use dpp::data_contract::accessors::v0::DataContractV0Getters; use dpp::data_contract::accessors::v1::DataContractV1Getters; use dpp::data_contract::associated_token::token_configuration::accessors::v0::TokenConfigurationV0Getters; +use dpp::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; use dpp::group::action_taker::{ActionGoal, ActionTaker}; use dpp::prelude::Identifier; use dpp::validation::SimpleConsensusValidationResult; use drive::state_transition_action::batch::batched_transition::token_transition::token_config_update_transition_action::{TokenConfigUpdateTransitionAction, TokenConfigUpdateTransitionActionAccessorsV0}; use dpp::version::PlatformVersion; +use drive::error::drive::DriveError; use drive::query::TransactionArg; +use drive::state_transition_action::batch::batched_transition::token_transition::token_base_transition_action::TokenBaseTransitionActionAccessorsV0; use crate::error::Error; -use crate::execution::types::state_transition_execution_context::{StateTransitionExecutionContext}; +use crate::execution::types::execution_operation::ValidationOperation; +use crate::execution::types::state_transition_execution_context::{StateTransitionExecutionContext, StateTransitionExecutionContextMethodsV0}; use crate::execution::validation::state_transition::batch::action_validation::token::token_base_transition_action::TokenBaseTransitionActionValidation; use crate::platform_types::platform::PlatformStateRef; @@ -54,13 +58,23 @@ impl TokenConfigUpdateTransitionActionStateValidationV0 for TokenConfigUpdateTra let token_configuration = contract.expected_token_configuration(self.token_position())?; let main_control_group = token_configuration.main_control_group(); + let goal = if self.base().store_in_group().is_some() { + // We are using a group + // We just need to be able to participate + ActionGoal::ActionParticipation + } else { + // We are not using a group + // We need to make sure that for the change we are doing that we can finish it + ActionGoal::ActionCompletion + }; + if !token_configuration.can_apply_token_configuration_item( self.update_token_configuration_item(), &contract.owner_id(), main_control_group, contract.groups(), &ActionTaker::SingleIdentity(owner_id), - ActionGoal::ActionParticipation, + goal, ) { return Ok(SimpleConsensusValidationResult::new_with_error( ConsensusError::StateError(StateError::UnauthorizedTokenActionError( @@ -75,6 +89,58 @@ impl TokenConfigUpdateTransitionActionStateValidationV0 for TokenConfigUpdateTra )), )); } + + match self.update_token_configuration_item() { + TokenConfigurationChangeItem::MaxSupply(Some(max_supply)) => { + // If we are setting a max supply we need to make sure it isn't less than the + // current supply of the token + let (token_total_supply, fee) = platform.drive.fetch_token_total_supply_with_cost(self.token_id().to_buffer(), block_info, transaction, platform_version)?; + execution_context.add_operation(ValidationOperation::PrecalculatedOperation(fee)); + if let Some(token_total_supply) = token_total_supply { + if token_total_supply > *max_supply { + // We are trying to set a max supply smaller than the token total supply + return Ok(SimpleConsensusValidationResult::new_with_error( + ConsensusError::StateError(StateError::TokenSettingMaxSupplyToLessThanCurrentSupplyError( + TokenSettingMaxSupplyToLessThanCurrentSupplyError::new( + self.token_id(), + *max_supply, + token_total_supply, + ), + )), + )); + } + } else { + return Err(Error::Drive(drive::error::Error::Drive(DriveError::CorruptedDriveState(format!("token {} total supply not found", self.token_id()))))); + } + } + TokenConfigurationChangeItem::NewTokensDestinationIdentity(Some(identity_id)) => { + // We need to make sure the identity exists + let (identity_balance, fee) = platform.drive.fetch_identity_balance_with_costs(identity_id.to_buffer(), block_info, true, transaction, platform_version)?; + execution_context.add_operation(ValidationOperation::PrecalculatedOperation(fee)); + if identity_balance.is_none() { + return Ok(SimpleConsensusValidationResult::new_with_error( + ConsensusError::StateError(StateError::NewTokensDestinationIdentityDoesNotExistError( + NewTokensDestinationIdentityDoesNotExistError::new( + *identity_id + ), + )), + )); + } + } + TokenConfigurationChangeItem::MainControlGroup(Some(control_group)) => { + if !self.data_contract_fetch_info().contract.groups().contains_key(control_group) { + return Ok(SimpleConsensusValidationResult::new_with_error( + ConsensusError::StateError(StateError::InvalidGroupPositionError( + InvalidGroupPositionError::new( + self.data_contract_fetch_info().contract.groups().keys().last().copied().unwrap_or_default(), + *control_group, + ), + )), + )); + } + } + _ => {} + } Ok(SimpleConsensusValidationResult::new()) } diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_mint_transition_action/state_v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_mint_transition_action/state_v0/mod.rs index eba4356e116..5efb70f6f53 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_mint_transition_action/state_v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_mint_transition_action/state_v0/mod.rs @@ -2,7 +2,7 @@ use dpp::block::block_info::BlockInfo; use dpp::consensus::ConsensusError; use dpp::consensus::state::identity::RecipientIdentityDoesNotExistError; use dpp::consensus::state::state_error::StateError; -use dpp::consensus::state::token::UnauthorizedTokenActionError; +use dpp::consensus::state::token::{TokenMintPastMaxSupplyError, UnauthorizedTokenActionError}; use dpp::data_contract::accessors::v0::DataContractV0Getters; use dpp::data_contract::accessors::v1::DataContractV1Getters; use dpp::data_contract::associated_token::token_configuration::accessors::v0::TokenConfigurationV0Getters; @@ -12,6 +12,7 @@ use dpp::prelude::Identifier; use dpp::validation::SimpleConsensusValidationResult; use drive::state_transition_action::batch::batched_transition::token_transition::token_mint_transition_action::{TokenMintTransitionAction, TokenMintTransitionActionAccessorsV0}; use dpp::version::PlatformVersion; +use drive::error::drive::DriveError; use drive::query::TransactionArg; use drive::state_transition_action::batch::batched_transition::token_transition::token_base_transition_action::TokenBaseTransitionActionAccessorsV0; use crate::error::Error; @@ -71,8 +72,43 @@ impl TokenMintTransitionActionStateValidationV0 for TokenMintTransitionAction { if !validation_result.is_valid() { return Ok(validation_result); } - - // todo verify that minting would not break max supply + + if let Some(max_supply) = token_configuration.max_supply() { + // We have a max supply, let's get the current supply + let (token_total_supply, fee) = platform.drive.fetch_token_total_supply_with_cost(self.token_id().to_buffer(), block_info, transaction, platform_version)?; + execution_context.add_operation(ValidationOperation::PrecalculatedOperation(fee)); + if let Some(token_total_supply) = token_total_supply { + if let Some(total_supply_after_mint) = token_total_supply.checked_add(self.mint_amount()) { + if total_supply_after_mint > max_supply { + // We are trying to set a max supply smaller than the token total supply + return Ok(SimpleConsensusValidationResult::new_with_error( + ConsensusError::StateError(StateError::TokenMintPastMaxSupplyError( + TokenMintPastMaxSupplyError::new( + self.token_id(), + self.mint_amount(), + token_total_supply, + max_supply, + ), + )), + )); + } + } else { + // if we overflow we would also always go over max supply + return Ok(SimpleConsensusValidationResult::new_with_error( + ConsensusError::StateError(StateError::TokenMintPastMaxSupplyError( + TokenMintPastMaxSupplyError::new( + self.token_id(), + self.mint_amount(), + token_total_supply, + max_supply, + ), + )), + )); + } + } else { + return Err(Error::Drive(drive::error::Error::Drive(DriveError::CorruptedDriveState(format!("token {} total supply not found", self.token_id()))))); + } + } // We need to verify that the receiver is a valid identity diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/mod.rs index 1cbb0cbfbab..bf9ee1a34cb 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/mod.rs @@ -10201,6 +10201,7 @@ mod tests { use dpp::data_contract::change_control_rules::ChangeControlRules; use dpp::state_transition::batch_transition::methods::v1::DocumentsBatchTransitionMethodsV1; use dpp::data_contract::associated_token::token_configuration::accessors::v0::TokenConfigurationV0Getters; + use dpp::group::GroupStateTransitionInfoStatus; mod token_mint_tests { use super::*; @@ -10295,6 +10296,99 @@ mod tests { assert_eq!(token_balance, Some(101337)); } + #[test] + fn test_token_mint_by_owner_can_not_mint_past_max_supply() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_max_supply(Some(1000000)); + }), + None, + platform_version, + ); + + let documents_batch_create_transition = + BatchTransition::new_token_mint_transition( + token_id, + identity.id(), + contract.id(), + 0, + 2000000, + Some(identity.id()), + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let documents_batch_create_serialized_transition = + documents_batch_create_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![documents_batch_create_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::PaidConsensusError( + ConsensusError::StateError(StateError::TokenMintPastMaxSupplyError(_)), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let token_balance = platform + .drive + .fetch_identity_token_balance( + token_id.to_buffer(), + identity.id().to_buffer(), + None, + platform_version, + ) + .expect("expected to fetch token balance"); + assert_eq!(token_balance, Some(100000)); + } + #[test] fn test_token_mint_by_owner_allowed_sending_to_other() { let platform_version = PlatformVersion::latest(); @@ -14417,9 +14511,10 @@ mod tests { mod token_config_update_tests { use super::*; + use dpp::data_contract::accessors::v1::DataContractV1Getters; + use dpp::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; + mod non_group { - use dpp::data_contract::accessors::v1::DataContractV1Getters; - use dpp::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; use super::*; #[test] fn test_token_config_update_by_owner_changing_total_max_supply() { @@ -14519,6 +14614,404 @@ mod tests { let updated_token_config = contract.contract.expected_token_configuration(0).expect("expected token configuration"); assert_eq!(updated_token_config.max_supply(), Some(1000000)); } + + #[test] + fn test_token_config_update_by_owner_changing_total_max_supply_to_less_than_current_supply() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_max_supply_change_rules(ChangeControlRules::V0( + ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::ContractOwner, + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }, + )); + }), + None, + platform_version, + ); + + let config_update_transition = + BatchTransition::new_token_config_update_transition( + token_id, + identity.id(), + contract.id(), + 0, + TokenConfigurationChangeItem::MaxSupply(Some(1000)), + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let config_update_transition_serialized_transition = + config_update_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![config_update_transition_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::PaidConsensusError( + ConsensusError::StateError(StateError::TokenSettingMaxSupplyToLessThanCurrentSupplyError(_)), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let contract = platform + .drive + .fetch_contract( + contract.id().to_buffer(), + None, + None, + None, + platform_version, + ).unwrap() + .expect("expected to fetch token balance").expect("expected contract"); + let updated_token_config = contract.contract.expected_token_configuration(0).expect("expected token configuration"); + assert_eq!(updated_token_config.max_supply(), None); + } + } + + mod with_group { + use dpp::data_contract::group::Group; + use dpp::data_contract::group::v0::GroupV0; + use dpp::group::GroupStateTransitionInfo; + use dpp::state_transition::batch_transition::{TokenConfigUpdateTransition, TokenMintTransition}; + use super::*; + + #[test] + fn test_token_config_update_by_group_member_changing_total_max_supply_not_using_group_gives_error() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (identity_2, _, _) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_max_supply_change_rules(ChangeControlRules::V0( + ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::Group(0), + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }, + )); + }), + Some( + [( + 0, + Group::V0(GroupV0 { + members: [(identity.id(), 1), (identity_2.id(), 1)].into(), + required_power: 2, + }), + )] + .into(), + ), + platform_version, + ); + + let config_update_transition = + BatchTransition::new_token_config_update_transition( + token_id, + identity.id(), + contract.id(), + 0, + TokenConfigurationChangeItem::MaxSupply(Some(1000000)), + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let config_update_transition_serialized_transition = + config_update_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![config_update_transition_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::PaidConsensusError( + ConsensusError::StateError(StateError::UnauthorizedTokenActionError(_)), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let contract = platform + .drive + .fetch_contract( + contract.id().to_buffer(), + None, + None, + None, + platform_version, + ).unwrap() + .expect("expected to fetch token balance").expect("expected contract"); + let updated_token_config = contract.contract.expected_token_configuration(0).expect("expected token configuration"); + assert_eq!(updated_token_config.max_supply(), None); + } + + #[test] + fn test_token_config_update_by_group_member_changing_total_max_supply() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (identity_2, signer_2, key_2) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_max_supply_change_rules(ChangeControlRules::V0( + ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::Group(0), + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }, + )); + }), + Some( + [( + 0, + Group::V0(GroupV0 { + members: [(identity.id(), 1), (identity_2.id(), 1)].into(), + required_power: 2, + }), + )] + .into(), + ), + platform_version, + ); + + let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields( + token_id.as_bytes(), + identity.id().as_bytes(), + 2, + 1337, + ); + + let config_update_transition = + BatchTransition::new_token_config_update_transition( + token_id, + identity.id(), + contract.id(), + 0, + TokenConfigurationChangeItem::MaxSupply(Some(1000000)), + None, + Some(GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer(0)), + &key, + 2, + 0, + &signer, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let config_update_transition_serialized_transition = + config_update_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![config_update_transition_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let new_contract = platform + .drive + .fetch_contract( + contract.id().to_buffer(), + None, + None, + None, + platform_version, + ).unwrap() + .expect("expected to fetch token balance").expect("expected contract"); + let updated_token_config = new_contract.contract.expected_token_configuration(0).expect("expected token configuration"); + assert_eq!(updated_token_config.max_supply(), None); + + let config_update_transition = + BatchTransition::new_token_config_update_transition( + token_id, + identity_2.id(), + contract.id(), + 0, + TokenConfigurationChangeItem::MaxSupply(Some(1000000)), + None, + Some(GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner(GroupStateTransitionInfo { + group_contract_position: 0, + action_id, + action_is_proposer: false, + })), + &key_2, + 2, + 0, + &signer_2, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let config_update_transition_serialized_transition = + config_update_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![config_update_transition_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + } } } } diff --git a/packages/rs-drive/src/drive/tokens/system/fetch_token_total_supply/mod.rs b/packages/rs-drive/src/drive/tokens/system/fetch_token_total_supply/mod.rs index fac0478a0e5..1677cb4935c 100644 --- a/packages/rs-drive/src/drive/tokens/system/fetch_token_total_supply/mod.rs +++ b/packages/rs-drive/src/drive/tokens/system/fetch_token_total_supply/mod.rs @@ -9,6 +9,8 @@ use dpp::version::PlatformVersion; use grovedb::batch::KeyInfoPath; use grovedb::{EstimatedLayerInformation, TransactionArg}; use std::collections::HashMap; +use dpp::block::block_info::BlockInfo; +use dpp::fee::fee_result::FeeResult; impl Drive { /// Fetches token's total supply @@ -33,6 +35,29 @@ impl Drive { })), } } + /// Fetches token's total supply and returns cost of doing so + pub fn fetch_token_total_supply_with_cost( + &self, + token_id: [u8; 32], + block_info: &BlockInfo, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result<(Option, FeeResult), Error> { + match platform_version + .drive + .methods + .token + .fetch + .token_total_supply + { + 0 => self.fetch_token_total_supply_with_cost_v0(token_id, block_info, transaction, platform_version), + version => Err(Error::Drive(DriveError::UnknownVersionMismatch { + method: "fetch_token_total_supply".to_string(), + known_versions: vec![0], + received: version, + })), + } + } /// Adds the operations of fetching the token total supply pub fn fetch_token_total_supply_add_to_operations( diff --git a/packages/rs-drive/src/drive/tokens/system/fetch_token_total_supply/v0/mod.rs b/packages/rs-drive/src/drive/tokens/system/fetch_token_total_supply/v0/mod.rs index 2d92c49bf37..f2543062b20 100644 --- a/packages/rs-drive/src/drive/tokens/system/fetch_token_total_supply/v0/mod.rs +++ b/packages/rs-drive/src/drive/tokens/system/fetch_token_total_supply/v0/mod.rs @@ -9,6 +9,8 @@ use dpp::version::PlatformVersion; use grovedb::batch::KeyInfoPath; use grovedb::{EstimatedLayerInformation, TransactionArg, TreeType}; use std::collections::HashMap; +use dpp::block::block_info::BlockInfo; +use dpp::fee::fee_result::FeeResult; impl Drive { pub(super) fn fetch_token_total_supply_v0( @@ -28,6 +30,33 @@ impl Drive { ) } + pub(super) fn fetch_token_total_supply_with_cost_v0( + &self, + token_id: [u8; 32], + block_info: &BlockInfo, + transaction: TransactionArg, + platform_version: &PlatformVersion, + ) -> Result<(Option, FeeResult), Error> { + let mut drive_operations = vec![]; + + let token_amount = self.fetch_token_total_supply_add_to_operations_v0( + token_id, + &mut None, + transaction, + &mut drive_operations, + platform_version, + )?; + let fees = Drive::calculate_fee( + None, + Some(drive_operations), + &block_info.epoch, + self.config.epochs_per_era, + platform_version, + None, + )?; + Ok((token_amount, fees)) + } + pub(super) fn fetch_token_total_supply_add_to_operations_v0( &self, token_id: [u8; 32], From acd855f6b12dd517b9136f4ce5f8b3c3eb29596a Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Thu, 23 Jan 2025 08:51:12 +0700 Subject: [PATCH 3/6] a lot more tests --- .../authorized_action_takers.rs | 8 + .../src/errors/consensus/basic/basic_error.rs | 4 +- packages/rs-dpp/src/errors/consensus/codes.rs | 5 +- .../src/errors/consensus/state/state_error.rs | 17 +- .../token/invalid_group_position_error.rs | 17 +- .../src/errors/consensus/state/token/mod.rs | 22 +- ...action_taker_group_does_not_exist_error.rs | 36 + ...ion_taker_identity_does_not_exist_error.rs | 32 + ...d_action_taker_main_group_not_set_error.rs | 31 + ...stination_identity_does_not_exist_error.rs | 6 +- .../token/token_mint_past_max_supply_error.rs | 13 +- ...upply_to_less_than_current_supply_error.rs | 2 +- .../token_burn_transition/v0/v0_methods.rs | 15 +- .../token_burn_transition/v0_methods.rs | 20 + .../v0/v0_methods.rs | 15 +- .../v0_methods.rs | 19 + .../v0/v0_methods.rs | 2 +- .../v0_methods.rs | 4 +- .../v0/v0_methods.rs | 2 +- .../v0_methods.rs | 4 +- .../token_freeze_transition/v0/v0_methods.rs | 2 +- .../token_freeze_transition/v0_methods.rs | 4 +- .../v0/v0_methods.rs | 2 +- .../token_unfreeze_transition/v0_methods.rs | 4 +- .../document/batch_transition/methods/mod.rs | 57 +- .../batch_transition/methods/v1/mod.rs | 4 +- .../batch_transition/v1/v0_methods.rs | 2 +- .../state_v0/mod.rs | 4 +- .../state_v0/mod.rs | 272 ++- .../state_v0/mod.rs | 20 +- .../state_transitions/batch/mod.rs | 1508 +++++++++++++++-- .../system/fetch_token_total_supply/mod.rs | 11 +- .../system/fetch_token_total_supply/v0/mod.rs | 4 +- .../token/token_config_update_transition.rs | 2 + 34 files changed, 1907 insertions(+), 263 deletions(-) create mode 100644 packages/rs-dpp/src/errors/consensus/state/token/new_authorized_action_taker_group_does_not_exist_error.rs create mode 100644 packages/rs-dpp/src/errors/consensus/state/token/new_authorized_action_taker_identity_does_not_exist_error.rs create mode 100644 packages/rs-dpp/src/errors/consensus/state/token/new_authorized_action_taker_main_group_not_set_error.rs diff --git a/packages/rs-dpp/src/data_contract/change_control_rules/authorized_action_takers.rs b/packages/rs-dpp/src/data_contract/change_control_rules/authorized_action_takers.rs index 97e83ac6eb1..fc5c531bec2 100644 --- a/packages/rs-dpp/src/data_contract/change_control_rules/authorized_action_takers.rs +++ b/packages/rs-dpp/src/data_contract/change_control_rules/authorized_action_takers.rs @@ -15,6 +15,7 @@ pub enum AuthorizedActionTakers { #[default] NoOne, ContractOwner, + Identity(Identifier), MainGroup, Group(GroupContractPosition), } @@ -26,6 +27,7 @@ impl fmt::Display for AuthorizedActionTakers { AuthorizedActionTakers::ContractOwner => write!(f, "ContractOwner"), AuthorizedActionTakers::MainGroup => write!(f, "MainGroup"), AuthorizedActionTakers::Group(position) => write!(f, "Group(Position: {})", position), + AuthorizedActionTakers::Identity(identifier) => write!(f, "Identity({})", identifier), } } } @@ -51,6 +53,12 @@ impl AuthorizedActionTakers { } }, + // Only an identity is allowed + AuthorizedActionTakers::Identity(identity) => match action_taker { + ActionTaker::SingleIdentity(action_taker) => action_taker == identity, + ActionTaker::SpecifiedIdentities(action_takers) => action_takers.contains(identity), + }, + // MainGroup allows multiparty actions with specific power requirements AuthorizedActionTakers::MainGroup => { if let Some(main_group_contract_position) = &main_group { 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 2b2127d0dcf..552a96a9490 100644 --- a/packages/rs-dpp/src/errors/consensus/basic/basic_error.rs +++ b/packages/rs-dpp/src/errors/consensus/basic/basic_error.rs @@ -73,8 +73,8 @@ use crate::consensus::basic::group::GroupActionNotAllowedOnTransitionError; use crate::consensus::basic::overflow_error::OverflowError; use crate::consensus::basic::token::{ ChoosingTokenMintRecipientNotAllowedError, ContractHasNoTokensError, - DestinationIdentityForTokenMintingNotSetError, InvalidActionIdError, - InvalidTokenIdError, InvalidTokenPositionError, TokenTransferToOurselfError, + DestinationIdentityForTokenMintingNotSetError, InvalidActionIdError, InvalidTokenIdError, + InvalidTokenPositionError, TokenTransferToOurselfError, }; use crate::consensus::basic::unsupported_version_error::UnsupportedVersionError; use crate::consensus::basic::value_error::ValueError; diff --git a/packages/rs-dpp/src/errors/consensus/codes.rs b/packages/rs-dpp/src/errors/consensus/codes.rs index 00cb785991f..9f1d7b7c626 100644 --- a/packages/rs-dpp/src/errors/consensus/codes.rs +++ b/packages/rs-dpp/src/errors/consensus/codes.rs @@ -250,7 +250,10 @@ impl ErrorWithCode for StateError { Self::TokenSettingMaxSupplyToLessThanCurrentSupplyError(_) => 40154, Self::TokenMintPastMaxSupplyError(_) => 40155, Self::NewTokensDestinationIdentityDoesNotExistError(_) => 40156, - Self::InvalidGroupPositionError(_) => 40157, + Self::NewAuthorizedActionTakerIdentityDoesNotExistError(_) => 40157, + Self::NewAuthorizedActionTakerGroupDoesNotExistError(_) => 40158, + Self::NewAuthorizedActionTakerMainGroupNotSetError(_) => 40159, + Self::InvalidGroupPositionError(_) => 40160, // Identity Errors: 40200-40299 Self::IdentityAlreadyExistsError(_) => 40200, diff --git a/packages/rs-dpp/src/errors/consensus/state/state_error.rs b/packages/rs-dpp/src/errors/consensus/state/state_error.rs index 279021f5dbd..e76d6b2c5b3 100644 --- a/packages/rs-dpp/src/errors/consensus/state/state_error.rs +++ b/packages/rs-dpp/src/errors/consensus/state/state_error.rs @@ -42,7 +42,7 @@ use crate::consensus::state::identity::missing_transfer_key_error::MissingTransf use crate::consensus::state::identity::no_transfer_key_for_core_withdrawal_available_error::NoTransferKeyForCoreWithdrawalAvailableError; use crate::consensus::state::prefunded_specialized_balances::prefunded_specialized_balance_insufficient_error::PrefundedSpecializedBalanceInsufficientError; use crate::consensus::state::prefunded_specialized_balances::prefunded_specialized_balance_not_found_error::PrefundedSpecializedBalanceNotFoundError; -use crate::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountFrozenError, IdentityTokenAccountNotFrozenError, InvalidGroupPositionError, NewTokensDestinationIdentityDoesNotExistError, TokenMintPastMaxSupplyError, TokenSettingMaxSupplyToLessThanCurrentSupplyError, UnauthorizedTokenActionError}; +use crate::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountFrozenError, IdentityTokenAccountNotFrozenError, InvalidGroupPositionError, NewAuthorizedActionTakerGroupDoesNotExistError, NewAuthorizedActionTakerIdentityDoesNotExistError, NewAuthorizedActionTakerMainGroupNotSetError, NewTokensDestinationIdentityDoesNotExistError, TokenMintPastMaxSupplyError, TokenSettingMaxSupplyToLessThanCurrentSupplyError, UnauthorizedTokenActionError}; use crate::consensus::state::voting::masternode_incorrect_voter_identity_id_error::MasternodeIncorrectVoterIdentityIdError; use crate::consensus::state::voting::masternode_incorrect_voting_address_error::MasternodeIncorrectVotingAddressError; use crate::consensus::state::voting::masternode_not_found_error::MasternodeNotFoundError; @@ -232,7 +232,9 @@ pub enum StateError { DataContractUpdateActionNotAllowedError(DataContractUpdateActionNotAllowedError), #[error(transparent)] - TokenSettingMaxSupplyToLessThanCurrentSupplyError(TokenSettingMaxSupplyToLessThanCurrentSupplyError), + TokenSettingMaxSupplyToLessThanCurrentSupplyError( + TokenSettingMaxSupplyToLessThanCurrentSupplyError, + ), #[error(transparent)] TokenMintPastMaxSupplyError(TokenMintPastMaxSupplyError), @@ -240,6 +242,17 @@ pub enum StateError { #[error(transparent)] NewTokensDestinationIdentityDoesNotExistError(NewTokensDestinationIdentityDoesNotExistError), + #[error(transparent)] + NewAuthorizedActionTakerIdentityDoesNotExistError( + NewAuthorizedActionTakerIdentityDoesNotExistError, + ), + + #[error(transparent)] + NewAuthorizedActionTakerGroupDoesNotExistError(NewAuthorizedActionTakerGroupDoesNotExistError), + + #[error(transparent)] + NewAuthorizedActionTakerMainGroupNotSetError(NewAuthorizedActionTakerMainGroupNotSetError), + #[error(transparent)] InvalidGroupPositionError(InvalidGroupPositionError), } diff --git a/packages/rs-dpp/src/errors/consensus/state/token/invalid_group_position_error.rs b/packages/rs-dpp/src/errors/consensus/state/token/invalid_group_position_error.rs index 1f2c746fd61..99958ec80e9 100644 --- a/packages/rs-dpp/src/errors/consensus/state/token/invalid_group_position_error.rs +++ b/packages/rs-dpp/src/errors/consensus/state/token/invalid_group_position_error.rs @@ -1,28 +1,31 @@ +use crate::consensus::state::state_error::StateError; use crate::consensus::ConsensusError; use crate::data_contract::GroupContractPosition; use crate::ProtocolError; use bincode::{Decode, Encode}; use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize}; use thiserror::Error; -use crate::consensus::state::state_error::StateError; #[derive( Error, Debug, Clone, PartialEq, Eq, Encode, Decode, PlatformSerialize, PlatformDeserialize, )] #[error( - "Invalid group position {}, max {}", - invalid_group_position, - max_group_position + "Invalid group position: {invalid_group_position}. {max_group_message}", + max_group_message = if let Some(max) = .max_group_position { + format!("The maximum allowed group position is {}", max) + } else { + "No maximum group position limit is set.".to_string() + } )] #[platform_serialize(unversioned)] pub struct InvalidGroupPositionError { - max_group_position: GroupContractPosition, + max_group_position: Option, invalid_group_position: GroupContractPosition, } impl InvalidGroupPositionError { pub fn new( - max_group_position: GroupContractPosition, + max_group_position: Option, invalid_group_position: GroupContractPosition, ) -> Self { Self { @@ -31,7 +34,7 @@ impl InvalidGroupPositionError { } } - pub fn max_group_position(&self) -> GroupContractPosition { + pub fn max_group_position(&self) -> Option { self.max_group_position } diff --git a/packages/rs-dpp/src/errors/consensus/state/token/mod.rs b/packages/rs-dpp/src/errors/consensus/state/token/mod.rs index 6d419607e81..f28964addf7 100644 --- a/packages/rs-dpp/src/errors/consensus/state/token/mod.rs +++ b/packages/rs-dpp/src/errors/consensus/state/token/mod.rs @@ -1,17 +1,23 @@ mod identity_does_not_have_enough_token_balance_error; mod identity_token_account_frozen_error; mod identity_token_account_not_frozen_error; -mod unauthorized_token_action_error; -mod token_setting_max_supply_to_less_than_current_supply_error; -mod token_mint_past_max_supply_error; -mod new_tokens_destination_identity_does_not_exist_error; mod invalid_group_position_error; +mod new_authorized_action_taker_group_does_not_exist_error; +mod new_authorized_action_taker_identity_does_not_exist_error; +mod new_authorized_action_taker_main_group_not_set_error; +mod new_tokens_destination_identity_does_not_exist_error; +mod token_mint_past_max_supply_error; +mod token_setting_max_supply_to_less_than_current_supply_error; +mod unauthorized_token_action_error; pub use identity_does_not_have_enough_token_balance_error::*; pub use identity_token_account_frozen_error::*; pub use identity_token_account_not_frozen_error::*; -pub use unauthorized_token_action_error::*; -pub use token_setting_max_supply_to_less_than_current_supply_error::*; -pub use token_mint_past_max_supply_error::*; -pub use new_tokens_destination_identity_does_not_exist_error::*; pub use invalid_group_position_error::*; +pub use new_authorized_action_taker_group_does_not_exist_error::*; +pub use new_authorized_action_taker_identity_does_not_exist_error::*; +pub use new_authorized_action_taker_main_group_not_set_error::*; +pub use new_tokens_destination_identity_does_not_exist_error::*; +pub use token_mint_past_max_supply_error::*; +pub use token_setting_max_supply_to_less_than_current_supply_error::*; +pub use unauthorized_token_action_error::*; diff --git a/packages/rs-dpp/src/errors/consensus/state/token/new_authorized_action_taker_group_does_not_exist_error.rs b/packages/rs-dpp/src/errors/consensus/state/token/new_authorized_action_taker_group_does_not_exist_error.rs new file mode 100644 index 00000000000..2ea523ddbbf --- /dev/null +++ b/packages/rs-dpp/src/errors/consensus/state/token/new_authorized_action_taker_group_does_not_exist_error.rs @@ -0,0 +1,36 @@ +use crate::consensus::state::state_error::StateError; +use crate::consensus::ConsensusError; +use crate::data_contract::GroupContractPosition; +use crate::ProtocolError; +use bincode::{Decode, Encode}; +use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize}; +use thiserror::Error; + +#[derive( + Error, Debug, Clone, PartialEq, Eq, Encode, Decode, PlatformSerialize, PlatformDeserialize, +)] +#[error("The specified new authorized action taker group {group_contract_position} does not exist")] +#[platform_serialize(unversioned)] +pub struct NewAuthorizedActionTakerGroupDoesNotExistError { + group_contract_position: GroupContractPosition, +} + +impl NewAuthorizedActionTakerGroupDoesNotExistError { + pub fn new(group_contract_position: GroupContractPosition) -> Self { + Self { + group_contract_position, + } + } + + pub fn group_contract_position(&self) -> GroupContractPosition { + self.group_contract_position + } +} + +impl From for ConsensusError { + fn from(err: NewAuthorizedActionTakerGroupDoesNotExistError) -> Self { + Self::StateError(StateError::NewAuthorizedActionTakerGroupDoesNotExistError( + err, + )) + } +} diff --git a/packages/rs-dpp/src/errors/consensus/state/token/new_authorized_action_taker_identity_does_not_exist_error.rs b/packages/rs-dpp/src/errors/consensus/state/token/new_authorized_action_taker_identity_does_not_exist_error.rs new file mode 100644 index 00000000000..9f02bc0117c --- /dev/null +++ b/packages/rs-dpp/src/errors/consensus/state/token/new_authorized_action_taker_identity_does_not_exist_error.rs @@ -0,0 +1,32 @@ +use crate::consensus::state::state_error::StateError; +use crate::consensus::ConsensusError; +use crate::ProtocolError; +use bincode::{Decode, Encode}; +use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize}; +use platform_value::Identifier; +use thiserror::Error; + +#[derive( + Error, Debug, Clone, PartialEq, Eq, Encode, Decode, PlatformSerialize, PlatformDeserialize, +)] +#[error("The specified new authorized action taker identity {identity_id} does not exist")] +#[platform_serialize(unversioned)] +pub struct NewAuthorizedActionTakerIdentityDoesNotExistError { + identity_id: Identifier, +} + +impl NewAuthorizedActionTakerIdentityDoesNotExistError { + pub fn new(identity_id: Identifier) -> Self { + Self { identity_id } + } + + pub fn identity_id(&self) -> &Identifier { + &self.identity_id + } +} + +impl From for ConsensusError { + fn from(err: NewAuthorizedActionTakerIdentityDoesNotExistError) -> Self { + Self::StateError(StateError::NewAuthorizedActionTakerIdentityDoesNotExistError(err)) + } +} diff --git a/packages/rs-dpp/src/errors/consensus/state/token/new_authorized_action_taker_main_group_not_set_error.rs b/packages/rs-dpp/src/errors/consensus/state/token/new_authorized_action_taker_main_group_not_set_error.rs new file mode 100644 index 00000000000..8d1790ca430 --- /dev/null +++ b/packages/rs-dpp/src/errors/consensus/state/token/new_authorized_action_taker_main_group_not_set_error.rs @@ -0,0 +1,31 @@ +use crate::consensus::state::state_error::StateError; +use crate::consensus::ConsensusError; +use crate::data_contract::GroupContractPosition; +use crate::ProtocolError; +use bincode::{Decode, Encode}; +use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize}; +use platform_value::Identifier; +use thiserror::Error; + +#[derive( + Error, Debug, Clone, PartialEq, Eq, Encode, Decode, PlatformSerialize, PlatformDeserialize, +)] +#[error( + "The specified new authorized action taker main group is not set in the token configuration" +)] +#[platform_serialize(unversioned)] +pub struct NewAuthorizedActionTakerMainGroupNotSetError {} + +impl NewAuthorizedActionTakerMainGroupNotSetError { + pub fn new() -> Self { + Self {} + } +} + +impl From for ConsensusError { + fn from(err: NewAuthorizedActionTakerMainGroupNotSetError) -> Self { + Self::StateError(StateError::NewAuthorizedActionTakerMainGroupNotSetError( + err, + )) + } +} diff --git a/packages/rs-dpp/src/errors/consensus/state/token/new_tokens_destination_identity_does_not_exist_error.rs b/packages/rs-dpp/src/errors/consensus/state/token/new_tokens_destination_identity_does_not_exist_error.rs index 99132ad070b..ae18548891a 100644 --- a/packages/rs-dpp/src/errors/consensus/state/token/new_tokens_destination_identity_does_not_exist_error.rs +++ b/packages/rs-dpp/src/errors/consensus/state/token/new_tokens_destination_identity_does_not_exist_error.rs @@ -27,6 +27,8 @@ impl NewTokensDestinationIdentityDoesNotExistError { impl From for ConsensusError { fn from(err: NewTokensDestinationIdentityDoesNotExistError) -> Self { - Self::StateError(StateError::NewTokensDestinationIdentityDoesNotExistError(err)) + Self::StateError(StateError::NewTokensDestinationIdentityDoesNotExistError( + err, + )) } -} \ No newline at end of file +} diff --git a/packages/rs-dpp/src/errors/consensus/state/token/token_mint_past_max_supply_error.rs b/packages/rs-dpp/src/errors/consensus/state/token/token_mint_past_max_supply_error.rs index e6d3817699a..c97a2d0830e 100644 --- a/packages/rs-dpp/src/errors/consensus/state/token/token_mint_past_max_supply_error.rs +++ b/packages/rs-dpp/src/errors/consensus/state/token/token_mint_past_max_supply_error.rs @@ -1,3 +1,4 @@ +use crate::balances::credits::TokenAmount; use crate::consensus::state::state_error::StateError; use crate::consensus::ConsensusError; use crate::ProtocolError; @@ -5,7 +6,6 @@ use bincode::{Decode, Encode}; use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize}; use platform_value::Identifier; use thiserror::Error; -use crate::balances::credits::TokenAmount; #[derive( Error, Debug, Clone, PartialEq, Eq, Encode, Decode, PlatformSerialize, PlatformDeserialize, @@ -22,7 +22,12 @@ pub struct TokenMintPastMaxSupplyError { } impl TokenMintPastMaxSupplyError { - pub fn new(token_id: Identifier, amount: TokenAmount, current_supply: TokenAmount, max_supply: TokenAmount) -> Self { + pub fn new( + token_id: Identifier, + amount: TokenAmount, + current_supply: TokenAmount, + max_supply: TokenAmount, + ) -> Self { Self { token_id, amount, @@ -42,7 +47,7 @@ impl TokenMintPastMaxSupplyError { pub fn current_supply(&self) -> TokenAmount { self.current_supply } - + pub fn max_supply(&self) -> TokenAmount { self.max_supply } @@ -52,4 +57,4 @@ impl From for ConsensusError { fn from(err: TokenMintPastMaxSupplyError) -> Self { Self::StateError(StateError::TokenMintPastMaxSupplyError(err)) } -} \ No newline at end of file +} diff --git a/packages/rs-dpp/src/errors/consensus/state/token/token_setting_max_supply_to_less_than_current_supply_error.rs b/packages/rs-dpp/src/errors/consensus/state/token/token_setting_max_supply_to_less_than_current_supply_error.rs index a24be7dcdaf..511263c695f 100644 --- a/packages/rs-dpp/src/errors/consensus/state/token/token_setting_max_supply_to_less_than_current_supply_error.rs +++ b/packages/rs-dpp/src/errors/consensus/state/token/token_setting_max_supply_to_less_than_current_supply_error.rs @@ -45,4 +45,4 @@ impl From for ConsensusError fn from(err: TokenSettingMaxSupplyToLessThanCurrentSupplyError) -> Self { Self::StateError(StateError::TokenSettingMaxSupplyToLessThanCurrentSupplyError(err)) } -} \ No newline at end of file +} diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0/v0_methods.rs index 8cdcd6e153f..ee24438ecf4 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0/v0_methods.rs @@ -4,7 +4,7 @@ use crate::state_transition::batch_transition::token_base_transition::token_base use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; use crate::state_transition::batch_transition::token_base_transition::v0::v0_methods::TokenBaseTransitionV0Methods; use crate::state_transition::batch_transition::token_burn_transition::TokenBurnTransitionV0; -use crate::util::hash::hash_double; +use crate::state_transition::batch_transition::TokenBurnTransition; impl TokenBaseTransitionAccessors for TokenBurnTransitionV0 { fn base(&self) -> &TokenBaseTransition { @@ -65,12 +65,11 @@ impl AllowedAsMultiPartyAction for TokenBurnTransitionV0 { base, burn_amount, .. } = self; - let mut bytes = b"action_token_burn".to_vec(); - bytes.extend_from_slice(base.token_id().as_bytes()); - bytes.extend_from_slice(owner_id.as_bytes()); - bytes.extend_from_slice(&base.identity_contract_nonce().to_be_bytes()); - bytes.extend_from_slice(&burn_amount.to_be_bytes()); - - hash_double(bytes).into() + TokenBurnTransition::calculate_action_id_with_fields( + base.token_id().as_bytes(), + owner_id.as_bytes(), + base.identity_contract_nonce(), + *burn_amount, + ) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0_methods.rs index 060ac9c70ba..d37a08463fe 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_burn_transition/v0_methods.rs @@ -1,9 +1,12 @@ use platform_value::Identifier; +use crate::balances::credits::TokenAmount; +use crate::prelude::IdentityNonce; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; use crate::state_transition::batch_transition::token_burn_transition::TokenBurnTransition; use crate::state_transition::batch_transition::token_burn_transition::v0::v0_methods::TokenBurnTransitionV0Methods; +use crate::util::hash::hash_double; impl TokenBaseTransitionAccessors for TokenBurnTransition { fn base(&self) -> &TokenBaseTransition { @@ -64,3 +67,20 @@ impl AllowedAsMultiPartyAction for TokenBurnTransition { } } } + +impl TokenBurnTransition { + pub fn calculate_action_id_with_fields( + token_id: &[u8; 32], + owner_id: &[u8; 32], + identity_contract_nonce: IdentityNonce, + burn_amount: TokenAmount, + ) -> Identifier { + let mut bytes = b"action_token_burn".to_vec(); + bytes.extend_from_slice(token_id); + bytes.extend_from_slice(owner_id); + bytes.extend_from_slice(&identity_contract_nonce.to_be_bytes()); + bytes.extend_from_slice(&burn_amount.to_be_bytes()); + + hash_double(bytes).into() + } +} diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs index 49ba01f625a..a9e24acc377 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0/v0_methods.rs @@ -5,7 +5,7 @@ use crate::state_transition::batch_transition::token_base_transition::token_base use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; use crate::state_transition::batch_transition::token_base_transition::v0::v0_methods::TokenBaseTransitionV0Methods; use crate::state_transition::batch_transition::token_config_update_transition::TokenConfigUpdateTransitionV0; -use crate::util::hash::hash_double; +use crate::state_transition::batch_transition::TokenConfigUpdateTransition; impl TokenBaseTransitionAccessors for TokenConfigUpdateTransitionV0 { fn base(&self) -> &TokenBaseTransition { @@ -76,12 +76,11 @@ impl AllowedAsMultiPartyAction for TokenConfigUpdateTransitionV0 { .. } = self; - let mut bytes = b"action_token_config_update".to_vec(); - bytes.extend_from_slice(base.token_id().as_bytes()); - bytes.extend_from_slice(owner_id.as_bytes()); - bytes.extend_from_slice(&base.identity_contract_nonce().to_be_bytes()); - bytes.extend_from_slice(&[update_token_configuration_item.u8_item_index()]); - - hash_double(bytes).into() + TokenConfigUpdateTransition::calculate_action_id_with_fields( + base.token_id().as_bytes(), + owner_id.as_bytes(), + base.identity_contract_nonce(), + update_token_configuration_item.u8_item_index(), + ) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs index b996f9a0b52..b16720f136c 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_config_update_transition/v0_methods.rs @@ -1,10 +1,12 @@ use platform_value::Identifier; use crate::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; +use crate::prelude::IdentityNonce; use crate::state_transition::batch_transition::batched_transition::multi_party_action::AllowedAsMultiPartyAction; use crate::state_transition::batch_transition::token_base_transition::token_base_transition_accessors::TokenBaseTransitionAccessors; use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition; use crate::state_transition::batch_transition::token_config_update_transition::TokenConfigUpdateTransition; use crate::state_transition::batch_transition::token_config_update_transition::v0::v0_methods::TokenConfigUpdateTransitionV0Methods; +use crate::util::hash::hash_double; impl TokenBaseTransitionAccessors for TokenConfigUpdateTransition { fn base(&self) -> &TokenBaseTransition { @@ -70,3 +72,20 @@ impl AllowedAsMultiPartyAction for TokenConfigUpdateTransition { } } } + +impl TokenConfigUpdateTransition { + pub fn calculate_action_id_with_fields( + token_id: &[u8; 32], + owner_id: &[u8; 32], + identity_contract_nonce: IdentityNonce, + update_token_config_item: u8, + ) -> Identifier { + let mut bytes = b"action_token_config_update".to_vec(); + bytes.extend_from_slice(token_id); + bytes.extend_from_slice(owner_id); + bytes.extend_from_slice(&identity_contract_nonce.to_be_bytes()); + bytes.extend_from_slice(&[update_token_config_item]); + + hash_double(bytes).into() + } +} diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0/v0_methods.rs index ef6015a3417..ccd6c0b24ce 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0/v0_methods.rs @@ -71,8 +71,8 @@ impl AllowedAsMultiPartyAction for TokenDestroyFrozenFundsTransitionV0 { TokenDestroyFrozenFundsTransition::calculate_action_id_with_fields( base.token_id().as_bytes(), owner_id.as_bytes(), - frozen_identity_id.as_bytes(), base.identity_contract_nonce(), + frozen_identity_id.as_bytes(), ) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0_methods.rs index 73582b9c978..e0560b49498 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_destroy_frozen_funds_transition/v0_methods.rs @@ -73,14 +73,14 @@ impl TokenDestroyFrozenFundsTransition { pub fn calculate_action_id_with_fields( token_id: &[u8; 32], owner_id: &[u8; 32], - target_id: &[u8; 32], identity_contract_nonce: IdentityNonce, + target_id: &[u8; 32], ) -> Identifier { let mut bytes = b"action_token_destroy".to_vec(); bytes.extend_from_slice(token_id); bytes.extend_from_slice(owner_id); - bytes.extend_from_slice(target_id); bytes.extend_from_slice(&identity_contract_nonce.to_be_bytes()); + bytes.extend_from_slice(target_id); hash_double(bytes).into() } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0/v0_methods.rs index 4f0e17f2c44..02abb8e415e 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0/v0_methods.rs @@ -73,8 +73,8 @@ impl AllowedAsMultiPartyAction for TokenEmergencyActionTransitionV0 { TokenEmergencyActionTransition::calculate_action_id_with_fields( base.token_id().as_bytes(), owner_id.as_bytes(), - *emergency_action, base.identity_contract_nonce(), + *emergency_action, ) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0_methods.rs index e089d2f8de8..c57cf214ae2 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_emergency_action_transition/v0_methods.rs @@ -72,14 +72,14 @@ impl TokenEmergencyActionTransition { pub fn calculate_action_id_with_fields( token_id: &[u8; 32], owner_id: &[u8; 32], - emergency_action: TokenEmergencyAction, identity_contract_nonce: IdentityNonce, + emergency_action: TokenEmergencyAction, ) -> Identifier { let mut bytes = b"action_token_emergency_action".to_vec(); bytes.extend_from_slice(token_id); bytes.extend_from_slice(owner_id); - bytes.extend_from_slice(&[emergency_action as u8]); bytes.extend_from_slice(&identity_contract_nonce.to_be_bytes()); + bytes.extend_from_slice(&[emergency_action as u8]); hash_double(bytes).into() } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0/v0_methods.rs index bec3466b7ba..20e9db996d4 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0/v0_methods.rs @@ -71,8 +71,8 @@ impl AllowedAsMultiPartyAction for TokenFreezeTransitionV0 { TokenFreezeTransition::calculate_action_id_with_fields( base.token_id().as_bytes(), owner_id.as_bytes(), - frozen_identity_id.as_bytes(), base.identity_contract_nonce(), + frozen_identity_id.as_bytes(), ) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0_methods.rs index 13c06dcd456..fc4642abefb 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_freeze_transition/v0_methods.rs @@ -71,14 +71,14 @@ impl TokenFreezeTransition { pub fn calculate_action_id_with_fields( token_id: &[u8; 32], owner_id: &[u8; 32], - target_id: &[u8; 32], identity_contract_nonce: IdentityNonce, + target_id: &[u8; 32], ) -> Identifier { let mut bytes = b"action_token_freeze".to_vec(); bytes.extend_from_slice(token_id); bytes.extend_from_slice(owner_id); - bytes.extend_from_slice(target_id); bytes.extend_from_slice(&identity_contract_nonce.to_be_bytes()); + bytes.extend_from_slice(target_id); hash_double(bytes).into() } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0/v0_methods.rs index d386f30bd66..0559542f7a7 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0/v0_methods.rs @@ -71,8 +71,8 @@ impl AllowedAsMultiPartyAction for TokenUnfreezeTransitionV0 { TokenUnfreezeTransition::calculate_action_id_with_fields( base.token_id().as_bytes(), owner_id.as_bytes(), - frozen_identity_id.as_bytes(), base.identity_contract_nonce(), + frozen_identity_id.as_bytes(), ) } } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0_methods.rs index 2f660df7167..b90cf25acbd 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/batched_transition/token_unfreeze_transition/v0_methods.rs @@ -71,14 +71,14 @@ impl TokenUnfreezeTransition { pub fn calculate_action_id_with_fields( token_id: &[u8; 32], owner_id: &[u8; 32], - target_id: &[u8; 32], identity_contract_nonce: IdentityNonce, + target_id: &[u8; 32], ) -> Identifier { let mut bytes = b"action_token_unfreeze".to_vec(); bytes.extend_from_slice(token_id); bytes.extend_from_slice(owner_id); - bytes.extend_from_slice(target_id); bytes.extend_from_slice(&identity_contract_nonce.to_be_bytes()); + bytes.extend_from_slice(target_id); hash_double(bytes).into() } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/methods/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/methods/mod.rs index 7324529d841..c4ee2c72510 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/methods/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/methods/mod.rs @@ -1,5 +1,6 @@ #[cfg(feature = "state-transition-signing")] use crate::balances::credits::TokenAmount; +use crate::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; #[cfg(feature = "state-transition-signing")] use crate::data_contract::document_type::DocumentTypeRef; #[cfg(feature = "state-transition-signing")] @@ -35,7 +36,6 @@ use crate::ProtocolError; use platform_value::Identifier; #[cfg(feature = "state-transition-signing")] use platform_version::version::{FeatureVersion, PlatformVersion}; -use crate::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; pub mod v0; pub mod v1; @@ -888,35 +888,34 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransition { .default_current_version, ) { 1 | 0 - if platform_version - .dpp - .state_transition_serialization_versions - .batch_state_transition - .max_version - >= 1 => - { - // Create the emergency action transition for batch version 1 - BatchTransitionV1::new_token_config_update_transition( - token_id, - owner_id, - data_contract_id, - token_contract_position, - update_token_configuration_item, - public_note, - using_group_info, - identity_public_key, - identity_contract_nonce, - user_fee_increase, - signer, - platform_version, - batch_feature_version, - config_update_feature_version, - base_feature_version, - ) - } + if platform_version + .dpp + .state_transition_serialization_versions + .batch_state_transition + .max_version + >= 1 => + { + // Create the emergency action transition for batch version 1 + BatchTransitionV1::new_token_config_update_transition( + token_id, + owner_id, + data_contract_id, + token_contract_position, + update_token_configuration_item, + public_note, + using_group_info, + identity_public_key, + identity_contract_nonce, + user_fee_increase, + signer, + platform_version, + batch_feature_version, + config_update_feature_version, + base_feature_version, + ) + } version => Err(ProtocolError::UnknownVersionMismatch { - method: "DocumentsBatchTransition::new_token_config_update_transition" - .to_string(), + method: "DocumentsBatchTransition::new_token_config_update_transition".to_string(), known_versions: vec![1], received: version, }), diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/methods/v1/mod.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/methods/v1/mod.rs index 67c4dfa7d6d..83d0be8635b 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/methods/v1/mod.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/methods/v1/mod.rs @@ -1,6 +1,8 @@ #[cfg(feature = "state-transition-signing")] use crate::balances::credits::TokenAmount; #[cfg(feature = "state-transition-signing")] +use crate::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; +#[cfg(feature = "state-transition-signing")] use crate::group::GroupStateTransitionInfoStatus; #[cfg(feature = "state-transition-signing")] use crate::identity::signer::Signer; @@ -24,8 +26,6 @@ use crate::ProtocolError; use platform_value::Identifier; #[cfg(feature = "state-transition-signing")] use platform_version::version::PlatformVersion; -#[cfg(feature = "state-transition-signing")] -use crate::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; pub trait DocumentsBatchTransitionMethodsV1: DocumentsBatchTransitionAccessorsV0 { #[cfg(feature = "state-transition-signing")] diff --git a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v0_methods.rs b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v0_methods.rs index da329aeccb6..c3dbdfa9c3f 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v0_methods.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/document/batch_transition/v1/v0_methods.rs @@ -974,7 +974,7 @@ impl DocumentsBatchTransitionMethodsV1 for BatchTransitionV1 { signature_public_key_id: 0, signature: Default::default(), } - .into(); + .into(); let mut state_transition: StateTransition = batch_transition.into(); state_transition.sign_external( identity_public_key, diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_base_transition_action/state_v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_base_transition_action/state_v0/mod.rs index ae145aeefd0..14c0b96d6a4 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_base_transition_action/state_v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_base_transition_action/state_v0/mod.rs @@ -101,7 +101,9 @@ impl TokenBaseTransitionActionStateValidationV0 for TokenBaseTransitionAction { // We have already checked when converting into an action that we are a member of the Group // Now we need to just check that the group is the actual group set by the contract match rules.authorized_to_make_change_action_takers() { - AuthorizedActionTakers::NoOne | AuthorizedActionTakers::ContractOwner => { + AuthorizedActionTakers::NoOne + | AuthorizedActionTakers::ContractOwner + | AuthorizedActionTakers::Identity(_) => { return Ok(SimpleConsensusValidationResult::new_with_error( ConsensusError::StateError(StateError::UnauthorizedTokenActionError( UnauthorizedTokenActionError::new( diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_config_update_transition_action/state_v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_config_update_transition_action/state_v0/mod.rs index 6bfa6e5f0ab..bc1935f9a35 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_config_update_transition_action/state_v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_config_update_transition_action/state_v0/mod.rs @@ -1,11 +1,12 @@ use dpp::block::block_info::BlockInfo; use dpp::consensus::ConsensusError; use dpp::consensus::state::state_error::StateError; -use dpp::consensus::state::token::{InvalidGroupPositionError, NewTokensDestinationIdentityDoesNotExistError, TokenSettingMaxSupplyToLessThanCurrentSupplyError, UnauthorizedTokenActionError}; +use dpp::consensus::state::token::{InvalidGroupPositionError, NewAuthorizedActionTakerGroupDoesNotExistError, NewAuthorizedActionTakerIdentityDoesNotExistError, NewAuthorizedActionTakerMainGroupNotSetError, NewTokensDestinationIdentityDoesNotExistError, TokenSettingMaxSupplyToLessThanCurrentSupplyError, UnauthorizedTokenActionError}; use dpp::data_contract::accessors::v0::DataContractV0Getters; use dpp::data_contract::accessors::v1::DataContractV1Getters; use dpp::data_contract::associated_token::token_configuration::accessors::v0::TokenConfigurationV0Getters; use dpp::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; +use dpp::data_contract::change_control_rules::authorized_action_takers::AuthorizedActionTakers; use dpp::group::action_taker::{ActionGoal, ActionTaker}; use dpp::prelude::Identifier; use dpp::validation::SimpleConsensusValidationResult; @@ -89,56 +90,293 @@ impl TokenConfigUpdateTransitionActionStateValidationV0 for TokenConfigUpdateTra )), )); } - + match self.update_token_configuration_item() { TokenConfigurationChangeItem::MaxSupply(Some(max_supply)) => { // If we are setting a max supply we need to make sure it isn't less than the // current supply of the token - let (token_total_supply, fee) = platform.drive.fetch_token_total_supply_with_cost(self.token_id().to_buffer(), block_info, transaction, platform_version)?; + let (token_total_supply, fee) = platform.drive.fetch_token_total_supply_with_cost( + self.token_id().to_buffer(), + block_info, + transaction, + platform_version, + )?; execution_context.add_operation(ValidationOperation::PrecalculatedOperation(fee)); if let Some(token_total_supply) = token_total_supply { if token_total_supply > *max_supply { // We are trying to set a max supply smaller than the token total supply return Ok(SimpleConsensusValidationResult::new_with_error( - ConsensusError::StateError(StateError::TokenSettingMaxSupplyToLessThanCurrentSupplyError( - TokenSettingMaxSupplyToLessThanCurrentSupplyError::new( - self.token_id(), - *max_supply, - token_total_supply, + ConsensusError::StateError( + StateError::TokenSettingMaxSupplyToLessThanCurrentSupplyError( + TokenSettingMaxSupplyToLessThanCurrentSupplyError::new( + self.token_id(), + *max_supply, + token_total_supply, + ), ), - )), + ), )); } } else { - return Err(Error::Drive(drive::error::Error::Drive(DriveError::CorruptedDriveState(format!("token {} total supply not found", self.token_id()))))); + return Err(Error::Drive(drive::error::Error::Drive( + DriveError::CorruptedDriveState(format!( + "token {} total supply not found", + self.token_id() + )), + ))); } } TokenConfigurationChangeItem::NewTokensDestinationIdentity(Some(identity_id)) => { // We need to make sure the identity exists - let (identity_balance, fee) = platform.drive.fetch_identity_balance_with_costs(identity_id.to_buffer(), block_info, true, transaction, platform_version)?; + let (identity_balance, fee) = platform.drive.fetch_identity_balance_with_costs( + identity_id.to_buffer(), + block_info, + true, + transaction, + platform_version, + )?; execution_context.add_operation(ValidationOperation::PrecalculatedOperation(fee)); if identity_balance.is_none() { return Ok(SimpleConsensusValidationResult::new_with_error( - ConsensusError::StateError(StateError::NewTokensDestinationIdentityDoesNotExistError( - NewTokensDestinationIdentityDoesNotExistError::new( - *identity_id + ConsensusError::StateError( + StateError::NewTokensDestinationIdentityDoesNotExistError( + NewTokensDestinationIdentityDoesNotExistError::new(*identity_id), ), - )), + ), )); } } TokenConfigurationChangeItem::MainControlGroup(Some(control_group)) => { - if !self.data_contract_fetch_info().contract.groups().contains_key(control_group) { + if !self + .data_contract_fetch_info() + .contract + .groups() + .contains_key(control_group) + { return Ok(SimpleConsensusValidationResult::new_with_error( ConsensusError::StateError(StateError::InvalidGroupPositionError( InvalidGroupPositionError::new( - self.data_contract_fetch_info().contract.groups().keys().last().copied().unwrap_or_default(), + self.data_contract_fetch_info() + .contract + .groups() + .keys() + .last() + .copied(), *control_group, ), )), )); } } + TokenConfigurationChangeItem::ConventionsControlGroup( + AuthorizedActionTakers::Identity(identity_id), + ) + | TokenConfigurationChangeItem::ConventionsAdminGroup( + AuthorizedActionTakers::Identity(identity_id), + ) + | TokenConfigurationChangeItem::MaxSupplyControlGroup( + AuthorizedActionTakers::Identity(identity_id), + ) + | TokenConfigurationChangeItem::MaxSupplyAdminGroup( + AuthorizedActionTakers::Identity(identity_id), + ) + | TokenConfigurationChangeItem::NewTokensDestinationIdentityControlGroup( + AuthorizedActionTakers::Identity(identity_id), + ) + | TokenConfigurationChangeItem::NewTokensDestinationIdentityAdminGroup( + AuthorizedActionTakers::Identity(identity_id), + ) + | TokenConfigurationChangeItem::MintingAllowChoosingDestinationControlGroup( + AuthorizedActionTakers::Identity(identity_id), + ) + | TokenConfigurationChangeItem::MintingAllowChoosingDestinationAdminGroup( + AuthorizedActionTakers::Identity(identity_id), + ) + | TokenConfigurationChangeItem::ManualMinting(AuthorizedActionTakers::Identity( + identity_id, + )) + | TokenConfigurationChangeItem::ManualMintingAdminGroup( + AuthorizedActionTakers::Identity(identity_id), + ) + | TokenConfigurationChangeItem::ManualBurning(AuthorizedActionTakers::Identity( + identity_id, + )) + | TokenConfigurationChangeItem::ManualBurningAdminGroup( + AuthorizedActionTakers::Identity(identity_id), + ) + | TokenConfigurationChangeItem::Freeze(AuthorizedActionTakers::Identity(identity_id)) + | TokenConfigurationChangeItem::FreezeAdminGroup(AuthorizedActionTakers::Identity( + identity_id, + )) + | TokenConfigurationChangeItem::Unfreeze(AuthorizedActionTakers::Identity( + identity_id, + )) + | TokenConfigurationChangeItem::UnfreezeAdminGroup(AuthorizedActionTakers::Identity( + identity_id, + )) + | TokenConfigurationChangeItem::DestroyFrozenFunds(AuthorizedActionTakers::Identity( + identity_id, + )) + | TokenConfigurationChangeItem::DestroyFrozenFundsAdminGroup( + AuthorizedActionTakers::Identity(identity_id), + ) + | TokenConfigurationChangeItem::EmergencyAction(AuthorizedActionTakers::Identity( + identity_id, + )) + | TokenConfigurationChangeItem::EmergencyActionAdminGroup( + AuthorizedActionTakers::Identity(identity_id), + ) => { + let (identity_balance, fee) = platform.drive.fetch_identity_balance_with_costs( + identity_id.to_buffer(), + block_info, + true, + transaction, + platform_version, + )?; + execution_context.add_operation(ValidationOperation::PrecalculatedOperation(fee)); + if identity_balance.is_none() { + return Ok(SimpleConsensusValidationResult::new_with_error( + ConsensusError::StateError( + StateError::NewAuthorizedActionTakerIdentityDoesNotExistError( + NewAuthorizedActionTakerIdentityDoesNotExistError::new( + *identity_id, + ), + ), + ), + )); + } + } + TokenConfigurationChangeItem::ConventionsControlGroup( + AuthorizedActionTakers::Group(group_contract_position), + ) + | TokenConfigurationChangeItem::ConventionsAdminGroup(AuthorizedActionTakers::Group( + group_contract_position, + )) + | TokenConfigurationChangeItem::MaxSupplyControlGroup(AuthorizedActionTakers::Group( + group_contract_position, + )) + | TokenConfigurationChangeItem::MaxSupplyAdminGroup(AuthorizedActionTakers::Group( + group_contract_position, + )) + | TokenConfigurationChangeItem::NewTokensDestinationIdentityControlGroup( + AuthorizedActionTakers::Group(group_contract_position), + ) + | TokenConfigurationChangeItem::NewTokensDestinationIdentityAdminGroup( + AuthorizedActionTakers::Group(group_contract_position), + ) + | TokenConfigurationChangeItem::MintingAllowChoosingDestinationControlGroup( + AuthorizedActionTakers::Group(group_contract_position), + ) + | TokenConfigurationChangeItem::MintingAllowChoosingDestinationAdminGroup( + AuthorizedActionTakers::Group(group_contract_position), + ) + | TokenConfigurationChangeItem::ManualMinting(AuthorizedActionTakers::Group( + group_contract_position, + )) + | TokenConfigurationChangeItem::ManualMintingAdminGroup( + AuthorizedActionTakers::Group(group_contract_position), + ) + | TokenConfigurationChangeItem::ManualBurning(AuthorizedActionTakers::Group( + group_contract_position, + )) + | TokenConfigurationChangeItem::ManualBurningAdminGroup( + AuthorizedActionTakers::Group(group_contract_position), + ) + | TokenConfigurationChangeItem::Freeze(AuthorizedActionTakers::Group( + group_contract_position, + )) + | TokenConfigurationChangeItem::FreezeAdminGroup(AuthorizedActionTakers::Group( + group_contract_position, + )) + | TokenConfigurationChangeItem::Unfreeze(AuthorizedActionTakers::Group( + group_contract_position, + )) + | TokenConfigurationChangeItem::UnfreezeAdminGroup(AuthorizedActionTakers::Group( + group_contract_position, + )) + | TokenConfigurationChangeItem::DestroyFrozenFunds(AuthorizedActionTakers::Group( + group_contract_position, + )) + | TokenConfigurationChangeItem::DestroyFrozenFundsAdminGroup( + AuthorizedActionTakers::Group(group_contract_position), + ) + | TokenConfigurationChangeItem::EmergencyAction(AuthorizedActionTakers::Group( + group_contract_position, + )) + | TokenConfigurationChangeItem::EmergencyActionAdminGroup( + AuthorizedActionTakers::Group(group_contract_position), + ) => { + if !self + .data_contract_fetch_info() + .contract + .groups() + .contains_key(group_contract_position) + { + return Ok(SimpleConsensusValidationResult::new_with_error( + ConsensusError::StateError( + StateError::NewAuthorizedActionTakerGroupDoesNotExistError( + NewAuthorizedActionTakerGroupDoesNotExistError::new( + *group_contract_position, + ), + ), + ), + )); + } + } + TokenConfigurationChangeItem::ConventionsControlGroup( + AuthorizedActionTakers::MainGroup, + ) + | TokenConfigurationChangeItem::ConventionsAdminGroup( + AuthorizedActionTakers::MainGroup, + ) + | TokenConfigurationChangeItem::MaxSupplyControlGroup( + AuthorizedActionTakers::MainGroup, + ) + | TokenConfigurationChangeItem::MaxSupplyAdminGroup( + AuthorizedActionTakers::MainGroup, + ) + | TokenConfigurationChangeItem::NewTokensDestinationIdentityControlGroup( + AuthorizedActionTakers::MainGroup, + ) + | TokenConfigurationChangeItem::NewTokensDestinationIdentityAdminGroup( + AuthorizedActionTakers::MainGroup, + ) + | TokenConfigurationChangeItem::MintingAllowChoosingDestinationControlGroup( + AuthorizedActionTakers::MainGroup, + ) + | TokenConfigurationChangeItem::MintingAllowChoosingDestinationAdminGroup( + AuthorizedActionTakers::MainGroup, + ) + | TokenConfigurationChangeItem::ManualMinting(AuthorizedActionTakers::MainGroup) + | TokenConfigurationChangeItem::ManualMintingAdminGroup( + AuthorizedActionTakers::MainGroup, + ) + | TokenConfigurationChangeItem::ManualBurning(AuthorizedActionTakers::MainGroup) + | TokenConfigurationChangeItem::ManualBurningAdminGroup( + AuthorizedActionTakers::MainGroup, + ) + | TokenConfigurationChangeItem::Freeze(AuthorizedActionTakers::MainGroup) + | TokenConfigurationChangeItem::FreezeAdminGroup(AuthorizedActionTakers::MainGroup) + | TokenConfigurationChangeItem::Unfreeze(AuthorizedActionTakers::MainGroup) + | TokenConfigurationChangeItem::UnfreezeAdminGroup(AuthorizedActionTakers::MainGroup) + | TokenConfigurationChangeItem::DestroyFrozenFunds(AuthorizedActionTakers::MainGroup) + | TokenConfigurationChangeItem::DestroyFrozenFundsAdminGroup( + AuthorizedActionTakers::MainGroup, + ) + | TokenConfigurationChangeItem::EmergencyAction(AuthorizedActionTakers::MainGroup) + | TokenConfigurationChangeItem::EmergencyActionAdminGroup( + AuthorizedActionTakers::MainGroup, + ) => { + if token_configuration.main_control_group().is_none() { + return Ok(SimpleConsensusValidationResult::new_with_error( + ConsensusError::StateError( + StateError::NewAuthorizedActionTakerMainGroupNotSetError( + NewAuthorizedActionTakerMainGroupNotSetError::new(), + ), + ), + )); + } + } _ => {} } diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_mint_transition_action/state_v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_mint_transition_action/state_v0/mod.rs index 5efb70f6f53..1edce2ea328 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_mint_transition_action/state_v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_mint_transition_action/state_v0/mod.rs @@ -72,13 +72,20 @@ impl TokenMintTransitionActionStateValidationV0 for TokenMintTransitionAction { if !validation_result.is_valid() { return Ok(validation_result); } - + if let Some(max_supply) = token_configuration.max_supply() { // We have a max supply, let's get the current supply - let (token_total_supply, fee) = platform.drive.fetch_token_total_supply_with_cost(self.token_id().to_buffer(), block_info, transaction, platform_version)?; + let (token_total_supply, fee) = platform.drive.fetch_token_total_supply_with_cost( + self.token_id().to_buffer(), + block_info, + transaction, + platform_version, + )?; execution_context.add_operation(ValidationOperation::PrecalculatedOperation(fee)); if let Some(token_total_supply) = token_total_supply { - if let Some(total_supply_after_mint) = token_total_supply.checked_add(self.mint_amount()) { + if let Some(total_supply_after_mint) = + token_total_supply.checked_add(self.mint_amount()) + { if total_supply_after_mint > max_supply { // We are trying to set a max supply smaller than the token total supply return Ok(SimpleConsensusValidationResult::new_with_error( @@ -106,7 +113,12 @@ impl TokenMintTransitionActionStateValidationV0 for TokenMintTransitionAction { )); } } else { - return Err(Error::Drive(drive::error::Error::Drive(DriveError::CorruptedDriveState(format!("token {} total supply not found", self.token_id()))))); + return Err(Error::Drive(drive::error::Error::Drive( + DriveError::CorruptedDriveState(format!( + "token {} total supply not found", + self.token_id() + )), + ))); } } diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/mod.rs index bf9ee1a34cb..8856c9e79fa 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/mod.rs @@ -254,11 +254,14 @@ mod tests { use dpp::data_contract::document_type::random_document::{ CreateRandomDocument, DocumentFieldFillSize, DocumentFieldFillType, }; + use dpp::data_contract::group::v0::GroupV0; + use dpp::data_contract::group::Group; use dpp::document::document_methods::DocumentMethodsV0; use dpp::document::transfer::Transferable; use dpp::document::{DocumentV0Getters, DocumentV0Setters}; use dpp::fee::fee_result::BalanceChange; use dpp::fee::Credits; + use dpp::group::GroupStateTransitionInfo; use dpp::identifier::Identifier; use dpp::identity::accessors::IdentityGettersV0; use dpp::nft::TradeMode; @@ -10194,14 +10197,17 @@ mod tests { mod token_tests { use super::*; use crate::execution::validation::state_transition::tests::create_token_contract_with_owner_identity; + use dpp::data_contract::associated_token::token_configuration::accessors::v0::TokenConfigurationV0Getters; use dpp::data_contract::associated_token::token_configuration::accessors::v0::TokenConfigurationV0Setters; use dpp::data_contract::associated_token::token_configuration::TokenConfiguration; + use dpp::data_contract::associated_token::token_configuration_convention::v0::TokenConfigurationConventionV0; + use dpp::data_contract::associated_token::token_configuration_convention::v0::TokenConfigurationLocalizationsV0; use dpp::data_contract::change_control_rules::authorized_action_takers::AuthorizedActionTakers; use dpp::data_contract::change_control_rules::v0::ChangeControlRulesV0; use dpp::data_contract::change_control_rules::ChangeControlRules; - use dpp::state_transition::batch_transition::methods::v1::DocumentsBatchTransitionMethodsV1; - use dpp::data_contract::associated_token::token_configuration::accessors::v0::TokenConfigurationV0Getters; use dpp::group::GroupStateTransitionInfoStatus; + use dpp::state_transition::batch_transition::methods::v1::DocumentsBatchTransitionMethodsV1; + use dpp::state_transition::batch_transition::TokenConfigUpdateTransition; mod token_mint_tests { use super::*; @@ -10340,7 +10346,7 @@ mod tests { None, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); let documents_batch_create_serialized_transition = documents_batch_create_transition @@ -10363,12 +10369,12 @@ mod tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( - ConsensusError::StateError(StateError::TokenMintPastMaxSupplyError(_)), - _ - )] - ); + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::PaidConsensusError( + ConsensusError::StateError(StateError::TokenMintPastMaxSupplyError(_)), + _ + )] + ); platform .drive @@ -14512,6 +14518,7 @@ mod tests { mod token_config_update_tests { use super::*; use dpp::data_contract::accessors::v1::DataContractV1Getters; + use dpp::data_contract::associated_token::token_configuration_convention::TokenConfigurationConvention; use dpp::data_contract::associated_token::token_configuration_item::TokenConfigurationChangeItem; mod non_group { @@ -14535,15 +14542,16 @@ mod tests { &mut platform, identity.id(), Some(|token_configuration: &mut TokenConfiguration| { - token_configuration.set_max_supply_change_rules(ChangeControlRules::V0( - ChangeControlRulesV0 { - authorized_to_make_change: AuthorizedActionTakers::ContractOwner, + token_configuration.set_max_supply_change_rules( + ChangeControlRules::V0(ChangeControlRulesV0 { + authorized_to_make_change: + AuthorizedActionTakers::ContractOwner, admin_action_takers: AuthorizedActionTakers::NoOne, changing_authorized_action_takers_to_no_one_allowed: false, changing_admin_action_takers_to_no_one_allowed: false, self_changing_admin_action_takers_allowed: false, - }, - )); + }), + ); }), None, platform_version, @@ -14567,12 +14575,11 @@ mod tests { None, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); - let config_update_transition_serialized_transition = - config_update_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); + let config_update_transition_serialized_transition = config_update_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); let transaction = platform.drive.grove.start_transaction(); @@ -14609,14 +14616,20 @@ mod tests { None, None, platform_version, - ).unwrap() - .expect("expected to fetch token balance").expect("expected contract"); - let updated_token_config = contract.contract.expected_token_configuration(0).expect("expected token configuration"); + ) + .unwrap() + .expect("expected to fetch token balance") + .expect("expected contract"); + let updated_token_config = contract + .contract + .expected_token_configuration(0) + .expect("expected token configuration"); assert_eq!(updated_token_config.max_supply(), Some(1000000)); } #[test] - fn test_token_config_update_by_owner_changing_total_max_supply_to_less_than_current_supply() { + fn test_token_config_update_by_owner_changing_total_max_supply_to_less_than_current_supply( + ) { let platform_version = PlatformVersion::latest(); let mut platform = TestPlatformBuilder::new() .with_latest_protocol_version() @@ -14634,15 +14647,16 @@ mod tests { &mut platform, identity.id(), Some(|token_configuration: &mut TokenConfiguration| { - token_configuration.set_max_supply_change_rules(ChangeControlRules::V0( - ChangeControlRulesV0 { - authorized_to_make_change: AuthorizedActionTakers::ContractOwner, + token_configuration.set_max_supply_change_rules( + ChangeControlRules::V0(ChangeControlRulesV0 { + authorized_to_make_change: + AuthorizedActionTakers::ContractOwner, admin_action_takers: AuthorizedActionTakers::NoOne, changing_authorized_action_takers_to_no_one_allowed: false, changing_admin_action_takers_to_no_one_allowed: false, self_changing_admin_action_takers_allowed: false, - }, - )); + }), + ); }), None, platform_version, @@ -14666,12 +14680,11 @@ mod tests { None, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); - let config_update_transition_serialized_transition = - config_update_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); + let config_update_transition_serialized_transition = config_update_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); let transaction = platform.drive.grove.start_transaction(); @@ -14689,12 +14702,14 @@ mod tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( - ConsensusError::StateError(StateError::TokenSettingMaxSupplyToLessThanCurrentSupplyError(_)), - _ - )] - ); + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::PaidConsensusError( + ConsensusError::StateError( + StateError::TokenSettingMaxSupplyToLessThanCurrentSupplyError(_) + ), + _ + )] + ); platform .drive @@ -14711,22 +14726,19 @@ mod tests { None, None, platform_version, - ).unwrap() - .expect("expected to fetch token balance").expect("expected contract"); - let updated_token_config = contract.contract.expected_token_configuration(0).expect("expected token configuration"); + ) + .unwrap() + .expect("expected to fetch token balance") + .expect("expected contract"); + let updated_token_config = contract + .contract + .expected_token_configuration(0) + .expect("expected token configuration"); assert_eq!(updated_token_config.max_supply(), None); } - } - - mod with_group { - use dpp::data_contract::group::Group; - use dpp::data_contract::group::v0::GroupV0; - use dpp::group::GroupStateTransitionInfo; - use dpp::state_transition::batch_transition::{TokenConfigUpdateTransition, TokenMintTransition}; - use super::*; #[test] - fn test_token_config_update_by_group_member_changing_total_max_supply_not_using_group_gives_error() { + fn test_token_config_update_by_owner_change_admin_to_another_identity() { let platform_version = PlatformVersion::latest(); let mut platform = TestPlatformBuilder::new() .with_latest_protocol_version() @@ -14740,33 +14752,25 @@ mod tests { let (identity, signer, key) = setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - let (identity_2, _, _) = + let (identity_2, signer_2, key_2) = setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); let (contract, token_id) = create_token_contract_with_owner_identity( &mut platform, identity.id(), Some(|token_configuration: &mut TokenConfiguration| { - token_configuration.set_max_supply_change_rules(ChangeControlRules::V0( - ChangeControlRulesV0 { - authorized_to_make_change: AuthorizedActionTakers::Group(0), - admin_action_takers: AuthorizedActionTakers::NoOne, + token_configuration.set_max_supply_change_rules( + ChangeControlRules::V0(ChangeControlRulesV0 { + authorized_to_make_change: + AuthorizedActionTakers::ContractOwner, + admin_action_takers: AuthorizedActionTakers::ContractOwner, changing_authorized_action_takers_to_no_one_allowed: false, changing_admin_action_takers_to_no_one_allowed: false, self_changing_admin_action_takers_allowed: false, - }, - )); - }), - Some( - [( - 0, - Group::V0(GroupV0 { - members: [(identity.id(), 1), (identity_2.id(), 1)].into(), - required_power: 2, }), - )] - .into(), - ), + ); + }), + None, platform_version, ); @@ -14776,7 +14780,9 @@ mod tests { identity.id(), contract.id(), 0, - TokenConfigurationChangeItem::MaxSupply(Some(1000000)), + TokenConfigurationChangeItem::MaxSupplyControlGroup( + AuthorizedActionTakers::Identity(identity_2.id()), + ), None, None, &key, @@ -14788,12 +14794,11 @@ mod tests { None, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); - let config_update_transition_serialized_transition = - config_update_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); + let config_update_transition_serialized_transition = config_update_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); let transaction = platform.drive.grove.start_transaction(); @@ -14811,12 +14816,60 @@ mod tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::PaidConsensusError( - ConsensusError::StateError(StateError::UnauthorizedTokenActionError(_)), - _ - )] - ); + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let config_update_transition = + BatchTransition::new_token_config_update_transition( + token_id, + identity_2.id(), + contract.id(), + 0, + TokenConfigurationChangeItem::MaxSupply(Some(1000000)), + None, + None, + &key_2, + 2, + 0, + &signer_2, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let config_update_transition_serialized_transition = config_update_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![config_update_transition_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); platform .drive @@ -14833,14 +14886,20 @@ mod tests { None, None, platform_version, - ).unwrap() - .expect("expected to fetch token balance").expect("expected contract"); - let updated_token_config = contract.contract.expected_token_configuration(0).expect("expected token configuration"); - assert_eq!(updated_token_config.max_supply(), None); + ) + .unwrap() + .expect("expected to fetch token balance") + .expect("expected contract"); + let updated_token_config = contract + .contract + .expected_token_configuration(0) + .expect("expected token configuration"); + assert_eq!(updated_token_config.max_supply(), Some(1000000)); } #[test] - fn test_token_config_update_by_group_member_changing_total_max_supply() { + fn test_token_config_update_by_owner_change_admin_to_a_non_existent_identity_error() + { let platform_version = PlatformVersion::latest(); let mut platform = TestPlatformBuilder::new() .with_latest_protocol_version() @@ -14854,52 +14913,38 @@ mod tests { let (identity, signer, key) = setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); - let (identity_2, signer_2, key_2) = - setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + let identity_2_id = Identifier::random_with_rng(&mut rng); let (contract, token_id) = create_token_contract_with_owner_identity( &mut platform, identity.id(), Some(|token_configuration: &mut TokenConfiguration| { - token_configuration.set_max_supply_change_rules(ChangeControlRules::V0( - ChangeControlRulesV0 { - authorized_to_make_change: AuthorizedActionTakers::Group(0), - admin_action_takers: AuthorizedActionTakers::NoOne, + token_configuration.set_max_supply_change_rules( + ChangeControlRules::V0(ChangeControlRulesV0 { + authorized_to_make_change: + AuthorizedActionTakers::ContractOwner, + admin_action_takers: AuthorizedActionTakers::ContractOwner, changing_authorized_action_takers_to_no_one_allowed: false, changing_admin_action_takers_to_no_one_allowed: false, self_changing_admin_action_takers_allowed: false, - }, - )); - }), - Some( - [( - 0, - Group::V0(GroupV0 { - members: [(identity.id(), 1), (identity_2.id(), 1)].into(), - required_power: 2, }), - )] - .into(), - ), + ); + }), + None, platform_version, ); - let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields( - token_id.as_bytes(), - identity.id().as_bytes(), - 2, - 1337, - ); - let config_update_transition = BatchTransition::new_token_config_update_transition( token_id, identity.id(), contract.id(), 0, - TokenConfigurationChangeItem::MaxSupply(Some(1000000)), + TokenConfigurationChangeItem::MaxSupplyControlGroup( + AuthorizedActionTakers::Identity(identity_2_id), + ), + None, None, - Some(GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer(0)), &key, 2, 0, @@ -14909,12 +14954,11 @@ mod tests { None, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); - let config_update_transition_serialized_transition = - config_update_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); + let config_update_transition_serialized_transition = config_update_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); let transaction = platform.drive.grove.start_transaction(); @@ -14932,9 +14976,14 @@ mod tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::PaidConsensusError( + ConsensusError::StateError( + StateError::NewAuthorizedActionTakerIdentityDoesNotExistError(_) + ), + _ + )] + ); platform .drive @@ -14942,48 +14991,69 @@ mod tests { .commit_transaction(transaction) .unwrap() .expect("expected to commit transaction"); + } - let new_contract = platform - .drive - .fetch_contract( - contract.id().to_buffer(), - None, - None, - None, - platform_version, - ).unwrap() - .expect("expected to fetch token balance").expect("expected contract"); - let updated_token_config = new_contract.contract.expected_token_configuration(0).expect("expected token configuration"); - assert_eq!(updated_token_config.max_supply(), None); + #[test] + fn test_token_config_update_by_owner_change_admin_to_a_non_existent_group_error() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let identity_2_id = Identifier::random_with_rng(&mut rng); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_max_supply_change_rules( + ChangeControlRules::V0(ChangeControlRulesV0 { + authorized_to_make_change: + AuthorizedActionTakers::ContractOwner, + admin_action_takers: AuthorizedActionTakers::ContractOwner, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }), + ); + }), + None, + platform_version, + ); let config_update_transition = BatchTransition::new_token_config_update_transition( token_id, - identity_2.id(), + identity.id(), contract.id(), 0, - TokenConfigurationChangeItem::MaxSupply(Some(1000000)), + TokenConfigurationChangeItem::MaxSupplyControlGroup( + AuthorizedActionTakers::Group(0), + ), None, - Some(GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner(GroupStateTransitionInfo { - group_contract_position: 0, - action_id, - action_is_proposer: false, - })), - &key_2, + None, + &key, 2, 0, - &signer_2, + &signer, platform_version, None, None, None, ) - .expect("expect to create documents batch transition"); + .expect("expect to create documents batch transition"); - let config_update_transition_serialized_transition = - config_update_transition - .serialize_to_bytes() - .expect("expected documents batch serialized state transition"); + let config_update_transition_serialized_transition = config_update_transition + .serialize_to_bytes() + .expect("expected documents batch serialized state transition"); let transaction = platform.drive.grove.start_transaction(); @@ -15001,9 +15071,1149 @@ mod tests { .expect("expected to process state transition"); assert_matches!( - processing_result.execution_results().as_slice(), - [StateTransitionExecutionResult::SuccessfulExecution(_, _)] - ); + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::PaidConsensusError( + ConsensusError::StateError( + StateError::NewAuthorizedActionTakerGroupDoesNotExistError(_) + ), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + } + } + + mod with_group { + use super::*; + + #[test] + fn test_token_config_update_by_group_member_changing_total_max_supply_not_using_group_gives_error( + ) { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (identity_2, _, _) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_max_supply_change_rules( + ChangeControlRules::V0(ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::Group(0), + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }), + ); + }), + Some( + [( + 0, + Group::V0(GroupV0 { + members: [(identity.id(), 1), (identity_2.id(), 1)].into(), + required_power: 2, + }), + )] + .into(), + ), + platform_version, + ); + + let config_update_transition = + BatchTransition::new_token_config_update_transition( + token_id, + identity.id(), + contract.id(), + 0, + TokenConfigurationChangeItem::MaxSupply(Some(1000000)), + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let config_update_transition_serialized_transition = config_update_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![config_update_transition_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::PaidConsensusError( + ConsensusError::StateError(StateError::UnauthorizedTokenActionError(_)), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let contract = platform + .drive + .fetch_contract( + contract.id().to_buffer(), + None, + None, + None, + platform_version, + ) + .unwrap() + .expect("expected to fetch token balance") + .expect("expected contract"); + let updated_token_config = contract + .contract + .expected_token_configuration(0) + .expect("expected token configuration"); + assert_eq!(updated_token_config.max_supply(), None); + } + + #[test] + fn test_token_config_update_by_group_member_changing_total_max_supply() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (identity_2, signer_2, key_2) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_max_supply_change_rules( + ChangeControlRules::V0(ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::Group(0), + admin_action_takers: AuthorizedActionTakers::NoOne, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }), + ); + }), + Some( + [( + 0, + Group::V0(GroupV0 { + members: [(identity.id(), 1), (identity_2.id(), 1)].into(), + required_power: 2, + }), + )] + .into(), + ), + platform_version, + ); + + let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields( + token_id.as_bytes(), + identity.id().as_bytes(), + 2, + TokenConfigurationChangeItem::MaxSupply(Some(1000000)).u8_item_index(), + ); + + let config_update_transition = + BatchTransition::new_token_config_update_transition( + token_id, + identity.id(), + contract.id(), + 0, + TokenConfigurationChangeItem::MaxSupply(Some(1000000)), + None, + Some( + GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer(0), + ), + &key, + 2, + 0, + &signer, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let config_update_transition_serialized_transition = config_update_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![config_update_transition_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let new_contract = platform + .drive + .fetch_contract( + contract.id().to_buffer(), + None, + None, + None, + platform_version, + ) + .unwrap() + .expect("expected to fetch token balance") + .expect("expected contract"); + let updated_token_config = new_contract + .contract + .expected_token_configuration(0) + .expect("expected token configuration"); + assert_eq!(updated_token_config.max_supply(), None); + + let config_update_transition = + BatchTransition::new_token_config_update_transition( + token_id, + identity_2.id(), + contract.id(), + 0, + TokenConfigurationChangeItem::MaxSupply(Some(1000000)), + None, + Some( + GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( + GroupStateTransitionInfo { + group_contract_position: 0, + action_id, + action_is_proposer: false, + }, + ), + ), + &key_2, + 2, + 0, + &signer_2, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let config_update_transition_serialized_transition = config_update_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![config_update_transition_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let new_contract = platform + .drive + .fetch_contract( + contract.id().to_buffer(), + None, + None, + None, + platform_version, + ) + .unwrap() + .expect("expected to fetch token balance") + .expect("expected contract"); + let updated_token_config = new_contract + .contract + .expected_token_configuration(0) + .expect("expected token configuration"); + assert_eq!(updated_token_config.max_supply(), Some(1000000)); + } + + #[test] + fn test_token_config_change_own_admin_group_give_control_power_and_change_admin_back( + ) { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (identity_2, signer_2, key_2) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (identity_3, signer_3, key_3) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (identity_4, signer_4, key_4) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (identity_5, signer_5, key_5) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_conventions_change_rules( + ChangeControlRules::V0(ChangeControlRulesV0 { + authorized_to_make_change: AuthorizedActionTakers::Group(0), + admin_action_takers: AuthorizedActionTakers::Group(1), + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: true, + }), + ); + }), + Some( + [ + ( + 0, + Group::V0(GroupV0 { + members: [(identity.id(), 1), (identity_2.id(), 1)].into(), + required_power: 2, + }), + ), + ( + 1, + Group::V0(GroupV0 { + members: [ + (identity_3.id(), 1), + (identity_4.id(), 1), + (identity_5.id(), 1), + ] + .into(), + required_power: 2, + }), + ), + ] + .into(), + ), + platform_version, + ); + + let action_id = TokenConfigUpdateTransition::calculate_action_id_with_fields( + token_id.as_bytes(), + identity_3.id().as_bytes(), + 2, + TokenConfigurationChangeItem::ConventionsAdminGroup( + AuthorizedActionTakers::Group(0), + ) + .u8_item_index(), + ); + + let config_update_transition = + BatchTransition::new_token_config_update_transition( + token_id, + identity_3.id(), + contract.id(), + 0, + TokenConfigurationChangeItem::ConventionsAdminGroup( + AuthorizedActionTakers::Group(0), + ), + None, + Some( + GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer(1), + ), + &key_3, + 2, + 0, + &signer_3, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let config_update_transition_serialized_transition = config_update_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![config_update_transition_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let new_contract = platform + .drive + .fetch_contract( + contract.id().to_buffer(), + None, + None, + None, + platform_version, + ) + .unwrap() + .expect("expected to fetch token balance") + .expect("expected contract"); + let updated_token_config = new_contract + .contract + .expected_token_configuration(0) + .expect("expected token configuration"); + assert_eq!( + updated_token_config + .conventions_change_rules() + .admin_action_takers(), + &AuthorizedActionTakers::Group(1) + ); + + let config_update_transition = + BatchTransition::new_token_config_update_transition( + token_id, + identity_4.id(), + contract.id(), + 0, + TokenConfigurationChangeItem::ConventionsAdminGroup( + AuthorizedActionTakers::Group(0), + ), + None, + Some( + GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( + GroupStateTransitionInfo { + group_contract_position: 1, + action_id, + action_is_proposer: false, + }, + ), + ), + &key_4, + 2, + 0, + &signer_4, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let config_update_transition_serialized_transition = config_update_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![config_update_transition_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let new_contract = platform + .drive + .fetch_contract( + contract.id().to_buffer(), + None, + None, + None, + platform_version, + ) + .unwrap() + .expect("expected to fetch token balance") + .expect("expected contract"); + let updated_token_config = new_contract + .contract + .expected_token_configuration(0) + .expect("expected token configuration"); + assert_eq!( + updated_token_config + .conventions_change_rules() + .admin_action_takers(), + &AuthorizedActionTakers::Group(0) + ); + assert_eq!(new_contract.contract.version(), 2); + + // 5 is late to the game, admin control has already been transferred, he should get an error + let config_update_transition = + BatchTransition::new_token_config_update_transition( + token_id, + identity_5.id(), + contract.id(), + 0, + TokenConfigurationChangeItem::ConventionsAdminGroup( + AuthorizedActionTakers::Group(0), + ), + None, + Some( + GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( + GroupStateTransitionInfo { + group_contract_position: 1, + action_id, + action_is_proposer: false, + }, + ), + ), + &key_5, + 2, + 0, + &signer_5, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let config_update_transition_serialized_transition = config_update_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![config_update_transition_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::PaidConsensusError( + ConsensusError::StateError( + StateError::GroupActionAlreadyCompletedError(_) + ), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + // Let's try if he proposes it now + + let config_update_transition = + BatchTransition::new_token_config_update_transition( + token_id, + identity_5.id(), + contract.id(), + 0, + TokenConfigurationChangeItem::ConventionsAdminGroup( + AuthorizedActionTakers::Group(0), + ), + None, + Some( + GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer(1), + ), + &key_5, + 3, + 0, + &signer_5, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let config_update_transition_serialized_transition = config_update_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![config_update_transition_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::PaidConsensusError( + ConsensusError::StateError(StateError::UnauthorizedTokenActionError(_)), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + // Now let's have Group 0 change the control of the conventions to identity 2 only + + let action_id_change_control = + TokenConfigUpdateTransition::calculate_action_id_with_fields( + token_id.as_bytes(), + identity.id().as_bytes(), + 2, + TokenConfigurationChangeItem::ConventionsControlGroup( + AuthorizedActionTakers::Identity(identity_2.id()), + ) + .u8_item_index(), + ); + + let config_update_transition = + BatchTransition::new_token_config_update_transition( + token_id, + identity.id(), + contract.id(), + 0, + TokenConfigurationChangeItem::ConventionsControlGroup( + AuthorizedActionTakers::Identity(identity_2.id()), + ), + None, + Some( + GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer(0), + ), + &key, + 2, + 0, + &signer, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let config_update_transition_serialized_transition = config_update_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![config_update_transition_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let config_update_transition = + BatchTransition::new_token_config_update_transition( + token_id, + identity_2.id(), + contract.id(), + 0, + TokenConfigurationChangeItem::ConventionsControlGroup( + AuthorizedActionTakers::Identity(identity_2.id()), + ), + None, + Some( + GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( + GroupStateTransitionInfo { + group_contract_position: 0, + action_id: action_id_change_control, + action_is_proposer: false, + }, + ), + ), + &key_2, + 2, + 0, + &signer_2, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let config_update_transition_serialized_transition = config_update_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![config_update_transition_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let new_contract = platform + .drive + .fetch_contract( + contract.id().to_buffer(), + None, + None, + None, + platform_version, + ) + .unwrap() + .expect("expected to fetch token balance") + .expect("expected contract"); + let updated_token_config = new_contract + .contract + .expected_token_configuration(0) + .expect("expected token configuration"); + assert_eq!( + updated_token_config + .conventions_change_rules() + .authorized_to_make_change_action_takers(), + &AuthorizedActionTakers::Identity(identity_2.id()) + ); + assert_eq!(new_contract.contract.version(), 3); + + // Now let's have Group 0 hand it back to Group 1 + + let action_id_return = + TokenConfigUpdateTransition::calculate_action_id_with_fields( + token_id.as_bytes(), + identity.id().as_bytes(), + 3, + TokenConfigurationChangeItem::ConventionsAdminGroup( + AuthorizedActionTakers::Group(1), + ) + .u8_item_index(), + ); + + let config_update_transition = + BatchTransition::new_token_config_update_transition( + token_id, + identity.id(), + contract.id(), + 0, + TokenConfigurationChangeItem::ConventionsAdminGroup( + AuthorizedActionTakers::Group(1), + ), + None, + Some( + GroupStateTransitionInfoStatus::GroupStateTransitionInfoProposer(0), + ), + &key, + 3, + 0, + &signer, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let config_update_transition_serialized_transition = config_update_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![config_update_transition_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let config_update_transition = + BatchTransition::new_token_config_update_transition( + token_id, + identity_2.id(), + contract.id(), + 0, + TokenConfigurationChangeItem::ConventionsAdminGroup( + AuthorizedActionTakers::Group(1), + ), + None, + Some( + GroupStateTransitionInfoStatus::GroupStateTransitionInfoOtherSigner( + GroupStateTransitionInfo { + group_contract_position: 0, + action_id: action_id_return, + action_is_proposer: false, + }, + ), + ), + &key_2, + 3, + 0, + &signer_2, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let config_update_transition_serialized_transition = config_update_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![config_update_transition_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + let new_contract = platform + .drive + .fetch_contract( + contract.id().to_buffer(), + None, + None, + None, + platform_version, + ) + .unwrap() + .expect("expected to fetch token balance") + .expect("expected contract"); + let updated_token_config = new_contract + .contract + .expected_token_configuration(0) + .expect("expected token configuration"); + assert_eq!( + updated_token_config + .conventions_change_rules() + .admin_action_takers(), + &AuthorizedActionTakers::Group(1) + ); + assert_eq!(new_contract.contract.version(), 4); + + // Not let's try identity 3 to change the conventions (should fail) + + let config_update_transition = + BatchTransition::new_token_config_update_transition( + token_id, + identity_3.id(), + contract.id(), + 0, + TokenConfigurationChangeItem::Conventions( + TokenConfigurationConvention::V0(TokenConfigurationConventionV0 { + localizations: [( + "en".to_string(), + TokenConfigurationLocalizationsV0 { + should_capitalize: true, + singular_form: "garzon".to_string(), + plural_form: "garzons".to_string(), + }, + )] + .into(), + decimals: 8, + }), + ), + None, + None, + &key_3, + 3, + 0, + &signer_3, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let config_update_transition_serialized_transition = config_update_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![config_update_transition_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::PaidConsensusError( + ConsensusError::StateError(StateError::UnauthorizedTokenActionError(_)), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + // Not let's try identity 2 to change the conventions (should succeed) + + let config_update_transition = + BatchTransition::new_token_config_update_transition( + token_id, + identity_2.id(), + contract.id(), + 0, + TokenConfigurationChangeItem::Conventions( + TokenConfigurationConvention::V0(TokenConfigurationConventionV0 { + localizations: [( + "en".to_string(), + TokenConfigurationLocalizationsV0 { + should_capitalize: true, + singular_form: "garzon".to_string(), + plural_form: "garzons".to_string(), + }, + )] + .into(), + decimals: 8, + }), + ), + None, + None, + &key_2, + 4, + 0, + &signer_2, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let config_update_transition_serialized_transition = config_update_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![config_update_transition_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::SuccessfulExecution(_, _)] + ); platform .drive diff --git a/packages/rs-drive/src/drive/tokens/system/fetch_token_total_supply/mod.rs b/packages/rs-drive/src/drive/tokens/system/fetch_token_total_supply/mod.rs index 1677cb4935c..ca6a6f44acd 100644 --- a/packages/rs-drive/src/drive/tokens/system/fetch_token_total_supply/mod.rs +++ b/packages/rs-drive/src/drive/tokens/system/fetch_token_total_supply/mod.rs @@ -5,12 +5,12 @@ use crate::error::drive::DriveError; use crate::error::Error; use crate::fees::op::LowLevelDriveOperation; use dpp::balances::credits::TokenAmount; +use dpp::block::block_info::BlockInfo; +use dpp::fee::fee_result::FeeResult; use dpp::version::PlatformVersion; use grovedb::batch::KeyInfoPath; use grovedb::{EstimatedLayerInformation, TransactionArg}; use std::collections::HashMap; -use dpp::block::block_info::BlockInfo; -use dpp::fee::fee_result::FeeResult; impl Drive { /// Fetches token's total supply @@ -50,7 +50,12 @@ impl Drive { .fetch .token_total_supply { - 0 => self.fetch_token_total_supply_with_cost_v0(token_id, block_info, transaction, platform_version), + 0 => self.fetch_token_total_supply_with_cost_v0( + token_id, + block_info, + transaction, + platform_version, + ), version => Err(Error::Drive(DriveError::UnknownVersionMismatch { method: "fetch_token_total_supply".to_string(), known_versions: vec![0], diff --git a/packages/rs-drive/src/drive/tokens/system/fetch_token_total_supply/v0/mod.rs b/packages/rs-drive/src/drive/tokens/system/fetch_token_total_supply/v0/mod.rs index f2543062b20..abd233e22b8 100644 --- a/packages/rs-drive/src/drive/tokens/system/fetch_token_total_supply/v0/mod.rs +++ b/packages/rs-drive/src/drive/tokens/system/fetch_token_total_supply/v0/mod.rs @@ -5,12 +5,12 @@ use crate::fees::op::LowLevelDriveOperation; use crate::util::grove_operations::DirectQueryType; use crate::util::grove_operations::QueryTarget::QueryTargetValue; use dpp::balances::credits::TokenAmount; +use dpp::block::block_info::BlockInfo; +use dpp::fee::fee_result::FeeResult; use dpp::version::PlatformVersion; use grovedb::batch::KeyInfoPath; use grovedb::{EstimatedLayerInformation, TransactionArg, TreeType}; use std::collections::HashMap; -use dpp::block::block_info::BlockInfo; -use dpp::fee::fee_result::FeeResult; impl Drive { pub(super) fn fetch_token_total_supply_v0( diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_config_update_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_config_update_transition.rs index d1e20c0b7f9..ff0060b0f1f 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_config_update_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/token/token_config_update_transition.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; use dpp::block::epoch::Epoch; +use dpp::data_contract::accessors::v0::DataContractV0Setters; use dpp::data_contract::accessors::v1::DataContractV1Setters; use dpp::data_contract::associated_token::token_configuration::accessors::v0::TokenConfigurationV0Getters; use dpp::group::action_event::GroupActionEvent; @@ -78,6 +79,7 @@ impl DriveHighLevelBatchOperationConverter for TokenConfigUpdateTransitionAction if self.base().perform_action() { let mut contract = self.data_contract_fetch_info().contract.clone(); + contract.increment_version(); let mut token_configuration = self.base().token_configuration()?.clone(); token_configuration.apply_token_configuration_item( self.update_token_configuration_item().clone(), From b9a0a26806d1c908eb8674d8a071d01aadd7490c Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Thu, 23 Jan 2025 08:54:02 +0700 Subject: [PATCH 4/6] more tests --- .../state_transitions/batch/mod.rs | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/mod.rs index 8856c9e79fa..bd1cf8d0f91 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/mod.rs @@ -15087,6 +15087,101 @@ mod tests { .unwrap() .expect("expected to commit transaction"); } + + #[test] + fn test_token_config_update_by_owner_change_admin_to_main_group_not_set_error() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_genesis_state(); + + let mut rng = StdRng::seed_from_u64(49853); + + let platform_state = platform.state.load(); + + let (identity, signer, key) = + setup_identity(&mut platform, rng.gen(), dash_to_credits!(0.5)); + + let identity_2_id = Identifier::random_with_rng(&mut rng); + + let (contract, token_id) = create_token_contract_with_owner_identity( + &mut platform, + identity.id(), + Some(|token_configuration: &mut TokenConfiguration| { + token_configuration.set_max_supply_change_rules( + ChangeControlRules::V0(ChangeControlRulesV0 { + authorized_to_make_change: + AuthorizedActionTakers::ContractOwner, + admin_action_takers: AuthorizedActionTakers::ContractOwner, + changing_authorized_action_takers_to_no_one_allowed: false, + changing_admin_action_takers_to_no_one_allowed: false, + self_changing_admin_action_takers_allowed: false, + }), + ); + }), + None, + platform_version, + ); + + let config_update_transition = + BatchTransition::new_token_config_update_transition( + token_id, + identity.id(), + contract.id(), + 0, + TokenConfigurationChangeItem::MaxSupplyControlGroup( + AuthorizedActionTakers::MainGroup, + ), + None, + None, + &key, + 2, + 0, + &signer, + platform_version, + None, + None, + None, + ) + .expect("expect to create documents batch transition"); + + let config_update_transition_serialized_transition = config_update_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![config_update_transition_serialized_transition.clone()], + &platform_state, + &BlockInfo::default(), + &transaction, + platform_version, + false, + None, + ) + .expect("expected to process state transition"); + + assert_matches!( + processing_result.execution_results().as_slice(), + [StateTransitionExecutionResult::PaidConsensusError( + ConsensusError::StateError( + StateError::NewAuthorizedActionTakerMainGroupNotSetError(_) + ), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + } } mod with_group { From 197de1e61cf4193d2584f260f612e102c4c6f8d3 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Thu, 23 Jan 2025 09:10:01 +0700 Subject: [PATCH 5/6] small fix --- .../consensus/state/token/invalid_group_position_error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rs-dpp/src/errors/consensus/state/token/invalid_group_position_error.rs b/packages/rs-dpp/src/errors/consensus/state/token/invalid_group_position_error.rs index 99958ec80e9..e93459d0365 100644 --- a/packages/rs-dpp/src/errors/consensus/state/token/invalid_group_position_error.rs +++ b/packages/rs-dpp/src/errors/consensus/state/token/invalid_group_position_error.rs @@ -11,7 +11,7 @@ use thiserror::Error; )] #[error( "Invalid group position: {invalid_group_position}. {max_group_message}", - max_group_message = if let Some(max) = .max_group_position { + max_group_message = if let Some(max) = self.max_group_position { format!("The maximum allowed group position is {}", max) } else { "No maximum group position limit is set.".to_string() From b59f1578f90f5c77f0bf112e8e988e4ec06820b6 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Thu, 23 Jan 2025 09:14:57 +0700 Subject: [PATCH 6/6] wasm fixes --- ...d_action_taker_main_group_not_set_error.rs | 2 -- .../src/errors/consensus/consensus_error.rs | 29 +++++++++++++++---- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/packages/rs-dpp/src/errors/consensus/state/token/new_authorized_action_taker_main_group_not_set_error.rs b/packages/rs-dpp/src/errors/consensus/state/token/new_authorized_action_taker_main_group_not_set_error.rs index 8d1790ca430..bc585884c28 100644 --- a/packages/rs-dpp/src/errors/consensus/state/token/new_authorized_action_taker_main_group_not_set_error.rs +++ b/packages/rs-dpp/src/errors/consensus/state/token/new_authorized_action_taker_main_group_not_set_error.rs @@ -1,10 +1,8 @@ use crate::consensus::state::state_error::StateError; use crate::consensus::ConsensusError; -use crate::data_contract::GroupContractPosition; use crate::ProtocolError; use bincode::{Decode, Encode}; use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize}; -use platform_value::Identifier; use thiserror::Error; #[derive( diff --git a/packages/wasm-dpp/src/errors/consensus/consensus_error.rs b/packages/wasm-dpp/src/errors/consensus/consensus_error.rs index 36d42b59360..67cc9a39f27 100644 --- a/packages/wasm-dpp/src/errors/consensus/consensus_error.rs +++ b/packages/wasm-dpp/src/errors/consensus/consensus_error.rs @@ -66,7 +66,7 @@ use dpp::consensus::basic::document::{ContestedDocumentsTemporarilyNotAllowedErr use dpp::consensus::basic::group::GroupActionNotAllowedOnTransitionError; use dpp::consensus::basic::identity::{DataContractBoundsNotPresentError, DisablingKeyIdAlsoBeingAddedInSameTransitionError, InvalidIdentityCreditWithdrawalTransitionAmountError, InvalidIdentityUpdateTransitionDisableKeysError, InvalidIdentityUpdateTransitionEmptyError, TooManyMasterPublicKeyError, WithdrawalOutputScriptNotAllowedWhenSigningWithOwnerKeyError}; use dpp::consensus::basic::overflow_error::OverflowError; -use dpp::consensus::basic::token::{ChoosingTokenMintRecipientNotAllowedError, ContractHasNoTokensError, DestinationIdentityForTokenMintingNotSetError, InvalidActionIdError, InvalidGroupPositionError, InvalidTokenIdError, InvalidTokenPositionError, TokenTransferToOurselfError}; +use dpp::consensus::basic::token::{ChoosingTokenMintRecipientNotAllowedError, ContractHasNoTokensError, DestinationIdentityForTokenMintingNotSetError, InvalidActionIdError, InvalidTokenIdError, InvalidTokenPositionError, TokenTransferToOurselfError}; use dpp::consensus::state::data_contract::data_contract_update_action_not_allowed_error::DataContractUpdateActionNotAllowedError; use dpp::consensus::state::data_contract::document_type_update_error::DocumentTypeUpdateError; use dpp::consensus::state::document::document_contest_currently_locked_error::DocumentContestCurrentlyLockedError; @@ -84,7 +84,7 @@ use dpp::consensus::state::identity::no_transfer_key_for_core_withdrawal_availab use dpp::consensus::state::identity::RecipientIdentityDoesNotExistError; use dpp::consensus::state::prefunded_specialized_balances::prefunded_specialized_balance_insufficient_error::PrefundedSpecializedBalanceInsufficientError; use dpp::consensus::state::prefunded_specialized_balances::prefunded_specialized_balance_not_found_error::PrefundedSpecializedBalanceNotFoundError; -use dpp::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountNotFrozenError, IdentityTokenAccountFrozenError, UnauthorizedTokenActionError}; +use dpp::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountNotFrozenError, IdentityTokenAccountFrozenError, UnauthorizedTokenActionError, TokenSettingMaxSupplyToLessThanCurrentSupplyError, TokenMintPastMaxSupplyError, NewTokensDestinationIdentityDoesNotExistError, NewAuthorizedActionTakerIdentityDoesNotExistError, NewAuthorizedActionTakerGroupDoesNotExistError, NewAuthorizedActionTakerMainGroupNotSetError, InvalidGroupPositionError}; use dpp::consensus::state::voting::masternode_incorrect_voter_identity_id_error::MasternodeIncorrectVoterIdentityIdError; use dpp::consensus::state::voting::masternode_incorrect_voting_address_error::MasternodeIncorrectVotingAddressError; use dpp::consensus::state::voting::masternode_not_found_error::MasternodeNotFoundError; @@ -344,6 +344,27 @@ pub fn from_state_error(state_error: &StateError) -> JsValue { StateError::DataContractUpdateActionNotAllowedError(e) => { generic_consensus_error!(DataContractUpdateActionNotAllowedError, e).into() } + StateError::TokenSettingMaxSupplyToLessThanCurrentSupplyError(e) => { + generic_consensus_error!(TokenSettingMaxSupplyToLessThanCurrentSupplyError, e).into() + } + StateError::TokenMintPastMaxSupplyError(e) => { + generic_consensus_error!(TokenMintPastMaxSupplyError, e).into() + } + StateError::NewTokensDestinationIdentityDoesNotExistError(e) => { + generic_consensus_error!(NewTokensDestinationIdentityDoesNotExistError, e).into() + } + StateError::NewAuthorizedActionTakerIdentityDoesNotExistError(e) => { + generic_consensus_error!(NewAuthorizedActionTakerIdentityDoesNotExistError, e).into() + } + StateError::NewAuthorizedActionTakerGroupDoesNotExistError(e) => { + generic_consensus_error!(NewAuthorizedActionTakerGroupDoesNotExistError, e).into() + } + StateError::NewAuthorizedActionTakerMainGroupNotSetError(e) => { + generic_consensus_error!(NewAuthorizedActionTakerMainGroupNotSetError, e).into() + } + StateError::InvalidGroupPositionError(e) => { + generic_consensus_error!(InvalidGroupPositionError, e).into() + } } } @@ -622,10 +643,6 @@ fn from_basic_error(basic_error: &BasicError) -> JsValue { generic_consensus_error!(ContractHasNoTokensError, e).into() } - BasicError::InvalidGroupPositionError(e) => { - generic_consensus_error!(InvalidGroupPositionError, e).into() - } - BasicError::InvalidActionIdError(e) => { generic_consensus_error!(InvalidActionIdError, e).into() }