diff --git a/sdk/src/client/api/block_builder/input_selection/mod.rs b/sdk/src/client/api/block_builder/input_selection/mod.rs index 4baa80fc55..27b92a05b7 100644 --- a/sdk/src/client/api/block_builder/input_selection/mod.rs +++ b/sdk/src/client/api/block_builder/input_selection/mod.rs @@ -499,6 +499,7 @@ impl InputSelection { foundry_output, input_native_tokens_builder.deref(), output_native_tokens_builder.deref(), + Default::default(), ) { log::debug!("validate_transitions error {err:?}"); return Err(Error::UnfulfillableRequirement(Requirement::Foundry( diff --git a/sdk/src/types/block/output/account.rs b/sdk/src/types/block/output/account.rs index 5e1d832928..db78b19f8d 100644 --- a/sdk/src/types/block/output/account.rs +++ b/sdk/src/types/block/output/account.rs @@ -25,6 +25,7 @@ use crate::types::{ NativeTokens, Output, OutputBuilderAmount, OutputId, Rent, RentStructure, StateTransitionError, StateTransitionVerifier, }, + payload::transaction::TransactionCapabilityFlag, protocol::ProtocolParameters, semantic::{TransactionFailureReason, ValidationContext}, unlock::Unlock, @@ -646,7 +647,15 @@ impl StateTransitionVerifier for AccountOutput { ) } - fn destruction(_current_state: &Self, _context: &ValidationContext<'_>) -> Result<(), StateTransitionError> { + fn destruction(_current_state: &Self, context: &ValidationContext<'_>) -> Result<(), StateTransitionError> { + if context + .essence + .capabilities() + .has_capability(TransactionCapabilityFlag::DestroyAccountOutputs) + { + // TODO: is this correct? + return Err(StateTransitionError::UnsupportedStateTransition); + } Ok(()) } } diff --git a/sdk/src/types/block/output/delegation.rs b/sdk/src/types/block/output/delegation.rs index 18174aaf8e..d90b378ff3 100644 --- a/sdk/src/types/block/output/delegation.rs +++ b/sdk/src/types/block/output/delegation.rs @@ -359,7 +359,8 @@ impl DelegationOutput { // Transition, just without full ValidationContext. pub(crate) fn transition_inner(current_state: &Self, next_state: &Self) -> Result<(), StateTransitionError> { - if !current_state.delegation_id.is_null() || next_state.delegation_id().is_null() { + #[allow(clippy::nonminimal_bool)] + if !(current_state.delegation_id.is_null() && !next_state.delegation_id().is_null()) { return Err(StateTransitionError::NonDelayedClaimingTransition); } diff --git a/sdk/src/types/block/output/foundry.rs b/sdk/src/types/block/output/foundry.rs index f8b0854ee5..2664bb9e05 100644 --- a/sdk/src/types/block/output/foundry.rs +++ b/sdk/src/types/block/output/foundry.rs @@ -25,6 +25,7 @@ use crate::types::{ NativeTokens, Output, OutputBuilderAmount, OutputId, Rent, RentStructure, StateTransitionError, StateTransitionVerifier, TokenId, TokenScheme, }, + payload::transaction::{TransactionCapabilities, TransactionCapabilityFlag}, protocol::ProtocolParameters, semantic::{TransactionFailureReason, ValidationContext}, unlock::Unlock, @@ -457,6 +458,7 @@ impl FoundryOutput { next_state: &Self, input_native_tokens: &BTreeMap, output_native_tokens: &BTreeMap, + capabilities: TransactionCapabilities, ) -> Result<(), StateTransitionError> { if current_state.account_address() != next_state.account_address() || current_state.serial_number != next_state.serial_number @@ -510,6 +512,11 @@ impl FoundryOutput { Ordering::Greater => { // Melt / Burn + if capabilities.has_capability(TransactionCapabilityFlag::BurnNativeTokens) { + // TODO: is this correct? + return Err(StateTransitionError::UnsupportedStateTransition); + } + if current_token_scheme.melted_tokens() != next_token_scheme.melted_tokens() && current_token_scheme.minted_tokens() != next_token_scheme.minted_tokens() { @@ -574,10 +581,20 @@ impl StateTransitionVerifier for FoundryOutput { next_state, &context.input_native_tokens, &context.output_native_tokens, + context.essence.capabilities(), ) } fn destruction(current_state: &Self, context: &ValidationContext<'_>) -> Result<(), StateTransitionError> { + if context + .essence + .capabilities() + .has_capability(TransactionCapabilityFlag::DestroyFoundryOutputs) + { + // TODO: is this correct? + return Err(StateTransitionError::UnsupportedStateTransition); + } + let token_id = current_state.token_id(); let input_tokens = context.input_native_tokens.get(&token_id).copied().unwrap_or_default(); let TokenScheme::Simple(ref current_token_scheme) = current_state.token_scheme; diff --git a/sdk/src/types/block/output/nft.rs b/sdk/src/types/block/output/nft.rs index 957e384e51..babed75f71 100644 --- a/sdk/src/types/block/output/nft.rs +++ b/sdk/src/types/block/output/nft.rs @@ -22,6 +22,7 @@ use crate::types::{ NativeTokens, Output, OutputBuilderAmount, OutputId, Rent, RentStructure, StateTransitionError, StateTransitionVerifier, }, + payload::transaction::TransactionCapabilityFlag, protocol::ProtocolParameters, semantic::{TransactionFailureReason, ValidationContext}, unlock::Unlock, @@ -456,7 +457,15 @@ impl StateTransitionVerifier for NftOutput { Self::transition_inner(current_state, next_state) } - fn destruction(_current_state: &Self, _context: &ValidationContext<'_>) -> Result<(), StateTransitionError> { + fn destruction(_current_state: &Self, context: &ValidationContext<'_>) -> Result<(), StateTransitionError> { + if context + .essence + .capabilities() + .has_capability(TransactionCapabilityFlag::DestroyNftOutputs) + { + // TODO: is this correct? + return Err(StateTransitionError::UnsupportedStateTransition); + } Ok(()) } } diff --git a/sdk/src/types/block/semantic.rs b/sdk/src/types/block/semantic.rs index 369c2b4a08..955dac96b3 100644 --- a/sdk/src/types/block/semantic.rs +++ b/sdk/src/types/block/semantic.rs @@ -268,45 +268,6 @@ pub fn semantic_validation( ), }; - if let Output::Account(consumed) = consumed_output { - let match_fn = |created_output: &Output| matches!(created_output, Output::Account(created) if created.account_id() == consumed.account_id()); - if !context.essence.outputs().iter().any(match_fn) - && !context - .essence - .capabilities() - .has_capability(TransactionCapabilityFlag::DestroyAccountOutputs) - { - // TODO: better failure reason incoming? - return Ok(Some(TransactionFailureReason::SemanticValidationFailed)); - } - } - - if let Output::Foundry(consumed) = consumed_output { - let match_fn = |created_output: &Output| matches!(created_output, Output::Foundry(created) if created.id() == consumed.id()); - if !context.essence.outputs().iter().any(match_fn) - && !context - .essence - .capabilities() - .has_capability(TransactionCapabilityFlag::DestroyFoundryOutputs) - { - // TODO: better failure reason incoming? - return Ok(Some(TransactionFailureReason::SemanticValidationFailed)); - } - } - - if let Output::Nft(consumed) = consumed_output { - let match_fn = |created_output: &Output| matches!(created_output, Output::Nft(created) if created.nft_id() == consumed.nft_id()); - if !context.essence.outputs().iter().any(match_fn) - && !context - .essence - .capabilities() - .has_capability(TransactionCapabilityFlag::DestroyNftOutputs) - { - // TODO: better failure reason incoming? - return Ok(Some(TransactionFailureReason::SemanticValidationFailed)); - } - } - if let Err(conflict) = conflict { return Ok(Some(conflict)); } @@ -450,16 +411,6 @@ pub fn semantic_validation( // Validation of input native tokens. for (token_id, _input_amount) in context.input_native_tokens.iter() { native_token_ids.insert(token_id); - - if !context.output_native_tokens.contains_key(token_id) - && context - .essence - .capabilities() - .has_capability(TransactionCapabilityFlag::BurnNativeTokens) - { - // TODO: better failure reason incoming? - return Ok(Some(TransactionFailureReason::SemanticValidationFailed)); - } } // Validation of output native tokens.