Skip to content

Commit

Permalink
Add semantic module and move transitions to it (#1770)
Browse files Browse the repository at this point in the history
* Semantic module

* Move transitions to semantic
  • Loading branch information
thibault-martinez authored Dec 12, 2023
1 parent 0605c66 commit d8791f5
Show file tree
Hide file tree
Showing 9 changed files with 346 additions and 350 deletions.
45 changes: 2 additions & 43 deletions sdk/src/types/block/output/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@ use crate::types::block::{
output::{
feature::{verify_allowed_features, Feature, FeatureFlags, Features},
unlock_condition::{verify_allowed_unlock_conditions, UnlockCondition, UnlockConditionFlags, UnlockConditions},
ChainId, MinimumOutputAmount, Output, OutputBuilderAmount, OutputId, StateTransitionError,
StateTransitionVerifier, StorageScore, StorageScoreParameters,
ChainId, MinimumOutputAmount, Output, OutputBuilderAmount, OutputId, StorageScore, StorageScoreParameters,
},
payload::signed_transaction::TransactionCapabilityFlag,
protocol::{ProtocolParameters, WorkScore, WorkScoreParameters},
semantic::{SemanticValidationContext, TransactionFailureReason},
semantic::{SemanticValidationContext, StateTransitionError, TransactionFailureReason},
unlock::Unlock,
Error,
};
Expand Down Expand Up @@ -464,45 +462,6 @@ impl AccountOutput {
}
}

impl StateTransitionVerifier for AccountOutput {
fn creation(next_state: &Self, context: &SemanticValidationContext<'_>) -> Result<(), StateTransitionError> {
if !next_state.account_id.is_null() {
return Err(StateTransitionError::NonZeroCreatedId);
}

if let Some(issuer) = next_state.immutable_features().issuer() {
if !context.unlocked_addresses.contains(issuer.address()) {
return Err(StateTransitionError::IssuerNotUnlocked);
}
}

Ok(())
}

fn transition(
current_state: &Self,
next_state: &Self,
context: &SemanticValidationContext<'_>,
) -> Result<(), StateTransitionError> {
Self::transition_inner(
current_state,
next_state,
&context.input_chains,
context.transaction.outputs(),
)
}

fn destruction(_current_state: &Self, context: &SemanticValidationContext<'_>) -> Result<(), StateTransitionError> {
if !context
.transaction
.has_capability(TransactionCapabilityFlag::DestroyAccountOutputs)
{
return Err(TransactionFailureReason::TransactionCapabilityAccountDestructionNotAllowed)?;
}
Ok(())
}
}

