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 31e816a0b1..0429831203 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/data_contract/change_control_rules/authorized_action_takers.rs b/packages/rs-dpp/src/data_contract/change_control_rules/authorized_action_takers.rs index 97e83ac6eb..fc5c531bec 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 e89758dde7..552a96a949 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, InvalidGroupPositionError, - InvalidTokenIdError, InvalidTokenPositionError, TokenTransferToOurselfError, + DestinationIdentityForTokenMintingNotSetError, InvalidActionIdError, InvalidTokenIdError, + InvalidTokenPositionError, TokenTransferToOurselfError, }; use crate::consensus::basic::unsupported_version_error::UnsupportedVersionError; use crate::consensus::basic::value_error::ValueError; @@ -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 efdcadc77a..d77dfff6fa 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 00ee6d4da5..9f1d7b7c62 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,13 @@ impl ErrorWithCode for StateError { Self::UnauthorizedTokenActionError(_) => 40151, Self::IdentityTokenAccountFrozenError(_) => 40152, Self::IdentityTokenAccountNotFrozenError(_) => 40153, + Self::TokenSettingMaxSupplyToLessThanCurrentSupplyError(_) => 40154, + Self::TokenMintPastMaxSupplyError(_) => 40155, + Self::NewTokensDestinationIdentityDoesNotExistError(_) => 40156, + 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 91eaf396cc..e76d6b2c5b 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, 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; @@ -230,6 +230,31 @@ pub enum StateError { #[error(transparent)] DataContractUpdateActionNotAllowedError(DataContractUpdateActionNotAllowedError), + + #[error(transparent)] + TokenSettingMaxSupplyToLessThanCurrentSupplyError( + TokenSettingMaxSupplyToLessThanCurrentSupplyError, + ), + + #[error(transparent)] + TokenMintPastMaxSupplyError(TokenMintPastMaxSupplyError), + + #[error(transparent)] + NewTokensDestinationIdentityDoesNotExistError(NewTokensDestinationIdentityDoesNotExistError), + + #[error(transparent)] + NewAuthorizedActionTakerIdentityDoesNotExistError( + NewAuthorizedActionTakerIdentityDoesNotExistError, + ), + + #[error(transparent)] + NewAuthorizedActionTakerGroupDoesNotExistError(NewAuthorizedActionTakerGroupDoesNotExistError), + + #[error(transparent)] + NewAuthorizedActionTakerMainGroupNotSetError(NewAuthorizedActionTakerMainGroupNotSetError), + + #[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 58% 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 8464f1064a..e93459d036 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,4 +1,4 @@ -use crate::consensus::basic::BasicError; +use crate::consensus::state::state_error::StateError; use crate::consensus::ConsensusError; use crate::data_contract::GroupContractPosition; use crate::ProtocolError; @@ -10,29 +10,32 @@ use thiserror::Error; Error, Debug, Clone, PartialEq, Eq, Encode, Decode, PlatformSerialize, PlatformDeserialize, )] #[error( - "Invalid group position {}, expected {}", - invalid_group_position, - expected_group_position + "Invalid group position: {invalid_group_position}. {max_group_message}", + 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() + } )] #[platform_serialize(unversioned)] pub struct InvalidGroupPositionError { - expected_group_position: GroupContractPosition, + max_group_position: Option, invalid_group_position: GroupContractPosition, } impl InvalidGroupPositionError { pub fn new( - expected_group_position: GroupContractPosition, + max_group_position: Option, 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) -> Option { + self.max_group_position } pub fn invalid_group_position(&self) -> GroupContractPosition { @@ -42,6 +45,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 6838bc261c..f28964addf 100644 --- a/packages/rs-dpp/src/errors/consensus/state/token/mod.rs +++ b/packages/rs-dpp/src/errors/consensus/state/token/mod.rs @@ -1,9 +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 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 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 0000000000..2ea523ddbb --- /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 0000000000..9f02bc0117 --- /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 0000000000..bc585884c2 --- /dev/null +++ b/packages/rs-dpp/src/errors/consensus/state/token/new_authorized_action_taker_main_group_not_set_error.rs @@ -0,0 +1,29 @@ +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 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 new file mode 100644 index 0000000000..ae18548891 --- /dev/null +++ b/packages/rs-dpp/src/errors/consensus/state/token/new_tokens_destination_identity_does_not_exist_error.rs @@ -0,0 +1,34 @@ +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, + )) + } +} 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 0000000000..c97a2d0830 --- /dev/null +++ b/packages/rs-dpp/src/errors/consensus/state/token/token_mint_past_max_supply_error.rs @@ -0,0 +1,60 @@ +use crate::balances::credits::TokenAmount; +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 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)) + } +} 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 0000000000..511263c695 --- /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)) + } +} 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 8cdcd6e153..ee24438ecf 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 060ac9c70b..d37a08463f 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 49ba01f625..a9e24acc37 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 b996f9a0b5..b16720f136 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 ef6015a341..ccd6c0b24c 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 73582b9c97..e0560b4949 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 4f0e17f2c4..02abb8e415 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 e089d2f8de..c57cf214ae 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 bec3466b7b..20e9db996d 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 13c06dcd45..fc4642abef 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 d386f30bd6..0559542f7a 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 2f660df716..b90cf25acb 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 a92bc453fb..c4ee2c7251 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")] @@ -860,4 +861,64 @@ 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 ea9c43e7d4..83d0be8635 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; @@ -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 0ae1f0abcc..c3dbdfa9c3 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/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 ae145aeefd..14c0b96d6a 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 c4aec5e98f..bc1935f9a3 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,23 @@ 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, 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; 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 +59,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( @@ -76,6 +91,295 @@ 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(), + *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(), + ), + ), + )); + } + } + _ => {} + } + 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 eba4356e11..1edce2ea32 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; @@ -72,7 +73,54 @@ impl TokenMintTransitionActionStateValidationV0 for TokenMintTransitionAction { 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 f20e61f03b..bd1cf8d0f9 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,12 +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::group::GroupStateTransitionInfoStatus; use dpp::state_transition::batch_transition::methods::v1::DocumentsBatchTransitionMethodsV1; + use dpp::state_transition::batch_transition::TokenConfigUpdateTransition; mod token_mint_tests { use super::*; @@ -10294,6 +10302,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(); @@ -14413,5 +14514,1810 @@ mod tests { assert_eq!(token_balance, Some(expected_amount)); } } + + 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 { + 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)); + } + + #[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); + } + + #[test] + 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() + .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::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::Identity(identity_2.id()), + ), + 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 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 + .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)); + } + + #[test] + 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() + .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::Identity(identity_2_id), + ), + 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::NewAuthorizedActionTakerIdentityDoesNotExistError(_) + ), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + } + + #[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.id(), + contract.id(), + 0, + TokenConfigurationChangeItem::MaxSupplyControlGroup( + AuthorizedActionTakers::Group(0), + ), + 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::NewAuthorizedActionTakerGroupDoesNotExistError(_) + ), + _ + )] + ); + + platform + .drive + .grove + .commit_transaction(transaction) + .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 { + 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 + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + } + } + } } } diff --git a/packages/rs-drive/src/drive/group/fetch/mod.rs b/packages/rs-drive/src/drive/group/fetch/mod.rs index 32186f8a6e..473e9dfa48 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 a5fec1b5de..156fc42f0c 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 c1f8f4fa90..146fd9b29b 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/drive/tokens/system/fetch_token_total_supply/mod.rs b/packages/rs-drive/src/drive/tokens/system/fetch_token_total_supply/mod.rs index fac0478a0e..ca6a6f44ac 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,6 +5,8 @@ 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}; @@ -33,6 +35,34 @@ 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 2d92c49bf3..abd233e22b 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,6 +5,8 @@ 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}; @@ -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], 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 d1e20c0b7f..ff0060b0f1 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(), 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 507ebc81a5..d579d94dc1 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 diff --git a/packages/wasm-dpp/src/errors/consensus/consensus_error.rs b/packages/wasm-dpp/src/errors/consensus/consensus_error.rs index 36d42b5936..67cc9a39f2 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() }