Skip to content

Commit

Permalink
fix: token already paused unpaused and frozen validation
Browse files Browse the repository at this point in the history
  • Loading branch information
pauldelucia committed Feb 18, 2025
1 parent ddf4e67 commit 341afed
Show file tree
Hide file tree
Showing 16 changed files with 275 additions and 48 deletions.
3 changes: 3 additions & 0 deletions packages/rs-dpp/src/errors/consensus/codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@ impl ErrorWithCode for StateError {
Self::NewAuthorizedActionTakerMainGroupNotSetError(_) => 40159,
Self::InvalidGroupPositionError(_) => 40160,
Self::TokenIsPausedError(_) => 40161,
Self::IdentityTokenAccountAlreadyFrozenError(_) => 40162,
Self::TokenAlreadyPausedError(_) => 40163,
Self::TokenNotPausedError(_) => 40164,

// Identity Errors: 40200-40299
Self::IdentityAlreadyExistsError(_) => 40200,
Expand Down
14 changes: 12 additions & 2 deletions packages/rs-dpp/src/errors/consensus/state/state_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ use crate::consensus::state::identity::missing_transfer_key_error::MissingTransf
use crate::consensus::state::identity::no_transfer_key_for_core_withdrawal_available_error::NoTransferKeyForCoreWithdrawalAvailableError;
use crate::consensus::state::prefunded_specialized_balances::prefunded_specialized_balance_insufficient_error::PrefundedSpecializedBalanceInsufficientError;
use crate::consensus::state::prefunded_specialized_balances::prefunded_specialized_balance_not_found_error::PrefundedSpecializedBalanceNotFoundError;
use crate::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountFrozenError, IdentityTokenAccountNotFrozenError, InvalidGroupPositionError, NewAuthorizedActionTakerGroupDoesNotExistError, NewAuthorizedActionTakerIdentityDoesNotExistError, NewAuthorizedActionTakerMainGroupNotSetError, NewTokensDestinationIdentityDoesNotExistError, TokenMintPastMaxSupplyError, TokenSettingMaxSupplyToLessThanCurrentSupplyError, UnauthorizedTokenActionError};
use crate::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountFrozenError, IdentityTokenAccountNotFrozenError, InvalidGroupPositionError, NewAuthorizedActionTakerGroupDoesNotExistError, NewAuthorizedActionTakerIdentityDoesNotExistError, NewAuthorizedActionTakerMainGroupNotSetError, NewTokensDestinationIdentityDoesNotExistError, TokenMintPastMaxSupplyError, TokenSettingMaxSupplyToLessThanCurrentSupplyError, UnauthorizedTokenActionError, IdentityTokenAccountAlreadyFrozenError, TokenAlreadyPausedError, TokenIsPausedError,
TokenNotPausedError,
};
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;
Expand All @@ -52,7 +54,6 @@ 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,
Expand Down Expand Up @@ -259,6 +260,15 @@ pub enum StateError {

#[error(transparent)]
TokenIsPausedError(TokenIsPausedError),

#[error(transparent)]
IdentityTokenAccountAlreadyFrozenError(IdentityTokenAccountAlreadyFrozenError),

#[error(transparent)]
TokenAlreadyPausedError(TokenAlreadyPausedError),

#[error(transparent)]
TokenNotPausedError(TokenNotPausedError),
}

impl From<StateError> for ConsensusError {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
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(
"Identity {} account is already frozen for token {}. Action attempted: {}",
identity_id,
token_id,
action
)]
#[platform_serialize(unversioned)]
pub struct IdentityTokenAccountAlreadyFrozenError {
token_id: Identifier,
identity_id: Identifier,
action: String,
}

impl IdentityTokenAccountAlreadyFrozenError {
pub fn new(token_id: Identifier, identity_id: Identifier, action: String) -> Self {
Self {
token_id,
identity_id,
action,
}
}

pub fn token_id(&self) -> &Identifier {
&self.token_id
}

pub fn identity_id(&self) -> &Identifier {
&self.identity_id
}

pub fn action(&self) -> &str {
&self.action
}
}

