diff --git a/packages/rs-dpp/src/errors/consensus/codes.rs b/packages/rs-dpp/src/errors/consensus/codes.rs index e768aee5803..177cfe8fd43 100644 --- a/packages/rs-dpp/src/errors/consensus/codes.rs +++ b/packages/rs-dpp/src/errors/consensus/codes.rs @@ -304,6 +304,7 @@ impl ErrorWithCode for StateError { Self::InvalidTokenClaimPropertyMismatch(_) => 40715, Self::InvalidTokenClaimNoCurrentRewards(_) => 40716, Self::InvalidTokenClaimWrongClaimant(_) => 40717, + Self::TokenTransferRecipientIdentityNotExistError(_) => 40718, // Group errors: 40800-40899 Self::IdentityNotMemberOfGroupError(_) => 40800, 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 39d696bd6b7..09306815745 100644 --- a/packages/rs-dpp/src/errors/consensus/state/state_error.rs +++ b/packages/rs-dpp/src/errors/consensus/state/state_error.rs @@ -42,7 +42,7 @@ use crate::consensus::state::identity::missing_transfer_key_error::MissingTransf use crate::consensus::state::identity::no_transfer_key_for_core_withdrawal_available_error::NoTransferKeyForCoreWithdrawalAvailableError; use crate::consensus::state::prefunded_specialized_balances::prefunded_specialized_balance_insufficient_error::PrefundedSpecializedBalanceInsufficientError; use crate::consensus::state::prefunded_specialized_balances::prefunded_specialized_balance_not_found_error::PrefundedSpecializedBalanceNotFoundError; -use crate::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountFrozenError, IdentityTokenAccountNotFrozenError, InvalidGroupPositionError, NewAuthorizedActionTakerGroupDoesNotExistError, NewAuthorizedActionTakerIdentityDoesNotExistError, NewAuthorizedActionTakerMainGroupNotSetError, NewTokensDestinationIdentityDoesNotExistError, TokenMintPastMaxSupplyError, TokenSettingMaxSupplyToLessThanCurrentSupplyError, UnauthorizedTokenActionError, IdentityTokenAccountAlreadyFrozenError, TokenAlreadyPausedError, TokenIsPausedError, TokenNotPausedError, InvalidTokenClaimPropertyMismatch, InvalidTokenClaimNoCurrentRewards, InvalidTokenClaimWrongClaimant}; +use crate::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountFrozenError, IdentityTokenAccountNotFrozenError, InvalidGroupPositionError, NewAuthorizedActionTakerGroupDoesNotExistError, NewAuthorizedActionTakerIdentityDoesNotExistError, NewAuthorizedActionTakerMainGroupNotSetError, NewTokensDestinationIdentityDoesNotExistError, TokenMintPastMaxSupplyError, TokenSettingMaxSupplyToLessThanCurrentSupplyError, UnauthorizedTokenActionError, IdentityTokenAccountAlreadyFrozenError, TokenAlreadyPausedError, TokenIsPausedError, TokenNotPausedError, InvalidTokenClaimPropertyMismatch, InvalidTokenClaimNoCurrentRewards, InvalidTokenClaimWrongClaimant, TokenTransferRecipientIdentityNotExistError}; 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; @@ -276,6 +276,9 @@ pub enum StateError { #[error(transparent)] TokenNotPausedError(TokenNotPausedError), + + #[error(transparent)] + TokenTransferRecipientIdentityNotExistError(TokenTransferRecipientIdentityNotExistError), } 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 a7bd5f9dcdd..d68214c41a1 100644 --- a/packages/rs-dpp/src/errors/consensus/state/token/mod.rs +++ b/packages/rs-dpp/src/errors/consensus/state/token/mod.rs @@ -15,6 +15,7 @@ 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 token_transfer_recipient_identity_not_exist_error; mod unauthorized_token_action_error; pub use identity_does_not_have_enough_token_balance_error::*; @@ -34,4 +35,5 @@ 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 token_transfer_recipient_identity_not_exist_error::*; pub use unauthorized_token_action_error::*; diff --git a/packages/rs-dpp/src/errors/consensus/state/token/token_transfer_recipient_identity_not_exist_error.rs b/packages/rs-dpp/src/errors/consensus/state/token/token_transfer_recipient_identity_not_exist_error.rs new file mode 100644 index 00000000000..d88d5695b6f --- /dev/null +++ b/packages/rs-dpp/src/errors/consensus/state/token/token_transfer_recipient_identity_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("Token transfer recipient identity {recipient_id} doesn't exist")] +#[platform_serialize(unversioned)] +pub struct TokenTransferRecipientIdentityNotExistError { + recipient_id: Identifier, +} + +impl TokenTransferRecipientIdentityNotExistError { + pub fn new(recipient_id: Identifier) -> Self { + Self { recipient_id } + } + + pub fn recipient_id(&self) -> Identifier { + self.recipient_id + } +} + +impl From for ConsensusError { + fn from(err: TokenTransferRecipientIdentityNotExistError) -> Self { + Self::StateError(StateError::TokenTransferRecipientIdentityNotExistError(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 4541089195b..abca52e3b12 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,7 +1,7 @@ use dpp::block::block_info::BlockInfo; use dpp::consensus::ConsensusError; use dpp::consensus::state::state_error::StateError; -use dpp::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountFrozenError, TokenIsPausedError}; +use dpp::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountFrozenError, TokenIsPausedError, TokenTransferRecipientIdentityNotExistError}; use dpp::prelude::Identifier; use dpp::tokens::info::v0::IdentityTokenInfoV0Accessors; use dpp::tokens::status::v0::TokenStatusV0Accessors; @@ -45,6 +45,7 @@ impl TokenTransferTransitionActionStateValidationV0 for TokenTransferTransitionA transaction, platform_version, )?; + if !validation_result.is_valid() { return Ok(validation_result); } @@ -59,7 +60,9 @@ impl TokenTransferTransitionActionStateValidationV0 for TokenTransferTransitionA platform_version, )? .unwrap_or_default(); + execution_context.add_operation(ValidationOperation::RetrieveIdentityTokenBalance); + if balance < self.amount() { return Ok(SimpleConsensusValidationResult::new_with_error( ConsensusError::StateError(StateError::IdentityDoesNotHaveEnoughTokenBalanceError( @@ -83,7 +86,9 @@ impl TokenTransferTransitionActionStateValidationV0 for TokenTransferTransitionA transaction, platform_version, )?; + execution_context.add_operation(ValidationOperation::PrecalculatedOperation(fee_result)); + if let Some(info) = info { if info.frozen() { return Ok(SimpleConsensusValidationResult::new_with_error( @@ -106,7 +111,9 @@ impl TokenTransferTransitionActionStateValidationV0 for TokenTransferTransitionA 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( @@ -117,6 +124,19 @@ impl TokenTransferTransitionActionStateValidationV0 for TokenTransferTransitionA } } + // Make sure recipient exists + let recipient_balance = platform.drive.fetch_identity_balance( + self.recipient_id().to_buffer(), + transaction, + platform_version, + )?; + + if recipient_balance.is_none() { + return Ok(SimpleConsensusValidationResult::new_with_error( + TokenTransferRecipientIdentityNotExistError::new(self.recipient_id()).into(), + )); + } + 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 1a77988f810..225082d7367 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, TokenIsPausedError, IdentityTokenAccountAlreadyFrozenError, UnauthorizedTokenActionError, TokenSettingMaxSupplyToLessThanCurrentSupplyError, TokenMintPastMaxSupplyError, NewTokensDestinationIdentityDoesNotExistError, NewAuthorizedActionTakerIdentityDoesNotExistError, NewAuthorizedActionTakerGroupDoesNotExistError, NewAuthorizedActionTakerMainGroupNotSetError, InvalidGroupPositionError, TokenAlreadyPausedError, TokenNotPausedError, InvalidTokenClaimPropertyMismatch, InvalidTokenClaimNoCurrentRewards, InvalidTokenClaimWrongClaimant}; +use dpp::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountNotFrozenError, IdentityTokenAccountFrozenError, TokenIsPausedError, IdentityTokenAccountAlreadyFrozenError, UnauthorizedTokenActionError, TokenSettingMaxSupplyToLessThanCurrentSupplyError, TokenMintPastMaxSupplyError, NewTokensDestinationIdentityDoesNotExistError, NewAuthorizedActionTakerIdentityDoesNotExistError, NewAuthorizedActionTakerGroupDoesNotExistError, NewAuthorizedActionTakerMainGroupNotSetError, InvalidGroupPositionError, TokenAlreadyPausedError, TokenNotPausedError, InvalidTokenClaimPropertyMismatch, InvalidTokenClaimNoCurrentRewards, InvalidTokenClaimWrongClaimant, TokenTransferRecipientIdentityNotExistError}; 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; @@ -384,6 +384,9 @@ pub fn from_state_error(state_error: &StateError) -> JsValue { StateError::InvalidTokenClaimWrongClaimant(e) => { generic_consensus_error!(InvalidTokenClaimWrongClaimant, e).into() } + StateError::TokenTransferRecipientIdentityNotExistError(e) => { + generic_consensus_error!(TokenTransferRecipientIdentityNotExistError, e).into() + } } }