From 7dfc8e3bc3cf254e74d8d53eac8ad9215a5fc044 Mon Sep 17 00:00:00 2001 From: Marc Espin Date: Wed, 10 Jan 2024 11:05:05 +0100 Subject: [PATCH 1/2] feat(2.0, nodejs): Silently request to stop background syncing when `WalletMethodHandler` is dropped (#1793) * feat(nodejs): Silently stop background syncing when Wallet is dropped * fmt * chore: Also call request_stop_background_syncing when destroyed manually --- bindings/nodejs/src/wallet.rs | 51 ++++++++++++++++--- .../core/operations/background_syncing.rs | 8 ++- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/bindings/nodejs/src/wallet.rs b/bindings/nodejs/src/wallet.rs index 6bc039b6fc..ae90b2131d 100644 --- a/bindings/nodejs/src/wallet.rs +++ b/bindings/nodejs/src/wallet.rs @@ -1,7 +1,10 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use std::sync::Arc; +use std::{ + ops::{Deref, DerefMut}, + sync::Arc, +}; use iota_sdk_bindings_core::{ call_wallet_method as rust_call_wallet_method, @@ -16,26 +19,58 @@ use crate::{ build_js_error, client::ClientMethodHandler, destroyed_err, secret_manager::SecretManagerMethodHandler, NodejsError, }; -pub type WalletMethodHandler = Arc>>; +pub struct WalletMethodHandlerInner(Option); + +impl Drop for WalletMethodHandlerInner { + fn drop(&mut self) { + log::debug!("drop WalletMethodHandlerInner"); + // Request to stop the background syncing silently if this wallet hasn't been destroyed yet + if let Some(wallet) = self.0.take() { + wallet.request_stop_background_syncing(); + } + } +} + +impl Deref for WalletMethodHandlerInner { + type Target = Option; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for WalletMethodHandlerInner { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +pub type WalletMethodHandler = Arc>; #[napi(js_name = "createWallet")] pub async fn create_wallet(options: String) -> Result> { let wallet_options = serde_json::from_str::(&options).map_err(NodejsError::new)?; let wallet = wallet_options.build().await.map_err(NodejsError::new)?; - Ok(External::new(Arc::new(RwLock::new(Some(wallet))))) + Ok(External::new(Arc::new(RwLock::new(WalletMethodHandlerInner(Some( + wallet, + )))))) } #[napi(js_name = "destroyWallet")] pub async fn destroy_wallet(wallet: External) { - *wallet.as_ref().write().await = None; + let mut wallet = wallet.as_ref().write().await; + if let Some(wallet) = &**wallet { + wallet.request_stop_background_syncing(); + } + **wallet = None; } #[napi(js_name = "callWalletMethod")] pub async fn call_wallet_method(wallet: External, method: String) -> Result { let method = serde_json::from_str::(&method).map_err(NodejsError::new)?; - match &*wallet.as_ref().read().await { + match &**wallet.as_ref().read().await { Some(wallet) => { let response = rust_call_wallet_method(wallet, method).await; match response { @@ -58,7 +93,7 @@ pub async fn listen_wallet( validated_event_types.push(WalletEventType::try_from(event_type).map_err(NodejsError::new)?); } - match &*wallet.as_ref().read().await { + match &**wallet.as_ref().read().await { Some(wallet) => { wallet .listen(validated_event_types, move |event_data| { @@ -78,7 +113,7 @@ pub async fn listen_wallet( #[napi(js_name = "getClient")] pub async fn get_client(wallet: External) -> Result> { - if let Some(wallet) = &*wallet.as_ref().read().await { + if let Some(wallet) = &**wallet.as_ref().read().await { Ok(External::new(Arc::new(RwLock::new(Some(wallet.client().clone()))))) } else { Err(destroyed_err("Wallet")) @@ -87,7 +122,7 @@ pub async fn get_client(wallet: External) -> Result) -> Result> { - if let Some(wallet) = &*wallet.as_ref().read().await { + if let Some(wallet) = &**wallet.as_ref().read().await { Ok(External::new(wallet.get_secret_manager().clone())) } else { Err(destroyed_err("Wallet")) diff --git a/sdk/src/wallet/core/operations/background_syncing.rs b/sdk/src/wallet/core/operations/background_syncing.rs index 9840b967b6..9bea901acc 100644 --- a/sdk/src/wallet/core/operations/background_syncing.rs +++ b/sdk/src/wallet/core/operations/background_syncing.rs @@ -72,6 +72,12 @@ where Ok(()) } + /// Request to stop the background syncing of the wallet + pub fn request_stop_background_syncing(&self) { + log::debug!("[request_stop_background_syncing]"); + self.background_syncing_status.store(2, Ordering::Relaxed); + } + /// Stop the background syncing of the wallet pub async fn stop_background_syncing(&self) -> crate::wallet::Result<()> { log::debug!("[stop_background_syncing]"); @@ -80,7 +86,7 @@ where return Ok(()); } // send stop request - self.background_syncing_status.store(2, Ordering::Relaxed); + self.request_stop_background_syncing(); // wait until it stopped while self.background_syncing_status.load(Ordering::Relaxed) != 0 { #[cfg(target_family = "wasm")] From 5620e3d2c74742777110199f8afff612c12956b8 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Wed, 10 Jan 2024 11:09:55 +0100 Subject: [PATCH 2/2] Move address caps verification to syntactic (#1804) * Move address caps verification to syntactic * verify_restricted_addresses * Use verify_restricted_addresses in builders * Use it with Packable * Fix match * Update sdk/src/types/block/output/account.rs Co-authored-by: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> * Self:: * Better error * Update sdk/src/types/block/output/unlock_condition/mod.rs Co-authored-by: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> --------- Co-authored-by: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> --- sdk/src/types/block/error.rs | 4 +- sdk/src/types/block/output/account.rs | 20 +++-- sdk/src/types/block/output/anchor.rs | 24 ++--- sdk/src/types/block/output/basic.rs | 20 ++++- sdk/src/types/block/output/delegation.rs | 14 ++- sdk/src/types/block/output/nft.rs | 7 +- .../block/output/unlock_condition/mod.rs | 90 ++++++++++++++++++- sdk/src/types/block/semantic/mod.rs | 63 +------------ 8 files changed, 155 insertions(+), 87 deletions(-) diff --git a/sdk/src/types/block/error.rs b/sdk/src/types/block/error.rs index ee8d5b44c8..6e293465ad 100644 --- a/sdk/src/types/block/error.rs +++ b/sdk/src/types/block/error.rs @@ -11,7 +11,7 @@ use primitive_types::U256; use super::slot::EpochIndex; use crate::types::block::{ - address::WeightedAddressCount, + address::{AddressCapabilityFlag, WeightedAddressCount}, context_input::RewardContextInputIndex, input::UtxoInput, mana::ManaAllotmentCount, @@ -201,6 +201,7 @@ pub enum Error { target: EpochIndex, }, TrailingCapabilityBytes, + RestrictedAddressCapability(AddressCapabilityFlag), } #[cfg(feature = "std")] @@ -432,6 +433,7 @@ impl fmt::Display for Error { write!(f, "invalid epoch delta: created {created}, target {target}") } Self::TrailingCapabilityBytes => write!(f, "capability bytes have trailing zeroes"), + Self::RestrictedAddressCapability(cap) => write!(f, "restricted address capability: {cap:?}"), } } } diff --git a/sdk/src/types/block/output/account.rs b/sdk/src/types/block/output/account.rs index 04f601d233..e2792c4362 100644 --- a/sdk/src/types/block/output/account.rs +++ b/sdk/src/types/block/output/account.rs @@ -15,7 +15,10 @@ use crate::types::block::{ address::{AccountAddress, Address}, output::{ feature::{verify_allowed_features, Feature, FeatureFlags, Features}, - unlock_condition::{verify_allowed_unlock_conditions, UnlockCondition, UnlockConditionFlags, UnlockConditions}, + unlock_condition::{ + verify_allowed_unlock_conditions, verify_restricted_addresses, UnlockCondition, UnlockConditionFlags, + UnlockConditions, + }, ChainId, MinimumOutputAmount, Output, OutputBuilderAmount, OutputId, StorageScore, StorageScoreParameters, }, protocol::{ProtocolParameters, WorkScore, WorkScoreParameters}, @@ -217,6 +220,12 @@ impl AccountOutputBuilder { let features = Features::from_set(self.features)?; + verify_restricted_addresses( + &unlock_conditions, + AccountOutput::KIND, + features.native_token(), + self.mana, + )?; verify_allowed_features(&features, AccountOutput::ALLOWED_FEATURES)?; let immutable_features = Features::from_set(self.immutable_features)?; @@ -493,6 +502,8 @@ impl Packable for AccountOutput { let features = Features::unpack::<_, VERIFY>(unpacker, &())?; if VERIFY { + verify_restricted_addresses(&unlock_conditions, Self::KIND, features.native_token(), mana) + .map_err(UnpackError::Packable)?; verify_allowed_features(&features, Self::ALLOWED_FEATURES).map_err(UnpackError::Packable)?; } @@ -611,12 +622,7 @@ mod tests { use super::*; use crate::types::block::{ - output::account::dto::AccountOutputDto, - protocol::protocol_parameters, - rand::output::{ - feature::rand_allowed_features, rand_account_id, rand_account_output, - unlock_condition::rand_address_unlock_condition_different_from_account_id, - }, + output::account::dto::AccountOutputDto, protocol::protocol_parameters, rand::output::rand_account_output, }; #[test] diff --git a/sdk/src/types/block/output/anchor.rs b/sdk/src/types/block/output/anchor.rs index 6f5c926934..958b8eecda 100644 --- a/sdk/src/types/block/output/anchor.rs +++ b/sdk/src/types/block/output/anchor.rs @@ -15,7 +15,10 @@ use crate::types::block::{ address::{Address, AnchorAddress}, output::{ feature::{verify_allowed_features, Feature, FeatureFlags, Features}, - unlock_condition::{verify_allowed_unlock_conditions, UnlockCondition, UnlockConditionFlags, UnlockConditions}, + unlock_condition::{ + verify_allowed_unlock_conditions, verify_restricted_addresses, UnlockCondition, UnlockConditionFlags, + UnlockConditions, + }, ChainId, MinimumOutputAmount, Output, OutputBuilderAmount, OutputId, StorageScore, StorageScoreParameters, }, protocol::{ProtocolParameters, WorkScore, WorkScoreParameters}, @@ -248,6 +251,12 @@ impl AnchorOutputBuilder { let features = Features::from_set(self.features)?; + verify_restricted_addresses( + &unlock_conditions, + AnchorOutput::KIND, + features.native_token(), + self.mana, + )?; verify_allowed_features(&features, AnchorOutput::ALLOWED_FEATURES)?; let immutable_features = Features::from_set(self.immutable_features)?; @@ -549,6 +558,8 @@ impl Packable for AnchorOutput { let features = Features::unpack::<_, VERIFY>(unpacker, &())?; if VERIFY { + verify_restricted_addresses(&unlock_conditions, Self::KIND, features.native_token(), mana) + .map_err(UnpackError::Packable)?; verify_allowed_features(&features, Self::ALLOWED_FEATURES).map_err(UnpackError::Packable)?; } @@ -675,16 +686,7 @@ mod dto { mod tests { use super::*; use crate::types::block::{ - output::anchor::dto::AnchorOutputDto, - protocol::protocol_parameters, - rand::output::{ - feature::rand_allowed_features, - rand_anchor_id, rand_anchor_output, - unlock_condition::{ - rand_governor_address_unlock_condition_different_from, - rand_state_controller_address_unlock_condition_different_from, - }, - }, + output::anchor::dto::AnchorOutputDto, protocol::protocol_parameters, rand::output::rand_anchor_output, }; #[test] diff --git a/sdk/src/types/block/output/basic.rs b/sdk/src/types/block/output/basic.rs index 9b31c18782..41f81ac911 100644 --- a/sdk/src/types/block/output/basic.rs +++ b/sdk/src/types/block/output/basic.rs @@ -10,8 +10,8 @@ use crate::types::block::{ output::{ feature::{verify_allowed_features, Feature, FeatureFlags, Features, NativeTokenFeature}, unlock_condition::{ - verify_allowed_unlock_conditions, AddressUnlockCondition, StorageDepositReturnUnlockCondition, - UnlockCondition, UnlockConditionFlags, UnlockConditions, + verify_allowed_unlock_conditions, verify_restricted_addresses, AddressUnlockCondition, + StorageDepositReturnUnlockCondition, UnlockCondition, UnlockConditionFlags, UnlockConditions, }, MinimumOutputAmount, NativeToken, Output, OutputBuilderAmount, StorageScore, StorageScoreParameters, }, @@ -192,6 +192,12 @@ impl BasicOutputBuilder { let features = Features::from_set(self.features)?; + verify_restricted_addresses( + &unlock_conditions, + BasicOutput::KIND, + features.native_token(), + self.mana, + )?; verify_features::(&features)?; let mut output = BasicOutput { @@ -230,6 +236,7 @@ impl From<&BasicOutput> for BasicOutputBuilder { #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Packable)] #[packable(unpack_error = Error)] #[packable(unpack_visitor = ProtocolParameters)] +#[packable(verify_with = verify_basic_output)] pub struct BasicOutput { /// Amount of IOTA coins held by the output. amount: u64, @@ -392,6 +399,15 @@ fn verify_features_packable(features: &Features, _: &Protoco verify_features::(features) } +fn verify_basic_output(output: &BasicOutput, _: &ProtocolParameters) -> Result<(), Error> { + verify_restricted_addresses( + output.unlock_conditions(), + BasicOutput::KIND, + output.features.native_token(), + output.mana, + ) +} + #[cfg(feature = "serde")] mod dto { use alloc::vec::Vec; diff --git a/sdk/src/types/block/output/delegation.rs b/sdk/src/types/block/output/delegation.rs index e3d8b9ffd9..d9e880019c 100644 --- a/sdk/src/types/block/output/delegation.rs +++ b/sdk/src/types/block/output/delegation.rs @@ -9,7 +9,10 @@ use crate::types::block::{ address::{AccountAddress, Address}, output::{ chain_id::ChainId, - unlock_condition::{verify_allowed_unlock_conditions, UnlockCondition, UnlockConditionFlags, UnlockConditions}, + unlock_condition::{ + verify_allowed_unlock_conditions, verify_restricted_addresses, UnlockCondition, UnlockConditionFlags, + UnlockConditions, + }, MinimumOutputAmount, Output, OutputBuilderAmount, OutputId, StorageScore, StorageScoreParameters, }, protocol::{ProtocolParameters, WorkScore, WorkScoreParameters}, @@ -172,6 +175,7 @@ impl DelegationOutputBuilder { let unlock_conditions = UnlockConditions::from_set(self.unlock_conditions)?; verify_unlock_conditions::(&unlock_conditions)?; + verify_restricted_addresses(&unlock_conditions, DelegationOutput::KIND, None, 0)?; let mut output = DelegationOutput { amount: 0, @@ -215,6 +219,7 @@ impl From<&DelegationOutput> for DelegationOutputBuilder { #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Packable)] #[packable(unpack_error = Error)] #[packable(unpack_visitor = ProtocolParameters)] +#[packable(verify_with = verify_delegation_output)] pub struct DelegationOutput { /// Amount of IOTA coins held by the output. amount: u64, @@ -392,6 +397,13 @@ fn verify_unlock_conditions_packable( verify_unlock_conditions::(unlock_conditions) } +fn verify_delegation_output( + output: &DelegationOutput, + _: &ProtocolParameters, +) -> Result<(), Error> { + verify_restricted_addresses(output.unlock_conditions(), DelegationOutput::KIND, None, 0) +} + #[cfg(feature = "serde")] mod dto { use alloc::vec::Vec; diff --git a/sdk/src/types/block/output/nft.rs b/sdk/src/types/block/output/nft.rs index 9636399d5c..d641ee1f46 100644 --- a/sdk/src/types/block/output/nft.rs +++ b/sdk/src/types/block/output/nft.rs @@ -15,8 +15,8 @@ use crate::types::block::{ output::{ feature::{verify_allowed_features, Feature, FeatureFlags, Features}, unlock_condition::{ - verify_allowed_unlock_conditions, AddressUnlockCondition, StorageDepositReturnUnlockCondition, - UnlockCondition, UnlockConditionFlags, UnlockConditions, + verify_allowed_unlock_conditions, verify_restricted_addresses, AddressUnlockCondition, + StorageDepositReturnUnlockCondition, UnlockCondition, UnlockConditionFlags, UnlockConditions, }, BasicOutputBuilder, ChainId, MinimumOutputAmount, Output, OutputBuilderAmount, OutputId, StorageScore, StorageScoreParameters, @@ -256,6 +256,7 @@ impl NftOutputBuilder { let features = Features::from_set(self.features)?; + verify_restricted_addresses(&unlock_conditions, NftOutput::KIND, features.native_token(), self.mana)?; verify_allowed_features(&features, NftOutput::ALLOWED_FEATURES)?; let immutable_features = Features::from_set(self.immutable_features)?; @@ -469,6 +470,8 @@ impl Packable for NftOutput { let features = Features::unpack::<_, VERIFY>(unpacker, &())?; if VERIFY { + verify_restricted_addresses(&unlock_conditions, Self::KIND, features.native_token(), mana) + .map_err(UnpackError::Packable)?; verify_allowed_features(&features, Self::ALLOWED_FEATURES).map_err(UnpackError::Packable)?; } diff --git a/sdk/src/types/block/output/unlock_condition/mod.rs b/sdk/src/types/block/output/unlock_condition/mod.rs index 6b2bc79ed5..b3506172c1 100644 --- a/sdk/src/types/block/output/unlock_condition/mod.rs +++ b/sdk/src/types/block/output/unlock_condition/mod.rs @@ -24,8 +24,11 @@ pub use self::{ storage_deposit_return::StorageDepositReturnUnlockCondition, timelock::TimelockUnlockCondition, }; use crate::types::block::{ - address::Address, - output::{StorageScore, StorageScoreParameters}, + address::{Address, AddressCapabilityFlag, RestrictedAddress}, + output::{ + feature::NativeTokenFeature, AccountOutput, AnchorOutput, DelegationOutput, NftOutput, StorageScore, + StorageScoreParameters, + }, protocol::{CommittableAgeRange, ProtocolParameters, WorkScore}, slot::SlotIndex, Error, @@ -314,6 +317,22 @@ impl UnlockConditions { Ok(address) } + + /// Returns an iterator over all addresses except StorageDepositReturn address. + pub fn addresses(&self) -> impl Iterator { + self.iter().filter_map(|uc| match uc { + UnlockCondition::Address(uc) => Some(uc.address()), + UnlockCondition::Expiration(uc) => Some(uc.return_address()), + UnlockCondition::StateControllerAddress(uc) => Some(uc.address()), + UnlockCondition::GovernorAddress(uc) => Some(uc.address()), + _ => None, + }) + } + + /// Returns an iterator over all restricted addresses. + pub fn restricted_addresses(&self) -> impl Iterator { + self.addresses().filter_map(Address::as_restricted_opt) + } } impl StorageScore for UnlockConditions { @@ -355,6 +374,73 @@ pub(crate) fn verify_allowed_unlock_conditions( Ok(()) } +pub(crate) fn verify_restricted_addresses( + unlock_conditions: &UnlockConditions, + output_kind: u8, + native_token: Option<&NativeTokenFeature>, + mana: u64, +) -> Result<(), Error> { + let addresses = unlock_conditions.restricted_addresses(); + + for address in addresses { + if native_token.is_some() && !address.has_capability(AddressCapabilityFlag::OutputsWithNativeTokens) { + return Err(Error::RestrictedAddressCapability( + AddressCapabilityFlag::OutputsWithNativeTokens, + )); + } + + if mana > 0 && !address.has_capability(AddressCapabilityFlag::OutputsWithMana) { + return Err(Error::RestrictedAddressCapability( + AddressCapabilityFlag::OutputsWithMana, + )); + } + + if unlock_conditions.timelock().is_some() && !address.has_capability(AddressCapabilityFlag::OutputsWithTimelock) + { + return Err(Error::RestrictedAddressCapability( + AddressCapabilityFlag::OutputsWithTimelock, + )); + } + + if unlock_conditions.expiration().is_some() + && !address.has_capability(AddressCapabilityFlag::OutputsWithExpiration) + { + return Err(Error::RestrictedAddressCapability( + AddressCapabilityFlag::OutputsWithExpiration, + )); + } + + if unlock_conditions.storage_deposit_return().is_some() + && !address.has_capability(AddressCapabilityFlag::OutputsWithStorageDepositReturn) + { + return Err(Error::RestrictedAddressCapability( + AddressCapabilityFlag::OutputsWithStorageDepositReturn, + )); + } + + match output_kind { + AccountOutput::KIND if !address.has_capability(AddressCapabilityFlag::AccountOutputs) => { + return Err(Error::RestrictedAddressCapability( + AddressCapabilityFlag::AccountOutputs, + )); + } + AnchorOutput::KIND if !address.has_capability(AddressCapabilityFlag::AnchorOutputs) => { + return Err(Error::RestrictedAddressCapability(AddressCapabilityFlag::AnchorOutputs)); + } + NftOutput::KIND if !address.has_capability(AddressCapabilityFlag::NftOutputs) => { + return Err(Error::RestrictedAddressCapability(AddressCapabilityFlag::NftOutputs)); + } + DelegationOutput::KIND if !address.has_capability(AddressCapabilityFlag::DelegationOutputs) => { + return Err(Error::RestrictedAddressCapability( + AddressCapabilityFlag::DelegationOutputs, + )); + } + _ => {} + } + } + Ok(()) +} + #[cfg(test)] mod test { use pretty_assertions::assert_eq; diff --git a/sdk/src/types/block/semantic/mod.rs b/sdk/src/types/block/semantic/mod.rs index b7573384e2..f92e5bab2e 100644 --- a/sdk/src/types/block/semantic/mod.rs +++ b/sdk/src/types/block/semantic/mod.rs @@ -15,8 +15,8 @@ pub use self::{ state_transition::{StateTransitionError, StateTransitionVerifier}, }; use crate::types::block::{ - address::{Address, AddressCapabilityFlag}, - output::{AccountId, AnchorOutput, ChainId, FoundryId, NativeTokens, Output, OutputId, TokenId, UnlockCondition}, + address::Address, + output::{AccountId, AnchorOutput, ChainId, FoundryId, NativeTokens, Output, OutputId, TokenId}, payload::signed_transaction::{Transaction, TransactionCapabilityFlag, TransactionSigningHash}, protocol::ProtocolParameters, unlock::Unlock, @@ -210,65 +210,6 @@ impl<'a> SemanticValidationContext<'a> { Output::Delegation(output) => (output.amount(), 0, None, None), }; - if let Some(unlock_conditions) = created_output.unlock_conditions() { - // Check the possibly restricted address-containing conditions - let addresses = unlock_conditions - .iter() - .filter_map(|uc| match uc { - UnlockCondition::Address(uc) => Some(uc.address()), - UnlockCondition::Expiration(uc) => Some(uc.return_address()), - UnlockCondition::StateControllerAddress(uc) => Some(uc.address()), - UnlockCondition::GovernorAddress(uc) => Some(uc.address()), - _ => None, - }) - .filter_map(Address::as_restricted_opt); - for address in addresses { - if created_native_token.is_some() - && !address.has_capability(AddressCapabilityFlag::OutputsWithNativeTokens) - { - // TODO: add a variant https://github.com/iotaledger/iota-sdk/issues/1430 - return Ok(Some(TransactionFailureReason::SemanticValidationFailed)); - } - - if mana > 0 && !address.has_capability(AddressCapabilityFlag::OutputsWithMana) { - // TODO: add a variant https://github.com/iotaledger/iota-sdk/issues/1430 - return Ok(Some(TransactionFailureReason::SemanticValidationFailed)); - } - - if unlock_conditions.timelock().is_some() - && !address.has_capability(AddressCapabilityFlag::OutputsWithTimelock) - { - // TODO: add a variant https://github.com/iotaledger/iota-sdk/issues/1430 - return Ok(Some(TransactionFailureReason::SemanticValidationFailed)); - } - - if unlock_conditions.expiration().is_some() - && !address.has_capability(AddressCapabilityFlag::OutputsWithExpiration) - { - // TODO: add a variant https://github.com/iotaledger/iota-sdk/issues/1430 - return Ok(Some(TransactionFailureReason::SemanticValidationFailed)); - } - - if unlock_conditions.storage_deposit_return().is_some() - && !address.has_capability(AddressCapabilityFlag::OutputsWithStorageDepositReturn) - { - // TODO: add a variant https://github.com/iotaledger/iota-sdk/issues/1430 - return Ok(Some(TransactionFailureReason::SemanticValidationFailed)); - } - - if match &created_output { - Output::Account(_) => !address.has_capability(AddressCapabilityFlag::AccountOutputs), - Output::Anchor(_) => !address.has_capability(AddressCapabilityFlag::AnchorOutputs), - Output::Nft(_) => !address.has_capability(AddressCapabilityFlag::NftOutputs), - Output::Delegation(_) => !address.has_capability(AddressCapabilityFlag::DelegationOutputs), - _ => false, - } { - // TODO: add a variant https://github.com/iotaledger/iota-sdk/issues/1430 - return Ok(Some(TransactionFailureReason::SemanticValidationFailed)); - } - } - } - if let Some(sender) = features.and_then(|f| f.sender()) { if !self.unlocked_addresses.contains(sender.address()) { return Ok(Some(TransactionFailureReason::SenderNotUnlocked));