Skip to content

Commit 03cdc36

Browse files
authored
fix: token transfer to non-existing identity (#2505)
1 parent 3316b47 commit 03cdc36

File tree

6 files changed

+64
-3
lines changed

6 files changed

+64
-3
lines changed

packages/rs-dpp/src/errors/consensus/codes.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ impl ErrorWithCode for StateError {
304304
Self::InvalidTokenClaimPropertyMismatch(_) => 40715,
305305
Self::InvalidTokenClaimNoCurrentRewards(_) => 40716,
306306
Self::InvalidTokenClaimWrongClaimant(_) => 40717,
307+
Self::TokenTransferRecipientIdentityNotExistError(_) => 40718,
307308

308309
// Group errors: 40800-40899
309310
Self::IdentityNotMemberOfGroupError(_) => 40800,

packages/rs-dpp/src/errors/consensus/state/state_error.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use crate::consensus::state::identity::missing_transfer_key_error::MissingTransf
4242
use crate::consensus::state::identity::no_transfer_key_for_core_withdrawal_available_error::NoTransferKeyForCoreWithdrawalAvailableError;
4343
use crate::consensus::state::prefunded_specialized_balances::prefunded_specialized_balance_insufficient_error::PrefundedSpecializedBalanceInsufficientError;
4444
use crate::consensus::state::prefunded_specialized_balances::prefunded_specialized_balance_not_found_error::PrefundedSpecializedBalanceNotFoundError;
45-
use crate::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountFrozenError, IdentityTokenAccountNotFrozenError, InvalidGroupPositionError, NewAuthorizedActionTakerGroupDoesNotExistError, NewAuthorizedActionTakerIdentityDoesNotExistError, NewAuthorizedActionTakerMainGroupNotSetError, NewTokensDestinationIdentityDoesNotExistError, TokenMintPastMaxSupplyError, TokenSettingMaxSupplyToLessThanCurrentSupplyError, UnauthorizedTokenActionError, IdentityTokenAccountAlreadyFrozenError, TokenAlreadyPausedError, TokenIsPausedError, TokenNotPausedError, InvalidTokenClaimPropertyMismatch, InvalidTokenClaimNoCurrentRewards, InvalidTokenClaimWrongClaimant};
45+
use crate::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountFrozenError, IdentityTokenAccountNotFrozenError, InvalidGroupPositionError, NewAuthorizedActionTakerGroupDoesNotExistError, NewAuthorizedActionTakerIdentityDoesNotExistError, NewAuthorizedActionTakerMainGroupNotSetError, NewTokensDestinationIdentityDoesNotExistError, TokenMintPastMaxSupplyError, TokenSettingMaxSupplyToLessThanCurrentSupplyError, UnauthorizedTokenActionError, IdentityTokenAccountAlreadyFrozenError, TokenAlreadyPausedError, TokenIsPausedError, TokenNotPausedError, InvalidTokenClaimPropertyMismatch, InvalidTokenClaimNoCurrentRewards, InvalidTokenClaimWrongClaimant, TokenTransferRecipientIdentityNotExistError};
4646
use crate::consensus::state::voting::masternode_incorrect_voter_identity_id_error::MasternodeIncorrectVoterIdentityIdError;
4747
use crate::consensus::state::voting::masternode_incorrect_voting_address_error::MasternodeIncorrectVotingAddressError;
4848
use crate::consensus::state::voting::masternode_not_found_error::MasternodeNotFoundError;
@@ -276,6 +276,9 @@ pub enum StateError {
276276

277277
#[error(transparent)]
278278
TokenNotPausedError(TokenNotPausedError),
279+
280+
#[error(transparent)]
281+
TokenTransferRecipientIdentityNotExistError(TokenTransferRecipientIdentityNotExistError),
279282
}
280283

