diff --git a/sdk/src/types/block/semantic/mod.rs b/sdk/src/types/block/semantic/mod.rs index c6ce5aea43..9a201c64e8 100644 --- a/sdk/src/types/block/semantic/mod.rs +++ b/sdk/src/types/block/semantic/mod.rs @@ -101,6 +101,8 @@ impl<'a> SemanticValidationContext<'a> { /// pub fn validate(mut self) -> Result, Error> { // Validation of inputs. + let mut has_implicit_account_creation_address = false; + for (index, (output_id, consumed_output)) in self.inputs.iter().enumerate() { let (amount, consumed_native_token, unlock_conditions) = match consumed_output { Output::Basic(output) => (output.amount(), output.native_token(), output.unlock_conditions()), @@ -111,6 +113,14 @@ impl<'a> SemanticValidationContext<'a> { Output::Delegation(output) => (output.amount(), None, output.unlock_conditions()), }; + if unlock_conditions.addresses().any(Address::is_implicit_account_creation) { + if has_implicit_account_creation_address { + return Ok(Some(TransactionFailureReason::SemanticValidationFailed)); + } else { + has_implicit_account_creation_address = true; + } + } + let commitment_slot_index = self .transaction .context_inputs() diff --git a/sdk/src/types/block/semantic/state_transition.rs b/sdk/src/types/block/semantic/state_transition.rs index 22afecc78c..8ab3364e03 100644 --- a/sdk/src/types/block/semantic/state_transition.rs +++ b/sdk/src/types/block/semantic/state_transition.rs @@ -2,7 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 use crate::types::block::{ - output::{AccountOutput, AnchorOutput, ChainId, DelegationOutput, FoundryOutput, NftOutput, Output, TokenScheme}, + output::{ + AccountOutput, AnchorOutput, BasicOutput, ChainId, DelegationOutput, FoundryOutput, NftOutput, Output, + TokenScheme, + }, payload::signed_transaction::TransactionCapabilityFlag, semantic::{SemanticValidationContext, TransactionFailureReason}, }; @@ -19,6 +22,7 @@ pub enum StateTransitionError { InconsistentNativeTokensTransition, InconsistentNativeTokensMeltBurn, InvalidDelegatedAmount, + InvalidBlockIssuerTransition, IssuerNotUnlocked, MissingAccountForFoundry, MutatedFieldWithoutRights, @@ -33,6 +37,7 @@ pub enum StateTransitionError { UnsupportedStateIndexOperation { current_state: u32, next_state: u32 }, UnsupportedStateTransition, TransactionFailure(TransactionFailureReason), + ZeroCreatedId, } impl From for StateTransitionError { @@ -72,12 +77,11 @@ impl SemanticValidationContext<'_> { (None, Some(Output::Delegation(next_state))) => DelegationOutput::creation(next_state, self), // Transitions. - (Some(Output::Basic(current_state)), Some(Output::Account(_next_state))) => { - if !current_state.is_implicit_account() { - Err(StateTransitionError::UnsupportedStateTransition) + (Some(Output::Basic(current_state)), Some(Output::Account(next_state))) => { + if current_state.is_implicit_account() { + BasicOutput::implicit_account_transition(current_state, next_state, self) } else { - // TODO https://github.com/iotaledger/iota-sdk/issues/1664 - Ok(()) + Err(StateTransitionError::UnsupportedStateTransition) } } (Some(Output::Account(current_state)), Some(Output::Account(next_state))) => { @@ -105,6 +109,35 @@ impl SemanticValidationContext<'_> { } } +impl BasicOutput { + pub(crate) fn implicit_account_transition( + _current_state: &Self, + next_state: &AccountOutput, + context: &SemanticValidationContext<'_>, + ) -> Result<(), StateTransitionError> { + if next_state.account_id().is_null() { + return Err(StateTransitionError::ZeroCreatedId); + } + + if let Some(_block_issuer) = next_state.features().block_issuer() { + // TODO https://github.com/iotaledger/iota-sdk/issues/1853 + // The Account must have a Block Issuer Feature and it must pass semantic validation as if the implicit + // account contained a Block Issuer Feature with its Expiry Slot set to the maximum value of + // slot indices and the feature was transitioned. + } else { + return Err(StateTransitionError::InvalidBlockIssuerTransition); + } + + if let Some(issuer) = next_state.immutable_features().issuer() { + if !context.unlocked_addresses.contains(issuer.address()) { + return Err(StateTransitionError::IssuerNotUnlocked); + } + } + + Ok(()) + } +} + impl StateTransitionVerifier for AccountOutput { fn creation(next_state: &Self, context: &SemanticValidationContext<'_>) -> Result<(), StateTransitionError> { if !next_state.account_id().is_null() {