diff --git a/packages/rs-dpp/src/errors/consensus/codes.rs b/packages/rs-dpp/src/errors/consensus/codes.rs index 9f1d7b7c626..fa1583ea088 100644 --- a/packages/rs-dpp/src/errors/consensus/codes.rs +++ b/packages/rs-dpp/src/errors/consensus/codes.rs @@ -254,6 +254,7 @@ impl ErrorWithCode for StateError { Self::NewAuthorizedActionTakerGroupDoesNotExistError(_) => 40158, Self::NewAuthorizedActionTakerMainGroupNotSetError(_) => 40159, Self::InvalidGroupPositionError(_) => 40160, + Self::TokenIsPausedError(_) => 40161, // 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 e76d6b2c5b3..d373725981b 100644 --- a/packages/rs-dpp/src/errors/consensus/state/state_error.rs +++ b/packages/rs-dpp/src/errors/consensus/state/state_error.rs @@ -52,6 +52,7 @@ use crate::consensus::state::voting::vote_poll_not_available_for_voting_error::V use crate::consensus::state::voting::vote_poll_not_found_error::VotePollNotFoundError; use super::document::document_timestamps_are_equal_error::DocumentTimestampsAreEqualError; +use super::token::TokenIsPausedError; #[derive( Error, Debug, PartialEq, Encode, Decode, PlatformSerialize, PlatformDeserialize, Clone, @@ -255,6 +256,9 @@ pub enum StateError { #[error(transparent)] InvalidGroupPositionError(InvalidGroupPositionError), + + #[error(transparent)] + TokenIsPausedError(TokenIsPausedError), } impl From for ConsensusError { 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 f28964addf7..9c2a5401ae8 100644 --- a/packages/rs-dpp/src/errors/consensus/state/token/mod.rs +++ b/packages/rs-dpp/src/errors/consensus/state/token/mod.rs @@ -6,6 +6,7 @@ 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_is_paused_error; mod token_mint_past_max_supply_error; mod token_setting_max_supply_to_less_than_current_supply_error; mod unauthorized_token_action_error; @@ -18,6 +19,7 @@ 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_is_paused_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/token_is_paused_error.rs b/packages/rs-dpp/src/errors/consensus/state/token/token_is_paused_error.rs new file mode 100644 index 00000000000..5681940847f --- /dev/null +++ b/packages/rs-dpp/src/errors/consensus/state/token/token_is_paused_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("Token {} is paused.", token_id)] +#[platform_serialize(unversioned)] +pub struct TokenIsPausedError { + token_id: Identifier, +} + +impl TokenIsPausedError { + pub fn new(token_id: Identifier) -> Self { + Self { token_id } + } + + pub fn token_id(&self) -> &Identifier { + &self.token_id + } +} + +impl From for ConsensusError { + fn from(err: TokenIsPausedError) -> Self { + Self::StateError(StateError::TokenIsPausedError(err)) + } +} diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_transfer_transition_action/state_v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_transfer_transition_action/state_v0/mod.rs index a40b0f30401..43d272522f1 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_transfer_transition_action/state_v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_transfer_transition_action/state_v0/mod.rs @@ -1,11 +1,12 @@ use dpp::block::block_info::BlockInfo; use dpp::consensus::ConsensusError; use dpp::consensus::state::state_error::StateError; -use dpp::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountFrozenError}; +use dpp::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountFrozenError, TokenIsPausedError}; use dpp::prelude::Identifier; use dpp::tokens::info::v0::IdentityTokenInfoV0Accessors; +use dpp::tokens::status::v0::TokenStatusV0Accessors; use dpp::validation::SimpleConsensusValidationResult; -use drive::state_transition_action::batch::batched_transition::token_transition::token_transfer_transition_action::{TokenTransferTransitionAction}; +use drive::state_transition_action::batch::batched_transition::token_transition::token_transfer_transition_action::TokenTransferTransitionAction; use dpp::version::PlatformVersion; use drive::query::TransactionArg; use drive::state_transition_action::batch::batched_transition::token_transition::token_transfer_transition_action::v0::TokenTransferTransitionActionAccessorsV0; @@ -60,7 +61,6 @@ impl TokenTransferTransitionActionStateValidationV0 for TokenTransferTransitionA .unwrap_or_default(); execution_context.add_operation(ValidationOperation::RetrieveIdentityTokenBalance); if balance < self.amount() { - // The identity does not exist return Ok(SimpleConsensusValidationResult::new_with_error( ConsensusError::StateError(StateError::IdentityDoesNotHaveEnoughTokenBalanceError( IdentityDoesNotHaveEnoughTokenBalanceError::new( @@ -75,16 +75,16 @@ impl TokenTransferTransitionActionStateValidationV0 for TokenTransferTransitionA } // We need to verify that our token account is not frozen - - // We need to verify that we have enough of the token - let info = platform.drive.fetch_identity_token_info( + let (info, fee_result) = platform.drive.fetch_identity_token_info_with_costs( self.token_id().to_buffer(), owner_id.to_buffer(), + block_info, + true, transaction, platform_version, )?; + execution_context.add_operation(ValidationOperation::PrecalculatedOperation(fee_result)); if let Some(info) = info { - // We have an info, we need to check that we are not frozen if info.frozen() == true { return Ok(SimpleConsensusValidationResult::new_with_error( ConsensusError::StateError(StateError::IdentityTokenAccountFrozenError( @@ -98,6 +98,25 @@ impl TokenTransferTransitionActionStateValidationV0 for TokenTransferTransitionA } }; + // We need to verify that the token is not paused + let (token_status, fee_result) = platform.drive.fetch_token_status_with_costs( + self.token_id().to_buffer(), + block_info, + true, + transaction, + platform_version, + )?; + execution_context.add_operation(ValidationOperation::PrecalculatedOperation(fee_result)); + if let Some(status) = token_status { + if status.paused() { + return Ok(SimpleConsensusValidationResult::new_with_error( + ConsensusError::StateError(StateError::TokenIsPausedError( + TokenIsPausedError::new(self.token_id()), + )), + )); + } + } + Ok(SimpleConsensusValidationResult::new()) } } diff --git a/packages/wasm-dpp/src/errors/consensus/consensus_error.rs b/packages/wasm-dpp/src/errors/consensus/consensus_error.rs index 67cc9a39f27..936eb1c4e73 100644 --- a/packages/wasm-dpp/src/errors/consensus/consensus_error.rs +++ b/packages/wasm-dpp/src/errors/consensus/consensus_error.rs @@ -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, TokenSettingMaxSupplyToLessThanCurrentSupplyError, TokenMintPastMaxSupplyError, NewTokensDestinationIdentityDoesNotExistError, NewAuthorizedActionTakerIdentityDoesNotExistError, NewAuthorizedActionTakerGroupDoesNotExistError, NewAuthorizedActionTakerMainGroupNotSetError, InvalidGroupPositionError}; +use dpp::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountNotFrozenError, IdentityTokenAccountFrozenError, TokenIsPausedError, 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; @@ -326,6 +326,7 @@ pub fn from_state_error(state_error: &StateError) -> JsValue { StateError::IdentityTokenAccountFrozenError(e) => { generic_consensus_error!(IdentityTokenAccountFrozenError, e).into() } + StateError::TokenIsPausedError(e) => generic_consensus_error!(TokenIsPausedError, e).into(), StateError::IdentityNotMemberOfGroupError(e) => { generic_consensus_error!(IdentityNotMemberOfGroupError, e).into() }