impl From<IdentityTokenAccountAlreadyFrozenError> for ConsensusError {
fn from(err: IdentityTokenAccountAlreadyFrozenError) -> Self {
Self::StateError(StateError::IdentityTokenAccountAlreadyFrozenError(err))
}
}
6 changes: 6 additions & 0 deletions packages/rs-dpp/src/errors/consensus/state/token/mod.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
mod identity_does_not_have_enough_token_balance_error;
mod identity_token_account_already_frozen_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_already_paused_error;
mod token_is_paused_error;
mod token_mint_past_max_supply_error;
mod token_not_paused_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_already_frozen_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_already_paused_error::*;
pub use token_is_paused_error::*;
pub use token_mint_past_max_supply_error::*;
pub use token_not_paused_error::*;
pub use token_setting_max_supply_to_less_than_current_supply_error::*;
pub use unauthorized_token_action_error::*;
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
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 already paused. Action attempted: {}", token_id, action)]
#[platform_serialize(unversioned)]
pub struct TokenAlreadyPausedError {
token_id: Identifier,
action: String,
}

impl TokenAlreadyPausedError {
pub fn new(token_id: Identifier, action: String) -> Self {
Self { token_id, action }
}

pub fn token_id(&self) -> &Identifier {
&self.token_id
}

pub fn action(&self) -> &str {
&self.action
}
}