281284
impl From<StateError> for ConsensusError {

packages/rs-dpp/src/errors/consensus/state/token/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ mod token_is_paused_error;
1515
mod token_mint_past_max_supply_error;
1616
mod token_not_paused_error;
1717
mod token_setting_max_supply_to_less_than_current_supply_error;
18+
mod token_transfer_recipient_identity_not_exist_error;
1819
mod unauthorized_token_action_error;
1920

2021
pub use identity_does_not_have_enough_token_balance_error::*;
@@ -34,4 +35,5 @@ pub use token_is_paused_error::*;
3435
pub use token_mint_past_max_supply_error::*;
3536
pub use token_not_paused_error::*;
3637
pub use token_setting_max_supply_to_less_than_current_supply_error::*;
38+
pub use token_transfer_recipient_identity_not_exist_error::*;
3739
pub use unauthorized_token_action_error::*;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use crate::consensus::state::state_error::StateError;
2+
use crate::consensus::ConsensusError;
3+
use crate::ProtocolError;
4+
use bincode::{Decode, Encode};
5+
use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize};
6+
use platform_value::Identifier;
7+
use thiserror::Error;
8+
9+
#[derive(
10+
Error, Debug, Clone, PartialEq, Eq, Encode, Decode, PlatformSerialize, PlatformDeserialize,
11+
)]
12+
#[error("Token transfer recipient identity {recipient_id} doesn't exist")]
13+
#[platform_serialize(unversioned)]
14+
pub struct TokenTransferRecipientIdentityNotExistError {
15+
recipient_id: Identifier,
16+
}
17+
18+
impl TokenTransferRecipientIdentityNotExistError {
19+
pub fn new(recipient_id: Identifier) -> Self {
20+
Self { recipient_id }
21+
}
22+
23+
pub fn recipient_id(&self) -> Identifier {
24+
self.recipient_id
25+
}
26+
}
27+
28+
impl From<TokenTransferRecipientIdentityNotExistError> for ConsensusError {
29+
fn from(err: TokenTransferRecipientIdentityNotExistError) -> Self {
30+
Self::StateError(StateError::TokenTransferRecipientIdentityNotExistError(err))
31+
}
32+
}

packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/token/token_transfer_transition_action/state_v0/mod.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use dpp::block::block_info::BlockInfo;
22
use dpp::consensus::ConsensusError;
33
use dpp::consensus::state::state_error::StateError;
4-
use dpp::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountFrozenError, TokenIsPausedError};
4+
use dpp::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountFrozenError, TokenIsPausedError, TokenTransferRecipientIdentityNotExistError};
55
use dpp::prelude::Identifier;
66
use dpp::tokens::info::v0::IdentityTokenInfoV0Accessors;
77
use dpp::tokens::status::v0::TokenStatusV0Accessors;
@@ -45,6 +45,7 @@ impl TokenTransferTransitionActionStateValidationV0 for TokenTransferTransitionA
4545
transaction,
4646
platform_version,
4747
)?;
48+
4849
if !validation_result.is_valid() {
4950
return Ok(validation_result);
5051
}
@@ -59,7 +60,9 @@ impl TokenTransferTransitionActionStateValidationV0 for TokenTransferTransitionA
5960
platform_version,
6061
)?
6162
.unwrap_or_default();
63+
6264
execution_context.add_operation(ValidationOperation::RetrieveIdentityTokenBalance);
65+
6366
if balance < self.amount() {
6467
return Ok(SimpleConsensusValidationResult::new_with_error(
6568
ConsensusError::StateError(StateError::IdentityDoesNotHaveEnoughTokenBalanceError(
@@ -83,7 +86,9 @@ impl TokenTransferTransitionActionStateValidationV0 for TokenTransferTransitionA
8386
transaction,
8487
platform_version,
8588
)?;
89+
8690
execution_context.add_operation(ValidationOperation::PrecalculatedOperation(fee_result));
91+
8792
if let Some(info) = info {
8893
if info.frozen() {
8994
return Ok(SimpleConsensusValidationResult::new_with_error(
@@ -106,7 +111,9 @@ impl TokenTransferTransitionActionStateValidationV0 for TokenTransferTransitionA
106111
transaction,
107112
platform_version,
108113
)?;
114+
109115
execution_context.add_operation(ValidationOperation::PrecalculatedOperation(fee_result));
116+
110117
if let Some(status) = token_status {
111118
if status.paused() {
112119
return Ok(SimpleConsensusValidationResult::new_with_error(
@@ -117,6 +124,19 @@ impl TokenTransferTransitionActionStateValidationV0 for TokenTransferTransitionA
117124
}
118125
}
119126

127+
// Make sure recipient exists
128+
let recipient_balance = platform.drive.fetch_identity_balance(
129+
self.recipient_id().to_buffer(),
130+
transaction,
131+
platform_version,
132+
)?;
133+
134+
if recipient_balance.is_none() {
135+
return Ok(SimpleConsensusValidationResult::new_with_error(
136+
TokenTransferRecipientIdentityNotExistError::new(self.recipient_id()).into(),
137+
));
138+
}
139+
120140
Ok(SimpleConsensusValidationResult::new())
121141
}
122142
}

packages/wasm-dpp/src/errors/consensus/consensus_error.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ use dpp::consensus::state::identity::no_transfer_key_for_core_withdrawal_availab
8484
use dpp::consensus::state::identity::RecipientIdentityDoesNotExistError;
8585
use dpp::consensus::state::prefunded_specialized_balances::prefunded_specialized_balance_insufficient_error::PrefundedSpecializedBalanceInsufficientError;
8686
use dpp::consensus::state::prefunded_specialized_balances::prefunded_specialized_balance_not_found_error::PrefundedSpecializedBalanceNotFoundError;
87-
use dpp::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountNotFrozenError, IdentityTokenAccountFrozenError, TokenIsPausedError, IdentityTokenAccountAlreadyFrozenError, UnauthorizedTokenActionError, TokenSettingMaxSupplyToLessThanCurrentSupplyError, TokenMintPastMaxSupplyError, NewTokensDestinationIdentityDoesNotExistError, NewAuthorizedActionTakerIdentityDoesNotExistError, NewAuthorizedActionTakerGroupDoesNotExistError, NewAuthorizedActionTakerMainGroupNotSetError, InvalidGroupPositionError, TokenAlreadyPausedError, TokenNotPausedError, InvalidTokenClaimPropertyMismatch, InvalidTokenClaimNoCurrentRewards, InvalidTokenClaimWrongClaimant};
87+
use dpp::consensus::state::token::{IdentityDoesNotHaveEnoughTokenBalanceError, IdentityTokenAccountNotFrozenError, IdentityTokenAccountFrozenError, TokenIsPausedError, IdentityTokenAccountAlreadyFrozenError, UnauthorizedTokenActionError, TokenSettingMaxSupplyToLessThanCurrentSupplyError, TokenMintPastMaxSupplyError, NewTokensDestinationIdentityDoesNotExistError, NewAuthorizedActionTakerIdentityDoesNotExistError, NewAuthorizedActionTakerGroupDoesNotExistError, NewAuthorizedActionTakerMainGroupNotSetError, InvalidGroupPositionError, TokenAlreadyPausedError, TokenNotPausedError, InvalidTokenClaimPropertyMismatch, InvalidTokenClaimNoCurrentRewards, InvalidTokenClaimWrongClaimant, TokenTransferRecipientIdentityNotExistError};
8888
use dpp::consensus::state::voting::masternode_incorrect_voter_identity_id_error::MasternodeIncorrectVoterIdentityIdError;
8989
use dpp::consensus::state::voting::masternode_incorrect_voting_address_error::MasternodeIncorrectVotingAddressError;
9090
use dpp::consensus::state::voting::masternode_not_found_error::MasternodeNotFoundError;
@@ -384,6 +384,9 @@ pub fn from_state_error(state_error: &StateError) -> JsValue {
384384
StateError::InvalidTokenClaimWrongClaimant(e) => {
385385
generic_consensus_error!(InvalidTokenClaimWrongClaimant, e).into()
386386
}
387+
StateError::TokenTransferRecipientIdentityNotExistError(e) => {
388+
generic_consensus_error!(TokenTransferRecipientIdentityNotExistError, e).into()
389+
}
387390
}
388391
}
389392

0 commit comments

Comments
 (0)