Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ImplicitAccount transition semantic validation #1705

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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));
Thoralf-M marked this conversation as resolved.
Show resolved Hide resolved
} 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