impl From<TokenAlreadyPausedError> for ConsensusError {
fn from(err: TokenAlreadyPausedError) -> Self {
Self::StateError(StateError::TokenAlreadyPausedError(err))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
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 not paused. Action attempted: {}", token_id, action)]
#[platform_serialize(unversioned)]
pub struct TokenNotPausedError {
token_id: Identifier,
action: String,
}

impl TokenNotPausedError {
pub fn new(token_id: Identifier, action: String) -> Self {
Self { token_id, action }
}

pub fn token_id(&self) -> &Identifier {
&self.token_id
}

pub fn action(&self) -> &str {
&self.action
}
}

impl From<TokenNotPausedError> for ConsensusError {
fn from(err: TokenNotPausedError) -> Self {
Self::StateError(StateError::TokenNotPausedError(err))
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
use dpp::block::block_info::BlockInfo;
use dpp::consensus::state::state_error::StateError;
use dpp::consensus::state::token::{TokenAlreadyPausedError, TokenNotPausedError};
use dpp::consensus::ConsensusError;
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::prelude::Identifier;
use dpp::tokens::emergency_action::TokenEmergencyAction;
use dpp::tokens::status::v0::TokenStatusV0Accessors;
use dpp::validation::SimpleConsensusValidationResult;
use drive::state_transition_action::batch::batched_transition::token_transition::token_emergency_action_transition_action::{TokenEmergencyActionTransitionAction, TokenEmergencyActionTransitionActionAccessorsV0};
use dpp::version::PlatformVersion;
use drive::query::TransactionArg;
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;

Expand Down Expand Up @@ -66,6 +72,44 @@ impl TokenEmergencyActionTransitionActionStateValidationV0
return Ok(validation_result);
}

// Check if we are paused
let (maybe_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(token_status) = maybe_token_status {
match self.emergency_action() {
TokenEmergencyAction::Pause => {
if token_status.paused() {
return Ok(SimpleConsensusValidationResult::new_with_error(
ConsensusError::StateError(StateError::TokenAlreadyPausedError(
TokenAlreadyPausedError::new(
self.token_id(),
"Pause Token".to_string(),
),
)),
));
}
}
TokenEmergencyAction::Resume => {
if token_status.paused() {
return Ok(SimpleConsensusValidationResult::new_with_error(
ConsensusError::StateError(StateError::TokenNotPausedError(
TokenNotPausedError::new(
self.token_id(),
"Resume Token".to_string(),
),
)),
));
}
}
}
}

Ok(SimpleConsensusValidationResult::new())
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use dpp::block::block_info::BlockInfo;
use dpp::consensus::state::state_error::StateError;
use dpp::consensus::state::token::IdentityTokenAccountAlreadyFrozenError;
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;
Expand All @@ -12,6 +14,11 @@ use crate::execution::types::state_transition_execution_context::StateTransition
use crate::execution::validation::state_transition::batch::action_validation::token::token_base_transition_action::TokenBaseTransitionActionValidation;
use crate::platform_types::platform::PlatformStateRef;

use crate::execution::types::execution_operation::ValidationOperation;
use crate::execution::types::state_transition_execution_context::StateTransitionExecutionContextMethodsV0;
use dpp::consensus::ConsensusError;
use dpp::tokens::info::v0::IdentityTokenInfoV0Accessors;

pub(in crate::execution::validation::state_transition::state_transitions::batch::action_validation) trait TokenFreezeTransitionActionStateValidationV0 {
fn validate_state_v0(
&self,
Expand All @@ -23,6 +30,7 @@ pub(in crate::execution::validation::state_transition::state_transitions::batch:
platform_version: &PlatformVersion,
) -> Result<SimpleConsensusValidationResult, Error>;
}

impl TokenFreezeTransitionActionStateValidationV0 for TokenFreezeTransitionAction {
fn validate_state_v0(
&self,
Expand Down Expand Up @@ -64,6 +72,30 @@ impl TokenFreezeTransitionActionStateValidationV0 for TokenFreezeTransitionActio
return Ok(validation_result);
}

// Check if the identity is already frozen
let (info, fee_result) = platform.drive.fetch_identity_token_info_with_costs(
self.token_id().to_buffer(),
self.identity_to_freeze_id().to_buffer(),
block_info,
true,
transaction,
platform_version,
)?;
execution_context.add_operation(ValidationOperation::PrecalculatedOperation(fee_result));
if let Some(info) = info {
if info.frozen() == true {
return Ok(SimpleConsensusValidationResult::new_with_error(
ConsensusError::StateError(StateError::IdentityTokenAccountAlreadyFrozenError(
IdentityTokenAccountAlreadyFrozenError::new(
self.token_id(),
owner_id,
"Freeze Identity Token Account".to_string(),
),
)),
));
}
};

Ok(SimpleConsensusValidationResult::new())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,26 @@ impl TokenUnfreezeTransitionActionStateValidationV0 for TokenUnfreezeTransitionA
return Ok(validation_result);
}

// We need to validate that we are frozen
// Let's first check to see if we are authorized to perform this action
let contract = &self.data_contract_fetch_info_ref().contract;
let token_configuration = contract.expected_token_configuration(self.token_position())?;
let rules = token_configuration.unfreeze_rules();
let main_control_group = token_configuration.main_control_group();
let validation_result = self.base().validate_group_action(
rules,
owner_id,
contract.owner_id(),
main_control_group,
contract.groups(),
"unfreeze".to_string(),
token_configuration,
platform_version,
)?;
if !validation_result.is_valid() {
return Ok(validation_result);
}

// Then validate that the identity is frozen
let (info, fee_result) = platform.drive.fetch_identity_token_info_with_costs(
self.token_id().to_buffer(),
self.frozen_identity_id().to_buffer(),
Expand All @@ -61,38 +79,18 @@ impl TokenUnfreezeTransitionActionStateValidationV0 for TokenUnfreezeTransitionA
platform_version,
)?;
execution_context.add_operation(ValidationOperation::PrecalculatedOperation(fee_result));

if info.is_none() || !info.unwrap().frozen() {
return Ok(SimpleConsensusValidationResult::new_with_error(
ConsensusError::StateError(StateError::IdentityTokenAccountNotFrozenError(
IdentityTokenAccountNotFrozenError::new(
self.token_id(),
self.frozen_identity_id(),
"destroy_frozen_funds".to_string(),
"Unfreeze".to_string(),
),
)),
));
}

// Let's first check to see if we are authorized to perform this action
let contract = &self.data_contract_fetch_info_ref().contract;
let token_configuration = contract.expected_token_configuration(self.token_position())?;
let rules = token_configuration.unfreeze_rules();
let main_control_group = token_configuration.main_control_group();
let validation_result = self.base().validate_group_action(
rules,
owner_id,
contract.owner_id(),
main_control_group,
contract.groups(),
"unfreeze".to_string(),
token_configuration,
platform_version,
)?;
if !validation_result.is_valid() {
return Ok(validation_result);
}

Ok(SimpleConsensusValidationResult::new())
}
}
Loading

0 comments on commit 341afed

Please sign in to comment.