diff --git a/sdk/src/types/block/error.rs b/sdk/src/types/block/error.rs index 760fd01820..d8aa35763e 100644 --- a/sdk/src/types/block/error.rs +++ b/sdk/src/types/block/error.rs @@ -9,6 +9,7 @@ use crypto::Error as CryptoError; use prefix_hex::Error as HexError; use primitive_types::U256; +use super::mana::AllotmentCount; use crate::types::block::{ input::UtxoInput, output::{ @@ -30,6 +31,7 @@ pub enum Error { CreatedAmountOverflow, CreatedNativeTokensAmountOverflow, Crypto(CryptoError), + DuplicateAllotment(AccountId), DuplicateSignatureUnlock(u16), DuplicateUtxo(UtxoInput), ExpirationUnlockConditionZero, @@ -55,6 +57,7 @@ pub enum Error { InvalidBech32Hrp(String), InvalidBlockLength(usize), InvalidStateMetadataLength(>::Error), + InvalidManaValue(u64), InvalidMetadataFeatureLength(>::Error), InvalidNativeTokenCount(>::Error), InvalidNetworkName(FromUtf8Error), @@ -62,6 +65,7 @@ pub enum Error { InvalidOutputAmount(u64), InvalidOutputCount(>::Error), InvalidOutputKind(u8), + InvalidAllotmentCount(>::Error), // TODO this would now need to be generic, not sure if possible. // https://github.com/iotaledger/iota-sdk/issues/647 // InvalidParentCount(>::Error), @@ -81,6 +85,7 @@ pub enum Error { InvalidTokenSchemeKind(u8), InvalidTransactionAmountSum(u128), InvalidTransactionNativeTokensCount(u16), + InvalidAllotmentManaSum(u128), InvalidUnlockCount(>::Error), InvalidUnlockKind(u8), InvalidUnlockReference(u16), @@ -125,6 +130,7 @@ impl fmt::Display for Error { Self::CreatedAmountOverflow => write!(f, "created amount overflow"), Self::CreatedNativeTokensAmountOverflow => write!(f, "created native tokens amount overflow"), Self::Crypto(e) => write!(f, "cryptographic error: {e}"), + Self::DuplicateAllotment(id) => write!(f, "duplicate allotment, account ID: {id}"), Self::DuplicateSignatureUnlock(index) => { write!(f, "duplicate signature unlock at index: {index}") } @@ -181,9 +187,10 @@ impl fmt::Display for Error { Self::InvalidInputCount(count) => write!(f, "invalid input count: {count}"), Self::InvalidInputOutputIndex(index) => write!(f, "invalid input or output index: {index}"), Self::InvalidBlockLength(length) => write!(f, "invalid block length {length}"), - Self::InvalidStateMetadataLength(length) => write!(f, "invalid state metadata length {length}"), + Self::InvalidStateMetadataLength(length) => write!(f, "invalid state metadata length: {length}"), + Self::InvalidManaValue(mana) => write!(f, "invalid mana value: {mana}"), Self::InvalidMetadataFeatureLength(length) => { - write!(f, "invalid metadata feature length {length}") + write!(f, "invalid metadata feature length: {length}") } Self::InvalidNativeTokenCount(count) => write!(f, "invalid native token count: {count}"), Self::InvalidNetworkName(err) => write!(f, "invalid network name: {err}"), @@ -191,6 +198,7 @@ impl fmt::Display for Error { Self::InvalidOutputAmount(amount) => write!(f, "invalid output amount: {amount}"), Self::InvalidOutputCount(count) => write!(f, "invalid output count: {count}"), Self::InvalidOutputKind(k) => write!(f, "invalid output kind: {k}"), + Self::InvalidAllotmentCount(count) => write!(f, "invalid allotment count: {count}"), Self::InvalidParentCount => { write!(f, "invalid parents count") } @@ -219,6 +227,7 @@ impl fmt::Display for Error { Self::InvalidTransactionNativeTokensCount(count) => { write!(f, "invalid transaction native tokens count: {count}") } + Self::InvalidAllotmentManaSum(value) => write!(f, "invalid allotment mana sum: {value}"), Self::InvalidUnlockCount(count) => write!(f, "invalid unlock count: {count}"), Self::InvalidUnlockKind(k) => write!(f, "invalid unlock kind: {k}"), Self::InvalidUnlockReference(index) => { diff --git a/sdk/src/types/block/mana/allotment.rs b/sdk/src/types/block/mana/allotment.rs new file mode 100644 index 0000000000..8c239032c5 --- /dev/null +++ b/sdk/src/types/block/mana/allotment.rs @@ -0,0 +1,94 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use packable::{ + error::{UnpackError, UnpackErrorExt}, + packer::Packer, + unpacker::Unpacker, + Packable, +}; + +use super::MAX_THEORETICAL_MANA; +use crate::types::block::{output::AccountId, 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. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(rename_all = "camelCase"))] +pub struct Allotment { + pub(crate) account_id: AccountId, + #[cfg_attr(feature = "serde", serde(with = "crate::utils::serde::string"))] + pub(crate) mana: u64, +} + +impl Allotment { + pub fn new(account_id: AccountId, mana: u64) -> Result { + if mana > MAX_THEORETICAL_MANA { + return Err(Error::InvalidManaValue(mana)); + } + Ok(Self { account_id, mana }) + } + + pub fn account_id(&self) -> &AccountId { + &self.account_id + } + + pub fn mana(&self) -> u64 { + self.mana + } +} + +impl Packable for Allotment { + type UnpackError = Error; + type UnpackVisitor = (); + + fn pack(&self, packer: &mut P) -> Result<(), P::Error> { + self.account_id.pack(packer)?; + self.mana.pack(packer)?; + Ok(()) + } + + fn unpack( + unpacker: &mut U, + visitor: &Self::UnpackVisitor, + ) -> Result> { + let account_id = AccountId::unpack::<_, VERIFY>(unpacker, visitor).coerce()?; + let mana = u64::unpack::<_, VERIFY>(unpacker, visitor).coerce()?; + Ok(Self { account_id, mana }) + } +} + +#[cfg(feature = "serde")] +mod dto { + use serde::Deserialize; + + use super::*; + use crate::utils::serde::string; + + impl<'de> Deserialize<'de> for Allotment { + fn deserialize(d: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(rename_all = "camelCase")] + struct AllotmentDto { + account_id: AccountId, + #[serde(with = "string")] + mana: u64, + } + + impl TryFrom for Allotment { + type Error = Error; + + fn try_from(value: AllotmentDto) -> Result { + Self::new(value.account_id, value.mana) + } + } + + AllotmentDto::deserialize(d)? + .try_into() + .map_err(serde::de::Error::custom) + } + } +} diff --git a/sdk/src/types/block/mana/mod.rs b/sdk/src/types/block/mana/mod.rs new file mode 100644 index 0000000000..ef2650f175 --- /dev/null +++ b/sdk/src/types/block/mana/mod.rs @@ -0,0 +1,95 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +mod allotment; + +use alloc::{boxed::Box, vec::Vec}; +use core::ops::RangeInclusive; + +use derive_more::Deref; +use hashbrown::HashSet; +use packable::{bounded::BoundedU16, prefix::BoxedSlicePrefix, Packable}; + +pub use self::allotment::Allotment; +use super::{output::AccountId, Error}; + +/// The number of bits that a given mana value can use, excluding the sign bit. +pub const MANA_BITS: u64 = 63; +/// Equivalent to `2^MANA_BITS - 1` +pub const MAX_THEORETICAL_MANA: u64 = u64::MAX >> 1; + +pub(crate) type AllotmentCount = BoundedU16<{ *Allotments::COUNT_RANGE.start() }, { *Allotments::COUNT_RANGE.end() }>; + +/// A list of [`Allotment`]s with unique [`AccountId`]s. +#[derive(Clone, Debug, Eq, PartialEq, Deref, Packable)] +#[packable(unpack_error = Error, with = |e| e.unwrap_item_err_or_else(|p| Error::InvalidAllotmentCount(p.into())))] +pub struct Allotments(#[packable(verify_with = verify_allotments)] BoxedSlicePrefix); + +fn verify_allotments(allotments: &[Allotment], _visitor: &()) -> Result<(), Error> { + if VERIFY { + let mut mana_sum: u64 = 0; + let mut unique_ids = HashSet::with_capacity(allotments.len()); + for Allotment { account_id, mana } in allotments.iter() { + mana_sum = mana_sum + .checked_add(*mana) + .ok_or(Error::InvalidAllotmentManaSum(mana_sum as u128 + *mana as u128))?; + + if mana_sum > MAX_THEORETICAL_MANA { + return Err(Error::InvalidAllotmentManaSum(mana_sum as u128)); + } + + if !unique_ids.insert(account_id) { + return Err(Error::DuplicateAllotment(*account_id)); + } + } + } + + Ok(()) +} + +impl TryFrom> for Allotments { + type Error = Error; + + #[inline(always)] + fn try_from(allotments: Vec) -> Result { + Self::from_vec(allotments) + } +} + +impl IntoIterator for Allotments { + type Item = Allotment; + type IntoIter = alloc::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + Vec::from(Into::>::into(self.0)).into_iter() + } +} + +impl Allotments { + /// The minimum number of allotments of a transaction. + pub const COUNT_MIN: u16 = 1; + /// The maximum number of allotments of a transaction. + pub const COUNT_MAX: u16 = 128; + /// The range of valid numbers of allotments of a transaction. + pub const COUNT_RANGE: RangeInclusive = Self::COUNT_MIN..=Self::COUNT_MAX; // [1..128] + + /// Creates a new [`Allotments`] from a vec. + pub fn from_vec(allotments: Vec) -> Result { + let allotments = BoxedSlicePrefix::::try_from(allotments.into_boxed_slice()) + .map_err(Error::InvalidAllotmentCount)?; + + verify_allotments::(&allotments, &())?; + + Ok(Self(allotments)) + } + + /// Gets a reference to an [`Allotment`], if one exists, using an [`AccountId`]. + #[inline(always)] + pub fn get(&self, account_id: &AccountId) -> Option<&Allotment> { + self.0 + .iter() + .position(|a| a.account_id() == account_id) + // PANIC: indexation is fine since the index has been found. + .map(|index| &self.0[index]) + } +} diff --git a/sdk/src/types/block/mod.rs b/sdk/src/types/block/mod.rs index 06b98b9feb..36f1fa5018 100644 --- a/sdk/src/types/block/mod.rs +++ b/sdk/src/types/block/mod.rs @@ -20,6 +20,8 @@ pub mod core; pub mod helper; /// A module that provides types and syntactic validations of inputs. pub mod input; +/// A module that provides types and syntactic validations of mana. +pub mod mana; /// A module that provides types and syntactic validations of outputs. pub mod output; /// A module that provides types and syntactic validations of parents. diff --git a/sdk/src/types/block/output/mod.rs b/sdk/src/types/block/output/mod.rs index cab4db489f..6fa5c0c972 100644 --- a/sdk/src/types/block/output/mod.rs +++ b/sdk/src/types/block/output/mod.rs @@ -74,11 +74,11 @@ use crate::types::{ /// The maximum number of outputs of a transaction. pub const OUTPUT_COUNT_MAX: u16 = 128; -/// The range of valid numbers of outputs of a transaction . +/// The range of valid numbers of outputs of a transaction. pub const OUTPUT_COUNT_RANGE: RangeInclusive = 1..=OUTPUT_COUNT_MAX; // [1..128] /// The maximum index of outputs of a transaction. pub const OUTPUT_INDEX_MAX: u16 = OUTPUT_COUNT_MAX - 1; // 127 -/// The range of valid indices of outputs of a transaction . +/// The range of valid indices of outputs of a transaction. pub const OUTPUT_INDEX_RANGE: RangeInclusive = 0..=OUTPUT_INDEX_MAX; // [0..127] #[derive(Copy, Clone)] diff --git a/sdk/src/types/block/payload/transaction/essence/regular.rs b/sdk/src/types/block/payload/transaction/essence/regular.rs index 9dbd3e092a..669d605cc1 100644 --- a/sdk/src/types/block/payload/transaction/essence/regular.rs +++ b/sdk/src/types/block/payload/transaction/essence/regular.rs @@ -9,6 +9,7 @@ use packable::{bounded::BoundedU16, prefix::BoxedSlicePrefix, Packable}; use crate::types::{ block::{ input::{Input, INPUT_COUNT_RANGE}, + mana::{Allotment, Allotments}, output::{InputsCommitment, NativeTokens, Output, OUTPUT_COUNT_RANGE}, payload::{OptionalPayload, Payload}, protocol::ProtocolParameters, @@ -25,6 +26,7 @@ pub struct RegularTransactionEssenceBuilder { inputs: Vec, inputs_commitment: InputsCommitment, outputs: Vec, + allotments: Vec, payload: OptionalPayload, creation_time: Option, } @@ -37,6 +39,7 @@ impl RegularTransactionEssenceBuilder { inputs: Vec::new(), inputs_commitment, outputs: Vec::new(), + allotments: Vec::new(), payload: OptionalPayload::default(), creation_time: None, } @@ -66,12 +69,24 @@ impl RegularTransactionEssenceBuilder { self } + /// Add allotments to a [`RegularTransactionEssenceBuilder`]. + pub fn with_allotments(mut self, allotments: impl IntoIterator) -> Self { + self.allotments = allotments.into_iter().collect(); + self + } + /// Add an output to a [`RegularTransactionEssenceBuilder`]. pub fn add_output(mut self, output: Output) -> Self { self.outputs.push(output); self } + /// Add an allotment to a [`RegularTransactionEssenceBuilder`]. + pub fn add_allotment(mut self, allotment: Allotment) -> Self { + self.allotments.push(allotment); + self + } + /// Add a payload to a [`RegularTransactionEssenceBuilder`]. pub fn with_payload(mut self, payload: impl Into) -> Self { self.payload = payload.into(); @@ -111,6 +126,8 @@ impl RegularTransactionEssenceBuilder { verify_outputs::(&outputs, protocol_parameters)?; } + let allotments = Allotments::from_vec(self.allotments)?; + verify_payload::(&self.payload)?; let creation_time = self.creation_time.unwrap_or_else(|| { @@ -133,6 +150,7 @@ impl RegularTransactionEssenceBuilder { inputs, inputs_commitment: self.inputs_commitment, outputs, + allotments, payload: self.payload, }) } @@ -165,6 +183,7 @@ pub struct RegularTransactionEssence { #[packable(verify_with = verify_outputs)] #[packable(unpack_error_with = |e| e.unwrap_item_err_or_else(|p| Error::InvalidOutputCount(p.into())))] outputs: BoxedSlicePrefix, + allotments: Allotments, #[packable(verify_with = verify_payload_packable)] payload: OptionalPayload, } @@ -203,6 +222,11 @@ impl RegularTransactionEssence { &self.outputs } + /// Returns the allotments of a [`RegularTransactionEssence`]. + pub fn allotments(&self) -> &[Allotment] { + &self.allotments + } + /// Returns the optional payload of a [`RegularTransactionEssence`]. pub fn payload(&self) -> Option<&Payload> { self.payload.as_ref() @@ -335,6 +359,7 @@ pub(crate) mod dto { pub inputs: Vec, pub inputs_commitment: String, pub outputs: Vec, + pub allotments: Vec, #[serde(default, skip_serializing_if = "Option::is_none")] pub payload: Option, } @@ -348,6 +373,7 @@ pub(crate) mod dto { inputs: value.inputs().to_vec(), inputs_commitment: value.inputs_commitment().to_string(), outputs: value.outputs().iter().map(Into::into).collect::>(), + allotments: value.allotments().to_vec(), payload: match value.payload() { Some(p @ Payload::TaggedData(_)) => Some(p.into()), Some(_) => unimplemented!(), @@ -375,7 +401,8 @@ pub(crate) mod dto { let mut builder = Self::builder(network_id, InputsCommitment::from_str(&dto.inputs_commitment)?) .with_creation_time(dto.creation_time) .with_inputs(dto.inputs) - .with_outputs(outputs); + .with_outputs(outputs) + .with_allotments(dto.allotments); builder = if let Some(p) = dto.payload { if let PayloadDto::TaggedData(i) = p { diff --git a/sdk/src/wallet/account/mod.rs b/sdk/src/wallet/account/mod.rs index 1d9a06f415..d96b5b239f 100644 --- a/sdk/src/wallet/account/mod.rs +++ b/sdk/src/wallet/account/mod.rs @@ -597,12 +597,14 @@ fn serialize() { use crate::types::block::{ address::{Address, Ed25519Address}, input::{Input, UtxoInput}, + mana::Allotment, output::{unlock_condition::AddressUnlockCondition, BasicOutput, InputsCommitment, Output}, payload::{ transaction::{RegularTransactionEssence, TransactionEssence, TransactionId}, TransactionPayload, }, protocol::ProtocolParameters, + rand::output::rand_account_id, signature::{Ed25519Signature, Signature}, unlock::{ReferenceUnlock, SignatureUnlock, Unlock, Unlocks}, }; @@ -640,6 +642,7 @@ fn serialize() { RegularTransactionEssence::builder(protocol_parameters.network_id(), InputsCommitment::from([0u8; 32])) .with_inputs([input1, input2]) .add_output(output) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(protocol_parameters) .unwrap(), ); diff --git a/sdk/src/wallet/account/operations/transaction/build_transaction.rs b/sdk/src/wallet/account/operations/transaction/build_transaction.rs index 58dea87931..c8a4404af8 100644 --- a/sdk/src/wallet/account/operations/transaction/build_transaction.rs +++ b/sdk/src/wallet/account/operations/transaction/build_transaction.rs @@ -50,10 +50,11 @@ where .map(|i| i.output.clone()) .collect::>(); let inputs_commitment = InputsCommitment::new(input_outputs.iter()); + // TODO: Add an appropriate mana allotment here for this account let mut essence_builder = - RegularTransactionEssence::builder(protocol_parameters.network_id(), inputs_commitment); - essence_builder = essence_builder.with_inputs(inputs_for_essence); - essence_builder = essence_builder.with_outputs(selected_transaction_data.outputs); + RegularTransactionEssence::builder(protocol_parameters.network_id(), inputs_commitment) + .with_inputs(inputs_for_essence) + .with_outputs(selected_transaction_data.outputs); // Optional add a tagged payload if let Some(options) = options.into() { diff --git a/sdk/tests/client/signing/account.rs b/sdk/tests/client/signing/account.rs index 50ca88fe5e..fa0a9aedc8 100644 --- a/sdk/tests/client/signing/account.rs +++ b/sdk/tests/client/signing/account.rs @@ -17,12 +17,14 @@ use iota_sdk::{ types::block::{ address::{AccountAddress, Address, ToBech32Ext}, input::{Input, UtxoInput}, + mana::Allotment, output::{AccountId, InputsCommitment}, payload::{ transaction::{RegularTransactionEssence, TransactionEssence}, TransactionPayload, }, protocol::protocol_parameters, + rand::output::rand_account_id, semantic::ConflictReason, unlock::{SignatureUnlock, Unlock}, }, @@ -94,6 +96,7 @@ async fn sign_account_state_transition() -> Result<()> { .collect::>(), ) .with_outputs(outputs) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(protocol_parameters)?, ); @@ -185,6 +188,7 @@ async fn sign_account_governance_transition() -> Result<()> { .collect::>(), ) .with_outputs(outputs) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(protocol_parameters)?, ); @@ -316,6 +320,7 @@ async fn account_reference_unlocks() -> Result<()> { .collect::>(), ) .with_outputs(outputs) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(protocol_parameters)?, ); diff --git a/sdk/tests/client/signing/basic.rs b/sdk/tests/client/signing/basic.rs index ca325ff6ba..6873caeef2 100644 --- a/sdk/tests/client/signing/basic.rs +++ b/sdk/tests/client/signing/basic.rs @@ -15,12 +15,14 @@ use iota_sdk::{ types::block::{ address::ToBech32Ext, input::{Input, UtxoInput}, + mana::Allotment, output::InputsCommitment, payload::{ transaction::{RegularTransactionEssence, TransactionEssence}, TransactionPayload, }, protocol::protocol_parameters, + rand::output::rand_account_id, semantic::ConflictReason, unlock::{SignatureUnlock, Unlock}, }, @@ -77,6 +79,7 @@ async fn single_ed25519_unlock() -> Result<()> { .collect::>(), ) .with_outputs(outputs) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(protocol_parameters)?, ); @@ -179,6 +182,7 @@ async fn ed25519_reference_unlocks() -> Result<()> { .collect::>(), ) .with_outputs(outputs) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(protocol_parameters)?, ); @@ -291,6 +295,7 @@ async fn two_signature_unlocks() -> Result<()> { .collect::>(), ) .with_outputs(outputs) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(protocol_parameters)?, ); diff --git a/sdk/tests/client/signing/mod.rs b/sdk/tests/client/signing/mod.rs index c4c0c2d861..ca18d141f0 100644 --- a/sdk/tests/client/signing/mod.rs +++ b/sdk/tests/client/signing/mod.rs @@ -21,12 +21,14 @@ use iota_sdk::{ types::block::{ address::{AccountAddress, Address, NftAddress, ToBech32Ext}, input::{Input, UtxoInput}, + mana::Allotment, output::{AccountId, InputsCommitment, NftId}, payload::{ transaction::{RegularTransactionEssence, TransactionEssence}, TransactionPayload, }, protocol::protocol_parameters, + rand::output::rand_account_id, semantic::ConflictReason, unlock::{SignatureUnlock, Unlock}, }, @@ -382,6 +384,7 @@ async fn all_combined() -> Result<()> { .collect::>(), ) .with_outputs(outputs) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(protocol_parameters)?, ); diff --git a/sdk/tests/client/signing/nft.rs b/sdk/tests/client/signing/nft.rs index 0beccf4fa5..26105cc224 100644 --- a/sdk/tests/client/signing/nft.rs +++ b/sdk/tests/client/signing/nft.rs @@ -17,12 +17,14 @@ use iota_sdk::{ types::block::{ address::{Address, NftAddress, ToBech32Ext}, input::{Input, UtxoInput}, + mana::Allotment, output::{InputsCommitment, NftId}, payload::{ transaction::{RegularTransactionEssence, TransactionEssence}, TransactionPayload, }, protocol::protocol_parameters, + rand::output::rand_account_id, semantic::ConflictReason, unlock::{SignatureUnlock, Unlock}, }, @@ -121,6 +123,7 @@ async fn nft_reference_unlocks() -> Result<()> { .collect::>(), ) .with_outputs(outputs) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(protocol_parameters)?, ); diff --git a/sdk/tests/types/payload.rs b/sdk/tests/types/payload.rs index baa5ce3790..efa9c382f1 100644 --- a/sdk/tests/types/payload.rs +++ b/sdk/tests/types/payload.rs @@ -4,13 +4,17 @@ use iota_sdk::types::block::{ address::{Address, Ed25519Address}, input::{Input, UtxoInput}, + mana::Allotment, output::{unlock_condition::AddressUnlockCondition, BasicOutput, Output}, payload::{ transaction::{RegularTransactionEssence, TransactionEssence, TransactionId, TransactionPayload}, Payload, TaggedDataPayload, }, protocol::protocol_parameters, - rand::{bytes::rand_bytes, output::rand_inputs_commitment}, + rand::{ + bytes::rand_bytes, + output::{rand_account_id, rand_inputs_commitment}, + }, signature::{Ed25519Signature, Signature}, unlock::{ReferenceUnlock, SignatureUnlock, Unlock, Unlocks}, }; @@ -40,6 +44,7 @@ fn transaction() { RegularTransactionEssence::builder(protocol_parameters.network_id(), rand_inputs_commitment()) .with_inputs(vec![input1, input2]) .add_output(output) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(&protocol_parameters) .unwrap(), ); diff --git a/sdk/tests/types/transaction_essence.rs b/sdk/tests/types/transaction_essence.rs index c444864f48..780f16bedc 100644 --- a/sdk/tests/types/transaction_essence.rs +++ b/sdk/tests/types/transaction_essence.rs @@ -4,10 +4,11 @@ use iota_sdk::types::block::{ address::{Address, Ed25519Address}, input::{Input, UtxoInput}, + mana::Allotment, output::{unlock_condition::AddressUnlockCondition, BasicOutput, Output}, payload::transaction::{RegularTransactionEssence, TransactionEssence, TransactionId}, protocol::protocol_parameters, - rand::output::rand_inputs_commitment, + rand::output::{rand_account_id, rand_inputs_commitment}, Error, }; use packable::{error::UnpackError, PackableExt}; @@ -34,6 +35,7 @@ fn essence_kind() { RegularTransactionEssence::builder(protocol_parameters.network_id(), rand_inputs_commitment()) .with_inputs([input1, input2]) .add_output(output) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(&protocol_parameters) .unwrap(), ); diff --git a/sdk/tests/types/transaction_payload.rs b/sdk/tests/types/transaction_payload.rs index 347de6099e..d8f316d0dc 100644 --- a/sdk/tests/types/transaction_payload.rs +++ b/sdk/tests/types/transaction_payload.rs @@ -4,10 +4,11 @@ use iota_sdk::types::block::{ address::{Address, Ed25519Address}, input::{Input, UtxoInput}, + mana::Allotment, output::{unlock_condition::AddressUnlockCondition, BasicOutput, Output}, payload::transaction::{RegularTransactionEssence, TransactionEssence, TransactionId, TransactionPayload}, protocol::protocol_parameters, - rand::output::rand_inputs_commitment, + rand::output::{rand_account_id, rand_inputs_commitment}, signature::{Ed25519Signature, Signature}, unlock::{ReferenceUnlock, SignatureUnlock, Unlock, Unlocks}, Error, @@ -45,6 +46,7 @@ fn builder_no_essence_too_few_unlocks() { RegularTransactionEssence::builder(protocol_parameters.network_id(), rand_inputs_commitment()) .with_inputs([input1, input2]) .add_output(output) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(&protocol_parameters) .unwrap(), ); @@ -82,6 +84,7 @@ fn builder_no_essence_too_many_unlocks() { RegularTransactionEssence::builder(protocol_parameters.network_id(), rand_inputs_commitment()) .add_input(input1) .add_output(output) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(&protocol_parameters) .unwrap(), ); @@ -122,6 +125,7 @@ fn pack_unpack_valid() { RegularTransactionEssence::builder(protocol_parameters.network_id(), rand_inputs_commitment()) .with_inputs([input1, input2]) .add_output(output) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(&protocol_parameters) .unwrap(), ); @@ -164,6 +168,7 @@ fn getters() { RegularTransactionEssence::builder(protocol_parameters.network_id(), rand_inputs_commitment()) .with_inputs([input1, input2]) .add_output(output) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(&protocol_parameters) .unwrap(), ); diff --git a/sdk/tests/types/transaction_regular_essence.rs b/sdk/tests/types/transaction_regular_essence.rs index ed00c71825..600e9956db 100644 --- a/sdk/tests/types/transaction_regular_essence.rs +++ b/sdk/tests/types/transaction_regular_essence.rs @@ -4,6 +4,7 @@ use iota_sdk::types::block::{ address::{AccountAddress, Address, Ed25519Address}, input::{Input, UtxoInput}, + mana::Allotment, output::{ unlock_condition::{ AddressUnlockCondition, GovernorAddressUnlockCondition, ImmutableAccountAddressUnlockCondition, @@ -17,7 +18,10 @@ use iota_sdk::types::block::{ Payload, }, protocol::protocol_parameters, - rand::{output::rand_inputs_commitment, payload::rand_tagged_data_payload}, + rand::{ + output::{rand_account_id, rand_inputs_commitment}, + payload::rand_tagged_data_payload, + }, signature::{Ed25519Signature, Signature}, unlock::{ReferenceUnlock, SignatureUnlock, Unlock, Unlocks}, Error, @@ -54,6 +58,7 @@ fn build_valid() { let essence = RegularTransactionEssence::builder(protocol_parameters.network_id(), rand_inputs_commitment()) .with_inputs([input1, input2]) .add_output(output) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(&protocol_parameters); assert!(essence.is_ok()); @@ -79,6 +84,7 @@ fn build_valid_with_payload() { .with_inputs([input1, input2]) .add_output(output) .with_payload(rand_tagged_data_payload()) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(&protocol_parameters); assert!(essence.is_ok()); @@ -103,6 +109,7 @@ fn build_valid_add_inputs_outputs() { let essence = RegularTransactionEssence::builder(protocol_parameters.network_id(), rand_inputs_commitment()) .with_inputs([input1, input2]) .add_output(output) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(&protocol_parameters); assert!(essence.is_ok()); @@ -128,6 +135,7 @@ fn build_invalid_payload_kind() { RegularTransactionEssence::builder(protocol_parameters.network_id(), rand_inputs_commitment()) .with_inputs([input1.clone(), input2.clone()]) .add_output(output.clone()) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(&protocol_parameters) .unwrap(), ); @@ -146,6 +154,7 @@ fn build_invalid_payload_kind() { .with_inputs(vec![input1, input2]) .add_output(output) .with_payload(tx_payload) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(&protocol_parameters); assert!(matches!(essence, Err(Error::InvalidPayloadKind(6)))); @@ -166,6 +175,7 @@ fn build_invalid_input_count_low() { let essence = RegularTransactionEssence::builder(protocol_parameters.network_id(), rand_inputs_commitment()) .add_output(output) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(&protocol_parameters); assert!(matches!( @@ -192,6 +202,7 @@ fn build_invalid_input_count_high() { let essence = RegularTransactionEssence::builder(protocol_parameters.network_id(), rand_inputs_commitment()) .with_inputs(vec![input; 129]) .add_output(output) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(&protocol_parameters); assert!(matches!( @@ -208,6 +219,7 @@ fn build_invalid_output_count_low() { let essence = RegularTransactionEssence::builder(protocol_parameters.network_id(), rand_inputs_commitment()) .add_input(input) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(&protocol_parameters); assert!(matches!( @@ -234,6 +246,7 @@ fn build_invalid_output_count_high() { let essence = RegularTransactionEssence::builder(protocol_parameters.network_id(), rand_inputs_commitment()) .add_input(input) .with_outputs(vec![output; 129]) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(&protocol_parameters); assert!(matches!( @@ -260,6 +273,7 @@ fn build_invalid_duplicate_utxo() { let essence = RegularTransactionEssence::builder(protocol_parameters.network_id(), rand_inputs_commitment()) .with_inputs(vec![input; 2]) .add_output(output) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(&protocol_parameters); assert!(matches!(essence, Err(Error::DuplicateUtxo(_)))); @@ -294,6 +308,7 @@ fn build_invalid_accumulated_output() { let essence = RegularTransactionEssence::builder(protocol_parameters.network_id(), rand_inputs_commitment()) .add_input(input) .with_outputs([output1, output2]) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(&protocol_parameters); assert!(matches!(essence, Err(Error::InvalidTransactionAmountSum(_)))); @@ -320,6 +335,7 @@ fn getters() { .with_inputs([input1, input2]) .with_outputs(outputs.clone()) .with_payload(payload.clone()) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(&protocol_parameters) .unwrap(); @@ -349,6 +365,7 @@ fn duplicate_output_nft() { let essence = RegularTransactionEssence::builder(protocol_parameters.network_id(), rand_inputs_commitment()) .with_inputs([input1, input2]) .with_outputs([basic, nft.clone(), nft]) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(&protocol_parameters); assert!(matches!( @@ -379,6 +396,7 @@ fn duplicate_output_nft_null() { let essence = RegularTransactionEssence::builder(protocol_parameters.network_id(), rand_inputs_commitment()) .with_inputs([input1, input2]) .with_outputs([basic, nft.clone(), nft]) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(&protocol_parameters); assert!(essence.is_ok()); @@ -407,6 +425,7 @@ fn duplicate_output_account() { let essence = RegularTransactionEssence::builder(protocol_parameters.network_id(), rand_inputs_commitment()) .with_inputs([input1, input2]) .with_outputs([basic, account.clone(), account]) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(&protocol_parameters); assert!(matches!( @@ -443,6 +462,7 @@ fn duplicate_output_foundry() { let essence = RegularTransactionEssence::builder(protocol_parameters.network_id(), rand_inputs_commitment()) .with_inputs([input1, input2]) .with_outputs([basic, foundry.clone(), foundry]) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(&protocol_parameters); assert!(matches!( diff --git a/sdk/tests/wallet/events.rs b/sdk/tests/wallet/events.rs index 13a10b5a19..8b0b47a8d3 100644 --- a/sdk/tests/wallet/events.rs +++ b/sdk/tests/wallet/events.rs @@ -6,10 +6,11 @@ use iota_sdk::{ types::block::{ address::{Address, Bech32Address, Ed25519Address}, input::{Input, UtxoInput}, + mana::Allotment, output::{unlock_condition::AddressUnlockCondition, BasicOutput, Output, OutputId}, payload::transaction::{RegularTransactionEssence, TransactionEssence, TransactionId}, protocol::protocol_parameters, - rand::output::{rand_basic_output, rand_inputs_commitment, rand_output_metadata}, + rand::output::{rand_account_id, rand_basic_output, rand_inputs_commitment, rand_output_metadata}, }, wallet::{ account::types::{InclusionState, OutputData, OutputDataDto}, @@ -95,6 +96,7 @@ fn wallet_events_serde() { RegularTransactionEssence::builder(protocol_parameters.network_id(), rand_inputs_commitment()) .with_inputs(vec![input1, input2]) .add_output(output) + .add_allotment(Allotment::new(rand_account_id(), 10).unwrap()) .finish_with_params(&protocol_parameters) .unwrap(), );