diff --git a/sdk/src/client/stronghold/secret.rs b/sdk/src/client/stronghold/secret.rs index 813fcf07d1..7568cc176d 100644 --- a/sdk/src/client/stronghold/secret.rs +++ b/sdk/src/client/stronghold/secret.rs @@ -583,7 +583,7 @@ mod tests { // Address generation returns an error when the key is cleared. assert!( stronghold_adapter - .generate_ed25519_addresses(IOTA_COIN_TYPE, 0, 0..1, None,) + .generate_ed25519_addresses(IOTA_COIN_TYPE, 0, 0..1, None) .await .is_err() ); diff --git a/sdk/src/types/block/context_input/block_issuance_credit.rs b/sdk/src/types/block/context_input/block_issuance_credit.rs index eedde882b9..fececf43f9 100644 --- a/sdk/src/types/block/context_input/block_issuance_credit.rs +++ b/sdk/src/types/block/context_input/block_issuance_credit.rs @@ -3,7 +3,10 @@ use derive_more::{Display, From}; -use crate::types::block::output::AccountId; +use crate::types::block::{ + output::AccountId, + protocol::{WorkScore, WorkScoreParameters}, +}; /// A Block Issuance Credit (BIC) Context Input provides the VM with context for the value of /// the BIC vector of a specific slot. @@ -25,6 +28,12 @@ impl BlockIssuanceCreditContextInput { } } +impl WorkScore for BlockIssuanceCreditContextInput { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + params.context_input() + } +} + #[cfg(feature = "serde")] mod dto { use serde::{Deserialize, Serialize}; diff --git a/sdk/src/types/block/context_input/commitment.rs b/sdk/src/types/block/context_input/commitment.rs index 23b9bcffe2..c799e7dc3b 100644 --- a/sdk/src/types/block/context_input/commitment.rs +++ b/sdk/src/types/block/context_input/commitment.rs @@ -3,7 +3,10 @@ use derive_more::{Display, From}; -use crate::types::block::slot::SlotCommitmentId; +use crate::types::block::{ + protocol::{WorkScore, WorkScoreParameters}, + slot::SlotCommitmentId, +}; /// A Commitment Context Input references a commitment to a certain slot. #[derive(Clone, Copy, Display, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, From, packable::Packable)] @@ -24,6 +27,12 @@ impl CommitmentContextInput { } } +impl WorkScore for CommitmentContextInput { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + params.context_input() + } +} + #[cfg(feature = "serde")] mod dto { use serde::{Deserialize, Serialize}; diff --git a/sdk/src/types/block/context_input/mod.rs b/sdk/src/types/block/context_input/mod.rs index 016931f09d..c7972d6bea 100644 --- a/sdk/src/types/block/context_input/mod.rs +++ b/sdk/src/types/block/context_input/mod.rs @@ -14,7 +14,10 @@ pub use self::{ block_issuance_credit::BlockIssuanceCreditContextInput, commitment::CommitmentContextInput, reward::RewardContextInput, }; -use crate::types::block::Error; +use crate::types::block::{ + protocol::{WorkScore, WorkScoreParameters}, + Error, +}; /// The maximum number of context inputs of a transaction. pub const CONTEXT_INPUT_COUNT_MAX: u16 = 128; @@ -39,16 +42,6 @@ pub enum ContextInput { Reward(RewardContextInput), } -impl core::fmt::Debug for ContextInput { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - Self::Commitment(input) => input.fmt(f), - Self::BlockIssuanceCredit(input) => input.fmt(f), - Self::Reward(input) => input.fmt(f), - } - } -} - impl ContextInput { /// Returns the context input kind of a `ContextInput`. pub fn kind(&self) -> u8 { @@ -62,6 +55,26 @@ impl ContextInput { crate::def_is_as_opt!(ContextInput: Commitment, BlockIssuanceCredit, Reward); } +impl WorkScore for ContextInput { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + match self { + Self::Commitment(commitment) => commitment.work_score(params), + Self::BlockIssuanceCredit(bic) => bic.work_score(params), + Self::Reward(reward) => reward.work_score(params), + } + } +} + +impl core::fmt::Debug for ContextInput { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Commitment(input) => input.fmt(f), + Self::BlockIssuanceCredit(input) => input.fmt(f), + Self::Reward(input) => input.fmt(f), + } + } +} + #[cfg(test)] mod tests { use pretty_assertions::assert_eq; diff --git a/sdk/src/types/block/context_input/reward.rs b/sdk/src/types/block/context_input/reward.rs index 12fdb58f5c..347cdaa350 100644 --- a/sdk/src/types/block/context_input/reward.rs +++ b/sdk/src/types/block/context_input/reward.rs @@ -4,7 +4,10 @@ use packable::bounded::BoundedU16; use super::CONTEXT_INPUT_COUNT_RANGE; -use crate::types::block::Error; +use crate::types::block::{ + protocol::{WorkScore, WorkScoreParameters}, + Error, +}; pub(crate) type RewardContextInputIndex = BoundedU16<{ *CONTEXT_INPUT_COUNT_RANGE.start() }, { *CONTEXT_INPUT_COUNT_RANGE.end() }>; @@ -29,6 +32,12 @@ impl RewardContextInput { } } +impl WorkScore for RewardContextInput { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + params.context_input() + } +} + impl core::fmt::Display for RewardContextInput { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "RewardContextInput({})", self.index()) diff --git a/sdk/src/types/block/core/basic.rs b/sdk/src/types/block/core/basic.rs index f31aeefb6f..8a7d63f66d 100644 --- a/sdk/src/types/block/core/basic.rs +++ b/sdk/src/types/block/core/basic.rs @@ -6,7 +6,7 @@ use packable::Packable; use crate::types::block::{ core::{parent::verify_parents_sets, BlockBody, Parents}, payload::{OptionalPayload, Payload}, - protocol::ProtocolParameters, + protocol::{ProtocolParameters, WorkScore, WorkScoreParameters}, Error, }; @@ -154,6 +154,17 @@ impl BasicBlockBody { } } +impl WorkScore for BasicBlockBody { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + params.block() + + self + .payload + .as_ref() + .map(|payload| payload.work_score(params)) + .unwrap_or(0) + } +} + fn verify_basic_block_body( basic_block_body: &BasicBlockBody, _: &ProtocolParameters, diff --git a/sdk/src/types/block/core/block.rs b/sdk/src/types/block/core/block.rs index b948107147..146ad4dadc 100644 --- a/sdk/src/types/block/core/block.rs +++ b/sdk/src/types/block/core/block.rs @@ -17,7 +17,7 @@ use crate::types::block::{ block_id::{BlockHash, BlockId}, core::{BasicBlockBody, ValidationBlockBody}, output::AccountId, - protocol::ProtocolParameters, + protocol::{ProtocolParameters, WorkScore, WorkScoreParameters}, signature::Signature, slot::{SlotCommitmentId, SlotIndex}, BlockBody, Error, @@ -110,6 +110,8 @@ impl BlockHeader { } } +impl WorkScore for BlockHeader {} + impl Packable for BlockHeader { type UnpackError = Error; type UnpackVisitor = ProtocolParameters; @@ -291,6 +293,12 @@ impl Block { } } +impl WorkScore for Block { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + self.header.work_score(params) + self.body.work_score(params) + self.signature.work_score(params) + } +} + impl Packable for Block { type UnpackError = Error; type UnpackVisitor = ProtocolParameters; diff --git a/sdk/src/types/block/core/mod.rs b/sdk/src/types/block/core/mod.rs index 316b75d75f..5b16fb9b0a 100644 --- a/sdk/src/types/block/core/mod.rs +++ b/sdk/src/types/block/core/mod.rs @@ -19,7 +19,7 @@ pub use self::{ validation::{ValidationBlockBody, ValidationBlockBodyBuilder}, }; use crate::types::block::{ - protocol::{ProtocolParameters, ProtocolParametersHash}, + protocol::{ProtocolParameters, ProtocolParametersHash, WorkScore, WorkScoreParameters}, Error, }; @@ -102,6 +102,15 @@ impl BlockBody { } } +impl WorkScore for BlockBody { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + match self { + Self::Basic(basic) => basic.work_score(params), + Self::Validation(validation) => validation.work_score(params), + } + } +} + #[cfg(feature = "serde")] pub(crate) mod dto { use alloc::format; diff --git a/sdk/src/types/block/core/validation.rs b/sdk/src/types/block/core/validation.rs index 43a9a5700a..d96d4c9af1 100644 --- a/sdk/src/types/block/core/validation.rs +++ b/sdk/src/types/block/core/validation.rs @@ -5,7 +5,7 @@ use packable::Packable; use crate::types::block::{ core::{parent::verify_parents_sets, BlockBody, Parents}, - protocol::{ProtocolParameters, ProtocolParametersHash}, + protocol::{ProtocolParameters, ProtocolParametersHash, WorkScore}, Error, }; @@ -160,6 +160,8 @@ impl ValidationBlockBody { } } +impl WorkScore for ValidationBlockBody {} + fn verify_protocol_parameters_hash( hash: &ProtocolParametersHash, params: &ProtocolParameters, diff --git a/sdk/src/types/block/error.rs b/sdk/src/types/block/error.rs index a2782f12b9..76698b2943 100644 --- a/sdk/src/types/block/error.rs +++ b/sdk/src/types/block/error.rs @@ -20,7 +20,10 @@ use crate::types::block::{ unlock_condition::UnlockConditionCount, AccountId, AnchorId, ChainId, MetadataFeatureLength, NativeTokenCount, NftId, OutputIndex, TagFeatureLength, }, - payload::{ContextInputCount, InputCount, OutputCount, TagLength, TaggedDataLength}, + payload::{ + tagged_data::{TagLength, TaggedDataLength}, + ContextInputCount, InputCount, OutputCount, + }, protocol::ProtocolParametersHash, unlock::{UnlockCount, UnlockIndex, UnlocksCount}, }; diff --git a/sdk/src/types/block/input/mod.rs b/sdk/src/types/block/input/mod.rs index cf65e8b967..0c586bee7b 100644 --- a/sdk/src/types/block/input/mod.rs +++ b/sdk/src/types/block/input/mod.rs @@ -8,7 +8,10 @@ use core::ops::RangeInclusive; use derive_more::From; pub use self::utxo::UtxoInput; -use crate::types::block::Error; +use crate::types::block::{ + protocol::{WorkScore, WorkScoreParameters}, + Error, +}; /// The maximum number of inputs of a transaction. pub const INPUT_COUNT_MAX: u16 = 128; @@ -30,6 +33,14 @@ pub enum Input { Utxo(UtxoInput), } +impl WorkScore for Input { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + match self { + Self::Utxo(utxo) => utxo.work_score(params), + } + } +} + impl core::fmt::Debug for Input { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { diff --git a/sdk/src/types/block/input/utxo.rs b/sdk/src/types/block/input/utxo.rs index 613effb9bc..24f4bcb8ee 100644 --- a/sdk/src/types/block/input/utxo.rs +++ b/sdk/src/types/block/input/utxo.rs @@ -5,7 +5,12 @@ use core::str::FromStr; use derive_more::From; -use crate::types::block::{output::OutputId, payload::signed_transaction::TransactionId, Error}; +use crate::types::block::{ + output::OutputId, + payload::signed_transaction::TransactionId, + protocol::{WorkScore, WorkScoreParameters}, + Error, +}; /// Represents an input referencing an output. #[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, From, packable::Packable)] @@ -26,6 +31,12 @@ impl UtxoInput { } } +impl WorkScore for UtxoInput { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + params.input() + } +} + impl FromStr for UtxoInput { type Err = Error; diff --git a/sdk/src/types/block/mana/allotment.rs b/sdk/src/types/block/mana/allotment.rs index 1069f9aace..dc9295c11d 100644 --- a/sdk/src/types/block/mana/allotment.rs +++ b/sdk/src/types/block/mana/allotment.rs @@ -3,7 +3,11 @@ use packable::Packable; -use crate::types::block::{output::AccountId, protocol::ProtocolParameters, Error}; +use crate::types::block::{ + output::AccountId, + protocol::{ProtocolParameters, WorkScore, WorkScoreParameters}, + Error, +}; /// An allotment of Mana which will be added upon commitment of the slot in which the containing transaction was issued, /// in the form of Block Issuance Credits to the account. @@ -16,18 +20,6 @@ pub struct ManaAllotment { pub(crate) mana: u64, } -impl PartialOrd for ManaAllotment { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for ManaAllotment { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.account_id.cmp(&other.account_id) - } -} - impl ManaAllotment { pub fn new(account_id: AccountId, mana: u64, protocol_params: &ProtocolParameters) -> Result { verify_mana::(&mana, protocol_params)?; @@ -44,6 +36,24 @@ impl ManaAllotment { } } +impl PartialOrd for ManaAllotment { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for ManaAllotment { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.account_id.cmp(&other.account_id) + } +} + +impl WorkScore for ManaAllotment { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + params.allotment() + } +} + fn verify_mana(mana: &u64, params: &ProtocolParameters) -> Result<(), Error> { if VERIFY && *mana > params.mana_parameters().max_mana() { return Err(Error::InvalidManaValue(*mana)); diff --git a/sdk/src/types/block/output/account.rs b/sdk/src/types/block/output/account.rs index 28bb146368..7ed6167793 100644 --- a/sdk/src/types/block/output/account.rs +++ b/sdk/src/types/block/output/account.rs @@ -20,7 +20,7 @@ use crate::types::block::{ StateTransitionVerifier, StorageScore, StorageScoreParameters, }, payload::signed_transaction::TransactionCapabilityFlag, - protocol::ProtocolParameters, + protocol::{ProtocolParameters, WorkScore, WorkScoreParameters}, semantic::{SemanticValidationContext, TransactionFailureReason}, unlock::Unlock, Error, @@ -505,6 +505,15 @@ impl StorageScore for AccountOutput { } } +impl WorkScore for AccountOutput { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + params.output() + + self.unlock_conditions.work_score(params) + + self.features.work_score(params) + + self.immutable_features.work_score(params) + } +} + impl MinimumOutputAmount for AccountOutput {} impl Packable for AccountOutput { diff --git a/sdk/src/types/block/output/anchor.rs b/sdk/src/types/block/output/anchor.rs index e71aa220cf..ddf81a8a36 100644 --- a/sdk/src/types/block/output/anchor.rs +++ b/sdk/src/types/block/output/anchor.rs @@ -20,7 +20,7 @@ use crate::types::block::{ StateTransitionVerifier, StorageScore, StorageScoreParameters, }, payload::signed_transaction::TransactionCapabilityFlag, - protocol::ProtocolParameters, + protocol::{ProtocolParameters, WorkScore, WorkScoreParameters}, semantic::{SemanticValidationContext, TransactionFailureReason}, unlock::Unlock, Error, @@ -505,6 +505,15 @@ impl StorageScore for AnchorOutput { } } +impl WorkScore for AnchorOutput { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + params.output() + + self.unlock_conditions.work_score(params) + + self.features.work_score(params) + + self.immutable_features.work_score(params) + } +} + impl MinimumOutputAmount for AnchorOutput {} impl StateTransitionVerifier for AnchorOutput { diff --git a/sdk/src/types/block/output/basic.rs b/sdk/src/types/block/output/basic.rs index 0f310d76f4..05fd6ee74b 100644 --- a/sdk/src/types/block/output/basic.rs +++ b/sdk/src/types/block/output/basic.rs @@ -15,7 +15,7 @@ use crate::types::block::{ }, MinimumOutputAmount, NativeToken, Output, OutputBuilderAmount, OutputId, StorageScore, StorageScoreParameters, }, - protocol::ProtocolParameters, + protocol::{ProtocolParameters, WorkScore, WorkScoreParameters}, semantic::{SemanticValidationContext, TransactionFailureReason}, unlock::Unlock, Error, @@ -368,6 +368,12 @@ impl StorageScore for BasicOutput { } } +impl WorkScore for BasicOutput { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + params.output() + self.unlock_conditions.work_score(params) + self.features.work_score(params) + } +} + impl MinimumOutputAmount for BasicOutput {} fn verify_unlock_conditions(unlock_conditions: &UnlockConditions) -> Result<(), Error> { diff --git a/sdk/src/types/block/output/delegation.rs b/sdk/src/types/block/output/delegation.rs index ed5974df31..61bdb1464a 100644 --- a/sdk/src/types/block/output/delegation.rs +++ b/sdk/src/types/block/output/delegation.rs @@ -13,7 +13,7 @@ use crate::types::block::{ MinimumOutputAmount, Output, OutputBuilderAmount, OutputId, StateTransitionError, StateTransitionVerifier, StorageScore, StorageScoreParameters, }, - protocol::ProtocolParameters, + protocol::{ProtocolParameters, WorkScore, WorkScoreParameters}, semantic::{SemanticValidationContext, TransactionFailureReason}, slot::EpochIndex, unlock::Unlock, @@ -390,6 +390,12 @@ impl StorageScore for DelegationOutput { } } +impl WorkScore for DelegationOutput { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + params.output() + self.unlock_conditions.work_score(params) + } +} + impl MinimumOutputAmount for DelegationOutput {} fn verify_validator_address(validator_address: &AccountAddress) -> Result<(), Error> { diff --git a/sdk/src/types/block/output/feature/block_issuer.rs b/sdk/src/types/block/output/feature/block_issuer.rs index 545d1b4f13..114226b14c 100644 --- a/sdk/src/types/block/output/feature/block_issuer.rs +++ b/sdk/src/types/block/output/feature/block_issuer.rs @@ -18,6 +18,7 @@ use packable::{ use crate::types::block::{ output::{StorageScore, StorageScoreParameters}, + protocol::{WorkScore, WorkScoreParameters}, slot::SlotIndex, Error, }; @@ -244,6 +245,12 @@ impl StorageScore for BlockIssuerFeature { } } +impl WorkScore for BlockIssuerFeature { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + params.block_issuer() + } +} + #[cfg(feature = "serde")] mod dto { use alloc::{string::String, vec::Vec}; diff --git a/sdk/src/types/block/output/feature/issuer.rs b/sdk/src/types/block/output/feature/issuer.rs index fe96f195f0..fe929e0006 100644 --- a/sdk/src/types/block/output/feature/issuer.rs +++ b/sdk/src/types/block/output/feature/issuer.rs @@ -6,6 +6,7 @@ use derive_more::From; use crate::types::block::{ address::Address, output::{StorageScore, StorageScoreParameters}, + protocol::WorkScore, }; /// Identifies the validated issuer of the UTXO state machine. @@ -35,6 +36,8 @@ impl StorageScore for IssuerFeature { } } +impl WorkScore for IssuerFeature {} + #[cfg(feature = "serde")] mod dto { use serde::{Deserialize, Serialize}; diff --git a/sdk/src/types/block/output/feature/metadata.rs b/sdk/src/types/block/output/feature/metadata.rs index b198e8c9f2..434235ff5b 100644 --- a/sdk/src/types/block/output/feature/metadata.rs +++ b/sdk/src/types/block/output/feature/metadata.rs @@ -6,7 +6,7 @@ use core::{ops::RangeInclusive, str::FromStr}; use packable::{bounded::BoundedU16, prefix::BoxedSlicePrefix}; -use crate::types::block::{output::StorageScore, Error}; +use crate::types::block::{output::StorageScore, protocol::WorkScore, Error}; pub(crate) type MetadataFeatureLength = BoundedU16<{ *MetadataFeature::LENGTH_RANGE.start() }, { *MetadataFeature::LENGTH_RANGE.end() }>; @@ -19,8 +19,29 @@ pub struct MetadataFeature( pub(crate) BoxedSlicePrefix, ); +impl MetadataFeature { + /// The [`Feature`](crate::types::block::output::Feature) kind of [`MetadataFeature`]. + pub const KIND: u8 = 2; + /// Valid lengths for a [`MetadataFeature`]. + pub const LENGTH_RANGE: RangeInclusive = 1..=8192; + + /// Creates a new [`MetadataFeature`]. + #[inline(always)] + pub fn new(data: impl Into>) -> Result { + Self::try_from(data.into()) + } + + /// Returns the data. + #[inline(always)] + pub fn data(&self) -> &[u8] { + &self.0 + } +} + impl StorageScore for MetadataFeature {} +impl WorkScore for MetadataFeature {} + macro_rules! impl_from_vec { ($type:ty) => { impl TryFrom<$type> for MetadataFeature { @@ -68,25 +89,6 @@ impl FromStr for MetadataFeature { } } -impl MetadataFeature { - /// The [`Feature`](crate::types::block::output::Feature) kind of [`MetadataFeature`]. - pub const KIND: u8 = 2; - /// Valid lengths for a [`MetadataFeature`]. - pub const LENGTH_RANGE: RangeInclusive = 1..=8192; - - /// Creates a new [`MetadataFeature`]. - #[inline(always)] - pub fn new(data: impl Into>) -> Result { - Self::try_from(data.into()) - } - - /// Returns the data. - #[inline(always)] - pub fn data(&self) -> &[u8] { - &self.0 - } -} - impl core::fmt::Display for MetadataFeature { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{}", prefix_hex::encode(self.data())) diff --git a/sdk/src/types/block/output/feature/mod.rs b/sdk/src/types/block/output/feature/mod.rs index 8f3067fcf7..bcff97711b 100644 --- a/sdk/src/types/block/output/feature/mod.rs +++ b/sdk/src/types/block/output/feature/mod.rs @@ -32,6 +32,7 @@ pub use self::{ }; use crate::types::block::{ output::{StorageScore, StorageScoreParameters}, + protocol::{WorkScore, WorkScoreParameters}, Error, }; @@ -79,13 +80,27 @@ impl Ord for Feature { impl StorageScore for Feature { fn storage_score(&self, params: StorageScoreParameters) -> u64 { match self { - Self::Sender(feature) => feature.storage_score(params), - Self::Issuer(feature) => feature.storage_score(params), - Self::Metadata(feature) => feature.storage_score(params), - Self::Tag(feature) => feature.storage_score(params), - Self::NativeToken(feature) => feature.storage_score(params), - Self::BlockIssuer(feature) => feature.storage_score(params), - Self::Staking(feature) => feature.storage_score(params), + Self::Sender(sender) => sender.storage_score(params), + Self::Issuer(issuer) => issuer.storage_score(params), + Self::Metadata(metadata) => metadata.storage_score(params), + Self::Tag(tag) => tag.storage_score(params), + Self::NativeToken(native_token) => native_token.storage_score(params), + Self::BlockIssuer(block_issuer) => block_issuer.storage_score(params), + Self::Staking(staking) => staking.storage_score(params), + } + } +} + +impl WorkScore for Feature { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + match self { + Self::Sender(sender) => sender.work_score(params), + Self::Issuer(issuer) => issuer.work_score(params), + Self::Metadata(metadata) => metadata.work_score(params), + Self::Tag(tag) => tag.work_score(params), + Self::NativeToken(native_token) => native_token.work_score(params), + Self::BlockIssuer(block_issuer) => block_issuer.work_score(params), + Self::Staking(staking) => staking.work_score(params), } } } diff --git a/sdk/src/types/block/output/feature/native_token.rs b/sdk/src/types/block/output/feature/native_token.rs index 191fec2070..932f536e01 100644 --- a/sdk/src/types/block/output/feature/native_token.rs +++ b/sdk/src/types/block/output/feature/native_token.rs @@ -3,7 +3,10 @@ use derive_more::{Deref, From}; -use crate::types::block::output::{NativeToken, StorageScore}; +use crate::types::block::{ + output::{NativeToken, StorageScore}, + protocol::{WorkScore, WorkScoreParameters}, +}; #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Deref, From, packable::Packable)] pub struct NativeTokenFeature(NativeToken); @@ -25,6 +28,12 @@ impl NativeTokenFeature { impl StorageScore for NativeTokenFeature {} +impl WorkScore for NativeTokenFeature { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + self.0.work_score(params) + } +} + #[cfg(feature = "serde")] mod dto { use primitive_types::U256; diff --git a/sdk/src/types/block/output/feature/sender.rs b/sdk/src/types/block/output/feature/sender.rs index f25ab1d97c..c53e70b7d7 100644 --- a/sdk/src/types/block/output/feature/sender.rs +++ b/sdk/src/types/block/output/feature/sender.rs @@ -6,6 +6,7 @@ use derive_more::From; use crate::types::block::{ address::Address, output::{storage_score::StorageScoreParameters, StorageScore}, + protocol::WorkScore, }; /// Identifies the validated sender of an output. @@ -35,6 +36,8 @@ impl StorageScore for SenderFeature { } } +impl WorkScore for SenderFeature {} + #[cfg(feature = "serde")] mod dto { use serde::{Deserialize, Serialize}; diff --git a/sdk/src/types/block/output/feature/staking.rs b/sdk/src/types/block/output/feature/staking.rs index 34d8da0094..c56b71b838 100644 --- a/sdk/src/types/block/output/feature/staking.rs +++ b/sdk/src/types/block/output/feature/staking.rs @@ -3,6 +3,7 @@ use crate::types::block::{ output::{StorageScore, StorageScoreParameters}, + protocol::{WorkScore, WorkScoreParameters}, slot::EpochIndex, }; @@ -65,6 +66,12 @@ impl StorageScore for StakingFeature { } } +impl WorkScore for StakingFeature { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + params.staking() + } +} + #[cfg(feature = "serde")] mod dto { use serde::{Deserialize, Serialize}; diff --git a/sdk/src/types/block/output/feature/tag.rs b/sdk/src/types/block/output/feature/tag.rs index 01c84d79cd..e150834d6f 100644 --- a/sdk/src/types/block/output/feature/tag.rs +++ b/sdk/src/types/block/output/feature/tag.rs @@ -6,7 +6,7 @@ use core::ops::RangeInclusive; use packable::{bounded::BoundedU8, prefix::BoxedSlicePrefix}; -use crate::types::block::{output::StorageScore, Error}; +use crate::types::block::{output::StorageScore, protocol::WorkScore, Error}; pub(crate) type TagFeatureLength = BoundedU8<{ *TagFeature::LENGTH_RANGE.start() }, { *TagFeature::LENGTH_RANGE.end() }>; @@ -19,24 +19,6 @@ pub struct TagFeature( pub(crate) BoxedSlicePrefix, ); -impl StorageScore for TagFeature {} - -impl TryFrom> for TagFeature { - type Error = Error; - - fn try_from(tag: Vec) -> Result { - tag.into_boxed_slice().try_into() - } -} - -impl TryFrom> for TagFeature { - type Error = Error; - - fn try_from(tag: Box<[u8]>) -> Result { - tag.try_into().map(Self).map_err(Error::InvalidTagFeatureLength) - } -} - impl TagFeature { /// The [`Feature`](crate::types::block::output::Feature) kind of an [`TagFeature`]. pub const KIND: u8 = 4; @@ -56,6 +38,26 @@ impl TagFeature { } } +impl StorageScore for TagFeature {} + +impl WorkScore for TagFeature {} + +impl TryFrom> for TagFeature { + type Error = Error; + + fn try_from(tag: Vec) -> Result { + tag.into_boxed_slice().try_into() + } +} + +impl TryFrom> for TagFeature { + type Error = Error; + + fn try_from(tag: Box<[u8]>) -> Result { + tag.try_into().map(Self).map_err(Error::InvalidTagFeatureLength) + } +} + impl core::fmt::Display for TagFeature { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{}", prefix_hex::encode(self.tag())) diff --git a/sdk/src/types/block/output/foundry.rs b/sdk/src/types/block/output/foundry.rs index 0bec812f32..218c526979 100644 --- a/sdk/src/types/block/output/foundry.rs +++ b/sdk/src/types/block/output/foundry.rs @@ -22,7 +22,7 @@ use crate::types::block::{ StateTransitionVerifier, StorageScore, StorageScoreParameters, TokenId, TokenScheme, }, payload::signed_transaction::{TransactionCapabilities, TransactionCapabilityFlag}, - protocol::ProtocolParameters, + protocol::{ProtocolParameters, WorkScore, WorkScoreParameters}, semantic::{SemanticValidationContext, TransactionFailureReason}, unlock::Unlock, Error, @@ -510,6 +510,16 @@ impl StorageScore for FoundryOutput { } } +impl WorkScore for FoundryOutput { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + params.output() + + self.token_scheme.work_score(params) + + self.unlock_conditions.work_score(params) + + self.features.work_score(params) + + self.immutable_features.work_score(params) + } +} + impl MinimumOutputAmount for FoundryOutput {} impl StateTransitionVerifier for FoundryOutput { diff --git a/sdk/src/types/block/output/mod.rs b/sdk/src/types/block/output/mod.rs index 1a43ccc664..ee4640877e 100644 --- a/sdk/src/types/block/output/mod.rs +++ b/sdk/src/types/block/output/mod.rs @@ -52,8 +52,13 @@ pub(crate) use self::{ output_id::OutputIndex, unlock_condition::AddressUnlockCondition, }; -use super::protocol::ProtocolParameters; -use crate::types::block::{address::Address, semantic::SemanticValidationContext, slot::SlotIndex, Error}; +use crate::types::block::{ + address::Address, + protocol::{ProtocolParameters, WorkScore, WorkScoreParameters}, + semantic::SemanticValidationContext, + slot::SlotIndex, + Error, +}; /// The maximum number of outputs of a transaction. pub const OUTPUT_COUNT_MAX: u16 = 128; @@ -396,12 +401,25 @@ impl Output { impl StorageScore for Output { fn storage_score(&self, params: StorageScoreParameters) -> u64 { match self { - Self::Basic(o) => o.storage_score(params), - Self::Account(o) => o.storage_score(params), - Self::Anchor(o) => o.storage_score(params), - Self::Foundry(o) => o.storage_score(params), - Self::Nft(o) => o.storage_score(params), - Self::Delegation(o) => o.storage_score(params), + Self::Basic(basic) => basic.storage_score(params), + Self::Account(account) => account.storage_score(params), + Self::Anchor(anchor) => anchor.storage_score(params), + Self::Foundry(foundry) => foundry.storage_score(params), + Self::Nft(nft) => nft.storage_score(params), + Self::Delegation(delegation) => delegation.storage_score(params), + } + } +} + +impl WorkScore for Output { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + match self { + Self::Basic(basic) => basic.work_score(params), + Self::Account(account) => account.work_score(params), + Self::Anchor(anchor) => anchor.work_score(params), + Self::Foundry(foundry) => foundry.work_score(params), + Self::Nft(nft) => nft.work_score(params), + Self::Delegation(delegation) => delegation.work_score(params), } } } diff --git a/sdk/src/types/block/output/native_token.rs b/sdk/src/types/block/output/native_token.rs index 5aee716319..6e48df309b 100644 --- a/sdk/src/types/block/output/native_token.rs +++ b/sdk/src/types/block/output/native_token.rs @@ -12,7 +12,11 @@ use iterator_sorted::is_unique_sorted; use packable::{bounded::BoundedU8, prefix::BoxedSlicePrefix, Packable}; use primitive_types::U256; -use crate::types::block::{output::FoundryId, Error}; +use crate::types::block::{ + output::FoundryId, + protocol::{WorkScore, WorkScoreParameters}, + Error, +}; crate::impl_id!( /// Unique identifier of a [`NativeToken`](crate::types::block::output::NativeToken). @@ -66,6 +70,12 @@ impl NativeToken { } } +impl WorkScore for NativeToken { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + params.native_token() + } +} + impl PartialOrd for NativeToken { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) diff --git a/sdk/src/types/block/output/nft.rs b/sdk/src/types/block/output/nft.rs index 0a48fe21ba..f8462da2a9 100644 --- a/sdk/src/types/block/output/nft.rs +++ b/sdk/src/types/block/output/nft.rs @@ -22,7 +22,7 @@ use crate::types::block::{ StateTransitionVerifier, StorageScore, StorageScoreParameters, }, payload::signed_transaction::TransactionCapabilityFlag, - protocol::ProtocolParameters, + protocol::{ProtocolParameters, WorkScore, WorkScoreParameters}, semantic::{SemanticValidationContext, TransactionFailureReason}, unlock::Unlock, Error, @@ -451,6 +451,15 @@ impl StorageScore for NftOutput { } } +impl WorkScore for NftOutput { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + params.output() + + self.unlock_conditions.work_score(params) + + self.features.work_score(params) + + self.immutable_features.work_score(params) + } +} + impl MinimumOutputAmount for NftOutput {} impl StateTransitionVerifier for NftOutput { diff --git a/sdk/src/types/block/output/token_scheme/mod.rs b/sdk/src/types/block/output/token_scheme/mod.rs index 25ff493749..91303f0ea1 100644 --- a/sdk/src/types/block/output/token_scheme/mod.rs +++ b/sdk/src/types/block/output/token_scheme/mod.rs @@ -4,7 +4,10 @@ mod simple; pub use self::simple::SimpleTokenScheme; -use crate::types::block::Error; +use crate::types::block::{ + protocol::{WorkScore, WorkScoreParameters}, + Error, +}; /// #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, derive_more::From, packable::Packable)] @@ -35,3 +38,11 @@ impl TokenScheme { crate::def_is_as_opt!(TokenScheme: Simple); } + +impl WorkScore for TokenScheme { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + match self { + Self::Simple(simple) => simple.work_score(params), + } + } +} diff --git a/sdk/src/types/block/output/token_scheme/simple.rs b/sdk/src/types/block/output/token_scheme/simple.rs index 1a971d91c6..328adf80c5 100644 --- a/sdk/src/types/block/output/token_scheme/simple.rs +++ b/sdk/src/types/block/output/token_scheme/simple.rs @@ -4,7 +4,10 @@ use packable::Packable; use primitive_types::U256; -use crate::types::block::Error; +use crate::types::block::{ + protocol::{WorkScore, WorkScoreParameters}, + Error, +}; /// #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Packable)] @@ -70,6 +73,12 @@ impl SimpleTokenScheme { } } +impl WorkScore for SimpleTokenScheme { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + params.native_token() + } +} + #[inline] fn verify_simple_token_scheme(token_scheme: &SimpleTokenScheme) -> Result<(), Error> { if VERIFY diff --git a/sdk/src/types/block/output/unlock_condition/mod.rs b/sdk/src/types/block/output/unlock_condition/mod.rs index 0c3e2d3d70..3791fcdf49 100644 --- a/sdk/src/types/block/output/unlock_condition/mod.rs +++ b/sdk/src/types/block/output/unlock_condition/mod.rs @@ -26,7 +26,7 @@ pub use self::{ use crate::types::block::{ address::Address, output::{StorageScore, StorageScoreParameters}, - protocol::ProtocolParameters, + protocol::{ProtocolParameters, WorkScore}, slot::SlotIndex, Error, }; @@ -85,16 +85,19 @@ impl StorageScore for UnlockCondition { } } +// TODO: check with TIP +impl WorkScore for UnlockCondition {} + impl core::fmt::Debug for UnlockCondition { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - Self::Address(unlock_condition) => unlock_condition.fmt(f), - Self::StorageDepositReturn(unlock_condition) => unlock_condition.fmt(f), - Self::Timelock(unlock_condition) => unlock_condition.fmt(f), - Self::Expiration(unlock_condition) => unlock_condition.fmt(f), - Self::StateControllerAddress(unlock_condition) => unlock_condition.fmt(f), - Self::GovernorAddress(unlock_condition) => unlock_condition.fmt(f), - Self::ImmutableAccountAddress(unlock_condition) => unlock_condition.fmt(f), + Self::Address(uc) => uc.fmt(f), + Self::StorageDepositReturn(uc) => uc.fmt(f), + Self::Timelock(uc) => uc.fmt(f), + Self::Expiration(uc) => uc.fmt(f), + Self::StateControllerAddress(uc) => uc.fmt(f), + Self::GovernorAddress(uc) => uc.fmt(f), + Self::ImmutableAccountAddress(uc) => uc.fmt(f), } } } @@ -304,7 +307,7 @@ impl UnlockConditions { impl StorageScore for UnlockConditions { fn storage_score(&self, params: StorageScoreParameters) -> u64 { - self.iter().map(|uc| uc.storage_score(params)).sum::() + self.iter().map(|uc| uc.storage_score(params)).sum() } } diff --git a/sdk/src/types/block/payload/candidacy_announcement.rs b/sdk/src/types/block/payload/candidacy_announcement.rs index 87313d9bd2..3c349bba04 100644 --- a/sdk/src/types/block/payload/candidacy_announcement.rs +++ b/sdk/src/types/block/payload/candidacy_announcement.rs @@ -1,7 +1,9 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use packable::Packable; +use packable::{Packable, PackableExt}; + +use crate::types::block::protocol::{WorkScore, WorkScoreParameters}; /// A payload which is used to indicate candidacy for committee selection for the next epoch. #[derive(Clone, Debug, Eq, PartialEq, Packable)] @@ -11,3 +13,11 @@ impl CandidacyAnnouncementPayload { /// The [`Payload`](crate::types::block::payload::Payload) kind of a [`CandidacyAnnouncementPayload`]. pub const KIND: u8 = 2; } + +// # TODO: check with TIP +impl WorkScore for CandidacyAnnouncementPayload { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + // 1 byte for the payload kind + (1 + self.packed_len()) as u32 * params.data_byte() + } +} diff --git a/sdk/src/types/block/payload/mod.rs b/sdk/src/types/block/payload/mod.rs index b20b03cfe4..f72f633450 100644 --- a/sdk/src/types/block/payload/mod.rs +++ b/sdk/src/types/block/payload/mod.rs @@ -18,15 +18,15 @@ use packable::{ Packable, PackableExt, }; +pub(crate) use self::signed_transaction::{ContextInputCount, InputCount, OutputCount}; pub use self::{ candidacy_announcement::CandidacyAnnouncementPayload, signed_transaction::SignedTransactionPayload, tagged_data::TaggedDataPayload, }; -pub(crate) use self::{ - signed_transaction::{ContextInputCount, InputCount, OutputCount}, - tagged_data::{TagLength, TaggedDataLength}, +use crate::types::block::{ + protocol::{ProtocolParameters, WorkScore, WorkScoreParameters}, + Error, }; -use crate::types::block::{protocol::ProtocolParameters, Error}; /// A generic payload that can represent different types defining block payloads. #[derive(Clone, Eq, PartialEq, From, Packable)] @@ -77,7 +77,17 @@ impl Payload { } } - crate::def_is_as_opt!(Payload: SignedTransaction, TaggedData); + crate::def_is_as_opt!(Payload: TaggedData, SignedTransaction, CandidacyAnnouncement); +} + +impl WorkScore for Payload { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + match self { + Self::TaggedData(tagged_data) => tagged_data.work_score(params), + Self::SignedTransaction(signed_transaction) => signed_transaction.work_score(params), + Self::CandidacyAnnouncement(candidacy_announcement) => candidacy_announcement.work_score(params), + } + } } /// Representation of an optional [`Payload`]. diff --git a/sdk/src/types/block/payload/signed_transaction/mod.rs b/sdk/src/types/block/payload/signed_transaction/mod.rs index a7cdc0a510..41a983c453 100644 --- a/sdk/src/types/block/payload/signed_transaction/mod.rs +++ b/sdk/src/types/block/payload/signed_transaction/mod.rs @@ -6,14 +6,18 @@ mod transaction; mod transaction_id; -use packable::{error::UnpackError, packer::Packer, unpacker::Unpacker, Packable}; +use packable::{error::UnpackError, packer::Packer, unpacker::Unpacker, Packable, PackableExt}; pub(crate) use self::transaction::{ContextInputCount, InputCount, OutputCount}; pub use self::{ transaction::{Transaction, TransactionBuilder, TransactionCapabilities, TransactionCapabilityFlag}, transaction_id::{TransactionHash, TransactionId, TransactionSigningHash}, }; -use crate::types::block::{protocol::ProtocolParameters, unlock::Unlocks, Error}; +use crate::types::block::{ + protocol::{ProtocolParameters, WorkScore, WorkScoreParameters}, + unlock::Unlocks, + Error, +}; /// A signed transaction to move funds. #[derive(Clone, Debug, Eq, PartialEq)] @@ -44,6 +48,15 @@ impl SignedTransactionPayload { } } +impl WorkScore for SignedTransactionPayload { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + // 1 byte for the payload kind + (1 + self.packed_len() as u32) * params.data_byte() + + self.transaction().work_score(params) + + self.unlocks().work_score(params) + } +} + impl Packable for SignedTransactionPayload { type UnpackError = Error; type UnpackVisitor = ProtocolParameters; diff --git a/sdk/src/types/block/payload/signed_transaction/transaction.rs b/sdk/src/types/block/payload/signed_transaction/transaction.rs index 2682350a3b..c9e6842add 100644 --- a/sdk/src/types/block/payload/signed_transaction/transaction.rs +++ b/sdk/src/types/block/payload/signed_transaction/transaction.rs @@ -18,7 +18,7 @@ use crate::{ signed_transaction::{TransactionHash, TransactionId, TransactionSigningHash}, OptionalPayload, Payload, }, - protocol::ProtocolParameters, + protocol::{ProtocolParameters, WorkScore, WorkScoreParameters}, slot::SlotIndex, Error, }, @@ -261,7 +261,7 @@ impl Transaction { } /// Returns the [`ManaAllotment`]s of a [`Transaction`]. - pub fn mana_allotments(&self) -> &[ManaAllotment] { + pub fn allotments(&self) -> &[ManaAllotment] { &self.allotments } @@ -324,6 +324,15 @@ impl Transaction { } } +impl WorkScore for Transaction { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + self.context_inputs().work_score(params) + + self.inputs().work_score(params) + + self.allotments().work_score(params) + + self.outputs().work_score(params) + } +} + fn verify_network_id(network_id: &u64, visitor: &ProtocolParameters) -> Result<(), Error> { if VERIFY { let expected = visitor.network_id(); @@ -553,7 +562,7 @@ pub(crate) mod dto { creation_slot: value.creation_slot(), context_inputs: value.context_inputs().to_vec(), inputs: value.inputs().to_vec(), - allotments: value.mana_allotments().iter().map(Into::into).collect(), + allotments: value.allotments().iter().map(Into::into).collect(), capabilities: value.capabilities().clone(), payload: match value.payload() { Some(p @ Payload::TaggedData(_)) => Some(p.into()), diff --git a/sdk/src/types/block/payload/tagged_data.rs b/sdk/src/types/block/payload/tagged_data.rs index 71a93a9a26..f99a2e280e 100644 --- a/sdk/src/types/block/payload/tagged_data.rs +++ b/sdk/src/types/block/payload/tagged_data.rs @@ -9,10 +9,13 @@ use core::ops::RangeInclusive; use packable::{ bounded::{BoundedU32, BoundedU8}, prefix::BoxedSlicePrefix, - Packable, + Packable, PackableExt, }; -use crate::types::block::Error; +use crate::types::block::{ + protocol::{WorkScore, WorkScoreParameters}, + Error, +}; pub(crate) type TagLength = BoundedU8<{ *TaggedDataPayload::TAG_LENGTH_RANGE.start() }, { *TaggedDataPayload::TAG_LENGTH_RANGE.end() }>; @@ -56,6 +59,13 @@ impl TaggedDataPayload { } } +impl WorkScore for TaggedDataPayload { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + // 1 byte for the payload kind + (1 + self.packed_len() as u32) * params.data_byte() + } +} + impl core::fmt::Debug for TaggedDataPayload { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("TaggedDataPayload") diff --git a/sdk/src/types/block/protocol.rs b/sdk/src/types/block/protocol/mod.rs similarity index 88% rename from sdk/src/types/block/protocol.rs rename to sdk/src/types/block/protocol/mod.rs index b07263d24d..36c6462241 100644 --- a/sdk/src/types/block/protocol.rs +++ b/sdk/src/types/block/protocol/mod.rs @@ -1,6 +1,8 @@ // Copyright 2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +mod work_score; + use alloc::string::String; use core::borrow::Borrow; @@ -8,6 +10,7 @@ use crypto::hashes::{blake2b::Blake2b256, Digest}; use getset::{CopyGetters, Getters}; use packable::{prefix::StringPrefix, Packable, PackableExt}; +pub use self::work_score::{WorkScore, WorkScoreParameters}; use crate::{ types::block::{ address::Hrp, @@ -205,54 +208,6 @@ impl ProtocolParameters { } } -#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Packable, CopyGetters)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize, serde::Deserialize), - serde(rename_all = "camelCase") -)] -#[packable(unpack_error = Error)] -#[getset(get_copy = "pub")] -pub struct WorkScoreParameters { - /// Modifier for network traffic per byte. - data_byte: u32, - /// Modifier for work done to process a block. - block: u32, - /// Modifier for loading UTXOs and performing mana calculations. - input: u32, - /// Modifier for loading and checking the context input. - context_input: u32, - /// Modifier for storing UTXOs. - output: u32, - /// Modifier for calculations using native tokens. - native_token: u32, - /// Modifier for storing staking features. - staking: u32, - /// Modifier for storing block issuer features. - block_issuer: u32, - /// Modifier for accessing the account-based ledger to transform mana to Block Issuance Credits. - allotment: u32, - /// Modifier for the block signature check. - signature_ed25519: u32, -} - -impl Default for WorkScoreParameters { - fn default() -> Self { - Self { - data_byte: 0, - block: 100, - input: 20, - context_input: 20, - output: 20, - native_token: 20, - staking: 100, - block_issuer: 100, - allotment: 100, - signature_ed25519: 200, - } - } -} - /// Defines the parameters used to calculate the Reference Mana Cost (RMC). #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Packable, CopyGetters)] #[cfg_attr( diff --git a/sdk/src/types/block/protocol/work_score.rs b/sdk/src/types/block/protocol/work_score.rs new file mode 100644 index 0000000000..fc96021a64 --- /dev/null +++ b/sdk/src/types/block/protocol/work_score.rs @@ -0,0 +1,74 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use getset::CopyGetters; +use packable::Packable; + +use crate::types::block::Error; + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Packable, CopyGetters)] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +#[packable(unpack_error = Error)] +#[getset(get_copy = "pub")] +pub struct WorkScoreParameters { + /// Accounts for the network traffic per byte. + data_byte: u32, + /// Accounts for work done to process a block in the node software. + block: u32, + /// Accounts for loading the UTXO from the database and performing the mana balance check. + input: u32, + /// Accounts for loading and checking the context input. + context_input: u32, + /// Accounts for storing the UTXO in the database. + output: u32, + /// Accounts for native token balance checks which use big integers. + native_token: u32, + /// Accounts for the cost of updating the staking vector when a staking feature is present. + staking: u32, + /// Accounts for the cost of updating the block issuer keys when a block issuer feature is present. + block_issuer: u32, + /// Accounts for accessing the account based ledger to transform the allotted mana to block issuance credits. + allotment: u32, + /// Accounts for an Ed25519 signature check. + signature_ed25519: u32, +} + +impl Default for WorkScoreParameters { + fn default() -> Self { + Self { + data_byte: 0, + block: 100, + input: 20, + context_input: 20, + output: 20, + native_token: 20, + staking: 100, + block_issuer: 100, + allotment: 100, + signature_ed25519: 200, + } + } +} + +/// A trait to facilitate the computation of the work score of a block, which is central to mana cost calculation. +pub trait WorkScore { + fn work_score(&self, _params: WorkScoreParameters) -> u32 { + 0 + } +} + +impl WorkScore for [T; N] { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + self.as_slice().work_score(params) + } +} + +impl WorkScore for [T] { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + self.iter().map(|o| o.work_score(params)).sum() + } +} diff --git a/sdk/src/types/block/signature/ed25519.rs b/sdk/src/types/block/signature/ed25519.rs index 69361c2309..d67abc4bb8 100644 --- a/sdk/src/types/block/signature/ed25519.rs +++ b/sdk/src/types/block/signature/ed25519.rs @@ -14,7 +14,11 @@ use packable::{ Packable, }; -use crate::types::block::{address::Ed25519Address, Error}; +use crate::types::block::{ + address::Ed25519Address, + protocol::{WorkScore, WorkScoreParameters}, + Error, +}; /// An Ed25519 signature. #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] @@ -104,6 +108,12 @@ impl fmt::Debug for Ed25519Signature { } } +impl WorkScore for Ed25519Signature { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + params.signature_ed25519() + } +} + impl Packable for Ed25519Signature { type UnpackError = Error; type UnpackVisitor = (); diff --git a/sdk/src/types/block/signature/mod.rs b/sdk/src/types/block/signature/mod.rs index e7762cdc1d..740a8a8279 100644 --- a/sdk/src/types/block/signature/mod.rs +++ b/sdk/src/types/block/signature/mod.rs @@ -6,7 +6,10 @@ mod ed25519; use derive_more::From; pub use self::ed25519::Ed25519Signature; -use crate::types::block::Error; +use crate::types::block::{ + protocol::{WorkScore, WorkScoreParameters}, + Error, +}; /// A `Signature` contains a signature which is used to unlock a transaction input. /// @@ -41,3 +44,11 @@ impl Signature { crate::def_is_as_opt!(Signature: Ed25519); } + +impl WorkScore for Signature { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + match self { + Self::Ed25519(ed25519) => ed25519.work_score(params), + } + } +} diff --git a/sdk/src/types/block/unlock/account.rs b/sdk/src/types/block/unlock/account.rs index f8f7872ac3..b33297478c 100644 --- a/sdk/src/types/block/unlock/account.rs +++ b/sdk/src/types/block/unlock/account.rs @@ -1,7 +1,7 @@ // Copyright 2020-2021 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crate::types::block::{unlock::UnlockIndex, Error}; +use crate::types::block::{protocol::WorkScore, unlock::UnlockIndex, Error}; /// Points to the unlock of a consumed account output. #[derive(Clone, Debug, Eq, PartialEq, Hash, packable::Packable)] @@ -11,14 +11,6 @@ pub struct AccountUnlock( UnlockIndex, ); -impl TryFrom for AccountUnlock { - type Error = Error; - - fn try_from(index: u16) -> Result { - Self::new(index) - } -} - impl AccountUnlock { /// The [`Unlock`](crate::types::block::unlock::Unlock) kind of an [`AccountUnlock`]. pub const KIND: u8 = 2; @@ -36,6 +28,16 @@ impl AccountUnlock { } } +impl WorkScore for AccountUnlock {} + +impl TryFrom for AccountUnlock { + type Error = Error; + + fn try_from(index: u16) -> Result { + Self::new(index) + } +} + #[cfg(feature = "serde")] mod dto { use serde::{Deserialize, Serialize}; diff --git a/sdk/src/types/block/unlock/anchor.rs b/sdk/src/types/block/unlock/anchor.rs index aad3c49b78..1b0a7a43b2 100644 --- a/sdk/src/types/block/unlock/anchor.rs +++ b/sdk/src/types/block/unlock/anchor.rs @@ -1,7 +1,7 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crate::types::block::{unlock::UnlockIndex, Error}; +use crate::types::block::{protocol::WorkScore, unlock::UnlockIndex, Error}; /// Points to the unlock of a consumed anchor output. #[derive(Clone, Debug, Eq, PartialEq, Hash, packable::Packable)] @@ -11,14 +11,6 @@ pub struct AnchorUnlock( UnlockIndex, ); -impl TryFrom for AnchorUnlock { - type Error = Error; - - fn try_from(index: u16) -> Result { - Self::new(index) - } -} - impl AnchorUnlock { /// The [`Unlock`](crate::types::block::unlock::Unlock) kind of an [`AnchorUnlock`]. pub const KIND: u8 = 3; @@ -36,6 +28,16 @@ impl AnchorUnlock { } } +impl WorkScore for AnchorUnlock {} + +impl TryFrom for AnchorUnlock { + type Error = Error; + + fn try_from(index: u16) -> Result { + Self::new(index) + } +} + #[cfg(feature = "serde")] mod dto { use serde::{Deserialize, Serialize}; diff --git a/sdk/src/types/block/unlock/empty.rs b/sdk/src/types/block/unlock/empty.rs index 2449e7668f..1778542695 100644 --- a/sdk/src/types/block/unlock/empty.rs +++ b/sdk/src/types/block/unlock/empty.rs @@ -1,6 +1,8 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use crate::types::block::protocol::WorkScore; + /// Used to maintain correct index relationship between addresses and signatures when unlocking a /// [`MultiAddress`](crate::types::block::address::MultiAddress) where not all addresses are unlocked. #[derive(Clone, Debug, Eq, PartialEq, Hash, packable::Packable)] @@ -11,6 +13,8 @@ impl EmptyUnlock { pub const KIND: u8 = 6; } +impl WorkScore for EmptyUnlock {} + #[cfg(feature = "serde")] mod dto { use serde::{Deserialize, Serialize}; diff --git a/sdk/src/types/block/unlock/mod.rs b/sdk/src/types/block/unlock/mod.rs index 35e0d6bdd6..8dbbef2cf4 100644 --- a/sdk/src/types/block/unlock/mod.rs +++ b/sdk/src/types/block/unlock/mod.rs @@ -23,6 +23,7 @@ pub use self::{ }; use crate::types::block::{ input::{INPUT_COUNT_MAX, INPUT_COUNT_RANGE, INPUT_INDEX_MAX}, + protocol::{WorkScore, WorkScoreParameters}, Error, }; @@ -67,6 +68,37 @@ pub enum Unlock { Empty(EmptyUnlock), } +impl Unlock { + /// Returns the unlock kind of an [`Unlock`]. + pub fn kind(&self) -> u8 { + match self { + Self::Signature(_) => SignatureUnlock::KIND, + Self::Reference(_) => ReferenceUnlock::KIND, + Self::Account(_) => AccountUnlock::KIND, + Self::Anchor(_) => AnchorUnlock::KIND, + Self::Nft(_) => NftUnlock::KIND, + Self::Multi(_) => MultiUnlock::KIND, + Self::Empty(_) => EmptyUnlock::KIND, + } + } + + crate::def_is_as_opt!(Unlock: Signature, Reference, Account, Anchor, Nft, Multi, Empty); +} + +impl WorkScore for Unlock { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + match self { + Self::Signature(unlock) => unlock.work_score(params), + Self::Reference(unlock) => unlock.work_score(params), + Self::Account(unlock) => unlock.work_score(params), + Self::Anchor(unlock) => unlock.work_score(params), + Self::Nft(unlock) => unlock.work_score(params), + Self::Multi(unlock) => unlock.work_score(params), + Self::Empty(unlock) => unlock.work_score(params), + } + } +} + impl From for Unlock { fn from(value: SignatureUnlock) -> Self { Self::Signature(value.into()) @@ -87,23 +119,6 @@ impl core::fmt::Debug for Unlock { } } -impl Unlock { - /// Returns the unlock kind of an [`Unlock`]. - pub fn kind(&self) -> u8 { - match self { - Self::Signature(_) => SignatureUnlock::KIND, - Self::Reference(_) => ReferenceUnlock::KIND, - Self::Account(_) => AccountUnlock::KIND, - Self::Anchor(_) => AnchorUnlock::KIND, - Self::Nft(_) => NftUnlock::KIND, - Self::Multi(_) => MultiUnlock::KIND, - Self::Empty(_) => EmptyUnlock::KIND, - } - } - - crate::def_is_as_opt!(Unlock: Signature, Reference, Account, Anchor, Nft, Multi, Empty); -} - pub(crate) type UnlockCount = BoundedU16<{ *UNLOCK_COUNT_RANGE.start() }, { *UNLOCK_COUNT_RANGE.end() }>; /// A collection of unlocks. diff --git a/sdk/src/types/block/unlock/multi.rs b/sdk/src/types/block/unlock/multi.rs index 7cf6dd66f1..3109645c4f 100644 --- a/sdk/src/types/block/unlock/multi.rs +++ b/sdk/src/types/block/unlock/multi.rs @@ -6,7 +6,12 @@ use alloc::boxed::Box; use derive_more::Deref; use packable::{prefix::BoxedSlicePrefix, Packable}; -use crate::types::block::{address::WeightedAddressCount, unlock::Unlock, Error}; +use crate::types::block::{ + address::WeightedAddressCount, + protocol::{WorkScore, WorkScoreParameters}, + unlock::Unlock, + Error, +}; pub(crate) type UnlocksCount = WeightedAddressCount; @@ -38,6 +43,12 @@ impl MultiUnlock { } } +impl WorkScore for MultiUnlock { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + self.0.work_score(params) + } +} + fn verify_unlocks(unlocks: &[Unlock]) -> Result<(), Error> { if VERIFY && unlocks.iter().any(Unlock::is_multi) { Err(Error::MultiUnlockRecursion) diff --git a/sdk/src/types/block/unlock/nft.rs b/sdk/src/types/block/unlock/nft.rs index 930b647824..cc7b6d4e79 100644 --- a/sdk/src/types/block/unlock/nft.rs +++ b/sdk/src/types/block/unlock/nft.rs @@ -1,7 +1,7 @@ // Copyright 2020-2021 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crate::types::block::{unlock::UnlockIndex, Error}; +use crate::types::block::{protocol::WorkScore, unlock::UnlockIndex, Error}; /// Points to the unlock of a consumed NFT output. #[derive(Clone, Debug, Eq, PartialEq, Hash, packable::Packable)] @@ -11,14 +11,6 @@ pub struct NftUnlock( UnlockIndex, ); -impl TryFrom for NftUnlock { - type Error = Error; - - fn try_from(index: u16) -> Result { - Self::new(index) - } -} - impl NftUnlock { /// The [`Unlock`](crate::types::block::unlock::Unlock) kind of a [`NftUnlock`]. pub const KIND: u8 = 4; @@ -36,6 +28,16 @@ impl NftUnlock { } } +impl WorkScore for NftUnlock {} + +impl TryFrom for NftUnlock { + type Error = Error; + + fn try_from(index: u16) -> Result { + Self::new(index) + } +} + #[cfg(feature = "serde")] pub(crate) mod dto { use serde::{Deserialize, Serialize}; diff --git a/sdk/src/types/block/unlock/reference.rs b/sdk/src/types/block/unlock/reference.rs index dc6b76c5d5..feef4e55e5 100644 --- a/sdk/src/types/block/unlock/reference.rs +++ b/sdk/src/types/block/unlock/reference.rs @@ -1,7 +1,7 @@ // Copyright 2020-2021 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crate::types::block::{unlock::UnlockIndex, Error}; +use crate::types::block::{protocol::WorkScore, unlock::UnlockIndex, Error}; /// An [`Unlock`](crate::types::block::unlock::Unlock) that refers to another unlock. #[derive(Clone, Debug, Eq, PartialEq, Hash, packable::Packable)] @@ -33,6 +33,8 @@ impl ReferenceUnlock { } } +impl WorkScore for ReferenceUnlock {} + #[cfg(feature = "serde")] pub(crate) mod dto { use serde::{Deserialize, Serialize}; diff --git a/sdk/src/types/block/unlock/signature.rs b/sdk/src/types/block/unlock/signature.rs index f5075361c2..bb9687a883 100644 --- a/sdk/src/types/block/unlock/signature.rs +++ b/sdk/src/types/block/unlock/signature.rs @@ -3,7 +3,10 @@ use derive_more::{Deref, From}; -use crate::types::block::signature::Signature; +use crate::types::block::{ + protocol::{WorkScore, WorkScoreParameters}, + signature::Signature, +}; /// An [`Unlock`](crate::types::block::unlock::Unlock) which is used to unlock a signature locked /// [`Input`](crate::types::block::input::Input). @@ -27,6 +30,12 @@ impl SignatureUnlock { } } +impl WorkScore for SignatureUnlock { + fn work_score(&self, params: WorkScoreParameters) -> u32 { + self.0.work_score(params) + } +} + #[cfg(feature = "serde")] pub(crate) mod dto { use serde::{Deserialize, Serialize};