From fe71ccaa258ac13f490121ca817e222d63d4cb63 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 30 Oct 2023 11:51:59 +0100 Subject: [PATCH] Add new TransactionFailureReason (#1525) * Add transaction caps related tx failures * Use the errors * Add to Python * Nodejs * camelCase --------- Co-authored-by: /alex/ --- .../models/transaction-failure-reason.ts | 42 +++++++++++ .../python/iota_sdk/types/block/metadata.py | 18 +++++ sdk/src/types/block/output/account.rs | 3 +- sdk/src/types/block/output/anchor.rs | 3 +- sdk/src/types/block/output/foundry.rs | 6 +- sdk/src/types/block/output/nft.rs | 3 +- .../types/block/output/state_transition.rs | 9 ++- sdk/src/types/block/semantic.rs | 69 ++++++++++++++++--- 8 files changed, 132 insertions(+), 21 deletions(-) diff --git a/bindings/nodejs/lib/types/models/transaction-failure-reason.ts b/bindings/nodejs/lib/types/models/transaction-failure-reason.ts index 988e9b5f6d..5bb9c857fb 100644 --- a/bindings/nodejs/lib/types/models/transaction-failure-reason.ts +++ b/bindings/nodejs/lib/types/models/transaction-failure-reason.ts @@ -107,6 +107,36 @@ export enum TransactionFailureReason { */ failedToClaimDelegationReward = 20, + /** + * Burning of native tokens is not allowed in the transaction capabilities. + */ + transactionCapabilityNativeTokenBurningNotAllowed = 21, + + /** + * Burning of mana is not allowed in the transaction capabilities. + */ + transactionCapabilityManaBurningNotAllowed = 22, + + /** + * Destruction of accounts is not allowed in the transaction capabilities. + */ + transactionCapabilityAccountDestructionNotAllowed = 23, + + /** + * Destruction of anchors is not allowed in the transaction capabilities. + */ + transactionCapabilityAnchorDestructionNotAllowed = 24, + + /** + * Destruction of foundries is not allowed in the transaction capabilities. + */ + transactionCapabilityFoundryDestructionNotAllowed = 25, + + /** + * Destruction of nfts is not allowed in the transaction capabilities. + */ + transactionCapabilityNftDestructionNotAllowed = 26, + /** * The semantic validation failed for a reason not covered by the previous variants. */ @@ -158,6 +188,18 @@ export const TRANSACTION_FAILURE_REASON_STRINGS: { 'Failed to claim staking reward.', [TransactionFailureReason.failedToClaimDelegationReward]: 'Failed to claim delegation reward.', + [TransactionFailureReason.transactionCapabilityNativeTokenBurningNotAllowed]: + 'Burning of native tokens is not allowed in the transaction capabilities.', + [TransactionFailureReason.transactionCapabilityManaBurningNotAllowed]: + 'Burning of mana is not allowed in the transaction capabilities.', + [TransactionFailureReason.transactionCapabilityAccountDestructionNotAllowed]: + 'Destruction of accounts is not allowed in the transaction capabilities.', + [TransactionFailureReason.transactionCapabilityAnchorDestructionNotAllowed]: + 'Destruction of anchors is not allowed in the transaction capabilities.', + [TransactionFailureReason.transactionCapabilityFoundryDestructionNotAllowed]: + 'Destruction of foundries is not allowed in the transaction capabilities.', + [TransactionFailureReason.transactionCapabilityNftDestructionNotAllowed]: + 'Destruction of nfts is not allowed in the transaction capabilities.', [TransactionFailureReason.semanticValidationFailed]: 'The semantic validation failed for a reason not covered by the previous variants.', }; diff --git a/bindings/python/iota_sdk/types/block/metadata.py b/bindings/python/iota_sdk/types/block/metadata.py index a80982d6a2..de34a46e6d 100644 --- a/bindings/python/iota_sdk/types/block/metadata.py +++ b/bindings/python/iota_sdk/types/block/metadata.py @@ -116,6 +116,12 @@ class TransactionFailureReason(Enum): MissingStakingFeature: Staking Feature is not provided in account output when claiming rewards. FailedToClaimStakingReward: Failed to claim staking reward. FailedToClaimDelegationReward: Failed to claim delegation reward. + TransactionCapabilityNativeTokenBurningNotAllowed: Burning of native tokens is not allowed in the transaction capabilities. + TransactionCapabilityManaBurningNotAllowed: Burning of mana is not allowed in the transaction capabilities. + TransactionCapabilityAccountDestructionNotAllowed: Destruction of accounts is not allowed in the transaction capabilities. + TransactionCapabilityAnchorDestructionNotAllowed: Destruction of anchors is not allowed in the transaction capabilities. + TransactionCapabilityFoundryDestructionNotAllowed: Destruction of foundries is not allowed in the transaction capabilities. + TransactionCapabilityNftDestructionNotAllowed: Destruction of nfts is not allowed in the transaction capabilities. SemanticValidationFailed: The semantic validation failed for a reason not covered by the previous variants. """ InputUtxoAlreadySpent = 1 @@ -138,6 +144,12 @@ class TransactionFailureReason(Enum): MissingStakingFeature = 18 FailedToClaimStakingReward = 19 FailedToClaimDelegationReward = 20 + TransactionCapabilityNativeTokenBurningNotAllowed = 21 + TransactionCapabilityManaBurningNotAllowed = 22 + TransactionCapabilityAccountDestructionNotAllowed = 23 + TransactionCapabilityAnchorDestructionNotAllowed = 24 + TransactionCapabilityFoundryDestructionNotAllowed = 25 + TransactionCapabilityNftDestructionNotAllowed = 26 SemanticValidationFailed = 255 def __str__(self): @@ -162,5 +174,11 @@ def __str__(self): 18: "Staking Feature is not provided in account output when claiming rewards.", 19: "Failed to claim staking reward.", 20: "Failed to claim delegation reward.", + 21: "Burning of native tokens is not allowed in the transaction capabilities.", + 22: "Burning of mana is not allowed in the transaction capabilities.", + 23: "Destruction of accounts is not allowed in the transaction capabilities.", + 24: "Destruction of anchors is not allowed in the transaction capabilities.", + 25: "Destruction of foundries is not allowed in the transaction capabilities.", + 26: "Destruction of nfts is not allowed in the transaction capabilities.", 255: "The semantic validation failed for a reason not covered by the previous variants." }[self.value] diff --git a/sdk/src/types/block/output/account.rs b/sdk/src/types/block/output/account.rs index 14ae4a60f3..9509345002 100644 --- a/sdk/src/types/block/output/account.rs +++ b/sdk/src/types/block/output/account.rs @@ -544,8 +544,7 @@ impl StateTransitionVerifier for AccountOutput { .transaction .has_capability(TransactionCapabilityFlag::DestroyAccountOutputs) { - // TODO: add a variant https://github.com/iotaledger/iota-sdk/issues/1430 - return Err(StateTransitionError::UnsupportedStateTransition); + return Err(TransactionFailureReason::TransactionCapabilityAccountDestructionNotAllowed)?; } Ok(()) } diff --git a/sdk/src/types/block/output/anchor.rs b/sdk/src/types/block/output/anchor.rs index 703b7719fd..21f363386f 100644 --- a/sdk/src/types/block/output/anchor.rs +++ b/sdk/src/types/block/output/anchor.rs @@ -604,8 +604,7 @@ impl StateTransitionVerifier for AnchorOutput { .capabilities() .has_capability(TransactionCapabilityFlag::DestroyAnchorOutputs) { - // TODO: add a variant https://github.com/iotaledger/iota-sdk/issues/1430 - return Err(StateTransitionError::UnsupportedStateTransition); + return Err(TransactionFailureReason::TransactionCapabilityAccountDestructionNotAllowed)?; } Ok(()) } diff --git a/sdk/src/types/block/output/foundry.rs b/sdk/src/types/block/output/foundry.rs index 2799fd9ac6..56c2782e14 100644 --- a/sdk/src/types/block/output/foundry.rs +++ b/sdk/src/types/block/output/foundry.rs @@ -532,8 +532,7 @@ impl FoundryOutput { let burned_diff = token_diff - melted_diff; if !burned_diff.is_zero() && !capabilities.has_capability(TransactionCapabilityFlag::BurnNativeTokens) { - // TODO: add a variant https://github.com/iotaledger/iota-sdk/issues/1430 - return Err(StateTransitionError::UnsupportedStateTransition); + return Err(TransactionFailureReason::TransactionCapabilityManaBurningNotAllowed)?; } } } @@ -594,8 +593,7 @@ impl StateTransitionVerifier for FoundryOutput { .transaction .has_capability(TransactionCapabilityFlag::DestroyFoundryOutputs) { - // TODO: add a variant https://github.com/iotaledger/iota-sdk/issues/1430 - return Err(StateTransitionError::UnsupportedStateTransition); + return Err(TransactionFailureReason::TransactionCapabilityFoundryDestructionNotAllowed)?; } let token_id = current_state.token_id(); diff --git a/sdk/src/types/block/output/nft.rs b/sdk/src/types/block/output/nft.rs index 973a67df1a..3b8b397888 100644 --- a/sdk/src/types/block/output/nft.rs +++ b/sdk/src/types/block/output/nft.rs @@ -464,8 +464,7 @@ impl StateTransitionVerifier for NftOutput { .transaction .has_capability(TransactionCapabilityFlag::DestroyNftOutputs) { - // TODO: add a variant https://github.com/iotaledger/iota-sdk/issues/1430 - return Err(StateTransitionError::UnsupportedStateTransition); + return Err(TransactionFailureReason::TransactionCapabilityNftDestructionNotAllowed)?; } Ok(()) } diff --git a/sdk/src/types/block/output/state_transition.rs b/sdk/src/types/block/output/state_transition.rs index 08ddbc78ff..bc1ca6fc36 100644 --- a/sdk/src/types/block/output/state_transition.rs +++ b/sdk/src/types/block/output/state_transition.rs @@ -1,7 +1,7 @@ // Copyright 2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crate::types::block::semantic::SemanticValidationContext; +use crate::types::block::semantic::{SemanticValidationContext, TransactionFailureReason}; /// #[allow(missing_docs)] @@ -28,6 +28,13 @@ pub enum StateTransitionError { UnsortedCreatedFoundries, UnsupportedStateIndexOperation { current_state: u32, next_state: u32 }, UnsupportedStateTransition, + TransactionFailure(TransactionFailureReason), +} + +impl From for StateTransitionError { + fn from(error: TransactionFailureReason) -> Self { + Self::TransactionFailure(error) + } } /// diff --git a/sdk/src/types/block/semantic.rs b/sdk/src/types/block/semantic.rs index bd5a8b1cb8..8edeacaebb 100644 --- a/sdk/src/types/block/semantic.rs +++ b/sdk/src/types/block/semantic.rs @@ -9,7 +9,10 @@ use primitive_types::U256; use crate::types::block::{ address::{Address, AddressCapabilityFlag}, - output::{AnchorOutput, ChainId, FoundryId, NativeTokens, Output, OutputId, TokenId, UnlockCondition}, + output::{ + AnchorOutput, ChainId, FoundryId, NativeTokens, Output, OutputId, StateTransitionError, TokenId, + UnlockCondition, + }, payload::signed_transaction::{Transaction, TransactionCapabilityFlag, TransactionId, TransactionSigningHash}, unlock::Unlocks, Error, @@ -64,6 +67,18 @@ pub enum TransactionFailureReason { FailedToClaimStakingReward = 19, /// Failed to claim delegation reward. FailedToClaimDelegationReward = 20, + /// Burning of native tokens is not allowed in the transaction capabilities. + TransactionCapabilityNativeTokenBurningNotAllowed = 21, + /// Burning of mana is not allowed in the transaction capabilities. + TransactionCapabilityManaBurningNotAllowed = 22, + /// Destruction of accounts is not allowed in the transaction capabilities. + TransactionCapabilityAccountDestructionNotAllowed = 23, + /// Destruction of anchors is not allowed in the transaction capabilities. + TransactionCapabilityAnchorDestructionNotAllowed = 24, + /// Destruction of foundries is not allowed in the transaction capabilities. + TransactionCapabilityFoundryDestructionNotAllowed = 25, + /// Destruction of nfts is not allowed in the transaction capabilities. + TransactionCapabilityNftDestructionNotAllowed = 26, /// The semantic validation failed for a reason not covered by the previous variants. SemanticValidationFailed = 255, } @@ -107,6 +122,28 @@ impl fmt::Display for TransactionFailureReason { ), Self::FailedToClaimStakingReward => write!(f, "Failed to claim staking reward."), Self::FailedToClaimDelegationReward => write!(f, "Failed to claim delegation reward."), + Self::TransactionCapabilityNativeTokenBurningNotAllowed => write!( + f, + "Burning of native tokens is not allowed in the transaction capabilities." + ), + Self::TransactionCapabilityManaBurningNotAllowed => { + write!(f, "Burning of mana is not allowed in the transaction capabilities.") + } + Self::TransactionCapabilityAccountDestructionNotAllowed => write!( + f, + "Destruction of accounts is not allowed in the transaction capabilities." + ), + Self::TransactionCapabilityAnchorDestructionNotAllowed => write!( + f, + "Destruction of anchors is not allowed in the transaction capabilities." + ), + Self::TransactionCapabilityFoundryDestructionNotAllowed => write!( + f, + "Destruction of foundries is not allowed in the transaction capabilities." + ), + Self::TransactionCapabilityNftDestructionNotAllowed => { + write!(f, "Destruction of nfts is not allowed in the transaction capabilities.") + } Self::SemanticValidationFailed => write!( f, "The semantic validation failed for a reason not covered by the previous variants." @@ -140,6 +177,12 @@ impl TryFrom for TransactionFailureReason { 18 => Self::MissingStakingFeature, 19 => Self::FailedToClaimStakingReward, 20 => Self::FailedToClaimDelegationReward, + 21 => Self::TransactionCapabilityNativeTokenBurningNotAllowed, + 22 => Self::TransactionCapabilityManaBurningNotAllowed, + 23 => Self::TransactionCapabilityAccountDestructionNotAllowed, + 24 => Self::TransactionCapabilityAnchorDestructionNotAllowed, + 25 => Self::TransactionCapabilityFoundryDestructionNotAllowed, + 26 => Self::TransactionCapabilityNftDestructionNotAllowed, 255 => Self::SemanticValidationFailed, x => return Err(Self::Error::InvalidTransactionFailureReason(x)), }) @@ -462,23 +505,29 @@ impl<'a> SemanticValidationContext<'a> { // Validation of state transitions and destructions. for (chain_id, current_state) in self.input_chains.iter() { - if Output::verify_state_transition( + match Output::verify_state_transition( Some(current_state), self.output_chains.get(chain_id).map(core::ops::Deref::deref), &self, - ) - .is_err() - { - return Ok(Some(TransactionFailureReason::InvalidChainStateTransition)); + ) { + Err(StateTransitionError::TransactionFailure(f)) => return Ok(Some(f)), + Err(_) => { + return Ok(Some(TransactionFailureReason::InvalidChainStateTransition)); + } + _ => {} } } // Validation of state creations. for (chain_id, next_state) in self.output_chains.iter() { - if self.input_chains.get(chain_id).is_none() - && Output::verify_state_transition(None, Some(next_state), &self).is_err() - { - return Ok(Some(TransactionFailureReason::InvalidChainStateTransition)); + if self.input_chains.get(chain_id).is_none() { + match Output::verify_state_transition(None, Some(next_state), &self) { + Err(StateTransitionError::TransactionFailure(f)) => return Ok(Some(f)), + Err(_) => { + return Ok(Some(TransactionFailureReason::InvalidChainStateTransition)); + } + _ => {} + } } }