Skip to content

Commit

Permalink
ImplicitAccount transition semantic validation (#1705)
Browse files Browse the repository at this point in the history
* ImplicitAccount transition semantic validation

* nits

* move to semantic module

* Add link to PR

* move to BasicOutput impl

* Return SemanticValidationFailed

* Cargo audit

* Clippy

* Better errors
  • Loading branch information
thibault-martinez authored Jan 18, 2024
1 parent df44abe commit 8412e53
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 6 deletions.
10 changes: 10 additions & 0 deletions sdk/src/types/block/semantic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ impl<'a> SemanticValidationContext<'a> {
///
pub fn validate(mut self) -> Result<Option<TransactionFailureReason>, 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()),
Expand All @@ -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()
Expand Down
45 changes: 39 additions & 6 deletions sdk/src/types/block/semantic/state_transition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};
Expand All @@ -19,6 +22,7 @@ pub enum StateTransitionError {
InconsistentNativeTokensTransition,
InconsistentNativeTokensMeltBurn,
InvalidDelegatedAmount,
InvalidBlockIssuerTransition,
IssuerNotUnlocked,
MissingAccountForFoundry,
MutatedFieldWithoutRights,
Expand All @@ -33,6 +37,7 @@ pub enum StateTransitionError {
UnsupportedStateIndexOperation { current_state: u32, next_state: u32 },
UnsupportedStateTransition,
TransactionFailure(TransactionFailureReason),
ZeroCreatedId,
}

impl From<TransactionFailureReason> for StateTransitionError {
Expand Down Expand Up @@ -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))) => {
Expand Down Expand Up @@ -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() {
Expand Down

0 comments on commit 8412e53

Please sign in to comment.