impl StorageScore for AccountOutput {
fn storage_score(&self, params: StorageScoreParameters) -> u64 {
params.output_offset()
Expand Down
46 changes: 2 additions & 44 deletions sdk/src/types/block/output/anchor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@ use crate::types::block::{
output::{
feature::{verify_allowed_features, Feature, FeatureFlags, Features},
unlock_condition::{verify_allowed_unlock_conditions, UnlockCondition, UnlockConditionFlags, UnlockConditions},
ChainId, MinimumOutputAmount, Output, OutputBuilderAmount, OutputId, StateTransitionError,
StateTransitionVerifier, StorageScore, StorageScoreParameters,
ChainId, MinimumOutputAmount, Output, OutputBuilderAmount, OutputId, StorageScore, StorageScoreParameters,
},
payload::signed_transaction::TransactionCapabilityFlag,
protocol::{ProtocolParameters, WorkScore, WorkScoreParameters},
semantic::{SemanticValidationContext, TransactionFailureReason},
semantic::{SemanticValidationContext, StateTransitionError, TransactionFailureReason},
unlock::Unlock,
Error,
};
Expand Down Expand Up @@ -517,46 +515,6 @@ impl WorkScore for AnchorOutput {

impl MinimumOutputAmount for AnchorOutput {}

impl StateTransitionVerifier for AnchorOutput {
fn creation(next_state: &Self, context: &SemanticValidationContext<'_>) -> Result<(), StateTransitionError> {
if !next_state.anchor_id.is_null() {
return Err(StateTransitionError::NonZeroCreatedId);
}

if let Some(issuer) = next_state.immutable_features().issuer() {
if !context.unlocked_addresses.contains(issuer.address()) {
return Err(StateTransitionError::IssuerNotUnlocked);
}
}

Ok(())
}

fn transition(
current_state: &Self,
next_state: &Self,
context: &SemanticValidationContext<'_>,
) -> Result<(), StateTransitionError> {
Self::transition_inner(
current_state,
next_state,
&context.input_chains,
context.transaction.outputs(),
)
}

fn destruction(_current_state: &Self, context: &SemanticValidationContext<'_>) -> Result<(), StateTransitionError> {
if !context
.transaction
.capabilities()
.has_capability(TransactionCapabilityFlag::DestroyAnchorOutputs)
{
return Err(TransactionFailureReason::TransactionCapabilityAccountDestructionNotAllowed)?;
}
Ok(())
}
}

impl Packable for AnchorOutput {
type UnpackError = Error;
type UnpackVisitor = ProtocolParameters;
Expand Down
39 changes: 2 additions & 37 deletions sdk/src/types/block/output/delegation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@ use crate::types::block::{
output::{
chain_id::ChainId,
unlock_condition::{verify_allowed_unlock_conditions, UnlockCondition, UnlockConditionFlags, UnlockConditions},
MinimumOutputAmount, Output, OutputBuilderAmount, OutputId, StateTransitionError, StateTransitionVerifier,
StorageScore, StorageScoreParameters,
MinimumOutputAmount, Output, OutputBuilderAmount, OutputId, StorageScore, StorageScoreParameters,
},
protocol::{ProtocolParameters, WorkScore, WorkScoreParameters},
semantic::{SemanticValidationContext, TransactionFailureReason},
semantic::{SemanticValidationContext, StateTransitionError, TransactionFailureReason},
slot::EpochIndex,
unlock::Unlock,
Error,
Expand Down Expand Up @@ -353,40 +352,6 @@ impl DelegationOutput {
}
}

impl StateTransitionVerifier for DelegationOutput {
fn creation(next_state: &Self, _context: &SemanticValidationContext<'_>) -> Result<(), StateTransitionError> {
if !next_state.delegation_id.is_null() {
return Err(StateTransitionError::NonZeroCreatedId);
}

if next_state.amount != next_state.delegated_amount {
return Err(StateTransitionError::InvalidDelegatedAmount);
}

if next_state.end_epoch != 0 {
return Err(StateTransitionError::NonZeroDelegationEndEpoch);
}

Ok(())
}

fn transition(
current_state: &Self,
next_state: &Self,
_context: &SemanticValidationContext<'_>,
) -> Result<(), StateTransitionError> {
Self::transition_inner(current_state, next_state)
}

fn destruction(
_current_state: &Self,
_context: &SemanticValidationContext<'_>,
) -> Result<(), StateTransitionError> {
// TODO handle mana rewards
Ok(())
}
}

impl StorageScore for DelegationOutput {
fn storage_score(&self, params: StorageScoreParameters) -> u64 {
params.output_offset()
Expand Down
81 changes: 3 additions & 78 deletions sdk/src/types/block/output/foundry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ use crate::types::block::{
account::AccountId,
feature::{verify_allowed_features, Feature, FeatureFlags, Features, NativeTokenFeature},
unlock_condition::{verify_allowed_unlock_conditions, UnlockCondition, UnlockConditionFlags, UnlockConditions},
ChainId, MinimumOutputAmount, NativeToken, Output, OutputBuilderAmount, OutputId, StateTransitionError,
StateTransitionVerifier, StorageScore, StorageScoreParameters, TokenId, TokenScheme,
ChainId, MinimumOutputAmount, NativeToken, Output, OutputBuilderAmount, OutputId, StorageScore,
StorageScoreParameters, TokenId, TokenScheme,
},
payload::signed_transaction::{TransactionCapabilities, TransactionCapabilityFlag},
protocol::{ProtocolParameters, WorkScore, WorkScoreParameters},
semantic::{SemanticValidationContext, TransactionFailureReason},
semantic::{SemanticValidationContext, StateTransitionError, TransactionFailureReason},
unlock::Unlock,
Error,
};
Expand Down Expand Up @@ -522,81 +522,6 @@ impl WorkScore for FoundryOutput {

impl MinimumOutputAmount for FoundryOutput {}

impl StateTransitionVerifier for FoundryOutput {
fn creation(next_state: &Self, context: &SemanticValidationContext<'_>) -> Result<(), StateTransitionError> {
let account_chain_id = ChainId::from(*next_state.account_address().account_id());

if let (Some(Output::Account(input_account)), Some(Output::Account(output_account))) = (
context.input_chains.get(&account_chain_id),
context.output_chains.get(&account_chain_id),
) {
if input_account.foundry_counter() >= next_state.serial_number()
|| next_state.serial_number() > output_account.foundry_counter()
{
return Err(StateTransitionError::InconsistentFoundrySerialNumber);
}
} else {
return Err(StateTransitionError::MissingAccountForFoundry);
}

let token_id = next_state.token_id();
let output_tokens = context.output_native_tokens.get(&token_id).copied().unwrap_or_default();
let TokenScheme::Simple(ref next_token_scheme) = next_state.token_scheme;

// No native tokens should be referenced prior to the foundry creation.
if context.input_native_tokens.contains_key(&token_id) {
return Err(StateTransitionError::InconsistentNativeTokensFoundryCreation);
}

if output_tokens != next_token_scheme.minted_tokens() || !next_token_scheme.melted_tokens().is_zero() {
return Err(StateTransitionError::InconsistentNativeTokensFoundryCreation);
}

Ok(())
}

fn transition(
current_state: &Self,
next_state: &Self,
context: &SemanticValidationContext<'_>,
) -> Result<(), StateTransitionError> {
Self::transition_inner(
current_state,
next_state,
&context.input_native_tokens,
&context.output_native_tokens,
context.transaction.capabilities(),
)
}

fn destruction(current_state: &Self, context: &SemanticValidationContext<'_>) -> Result<(), StateTransitionError> {
if !context
.transaction
.has_capability(TransactionCapabilityFlag::DestroyFoundryOutputs)
{
return Err(TransactionFailureReason::TransactionCapabilityFoundryDestructionNotAllowed)?;
}

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;

// No native tokens should be referenced after the foundry destruction.
if context.output_native_tokens.contains_key(&token_id) {
return Err(StateTransitionError::InconsistentNativeTokensFoundryDestruction);
}

// This can't underflow as it is known that minted_tokens >= melted_tokens (syntactic rule).
let minted_melted_diff = current_token_scheme.minted_tokens() - current_token_scheme.melted_tokens();

if minted_melted_diff != input_tokens {
return Err(StateTransitionError::InconsistentNativeTokensFoundryDestruction);
}

Ok(())
}
}

impl Packable for FoundryOutput {
type UnpackError = Error;
type UnpackVisitor = ProtocolParameters;
Expand Down
49 changes: 0 additions & 49 deletions sdk/src/types/block/output/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ mod metadata;
mod native_token;
mod output_id;
mod output_id_proof;
mod state_transition;
mod storage_score;
mod token_scheme;

Expand Down Expand Up @@ -43,7 +42,6 @@ pub use self::{
nft::{NftId, NftOutput, NftOutputBuilder},
output_id::OutputId,
output_id_proof::{HashableNode, LeafHash, OutputCommitmentProof, OutputIdProof, ValueHash},
state_transition::{StateTransitionError, StateTransitionVerifier},
storage_score::{StorageScore, StorageScoreParameters},
token_scheme::{SimpleTokenScheme, TokenScheme},
unlock_condition::{UnlockCondition, UnlockConditions},
Expand All @@ -57,7 +55,6 @@ pub(crate) use self::{
use crate::types::block::{
address::Address,
protocol::{CommittableAgeRange, ProtocolParameters, WorkScore, WorkScoreParameters},
semantic::SemanticValidationContext,
slot::SlotIndex,
Error,
};
Expand Down Expand Up @@ -313,52 +310,6 @@ impl Output {
})
}

///
pub fn verify_state_transition(
current_state: Option<&Self>,
next_state: Option<&Self>,
context: &SemanticValidationContext<'_>,
) -> Result<(), StateTransitionError> {
match (current_state, next_state) {
// Creations.
(None, Some(Self::Account(next_state))) => AccountOutput::creation(next_state, context),
(None, Some(Self::Foundry(next_state))) => FoundryOutput::creation(next_state, context),
(None, Some(Self::Nft(next_state))) => NftOutput::creation(next_state, context),
(None, Some(Self::Delegation(next_state))) => DelegationOutput::creation(next_state, context),

// Transitions.
(Some(Self::Basic(current_state)), Some(Self::Account(_next_state))) => {
if !current_state.is_implicit_account() {
Err(StateTransitionError::UnsupportedStateTransition)
} else {
// TODO https://github.com/iotaledger/iota-sdk/issues/1664
Ok(())
}
}
(Some(Self::Account(current_state)), Some(Self::Account(next_state))) => {
AccountOutput::transition(current_state, next_state, context)
}
(Some(Self::Foundry(current_state)), Some(Self::Foundry(next_state))) => {
FoundryOutput::transition(current_state, next_state, context)
}
(Some(Self::Nft(current_state)), Some(Self::Nft(next_state))) => {
NftOutput::transition(current_state, next_state, context)
}
(Some(Self::Delegation(current_state)), Some(Self::Delegation(next_state))) => {
DelegationOutput::transition(current_state, next_state, context)
}

// Destructions.
(Some(Self::Account(current_state)), None) => AccountOutput::destruction(current_state, context),
(Some(Self::Foundry(current_state)), None) => FoundryOutput::destruction(current_state, context),
(Some(Self::Nft(current_state)), None) => NftOutput::destruction(current_state, context),
(Some(Self::Delegation(current_state)), None) => DelegationOutput::destruction(current_state, context),

// Unsupported.
_ => Err(StateTransitionError::UnsupportedStateTransition),
}
}

/// Verifies if a valid storage deposit was made. Each [`Output`] has to have an amount that covers its associated
/// byte cost, given by [`StorageScoreParameters`].
/// If there is a [`StorageDepositReturnUnlockCondition`](unlock_condition::StorageDepositReturnUnlockCondition),
Expand Down
Loading

0 comments on commit d8791f5

Please sign in to comment.