From 1b3f1280e1ffa82d0254f2c25f431b35b86fc83b Mon Sep 17 00:00:00 2001 From: Alex Coats Date: Fri, 15 Sep 2023 11:21:43 -0400 Subject: [PATCH 1/9] Use BTreeSets where possible --- Cargo.lock | 5 +- bindings/core/Cargo.toml | 2 +- bindings/core/src/method/client.rs | 32 +++-- sdk/Cargo.toml | 3 +- sdk/examples/client/get_block.rs | 2 +- .../client/node_api_core/06_get_block.rs | 2 +- .../client/node_api_core/07_get_block_raw.rs | 2 +- .../node_api_core/08_get_block_metadata.rs | 2 +- .../wallet/17_check_unlock_conditions.rs | 6 +- sdk/src/types/block/core/basic.rs | 6 +- sdk/src/types/block/core/mod.rs | 6 +- sdk/src/types/block/core/validation.rs | 6 +- sdk/src/types/block/error.rs | 6 +- sdk/src/types/block/mana/allotment.rs | 6 + sdk/src/types/block/mana/mod.rs | 53 +++---- sdk/src/types/block/output/account.rs | 34 ++--- sdk/src/types/block/output/basic.rs | 26 ++-- .../block/output/feature/block_issuer.rs | 65 ++++----- sdk/src/types/block/output/feature/mod.rs | 97 +++++++------ sdk/src/types/block/output/foundry.rs | 28 ++-- sdk/src/types/block/output/native_token.rs | 81 +++++------ sdk/src/types/block/output/nft.rs | 34 ++--- .../block/output/unlock_condition/mod.rs | 135 ++++++++++-------- .../storage_deposit_return.rs | 11 ++ sdk/src/types/block/parent.rs | 48 +++---- .../payload/transaction/essence/regular.rs | 2 +- sdk/src/types/block/rand/block.rs | 12 +- sdk/src/types/block/rand/parents.rs | 2 +- sdk/src/types/fuzz/Cargo.toml | 2 +- sdk/src/wallet/account/operations/balance.rs | 7 +- .../account/operations/participation/mod.rs | 6 +- sdk/tests/client/mod.rs | 4 +- sdk/tests/types/parents.rs | 53 ++++--- 33 files changed, 386 insertions(+), 400 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c21724636a..df3717c039 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1561,7 +1561,6 @@ dependencies = [ "iota-ledger-nano", "iota-sdk", "iota_stronghold", - "iterator-sorted", "lazy_static", "log", "num_cpus", @@ -2065,9 +2064,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "packable" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac6939609218dd61191d0ec495e00c9eac5c80f29986775be1f62bfae69dee3" +checksum = "d99ae55c2e3dc657f87a74d549bfe44187dba690310738d384454b8101f7c0f8" dependencies = [ "autocfg", "packable-derive", diff --git a/bindings/core/Cargo.toml b/bindings/core/Cargo.toml index 4ab7562e5f..897c2ef3fa 100644 --- a/bindings/core/Cargo.toml +++ b/bindings/core/Cargo.toml @@ -23,7 +23,7 @@ iota-crypto = { version = "0.23.0", default-features = false, features = [ "bip44", ] } log = { version = "0.4.20", default-features = false } -packable = { version = "0.8.1", default-features = false } +packable = { version = "0.8.2", default-features = false } prefix-hex = { version = "0.7.1", default-features = false } primitive-types = { version = "0.12.1", default-features = false } serde = { version = "1.0.188", default-features = false } diff --git a/bindings/core/src/method/client.rs b/bindings/core/src/method/client.rs index adfe2b977f..1eb4315499 100644 --- a/bindings/core/src/method/client.rs +++ b/bindings/core/src/method/client.rs @@ -1,6 +1,8 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use std::collections::BTreeSet; + use derivative::Derivative; #[cfg(feature = "mqtt")] use iota_sdk::client::mqtt::Topic; @@ -36,14 +38,14 @@ pub enum ClientMethod { // TODO: Determine if `default` is wanted here #[serde(default, with = "string")] mana: u64, - native_tokens: Option>, + native_tokens: Option>, account_id: AccountId, state_index: Option, state_metadata: Option, foundry_counter: Option, - unlock_conditions: Vec, - features: Option>, - immutable_features: Option>, + unlock_conditions: BTreeSet, + features: Option>, + immutable_features: Option>, }, /// Build a BasicOutput. /// Expected response: [`Output`](crate::Response::Output) @@ -56,9 +58,9 @@ pub enum ClientMethod { // TODO: Determine if `default` is wanted here #[serde(default, with = "string")] mana: u64, - native_tokens: Option>, - unlock_conditions: Vec, - features: Option>, + native_tokens: Option>, + unlock_conditions: BTreeSet, + features: Option>, }, /// Build a FoundryOutput. /// Expected response: [`Output`](crate::Response::Output) @@ -68,12 +70,12 @@ pub enum ClientMethod { // If not provided, minimum storage deposit will be used #[serde(default, with = "option_string")] amount: Option, - native_tokens: Option>, + native_tokens: Option>, serial_number: u32, token_scheme: TokenScheme, - unlock_conditions: Vec, - features: Option>, - immutable_features: Option>, + unlock_conditions: BTreeSet, + features: Option>, + immutable_features: Option>, }, /// Build an NftOutput. /// Expected response: [`Output`](crate::Response::Output) @@ -86,11 +88,11 @@ pub enum ClientMethod { // TODO: Determine if `default` is wanted here #[serde(default, with = "string")] mana: u64, - native_tokens: Option>, + native_tokens: Option>, nft_id: NftId, - unlock_conditions: Vec, - features: Option>, - immutable_features: Option>, + unlock_conditions: BTreeSet, + features: Option>, + immutable_features: Option>, }, /// Removes all listeners for the provided topics. /// Expected response: [`Ok`](crate::Response::Ok) diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 8e17f3879d..581a770041 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -41,8 +41,7 @@ iota-crypto = { version = "0.23.0", default-features = false, features = [ "ed25519", "secp256k1", ] } -iterator-sorted = { version = "0.1.0", default-features = false } -packable = { version = "0.8.1", default-features = false, features = [ +packable = { version = "0.8.2", default-features = false, features = [ "primitive-types", ] } prefix-hex = { version = "0.7.1", default-features = false, features = [ diff --git a/sdk/examples/client/get_block.rs b/sdk/examples/client/get_block.rs index 91d52ce065..0e215d7839 100644 --- a/sdk/examples/client/get_block.rs +++ b/sdk/examples/client/get_block.rs @@ -22,7 +22,7 @@ async fn main() -> Result<()> { .await?; // Fetch a block ID from the node. - let block_id = client.get_issuance().await?.strong_parents[0]; + let block_id = *client.get_issuance().await?.strong_parents.first().unwrap(); // Get the block. let block = client.get_block(&block_id).await?; diff --git a/sdk/examples/client/node_api_core/06_get_block.rs b/sdk/examples/client/node_api_core/06_get_block.rs index f08ac5c8f3..64a9de6274 100644 --- a/sdk/examples/client/node_api_core/06_get_block.rs +++ b/sdk/examples/client/node_api_core/06_get_block.rs @@ -28,7 +28,7 @@ async fn main() -> Result<()> { block_id } else { // ... fetch one from the node. - client.get_issuance().await?.strong_parents[0] + *client.get_issuance().await?.strong_parents.first().unwrap() }; // Get the block. diff --git a/sdk/examples/client/node_api_core/07_get_block_raw.rs b/sdk/examples/client/node_api_core/07_get_block_raw.rs index 30d9972bf8..421d931d17 100644 --- a/sdk/examples/client/node_api_core/07_get_block_raw.rs +++ b/sdk/examples/client/node_api_core/07_get_block_raw.rs @@ -28,7 +28,7 @@ async fn main() -> Result<()> { block_id } else { // ... fetch one from the node. - client.get_issuance().await?.strong_parents[0] + *client.get_issuance().await?.strong_parents.first().unwrap() }; // Get the block as raw bytes. diff --git a/sdk/examples/client/node_api_core/08_get_block_metadata.rs b/sdk/examples/client/node_api_core/08_get_block_metadata.rs index 49c20cd5c0..5a8aeba0f2 100644 --- a/sdk/examples/client/node_api_core/08_get_block_metadata.rs +++ b/sdk/examples/client/node_api_core/08_get_block_metadata.rs @@ -28,7 +28,7 @@ async fn main() -> Result<()> { block_id } else { // ... fetch one from the node. - client.get_issuance().await?.strong_parents[0] + *client.get_issuance().await?.strong_parents.first().unwrap() }; // Send the request. diff --git a/sdk/examples/wallet/17_check_unlock_conditions.rs b/sdk/examples/wallet/17_check_unlock_conditions.rs index 6ffdb3e445..7e25489261 100644 --- a/sdk/examples/wallet/17_check_unlock_conditions.rs +++ b/sdk/examples/wallet/17_check_unlock_conditions.rs @@ -13,7 +13,7 @@ use iota_sdk::{ types::block::{ address::Bech32Address, - output::{unlock_condition::AddressUnlockCondition, BasicOutputBuilder, UnlockCondition}, + output::{unlock_condition::AddressUnlockCondition, BasicOutputBuilder}, }, wallet::Result, Wallet, @@ -46,10 +46,10 @@ async fn main() -> Result<()> { .add_unlock_condition(AddressUnlockCondition::new(*account_addresses[0].as_ref())) .finish_output(account.client().get_token_supply().await?)?; - let controlled_by_account = if let [UnlockCondition::Address(address_unlock_condition)] = output + let controlled_by_account = if let Ok(address_unlock_condition) = output .unlock_conditions() .expect("output needs to have unlock conditions") - .as_ref() + .single_address() { // Check that address in the unlock condition belongs to the account account_addresses diff --git a/sdk/src/types/block/core/basic.rs b/sdk/src/types/block/core/basic.rs index 0478cb8d30..8feef2978e 100644 --- a/sdk/src/types/block/core/basic.rs +++ b/sdk/src/types/block/core/basic.rs @@ -211,9 +211,9 @@ pub(crate) mod dto { fn from(value: &BasicBlock) -> Self { Self { kind: BasicBlock::KIND, - strong_parents: value.strong_parents.to_set(), - weak_parents: value.weak_parents.to_set(), - shallow_like_parents: value.shallow_like_parents.to_set(), + strong_parents: value.strong_parents.as_set().clone(), + weak_parents: value.weak_parents.as_set().clone(), + shallow_like_parents: value.shallow_like_parents.as_set().clone(), payload: value.payload.as_ref().map(Into::into), burned_mana: value.burned_mana, } diff --git a/sdk/src/types/block/core/mod.rs b/sdk/src/types/block/core/mod.rs index b33e60ef97..9b1a6d1caa 100644 --- a/sdk/src/types/block/core/mod.rs +++ b/sdk/src/types/block/core/mod.rs @@ -142,9 +142,9 @@ pub(crate) fn verify_parents( shallow_like_parents: &ShallowLikeParents, ) -> Result<(), Error> { let (strong_parents, weak_parents, shallow_like_parents) = ( - strong_parents.to_set(), - weak_parents.to_set(), - shallow_like_parents.to_set(), + strong_parents.as_set().clone(), + weak_parents.as_set().clone(), + shallow_like_parents.as_set().clone(), ); if !weak_parents.is_disjoint(&strong_parents) || !weak_parents.is_disjoint(&shallow_like_parents) { diff --git a/sdk/src/types/block/core/validation.rs b/sdk/src/types/block/core/validation.rs index 5fa03fd65c..c05a80b4b5 100644 --- a/sdk/src/types/block/core/validation.rs +++ b/sdk/src/types/block/core/validation.rs @@ -231,9 +231,9 @@ pub(crate) mod dto { fn from(value: &ValidationBlock) -> Self { Self { kind: ValidationBlock::KIND, - strong_parents: value.strong_parents.to_set(), - weak_parents: value.weak_parents.to_set(), - shallow_like_parents: value.shallow_like_parents.to_set(), + strong_parents: value.strong_parents.as_set().clone(), + weak_parents: value.weak_parents.as_set().clone(), + shallow_like_parents: value.shallow_like_parents.as_set().clone(), highest_supported_version: value.highest_supported_version, protocol_parameters_hash: value.protocol_parameters_hash, } diff --git a/sdk/src/types/block/error.rs b/sdk/src/types/block/error.rs index 66468f1c4a..6f974a055f 100644 --- a/sdk/src/types/block/error.rs +++ b/sdk/src/types/block/error.rs @@ -163,10 +163,11 @@ pub enum Error { index: usize, kind: u8, }, - UnallowedUnlockCondition { + DisallowedUnlockCondition { index: usize, kind: u8, }, + TooManyUnlockConditions, UnlockConditionsNotUniqueSorted, UnsupportedOutputKind(u8), DuplicateOutputChain(ChainId), @@ -364,9 +365,10 @@ impl fmt::Display for Error { Self::UnallowedFeature { index, kind } => { write!(f, "unallowed feature at index {index} with kind {kind}") } - Self::UnallowedUnlockCondition { index, kind } => { + Self::DisallowedUnlockCondition { index, kind } => { write!(f, "unallowed unlock condition at index {index} with kind {kind}") } + Self::TooManyUnlockConditions => write!(f, "too many unlock conditions"), Self::UnlockConditionsNotUniqueSorted => write!(f, "unlock conditions are not unique and/or sorted"), Self::UnsupportedOutputKind(k) => write!(f, "unsupported output kind: {k}"), Self::DuplicateOutputChain(chain_id) => write!(f, "duplicate output chain {chain_id}"), diff --git a/sdk/src/types/block/mana/allotment.rs b/sdk/src/types/block/mana/allotment.rs index aea2c875cb..e65844728f 100644 --- a/sdk/src/types/block/mana/allotment.rs +++ b/sdk/src/types/block/mana/allotment.rs @@ -30,6 +30,12 @@ impl Ord for ManaAllotment { } } +impl core::borrow::Borrow for ManaAllotment { + fn borrow(&self) -> &AccountId { + &self.account_id + } +} + impl ManaAllotment { pub fn new(account_id: AccountId, mana: u64, protocol_params: &ProtocolParameters) -> Result { if mana > protocol_params.mana_structure().max_mana() { diff --git a/sdk/src/types/block/mana/mod.rs b/sdk/src/types/block/mana/mod.rs index 1c3007aec1..454981aef1 100644 --- a/sdk/src/types/block/mana/mod.rs +++ b/sdk/src/types/block/mana/mod.rs @@ -4,26 +4,25 @@ mod allotment; mod protocol; -use alloc::{boxed::Box, collections::BTreeSet, vec::Vec}; +use alloc::{collections::BTreeSet, vec::Vec}; use core::ops::RangeInclusive; use derive_more::Deref; -use iterator_sorted::is_unique_sorted; -use packable::{bounded::BoundedU16, prefix::BoxedSlicePrefix, Packable}; +use packable::{bounded::BoundedU16, prefix::BTreeSetPrefix, set::UnpackSetError, Packable}; #[cfg(feature = "serde")] pub use self::allotment::dto::ManaAllotmentDto; pub use self::{allotment::ManaAllotment, protocol::ManaStructure}; -use super::{output::AccountId, protocol::ProtocolParameters, Error}; +use super::{protocol::ProtocolParameters, Error}; pub(crate) type ManaAllotmentCount = BoundedU16<{ *ManaAllotments::COUNT_RANGE.start() }, { *ManaAllotments::COUNT_RANGE.end() }>; /// A list of [`ManaAllotment`]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::InvalidManaAllotmentCount(p.into())))] +#[packable(unpack_error = Error, with = map_mana_allotment_set_error)] pub struct ManaAllotments( - #[packable(verify_with = verify_mana_allotments)] BoxedSlicePrefix, + #[packable(verify_with = verify_mana_allotments)] BTreeSetPrefix, ); impl ManaAllotments { @@ -36,11 +35,10 @@ impl ManaAllotments { /// Creates a new [`ManaAllotments`] from a vec. pub fn from_vec(allotments: Vec) -> Result { - verify_mana_allotments_unique_sorted(&allotments)?; - Ok(Self( allotments - .into_boxed_slice() + .into_iter() + .collect::>() .try_into() .map_err(Error::InvalidManaAllotmentCount)?, )) @@ -48,43 +46,32 @@ impl ManaAllotments { /// Creates a new [`ManaAllotments`] from an ordered set. pub fn from_set(allotments: BTreeSet) -> Result { - Ok(Self( - allotments - .into_iter() - .collect::>() - .try_into() - .map_err(Error::InvalidManaAllotmentCount)?, - )) + Ok(Self(allotments.try_into().map_err(Error::InvalidManaAllotmentCount)?)) } +} - /// Gets a reference to an [`ManaAllotment`], if one exists, using an [`AccountId`]. - #[inline(always)] - pub fn get(&self, account_id: &AccountId) -> Option<&ManaAllotment> { - self.0.iter().find(|a| a.account_id() == account_id) +fn map_mana_allotment_set_error(error: UnpackSetError) -> Error +where + >::Error: From

, +{ + match error { + UnpackSetError::DuplicateItem(_) => Error::ManaAllotmentsNotUniqueSorted, + UnpackSetError::Item(e) => e, + UnpackSetError::Prefix(p) => Error::InvalidManaAllotmentCount(p.into()), } } fn verify_mana_allotments( - allotments: &[ManaAllotment], + allotments: &BTreeSet, protocol_params: &ProtocolParameters, ) -> Result<(), Error> { if VERIFY { - verify_mana_allotments_unique_sorted(allotments)?; verify_mana_allotments_sum(allotments, protocol_params)?; } Ok(()) } -fn verify_mana_allotments_unique_sorted<'a>( - allotments: impl IntoIterator, -) -> Result<(), Error> { - if !is_unique_sorted(allotments.into_iter()) { - return Err(Error::ManaAllotmentsNotUniqueSorted); - } - Ok(()) -} - pub(crate) fn verify_mana_allotments_sum<'a>( allotments: impl IntoIterator, protocol_params: &ProtocolParameters, @@ -129,9 +116,9 @@ impl TryFrom> for ManaAllotments { impl IntoIterator for ManaAllotments { type Item = ManaAllotment; - type IntoIter = alloc::vec::IntoIter; + type IntoIter = alloc::collections::btree_set::IntoIter; fn into_iter(self) -> Self::IntoIter { - Vec::from(Into::>::into(self.0)).into_iter() + BTreeSet::from(self.0).into_iter() } } diff --git a/sdk/src/types/block/output/account.rs b/sdk/src/types/block/output/account.rs index fb0abff849..5a586c33f2 100644 --- a/sdk/src/types/block/output/account.rs +++ b/sdk/src/types/block/output/account.rs @@ -762,8 +762,8 @@ pub(crate) mod dto { #[serde(with = "string")] pub mana: u64, // Native tokens held by the output. - #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub native_tokens: Vec, + #[serde(skip_serializing_if = "BTreeSet::is_empty", default)] + pub native_tokens: BTreeSet, // Unique identifier of the account. pub account_id: AccountId, // A counter that must increase by 1 every time the account is state transitioned. @@ -774,13 +774,13 @@ pub(crate) mod dto { // A counter that denotes the number of foundries created by this account. pub foundry_counter: u32, // - pub unlock_conditions: Vec, + pub unlock_conditions: BTreeSet, // - #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub features: Vec, + #[serde(skip_serializing_if = "BTreeSet::is_empty", default)] + pub features: BTreeSet, // - #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub immutable_features: Vec, + #[serde(skip_serializing_if = "BTreeSet::is_empty", default)] + pub immutable_features: BTreeSet, } impl From<&AccountOutput> for AccountOutputDto { @@ -789,14 +789,14 @@ pub(crate) mod dto { kind: AccountOutput::KIND, amount: value.amount(), mana: value.mana(), - native_tokens: value.native_tokens().to_vec(), + native_tokens: value.native_tokens().as_set().clone(), account_id: *value.account_id(), state_index: value.state_index(), state_metadata: value.state_metadata().into(), foundry_counter: value.foundry_counter(), unlock_conditions: value.unlock_conditions().iter().map(Into::into).collect::<_>(), - features: value.features().to_vec(), - immutable_features: value.immutable_features().to_vec(), + features: value.features().as_set().clone(), + immutable_features: value.immutable_features().as_set().clone(), } } } @@ -828,14 +828,14 @@ pub(crate) mod dto { pub fn try_from_dtos<'a>( amount: OutputBuilderAmount, mana: u64, - native_tokens: Option>, + native_tokens: Option>, account_id: &AccountId, state_index: Option, state_metadata: Option>, foundry_counter: Option, - unlock_conditions: Vec, - features: Option>, - immutable_features: Option>, + unlock_conditions: BTreeSet, + features: Option>, + immutable_features: Option>, params: impl Into> + Send, ) -> Result { let params = params.into(); @@ -1001,14 +1001,14 @@ mod tests { let output_split = AccountOutput::try_from_dtos( OutputBuilderAmount::Amount(output.amount()), output.mana(), - Some(output.native_tokens().to_vec()), + Some(output.native_tokens().as_set().clone()), output.account_id(), output.state_index().into(), output.state_metadata().to_owned().into(), output.foundry_counter().into(), output.unlock_conditions().iter().map(Into::into).collect(), - Some(output.features().to_vec()), - Some(output.immutable_features().to_vec()), + Some(output.features().as_set().clone()), + Some(output.immutable_features().as_set().clone()), &protocol_parameters, ) .unwrap(); diff --git a/sdk/src/types/block/output/basic.rs b/sdk/src/types/block/output/basic.rs index f8734b6b4e..80f8eec571 100644 --- a/sdk/src/types/block/output/basic.rs +++ b/sdk/src/types/block/output/basic.rs @@ -310,7 +310,7 @@ impl BasicOutput { /// Simple deposit outputs are basic outputs with only an address unlock condition, no native tokens and no /// features. They are used to return storage deposits. pub fn simple_deposit_address(&self) -> Option<&Address> { - if let [UnlockCondition::Address(address)] = self.unlock_conditions().as_ref() { + if let Ok(address) = self.unlock_conditions().single_address() { if self.native_tokens.is_empty() && self.features.is_empty() { return Some(address.address()); } @@ -378,11 +378,11 @@ pub(crate) mod dto { #[serde(with = "string")] pub mana: u64, // Native tokens held by the output. - #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub native_tokens: Vec, - pub unlock_conditions: Vec, - #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub features: Vec, + #[serde(skip_serializing_if = "BTreeSet::is_empty", default)] + pub native_tokens: BTreeSet, + pub unlock_conditions: BTreeSet, + #[serde(skip_serializing_if = "BTreeSet::is_empty", default)] + pub features: BTreeSet, } impl From<&BasicOutput> for BasicOutputDto { @@ -391,9 +391,9 @@ pub(crate) mod dto { kind: BasicOutput::KIND, amount: value.amount(), mana: value.mana(), - native_tokens: value.native_tokens().to_vec(), + native_tokens: value.native_tokens().as_set().clone(), unlock_conditions: value.unlock_conditions().iter().map(Into::into).collect::<_>(), - features: value.features().to_vec(), + features: value.features().as_set().clone(), } } } @@ -420,9 +420,9 @@ pub(crate) mod dto { pub fn try_from_dtos<'a>( amount: OutputBuilderAmount, mana: u64, - native_tokens: Option>, - unlock_conditions: Vec, - features: Option>, + native_tokens: Option>, + unlock_conditions: BTreeSet, + features: Option>, params: impl Into> + Send, ) -> Result { let params = params.into(); @@ -542,9 +542,9 @@ mod tests { let output_split = BasicOutput::try_from_dtos( OutputBuilderAmount::Amount(output.amount()), output.mana(), - Some(output.native_tokens().to_vec()), + Some(output.native_tokens().as_set().clone()), output.unlock_conditions().iter().map(Into::into).collect(), - Some(output.features().to_vec()), + Some(output.features().as_set().clone()), protocol_parameters.token_supply(), ) .unwrap(); diff --git a/sdk/src/types/block/output/feature/block_issuer.rs b/sdk/src/types/block/output/feature/block_issuer.rs index c7682bdf33..61e6b62933 100644 --- a/sdk/src/types/block/output/feature/block_issuer.rs +++ b/sdk/src/types/block/output/feature/block_issuer.rs @@ -1,17 +1,17 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use alloc::{boxed::Box, collections::BTreeSet, vec::Vec}; +use alloc::{collections::BTreeSet, vec::Vec}; use core::ops::RangeInclusive; use crypto::signatures::ed25519; use derive_more::{AsRef, Deref, From}; -use iterator_sorted::is_unique_sorted; use packable::{ bounded::BoundedU8, error::{UnpackError, UnpackErrorExt}, packer::Packer, - prefix::BoxedSlicePrefix, + prefix::BTreeSetPrefix, + set::UnpackSetError, unpacker::Unpacker, Packable, }; @@ -104,20 +104,18 @@ pub(crate) type BlockIssuerKeyCount = /// Lexicographically ordered list of unique [`BlockIssuerKey`] #[derive(Clone, Debug, Eq, PartialEq, Deref, Packable, Hash)] -#[packable(unpack_error = Error, with = |e| e.unwrap_item_err_or_else(|p| Error::InvalidBlockIssuerKeyCount(p.into())))] -pub struct BlockIssuerKeys( - #[packable(verify_with = verify_block_issuer_keys)] BoxedSlicePrefix, -); - -fn verify_block_issuer_keys( - block_issuer_keys: &[BlockIssuerKey], - _visitor: &(), -) -> Result<(), Error> { - if VERIFY && !is_unique_sorted(block_issuer_keys.iter()) { - return Err(Error::BlockIssuerKeysNotUniqueSorted); +#[packable(unpack_error = Error, with = map_block_issuer_keys_set_error)] +pub struct BlockIssuerKeys(BTreeSetPrefix); + +fn map_block_issuer_keys_set_error(error: UnpackSetError) -> Error +where + >::Error: From

, +{ + match error { + UnpackSetError::DuplicateItem(_) => Error::BlockIssuerKeysNotUniqueSorted, + UnpackSetError::Item(e) => e, + UnpackSetError::Prefix(p) => Error::InvalidBlockIssuerKeyCount(p.into()), } - - Ok(()) } impl TryFrom> for BlockIssuerKeys { @@ -140,10 +138,10 @@ impl TryFrom> for BlockIssuerKeys { impl IntoIterator for BlockIssuerKeys { type Item = BlockIssuerKey; - type IntoIter = alloc::vec::IntoIter; + type IntoIter = alloc::collections::btree_set::IntoIter; fn into_iter(self) -> Self::IntoIter { - Vec::from(Into::>::into(self.0)).into_iter() + BTreeSet::from(self.0).into_iter() } } @@ -157,27 +155,22 @@ impl BlockIssuerKeys { /// Creates a new [`BlockIssuerKeys`] from a vec. pub fn from_vec(block_issuer_keys: Vec) -> Result { - let mut block_issuer_keys = - BoxedSlicePrefix::::try_from(block_issuer_keys.into_boxed_slice()) - .map_err(Error::InvalidBlockIssuerKeyCount)?; - - block_issuer_keys.sort(); - - // Still need to verify the duplicate block issuer keys. - verify_block_issuer_keys::(&block_issuer_keys, &())?; - - Ok(Self(block_issuer_keys)) + Ok(Self( + block_issuer_keys + .into_iter() + .collect::>() + .try_into() + .map_err(Error::InvalidBlockIssuerKeyCount)?, + )) } /// Creates a new [`BlockIssuerKeys`] from an ordered set. pub fn from_set(block_issuer_keys: BTreeSet) -> Result { - let block_issuer_keys = BoxedSlicePrefix::::try_from( - block_issuer_keys.into_iter().collect::>(), - ) - .map_err(Error::InvalidBlockIssuerKeyCount)?; - - // We don't need to verify the block issuer keys here, because they are already verified by the BTreeSet. - Ok(Self(block_issuer_keys)) + Ok(Self( + block_issuer_keys + .try_into() + .map_err(Error::InvalidBlockIssuerKeyCount)?, + )) } } @@ -216,7 +209,7 @@ impl BlockIssuerFeature { } /// Returns the Block Issuer Keys. - pub fn block_issuer_keys(&self) -> &[BlockIssuerKey] { + pub fn block_issuer_keys(&self) -> &BlockIssuerKeys { &self.block_issuer_keys } } diff --git a/sdk/src/types/block/output/feature/mod.rs b/sdk/src/types/block/output/feature/mod.rs index 33904fe62e..69dd640f8d 100644 --- a/sdk/src/types/block/output/feature/mod.rs +++ b/sdk/src/types/block/output/feature/mod.rs @@ -8,12 +8,11 @@ mod sender; mod staking; mod tag; -use alloc::{boxed::Box, collections::BTreeSet, vec::Vec}; +use alloc::{collections::BTreeSet, vec::Vec}; use bitflags::bitflags; use derive_more::{Deref, From}; -use iterator_sorted::is_unique_sorted; -use packable::{bounded::BoundedU8, prefix::BoxedSlicePrefix, Packable}; +use packable::{bounded::BoundedU8, prefix::BTreeSetPrefix, set::UnpackSetError, Packable}; #[cfg(feature = "irc_27")] pub use self::metadata::irc_27::{Attribute, Irc27Metadata}; @@ -68,6 +67,12 @@ impl Ord for Feature { } } +impl core::borrow::Borrow for Feature { + fn borrow(&self) -> &u8 { + &self.ord_kind() + } +} + impl core::fmt::Debug for Feature { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { @@ -84,13 +89,17 @@ impl core::fmt::Debug for Feature { impl Feature { /// Return the output kind of an `Output`. pub fn kind(&self) -> u8 { + *self.ord_kind() + } + + fn ord_kind<'a>(&'a self) -> &'a u8 { match self { - Self::Sender(_) => SenderFeature::KIND, - Self::Issuer(_) => IssuerFeature::KIND, - Self::Metadata(_) => MetadataFeature::KIND, - Self::Tag(_) => TagFeature::KIND, - Self::BlockIssuer(_) => BlockIssuerFeature::KIND, - Self::Staking(_) => StakingFeature::KIND, + Self::Sender(_) => &SenderFeature::KIND, + Self::Issuer(_) => &IssuerFeature::KIND, + Self::Metadata(_) => &MetadataFeature::KIND, + Self::Tag(_) => &TagFeature::KIND, + Self::BlockIssuer(_) => &BlockIssuerFeature::KIND, + Self::Staking(_) => &StakingFeature::KIND, } } @@ -215,8 +224,19 @@ pub(crate) type FeatureCount = BoundedU8<0, { Features::COUNT_MAX }>; /// #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Deref, Packable)] -#[packable(unpack_error = Error, with = |e| e.unwrap_item_err_or_else(|p| Error::InvalidFeatureCount(p.into())))] -pub struct Features(#[packable(verify_with = verify_unique_sorted)] BoxedSlicePrefix); +#[packable(unpack_error = Error, with = map_features_set_error)] +pub struct Features(BTreeSetPrefix); + +fn map_features_set_error(error: UnpackSetError) -> Error +where + >::Error: From

, +{ + match error { + UnpackSetError::DuplicateItem(_) => Error::FeaturesNotUniqueSorted, + UnpackSetError::Item(e) => e, + UnpackSetError::Prefix(p) => Error::InvalidFeatureCount(p.into()), + } +} impl TryFrom> for Features { type Error = Error; @@ -238,10 +258,10 @@ impl TryFrom> for Features { impl IntoIterator for Features { type Item = Feature; - type IntoIter = alloc::vec::IntoIter; + type IntoIter = alloc::collections::btree_set::IntoIter; fn into_iter(self) -> Self::IntoIter { - Vec::from(Into::>::into(self.0)).into_iter() + BTreeSet::from(self.0).into_iter() } } @@ -251,74 +271,53 @@ impl Features { /// Creates a new [`Features`] from a vec. pub fn from_vec(features: Vec) -> Result { - let mut features = BoxedSlicePrefix::::try_from(features.into_boxed_slice()) - .map_err(Error::InvalidFeatureCount)?; - - features.sort_by_key(Feature::kind); - // Sort is obviously fine now but uniqueness still needs to be checked. - verify_unique_sorted::(&features, &())?; - - Ok(Self(features)) - } - - /// Creates a new [`Features`] from an ordered set. - pub fn from_set(features: BTreeSet) -> Result { Ok(Self( features .into_iter() - .collect::>() + .collect::>() .try_into() .map_err(Error::InvalidFeatureCount)?, )) } - /// Gets a reference to a [`Feature`] from a feature kind, if any. - #[inline(always)] - pub fn get(&self, key: u8) -> Option<&Feature> { - self.0 - .binary_search_by_key(&key, Feature::kind) - // PANIC: indexation is fine since the index has been found. - .map(|index| &self.0[index]) - .ok() + /// Creates a new [`Features`] from an ordered set. + pub fn from_set(features: BTreeSet) -> Result { + Ok(Self(features.try_into().map_err(Error::InvalidFeatureCount)?)) + } + + /// Gets the underlying set. + pub fn as_set(&self) -> &BTreeSet { + &self.0 } /// Gets a reference to a [`SenderFeature`], if any. pub fn sender(&self) -> Option<&SenderFeature> { - self.get(SenderFeature::KIND).map(Feature::as_sender) + self.get(&SenderFeature::KIND).map(Feature::as_sender) } /// Gets a reference to a [`IssuerFeature`], if any. pub fn issuer(&self) -> Option<&IssuerFeature> { - self.get(IssuerFeature::KIND).map(Feature::as_issuer) + self.get(&IssuerFeature::KIND).map(Feature::as_issuer) } /// Gets a reference to a [`MetadataFeature`], if any. pub fn metadata(&self) -> Option<&MetadataFeature> { - self.get(MetadataFeature::KIND).map(Feature::as_metadata) + self.get(&MetadataFeature::KIND).map(Feature::as_metadata) } /// Gets a reference to a [`TagFeature`], if any. pub fn tag(&self) -> Option<&TagFeature> { - self.get(TagFeature::KIND).map(Feature::as_tag) + self.get(&TagFeature::KIND).map(Feature::as_tag) } /// Gets a reference to a [`BlockIssuerFeature`], if any. pub fn block_issuer(&self) -> Option<&BlockIssuerFeature> { - self.get(BlockIssuerFeature::KIND).map(Feature::as_block_issuer) + self.get(&BlockIssuerFeature::KIND).map(Feature::as_block_issuer) } /// Gets a reference to a [`StakingFeature`], if any. pub fn staking(&self) -> Option<&StakingFeature> { - self.get(StakingFeature::KIND).map(Feature::as_staking) - } -} - -#[inline] -fn verify_unique_sorted(features: &[Feature], _: &()) -> Result<(), Error> { - if VERIFY && !is_unique_sorted(features.iter().map(Feature::kind)) { - Err(Error::FeaturesNotUniqueSorted) - } else { - Ok(()) + self.get(&StakingFeature::KIND).map(Feature::as_staking) } } diff --git a/sdk/src/types/block/output/foundry.rs b/sdk/src/types/block/output/foundry.rs index 7a3249a711..a71eeae866 100644 --- a/sdk/src/types/block/output/foundry.rs +++ b/sdk/src/types/block/output/foundry.rs @@ -637,16 +637,16 @@ pub(crate) mod dto { #[serde(with = "string")] pub amount: u64, // Native tokens held by the output. - #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub native_tokens: Vec, + #[serde(skip_serializing_if = "BTreeSet::is_empty", default)] + pub native_tokens: BTreeSet, // The serial number of the foundry with respect to the controlling account. pub serial_number: u32, pub token_scheme: TokenScheme, - pub unlock_conditions: Vec, - #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub features: Vec, - #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub immutable_features: Vec, + pub unlock_conditions: BTreeSet, + #[serde(skip_serializing_if = "BTreeSet::is_empty", default)] + pub features: BTreeSet, + #[serde(skip_serializing_if = "BTreeSet::is_empty", default)] + pub immutable_features: BTreeSet, } impl From<&FoundryOutput> for FoundryOutputDto { @@ -654,12 +654,12 @@ pub(crate) mod dto { Self { kind: FoundryOutput::KIND, amount: value.amount(), - native_tokens: value.native_tokens().to_vec(), + native_tokens: value.native_tokens().as_set().clone(), serial_number: value.serial_number(), token_scheme: value.token_scheme().clone(), unlock_conditions: value.unlock_conditions().iter().map(Into::into).collect::<_>(), - features: value.features().to_vec(), - immutable_features: value.immutable_features().to_vec(), + features: value.features().as_set().clone(), + immutable_features: value.immutable_features().as_set().clone(), } } } @@ -695,12 +695,12 @@ pub(crate) mod dto { #[allow(clippy::too_many_arguments)] pub fn try_from_dtos<'a>( amount: OutputBuilderAmount, - native_tokens: Option>, + native_tokens: Option>, serial_number: u32, token_scheme: TokenScheme, - unlock_conditions: Vec, - features: Option>, - immutable_features: Option>, + unlock_conditions: BTreeSet, + features: Option>, + immutable_features: Option>, params: impl Into> + Send, ) -> Result { let params = params.into(); diff --git a/sdk/src/types/block/output/native_token.rs b/sdk/src/types/block/output/native_token.rs index fb9f8851a7..92e88e5e21 100644 --- a/sdk/src/types/block/output/native_token.rs +++ b/sdk/src/types/block/output/native_token.rs @@ -2,14 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 use alloc::{ - boxed::Box, collections::{BTreeMap, BTreeSet}, vec::Vec, }; use derive_more::{Deref, DerefMut, From}; -use iterator_sorted::is_unique_sorted; -use packable::{bounded::BoundedU8, prefix::BoxedSlicePrefix, Packable}; +use packable::{bounded::BoundedU8, prefix::BTreeSetPrefix, set::UnpackSetError, Packable}; use primitive_types::U256; use crate::types::block::{output::TokenId, Error}; @@ -62,6 +60,12 @@ impl Ord for NativeToken { } } +impl core::borrow::Borrow for NativeToken { + fn borrow(&self) -> &TokenId { + &self.token_id + } +} + #[inline] fn verify_amount(amount: &U256, _: &()) -> Result<(), Error> { if VERIFY && amount.is_zero() { @@ -153,10 +157,19 @@ pub(crate) type NativeTokenCount = BoundedU8<0, { NativeTokens::COUNT_MAX }>; /// #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Deref, Packable)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[packable(unpack_error = Error, with = |e| e.unwrap_item_err_or_else(|p| Error::InvalidNativeTokenCount(p.into())))] -pub struct NativeTokens( - #[packable(verify_with = verify_unique_sorted)] BoxedSlicePrefix, -); +#[packable(unpack_error = Error, with = map_native_tokens_set_error)] +pub struct NativeTokens(BTreeSetPrefix); + +fn map_native_tokens_set_error(error: UnpackSetError) -> Error +where + >::Error: From

, +{ + match error { + UnpackSetError::DuplicateItem(_) => Error::NativeTokensNotUniqueSorted, + UnpackSetError::Item(e) => e, + UnpackSetError::Prefix(p) => Error::InvalidNativeTokenCount(p.into()), + } +} impl TryFrom> for NativeTokens { type Error = Error; @@ -178,10 +191,10 @@ impl TryFrom> for NativeTokens { impl IntoIterator for NativeTokens { type Item = NativeToken; - type IntoIter = alloc::vec::IntoIter; + type IntoIter = alloc::collections::btree_set::IntoIter; fn into_iter(self) -> Self::IntoIter { - Vec::from(Into::>::into(self.0)).into_iter() + BTreeSet::from(self.0).into_iter() } } @@ -191,56 +204,28 @@ impl NativeTokens { /// Creates a new [`NativeTokens`] from a vec. pub fn from_vec(native_tokens: Vec) -> Result { - let mut native_tokens = - BoxedSlicePrefix::::try_from(native_tokens.into_boxed_slice()) - .map_err(Error::InvalidNativeTokenCount)?; - - native_tokens.sort_by(|a, b| a.token_id().cmp(b.token_id())); - // Sort is obviously fine now but uniqueness still needs to be checked. - verify_unique_sorted::(&native_tokens, &())?; - - Ok(Self(native_tokens)) - } - - /// Creates a new [`NativeTokens`] from an ordered set. - pub fn from_set(native_tokens: BTreeSet) -> Result { Ok(Self( native_tokens .into_iter() - .collect::>() + .collect::>() .try_into() .map_err(Error::InvalidNativeTokenCount)?, )) } - /// Creates a new [`NativeTokensBuilder`]. - #[inline(always)] - pub fn build() -> NativeTokensBuilder { - NativeTokensBuilder::new() - } - - /// Checks whether the provided token ID is contained in the native tokens. - pub fn contains(&self, token_id: &TokenId) -> bool { - // Binary search is possible because native tokens are always ordered by token ID. - self.0 - .binary_search_by_key(token_id, |native_token| native_token.token_id) - .is_ok() + /// Creates a new [`NativeTokens`] from an ordered set. + pub fn from_set(native_tokens: BTreeSet) -> Result { + Ok(Self(native_tokens.try_into().map_err(Error::InvalidNativeTokenCount)?)) } - /// Gets the native token associated with the provided token ID if contained. - pub fn get(&self, token_id: &TokenId) -> Option<&NativeToken> { - // Binary search is possible because native tokens are always ordered by token ID. - self.0 - .binary_search_by_key(token_id, |native_token| native_token.token_id) - .map_or(None, |index| Some(&self.0[index])) + /// Gets the underlying set. + pub fn as_set(&self) -> &BTreeSet { + &self.0 } -} -#[inline] -fn verify_unique_sorted(native_tokens: &[NativeToken], _: &()) -> Result<(), Error> { - if VERIFY && !is_unique_sorted(native_tokens.iter().map(NativeToken::token_id)) { - Err(Error::NativeTokensNotUniqueSorted) - } else { - Ok(()) + /// Creates a new [`NativeTokensBuilder`]. + #[inline(always)] + pub fn build() -> NativeTokensBuilder { + NativeTokensBuilder::new() } } diff --git a/sdk/src/types/block/output/nft.rs b/sdk/src/types/block/output/nft.rs index 98133eeb5c..80b15c5aca 100644 --- a/sdk/src/types/block/output/nft.rs +++ b/sdk/src/types/block/output/nft.rs @@ -533,15 +533,15 @@ pub(crate) mod dto { #[serde(with = "string")] pub mana: u64, // Native tokens held by the output. - #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub native_tokens: Vec, + #[serde(skip_serializing_if = "BTreeSet::is_empty", default)] + pub native_tokens: BTreeSet, // Unique identifier of the NFT. pub nft_id: NftId, - pub unlock_conditions: Vec, - #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub features: Vec, - #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub immutable_features: Vec, + pub unlock_conditions: BTreeSet, + #[serde(skip_serializing_if = "BTreeSet::is_empty", default)] + pub features: BTreeSet, + #[serde(skip_serializing_if = "BTreeSet::is_empty", default)] + pub immutable_features: BTreeSet, } impl From<&NftOutput> for NftOutputDto { @@ -550,11 +550,11 @@ pub(crate) mod dto { kind: NftOutput::KIND, amount: value.amount(), mana: value.mana(), - native_tokens: value.native_tokens().to_vec(), + native_tokens: value.native_tokens().as_set().clone(), nft_id: *value.nft_id(), unlock_conditions: value.unlock_conditions().iter().map(Into::into).collect::<_>(), - features: value.features().to_vec(), - immutable_features: value.immutable_features().to_vec(), + features: value.features().as_set().clone(), + immutable_features: value.immutable_features().as_set().clone(), } } } @@ -583,11 +583,11 @@ pub(crate) mod dto { pub fn try_from_dtos<'a>( amount: OutputBuilderAmount, mana: u64, - native_tokens: Option>, + native_tokens: Option>, nft_id: &NftId, - unlock_conditions: Vec, - features: Option>, - immutable_features: Option>, + unlock_conditions: BTreeSet, + features: Option>, + immutable_features: Option>, params: impl Into> + Send, ) -> Result { let params = params.into(); @@ -715,11 +715,11 @@ mod tests { let output_split = NftOutput::try_from_dtos( OutputBuilderAmount::Amount(output.amount()), output.mana(), - Some(output.native_tokens().to_vec()), + Some(output.native_tokens().as_set().clone()), output.nft_id(), output.unlock_conditions().iter().map(Into::into).collect(), - Some(output.features().to_vec()), - Some(output.immutable_features().to_vec()), + Some(output.features().as_set().clone()), + Some(output.immutable_features().as_set().clone()), &protocol_parameters, ) .unwrap(); diff --git a/sdk/src/types/block/output/unlock_condition/mod.rs b/sdk/src/types/block/output/unlock_condition/mod.rs index de473e337f..dc84b72704 100644 --- a/sdk/src/types/block/output/unlock_condition/mod.rs +++ b/sdk/src/types/block/output/unlock_condition/mod.rs @@ -9,16 +9,16 @@ mod state_controller_address; mod storage_deposit_return; mod timelock; -use alloc::{boxed::Box, collections::BTreeSet, vec::Vec}; +use alloc::{collections::BTreeSet, vec::Vec}; use bitflags::bitflags; use derive_more::{Deref, From}; -use iterator_sorted::is_unique_sorted; use packable::{ bounded::BoundedU8, error::{UnpackError, UnpackErrorExt}, packer::Packer, - prefix::BoxedSlicePrefix, + prefix::BTreeSetPrefix, + set::UnpackSetError, unpacker::Unpacker, Packable, }; @@ -61,6 +61,11 @@ impl Ord for UnlockCondition { self.kind().cmp(&other.kind()) } } +impl core::borrow::Borrow for UnlockCondition { + fn borrow(&self) -> &u8 { + &self.ord_kind() + } +} impl core::fmt::Debug for UnlockCondition { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { @@ -79,14 +84,18 @@ impl core::fmt::Debug for UnlockCondition { impl UnlockCondition { /// Return the output kind of an `Output`. pub fn kind(&self) -> u8 { + *self.ord_kind() + } + + fn ord_kind<'a>(&'a self) -> &'a u8 { match self { - Self::Address(_) => AddressUnlockCondition::KIND, - Self::StorageDepositReturn(_) => StorageDepositReturnUnlockCondition::KIND, - Self::Timelock(_) => TimelockUnlockCondition::KIND, - Self::Expiration(_) => ExpirationUnlockCondition::KIND, - Self::StateControllerAddress(_) => StateControllerAddressUnlockCondition::KIND, - Self::GovernorAddress(_) => GovernorAddressUnlockCondition::KIND, - Self::ImmutableAccountAddress(_) => ImmutableAccountAddressUnlockCondition::KIND, + Self::Address(_) => &AddressUnlockCondition::KIND, + Self::StorageDepositReturn(_) => &StorageDepositReturnUnlockCondition::KIND, + Self::Timelock(_) => &TimelockUnlockCondition::KIND, + Self::Expiration(_) => &ExpirationUnlockCondition::KIND, + Self::StateControllerAddress(_) => &StateControllerAddressUnlockCondition::KIND, + Self::GovernorAddress(_) => &GovernorAddressUnlockCondition::KIND, + Self::ImmutableAccountAddress(_) => &ImmutableAccountAddressUnlockCondition::KIND, } } @@ -298,11 +307,20 @@ pub(crate) type UnlockConditionCount = BoundedU8<0, { UnlockConditions::COUNT_MA /// #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Deref, Packable)] -#[packable(unpack_error = Error, with = |e| e.unwrap_item_err_or_else(|p| Error::InvalidUnlockConditionCount(p.into())))] +#[packable(unpack_error = Error, with = map_unlock_conditions_set_error)] #[packable(unpack_visitor = ProtocolParameters)] -pub struct UnlockConditions( - #[packable(verify_with = verify_unique_sorted_packable)] BoxedSlicePrefix, -); +pub struct UnlockConditions(BTreeSetPrefix); + +fn map_unlock_conditions_set_error(error: UnpackSetError) -> Error +where + >::Error: From

, +{ + match error { + UnpackSetError::DuplicateItem(_) => Error::UnlockConditionsNotUniqueSorted, + UnpackSetError::Item(e) => e, + UnpackSetError::Prefix(p) => Error::InvalidUnlockConditionCount(p.into()), + } +} impl TryFrom> for UnlockConditions { type Error = Error; @@ -324,10 +342,10 @@ impl TryFrom> for UnlockConditions { impl IntoIterator for UnlockConditions { type Item = UnlockCondition; - type IntoIter = alloc::vec::IntoIter; + type IntoIter = alloc::collections::btree_set::IntoIter; fn into_iter(self) -> Self::IntoIter { - Vec::from(Into::>::into(self.0)).into_iter() + BTreeSet::from(self.0).into_iter() } } @@ -337,83 +355,86 @@ impl UnlockConditions { /// Creates a new [`UnlockConditions`] from a vec. pub fn from_vec(unlock_conditions: Vec) -> Result { - let mut unlock_conditions = - BoxedSlicePrefix::::try_from(unlock_conditions.into_boxed_slice()) - .map_err(Error::InvalidUnlockConditionCount)?; - - unlock_conditions.sort_by_key(UnlockCondition::kind); - // Sort is obviously fine now but uniqueness still needs to be checked. - verify_unique_sorted::(&unlock_conditions)?; - - Ok(Self(unlock_conditions)) + Ok(Self( + unlock_conditions + .into_iter() + .collect::>() + .try_into() + .map_err(Error::InvalidUnlockConditionCount)?, + )) } /// Creates a new [`UnlockConditions`] from an ordered set. pub fn from_set(unlock_conditions: BTreeSet) -> Result { Ok(Self( unlock_conditions - .into_iter() - .collect::>() .try_into() .map_err(Error::InvalidUnlockConditionCount)?, )) } - /// Gets a reference to an [`UnlockCondition`] from an unlock condition kind, if any. - #[inline(always)] - pub fn get(&self, key: u8) -> Option<&UnlockCondition> { - self.0 - .binary_search_by_key(&key, UnlockCondition::kind) - // PANIC: indexation is fine since the index has been found. - .map(|index| &self.0[index]) - .ok() + /// Gets the underlying set. + pub fn as_set(&self) -> &BTreeSet { + &self.0 } /// Gets a reference to an [`AddressUnlockCondition`], if any. #[inline(always)] pub fn address(&self) -> Option<&AddressUnlockCondition> { - self.get(AddressUnlockCondition::KIND).map(UnlockCondition::as_address) + self.get(&AddressUnlockCondition::KIND).map(UnlockCondition::as_address) + } + + /// Gets a reference to the [`AddressUnlockCondition`], if it is the only unlock condition. + #[inline(always)] + pub fn single_address(&self) -> Result<&AddressUnlockCondition, Error> { + (self.len() == 1) + .then(|| { + self.get(&AddressUnlockCondition::KIND) + .map(UnlockCondition::as_address) + .ok_or(Error::MissingAddressUnlockCondition) + }) + .ok_or(Error::TooManyUnlockConditions)? } /// Gets a reference to a [`StorageDepositReturnUnlockCondition`], if any. #[inline(always)] pub fn storage_deposit_return(&self) -> Option<&StorageDepositReturnUnlockCondition> { - self.get(StorageDepositReturnUnlockCondition::KIND) + self.get(&StorageDepositReturnUnlockCondition::KIND) .map(UnlockCondition::as_storage_deposit_return) } /// Gets a reference to a [`TimelockUnlockCondition`], if any. #[inline(always)] pub fn timelock(&self) -> Option<&TimelockUnlockCondition> { - self.get(TimelockUnlockCondition::KIND) + self.get(&TimelockUnlockCondition::KIND) .map(UnlockCondition::as_timelock) } /// Gets a reference to an [`ExpirationUnlockCondition`], if any. #[inline(always)] pub fn expiration(&self) -> Option<&ExpirationUnlockCondition> { - self.get(ExpirationUnlockCondition::KIND) + self.get(&ExpirationUnlockCondition::KIND) .map(UnlockCondition::as_expiration) } /// Gets a reference to a [`StateControllerAddressUnlockCondition`], if any. #[inline(always)] pub fn state_controller_address(&self) -> Option<&StateControllerAddressUnlockCondition> { - self.get(StateControllerAddressUnlockCondition::KIND) + self.get(&StateControllerAddressUnlockCondition::KIND) .map(UnlockCondition::as_state_controller_address) } /// Gets a reference to a [`GovernorAddressUnlockCondition`], if any. #[inline(always)] pub fn governor_address(&self) -> Option<&GovernorAddressUnlockCondition> { - self.get(GovernorAddressUnlockCondition::KIND) + self.get(&GovernorAddressUnlockCondition::KIND) .map(UnlockCondition::as_governor_address) } /// Gets a reference to an [`ImmutableAccountAddressUnlockCondition`], if any. #[inline(always)] pub fn immutable_account_address(&self) -> Option<&ImmutableAccountAddressUnlockCondition> { - self.get(ImmutableAccountAddressUnlockCondition::KIND) + self.get(&ImmutableAccountAddressUnlockCondition::KIND) .map(UnlockCondition::as_immutable_account_address) } @@ -444,30 +465,13 @@ impl UnlockConditions { } } -#[inline] -fn verify_unique_sorted(unlock_conditions: &[UnlockCondition]) -> Result<(), Error> { - if VERIFY && !is_unique_sorted(unlock_conditions.iter().map(UnlockCondition::kind)) { - Err(Error::UnlockConditionsNotUniqueSorted) - } else { - Ok(()) - } -} - -#[inline] -fn verify_unique_sorted_packable( - unlock_conditions: &[UnlockCondition], - _: &ProtocolParameters, -) -> Result<(), Error> { - verify_unique_sorted::(unlock_conditions) -} - pub(crate) fn verify_allowed_unlock_conditions( unlock_conditions: &UnlockConditions, allowed_unlock_conditions: UnlockConditionFlags, ) -> Result<(), Error> { for (index, unlock_condition) in unlock_conditions.iter().enumerate() { if !allowed_unlock_conditions.contains(unlock_condition.flag()) { - return Err(Error::UnallowedUnlockCondition { + return Err(Error::DisallowedUnlockCondition { index, kind: unlock_condition.kind(), }); @@ -572,4 +576,15 @@ pub mod dto { } } } + + impl PartialOrd for UnlockConditionDto { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } + } + impl Ord for UnlockConditionDto { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.kind().cmp(&other.kind()) + } + } } diff --git a/sdk/src/types/block/output/unlock_condition/storage_deposit_return.rs b/sdk/src/types/block/output/unlock_condition/storage_deposit_return.rs index bec25f3eca..e4517f9018 100644 --- a/sdk/src/types/block/output/unlock_condition/storage_deposit_return.rs +++ b/sdk/src/types/block/output/unlock_condition/storage_deposit_return.rs @@ -129,4 +129,15 @@ pub(crate) mod dto { StorageDepositReturnUnlockConditionDto::from(self).serialize(s) } } + + impl Ord for StorageDepositReturnUnlockConditionDto { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.kind.cmp(&other.kind) + } + } + impl PartialOrd for StorageDepositReturnUnlockConditionDto { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(&other)) + } + } } diff --git a/sdk/src/types/block/parent.rs b/sdk/src/types/block/parent.rs index 7c2116f319..673014050c 100644 --- a/sdk/src/types/block/parent.rs +++ b/sdk/src/types/block/parent.rs @@ -3,12 +3,11 @@ //! The parents module defines the core data type for storing the blocks directly approved by a block. -use alloc::{boxed::Box, collections::BTreeSet, vec::Vec}; +use alloc::{collections::BTreeSet, vec::Vec}; use core::ops::RangeInclusive; use derive_more::Deref; -use iterator_sorted::is_unique_sorted; -use packable::{bounded::BoundedU8, prefix::BoxedSlicePrefix, Packable}; +use packable::{bounded::BoundedU8, prefix::BTreeSetPrefix, Packable}; use crate::types::block::{BlockId, Error}; @@ -22,22 +21,18 @@ use crate::types::block::{BlockId, Error}; #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[deref(forward)] #[packable(unpack_error = Error, with = |_| Error::InvalidParentCount)] -pub struct Parents( - #[packable(verify_with = verify_parents)] BoxedSlicePrefix>, -); +pub struct Parents(BTreeSetPrefix>); impl Parents { /// The range representing the valid number of parents. pub const COUNT_RANGE: RangeInclusive = MIN..=MAX; /// Creates new [`Parents`] from a vec. - pub fn from_vec(mut inner: Vec) -> Result { - inner.sort_unstable(); - inner.dedup(); - + pub fn from_vec(inner: Vec) -> Result { Ok(Self( inner - .into_boxed_slice() + .into_iter() + .collect::>() .try_into() .map_err(|_| Error::InvalidParentCount)?, )) @@ -45,18 +40,12 @@ impl Parents { /// Creates new [`Parents`] from an ordered set. pub fn from_set(inner: BTreeSet) -> Result { - Ok(Self( - inner - .into_iter() - .collect::>() - .try_into() - .map_err(|_| Error::InvalidParentCount)?, - )) + Ok(Self(inner.try_into().map_err(|_| Error::InvalidParentCount)?)) } - /// Returns the unique, ordered set of parents. - pub fn to_set(&self) -> BTreeSet { - self.0.iter().copied().collect() + /// Gets the underlying set. + pub fn as_set(&self) -> &BTreeSet { + &self.0 } /// Returns the number of parents. @@ -75,20 +64,21 @@ impl Parents { } } -fn verify_parents(parents: &[BlockId], _: &()) -> Result<(), Error> { - if VERIFY && !is_unique_sorted(parents.iter().map(AsRef::as_ref)) { - Err(Error::ParentsNotUniqueSorted) - } else { - Ok(()) - } -} - impl Default for Parents<0, MAX> { fn default() -> Self { Self(Default::default()) } } +impl IntoIterator for Parents { + type Item = BlockId; + type IntoIter = alloc::collections::btree_set::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + BTreeSet::from(self.0).into_iter() + } +} + pub type StrongParents = Parents<1, 8>; pub type WeakParents = Parents<0, 8>; pub type ShallowLikeParents = Parents<0, 8>; diff --git a/sdk/src/types/block/payload/transaction/essence/regular.rs b/sdk/src/types/block/payload/transaction/essence/regular.rs index c21dfb0ddd..d551b993e5 100644 --- a/sdk/src/types/block/payload/transaction/essence/regular.rs +++ b/sdk/src/types/block/payload/transaction/essence/regular.rs @@ -264,7 +264,7 @@ impl RegularTransactionEssence { } /// Returns the [`ManaAllotment`]s of a [`RegularTransactionEssence`]. - pub fn mana_allotments(&self) -> &[ManaAllotment] { + pub fn mana_allotments(&self) -> &ManaAllotments { &self.allotments } diff --git a/sdk/src/types/block/rand/block.rs b/sdk/src/types/block/rand/block.rs index 188ecf92ef..578557c53a 100644 --- a/sdk/src/types/block/rand/block.rs +++ b/sdk/src/types/block/rand/block.rs @@ -1,7 +1,7 @@ // Copyright 2020-2021 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use alloc::vec::Vec; +use alloc::collections::BTreeSet; use crate::types::block::{ core::{BasicBlockBuilder, Block, BlockWrapper}, @@ -25,10 +25,12 @@ pub fn rand_block_id() -> BlockId { } /// Generates a vector of random block ids of a given length. -pub fn rand_block_ids(len: usize) -> Vec { - let mut parents = (0..len).map(|_| rand_block_id()).collect::>(); - parents.sort_by(|a, b| a.as_ref().cmp(b.as_ref())); - parents +pub fn rand_block_ids(len: usize) -> BTreeSet { + let mut res = BTreeSet::new(); + while res.len() < len { + res.insert(rand_block_id()); + } + res } /// Generates a random basic block with given strong parents. diff --git a/sdk/src/types/block/rand/parents.rs b/sdk/src/types/block/rand/parents.rs index 2b9040d396..2548eaeb3e 100644 --- a/sdk/src/types/block/rand/parents.rs +++ b/sdk/src/types/block/rand/parents.rs @@ -8,5 +8,5 @@ use crate::types::block::{ /// Generates random strong parents. pub fn rand_strong_parents() -> StrongParents { - StrongParents::from_vec(rand_block_ids(rand_number_range(StrongParents::COUNT_RANGE).into())).unwrap() + StrongParents::from_set(rand_block_ids(rand_number_range(StrongParents::COUNT_RANGE).into())).unwrap() } diff --git a/sdk/src/types/fuzz/Cargo.toml b/sdk/src/types/fuzz/Cargo.toml index 6797092072..43f256a55c 100644 --- a/sdk/src/types/fuzz/Cargo.toml +++ b/sdk/src/types/fuzz/Cargo.toml @@ -12,7 +12,7 @@ cargo-fuzz = true iota-types = { path = "..", default-features = false } libfuzzer-sys = { version = "0.4.7", default-features = false } -packable = { version = "0.8.1", default-features = false } +packable = { version = "0.8.2", default-features = false } # Prevent this from interfering with workspaces [workspace] diff --git a/sdk/src/wallet/account/operations/balance.rs b/sdk/src/wallet/account/operations/balance.rs index 0a6d8fb026..3c6f4b7c80 100644 --- a/sdk/src/wallet/account/operations/balance.rs +++ b/sdk/src/wallet/account/operations/balance.rs @@ -7,7 +7,7 @@ use crate::{ client::secret::SecretManage, types::block::{ address::Bech32Address, - output::{unlock_condition::UnlockCondition, FoundryId, NativeTokensBuilder, Output, Rent}, + output::{FoundryId, NativeTokensBuilder, Output, Rent}, ConvertTo, }, wallet::{ @@ -123,10 +123,11 @@ where _ => { // If there is only an [AddressUnlockCondition], then we can spend the output at any time // without restrictions - if let [UnlockCondition::Address(_)] = output + if output .unlock_conditions() .expect("output needs to have unlock conditions") - .as_ref() + .single_address() + .is_ok() { // add nft_id for nft outputs if let Output::Nft(output) = &output { diff --git a/sdk/src/wallet/account/operations/participation/mod.rs b/sdk/src/wallet/account/operations/participation/mod.rs index 0eb247c087..83797c34f2 100644 --- a/sdk/src/wallet/account/operations/participation/mod.rs +++ b/sdk/src/wallet/account/operations/participation/mod.rs @@ -23,7 +23,7 @@ use crate::{ responses::TrackedParticipation, types::{ParticipationEventData, ParticipationEventId, Participations, PARTICIPATION_TAG}, }, - block::output::{unlock_condition::UnlockCondition, Output, OutputId}, + block::output::{Output, OutputId}, }, wallet::{ account::{Account, OutputData}, @@ -309,9 +309,9 @@ fn is_valid_participation_output(output: &Output) -> bool { // Only basic outputs can be participation outputs. if let Output::Basic(basic_output) = &output { // Valid participation outputs can only have the AddressUnlockCondition. - let [UnlockCondition::Address(_)] = basic_output.unlock_conditions().as_ref() else { + if basic_output.unlock_conditions().single_address().is_err() { return false; - }; + } basic_output .features() diff --git a/sdk/tests/client/mod.rs b/sdk/tests/client/mod.rs index aebed6d716..87e85a9a9a 100644 --- a/sdk/tests/client/mod.rs +++ b/sdk/tests/client/mod.rs @@ -31,7 +31,7 @@ use iota_sdk::{ unlock_condition::{ AddressUnlockCondition, ExpirationUnlockCondition, GovernorAddressUnlockCondition, ImmutableAccountAddressUnlockCondition, StateControllerAddressUnlockCondition, - StorageDepositReturnUnlockCondition, TimelockUnlockCondition, UnlockCondition, + StorageDepositReturnUnlockCondition, TimelockUnlockCondition, }, AccountId, AccountOutputBuilder, BasicOutputBuilder, FoundryOutputBuilder, NativeToken, NativeTokens, NftId, NftOutputBuilder, Output, OutputId, OutputMetadata, SimpleTokenScheme, TokenId, TokenScheme, @@ -362,7 +362,7 @@ fn is_remainder_or_return( // assert_eq!(output.as_basic().native_tokens().len(), 0); - if let [UnlockCondition::Address(address_unlock_condition)] = output.unlock_conditions().as_ref() { + if let Ok(address_unlock_condition) = output.unlock_conditions().single_address() { if address_unlock_condition.address() != Bech32Address::try_from_str(address).unwrap().inner() { return false; } diff --git a/sdk/tests/types/parents.rs b/sdk/tests/types/parents.rs index e3c746ccbb..5047d11c63 100644 --- a/sdk/tests/types/parents.rs +++ b/sdk/tests/types/parents.rs @@ -2,46 +2,43 @@ // SPDX-License-Identifier: Apache-2.0 use core::ops::Deref; -use std::collections::BTreeSet; use iota_sdk::types::block::{ parent::StrongParents, rand::block::{rand_block_id, rand_block_ids}, - BlockId, Error, + Error, }; use packable::{error::UnpackError, prefix::VecPrefix, PackableExt}; #[test] fn len() { for i in 1..=8 { - assert_eq!(StrongParents::from_vec(rand_block_ids(i)).unwrap().len(), i); + assert_eq!(StrongParents::from_set(rand_block_ids(i)).unwrap().len(), i); } } #[test] fn new_valid_iter() { let inner = rand_block_ids(8); - let parents = StrongParents::from_vec(inner.clone()).unwrap(); + let parents = StrongParents::from_set(inner.clone()).unwrap(); - let parents_vec = parents.iter().copied().collect::>(); - - assert_eq!(inner, parents_vec[0..].to_vec()); + assert!(inner.into_iter().eq(parents.into_iter())); } #[test] fn new_from_set() { let inner = rand_block_ids(8); - let parents = StrongParents::from_set(BTreeSet::from_iter(inner.clone())).unwrap(); + let parents = StrongParents::from_set(inner.clone()).unwrap(); - assert_eq!(*parents.to_vec(), inner); + assert_eq!(parents.as_set(), &inner); } #[test] fn new_valid_deref() { let inner = rand_block_ids(8); - let parents = StrongParents::from_vec(inner.clone()).unwrap(); + let parents = StrongParents::from_set(inner.clone()).unwrap(); - assert_eq!(parents.deref(), &inner.into_boxed_slice()); + assert_eq!(parents.deref(), &inner); } #[test] @@ -64,29 +61,27 @@ fn new_invalid_more_than_max() { #[test] fn new_not_sorted() { - let mut inner_1 = rand_block_ids(8); - let inner_2 = inner_1.clone(); - inner_1.reverse(); + let inner = rand_block_ids(8); + let reversed = inner.iter().copied().rev().collect::>(); - let parents = StrongParents::from_vec(inner_1).unwrap(); + let parents = StrongParents::from_vec(reversed).unwrap(); - assert_eq!(*parents.to_vec(), inner_2); + assert_eq!(parents.as_set(), &inner); } #[test] fn new_not_unique() { - let mut inner_1 = rand_block_ids(7); - let inner_2 = inner_1.clone(); - inner_1.push(*inner_1.last().unwrap()); + let inner = rand_block_ids(7); + let non_unique = inner.iter().chain(&inner).copied().collect::>(); - let parents = StrongParents::from_vec(inner_1).unwrap(); + let parents = StrongParents::from_vec(non_unique).unwrap(); - assert_eq!(*parents.to_vec(), inner_2); + assert_eq!(parents.as_set(), &inner); } #[test] fn packed_len() { - let parents = StrongParents::from_vec(rand_block_ids(5)).unwrap(); + let parents = StrongParents::from_set(rand_block_ids(5)).unwrap(); assert_eq!(parents.packed_len(), 1 + 5 * 40); assert_eq!(parents.pack_to_vec().len(), 1 + 5 * 40); @@ -94,7 +89,7 @@ fn packed_len() { #[test] fn pack_unpack_valid() { - let parents_1 = StrongParents::from_vec(rand_block_ids(8)).unwrap(); + let parents_1 = StrongParents::from_set(rand_block_ids(8)).unwrap(); let parents_2 = StrongParents::unpack_verified(parents_1.pack_to_vec().as_slice(), &()).unwrap(); assert_eq!(parents_1, parents_2); @@ -144,9 +139,9 @@ fn pack_unpack_invalid_more_than_max() { #[test] fn unpack_invalid_not_sorted() { - let mut inner = rand_block_ids(8); - inner.reverse(); - let inner = VecPrefix::<_, u8>::try_from(inner).unwrap(); + let inner = rand_block_ids(8); + let reversed = inner.into_iter().rev().collect::>(); + let inner = VecPrefix::<_, u8>::try_from(reversed).unwrap(); let packed = inner.pack_to_vec(); let parents = StrongParents::unpack_verified(packed.as_slice(), &()); @@ -159,9 +154,9 @@ fn unpack_invalid_not_sorted() { #[test] fn unpack_invalid_not_unique() { - let mut inner = rand_block_ids(7); - inner.push(*inner.last().unwrap()); - let inner = VecPrefix::<_, u8>::try_from(inner).unwrap(); + let inner = rand_block_ids(7); + let non_unique = inner.iter().chain(&inner).copied().collect::>(); + let inner = VecPrefix::<_, u8>::try_from(non_unique).unwrap(); let packed = inner.pack_to_vec(); let parents = StrongParents::unpack_verified(packed.as_slice(), &()); From 038ee6d2a2ab6e2e37629db497459f42acb5631c Mon Sep 17 00:00:00 2001 From: Alex Coats Date: Fri, 15 Sep 2023 15:33:16 -0400 Subject: [PATCH 2/9] fixes --- sdk/src/types/block/parent.rs | 12 ++++++++++-- sdk/tests/types/parents.rs | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/sdk/src/types/block/parent.rs b/sdk/src/types/block/parent.rs index 673014050c..9c060beac2 100644 --- a/sdk/src/types/block/parent.rs +++ b/sdk/src/types/block/parent.rs @@ -7,7 +7,7 @@ use alloc::{collections::BTreeSet, vec::Vec}; use core::ops::RangeInclusive; use derive_more::Deref; -use packable::{bounded::BoundedU8, prefix::BTreeSetPrefix, Packable}; +use packable::{bounded::BoundedU8, prefix::BTreeSetPrefix, set::UnpackSetError, Packable}; use crate::types::block::{BlockId, Error}; @@ -20,9 +20,17 @@ use crate::types::block::{BlockId, Error}; #[derive(Clone, Debug, Eq, PartialEq, Deref, Packable)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[deref(forward)] -#[packable(unpack_error = Error, with = |_| Error::InvalidParentCount)] +#[packable(unpack_error = Error, with = map_parents_set_error)] pub struct Parents(BTreeSetPrefix>); +fn map_parents_set_error(error: UnpackSetError) -> Error { + match error { + UnpackSetError::DuplicateItem(_) => Error::ParentsNotUniqueSorted, + UnpackSetError::Item(e) => e, + UnpackSetError::Prefix(_) => Error::InvalidParentCount, + } +} + impl Parents { /// The range representing the valid number of parents. pub const COUNT_RANGE: RangeInclusive = MIN..=MAX; diff --git a/sdk/tests/types/parents.rs b/sdk/tests/types/parents.rs index 5047d11c63..6be193be74 100644 --- a/sdk/tests/types/parents.rs +++ b/sdk/tests/types/parents.rs @@ -154,7 +154,7 @@ fn unpack_invalid_not_sorted() { #[test] fn unpack_invalid_not_unique() { - let inner = rand_block_ids(7); + let inner = rand_block_ids(3); let non_unique = inner.iter().chain(&inner).copied().collect::>(); let inner = VecPrefix::<_, u8>::try_from(non_unique).unwrap(); From 7d69aaaa15678a85c94dac48c62a38b40e954be8 Mon Sep 17 00:00:00 2001 From: Alex Coats Date: Mon, 18 Sep 2023 09:31:30 -0400 Subject: [PATCH 3/9] update packable --- Cargo.lock | 4 +-- bindings/core/Cargo.toml | 2 +- sdk/Cargo.toml | 2 +- sdk/src/types/block/mana/mod.rs | 32 ++++++++++++------- .../block/output/feature/block_issuer.rs | 13 +++++--- sdk/src/types/block/output/feature/mod.rs | 18 ++++++++--- sdk/src/types/block/output/native_token.rs | 18 ++++++++--- .../block/output/unlock_condition/mod.rs | 13 +++++--- sdk/src/types/block/parent.rs | 18 ++++++++--- sdk/src/types/fuzz/Cargo.toml | 2 +- 10 files changed, 80 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index df3717c039..4e1bb5eac9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2064,9 +2064,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "packable" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d99ae55c2e3dc657f87a74d549bfe44187dba690310738d384454b8101f7c0f8" +checksum = "11259b086696fc9256f790485d8f14f11f0fa60a60351af9693e3d49fd24fdb6" dependencies = [ "autocfg", "packable-derive", diff --git a/bindings/core/Cargo.toml b/bindings/core/Cargo.toml index 897c2ef3fa..574507e14c 100644 --- a/bindings/core/Cargo.toml +++ b/bindings/core/Cargo.toml @@ -23,7 +23,7 @@ iota-crypto = { version = "0.23.0", default-features = false, features = [ "bip44", ] } log = { version = "0.4.20", default-features = false } -packable = { version = "0.8.2", default-features = false } +packable = { version = "0.8.3", default-features = false } prefix-hex = { version = "0.7.1", default-features = false } primitive-types = { version = "0.12.1", default-features = false } serde = { version = "1.0.188", default-features = false } diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 581a770041..64b4cd21e0 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -41,7 +41,7 @@ iota-crypto = { version = "0.23.0", default-features = false, features = [ "ed25519", "secp256k1", ] } -packable = { version = "0.8.2", default-features = false, features = [ +packable = { version = "0.8.3", default-features = false, features = [ "primitive-types", ] } prefix-hex = { version = "0.7.1", default-features = false, features = [ diff --git a/sdk/src/types/block/mana/mod.rs b/sdk/src/types/block/mana/mod.rs index 454981aef1..b346a7147b 100644 --- a/sdk/src/types/block/mana/mod.rs +++ b/sdk/src/types/block/mana/mod.rs @@ -8,7 +8,12 @@ use alloc::{collections::BTreeSet, vec::Vec}; use core::ops::RangeInclusive; use derive_more::Deref; -use packable::{bounded::BoundedU16, prefix::BTreeSetPrefix, set::UnpackSetError, Packable}; +use packable::{ + bounded::BoundedU16, + prefix::BTreeSetPrefix, + set::{UnpackOrderedSetError, UnpackSetError}, + Packable, +}; #[cfg(feature = "serde")] pub use self::allotment::dto::ManaAllotmentDto; @@ -25,6 +30,20 @@ pub struct ManaAllotments( #[packable(verify_with = verify_mana_allotments)] BTreeSetPrefix, ); +fn map_mana_allotment_set_error(error: UnpackOrderedSetError) -> Error +where + >::Error: From

, +{ + match error { + UnpackOrderedSetError::Set(e) => match e { + UnpackSetError::DuplicateItem(_) => Error::ManaAllotmentsNotUniqueSorted, + UnpackSetError::Item(e) => e, + UnpackSetError::Prefix(p) => Error::InvalidManaAllotmentCount(p.into()), + }, + UnpackOrderedSetError::Unordered => Error::ManaAllotmentsNotUniqueSorted, + } +} + impl ManaAllotments { /// The minimum number of mana allotments of a transaction. pub const COUNT_MIN: u16 = 0; @@ -50,17 +69,6 @@ impl ManaAllotments { } } -fn map_mana_allotment_set_error(error: UnpackSetError) -> Error -where - >::Error: From

, -{ - match error { - UnpackSetError::DuplicateItem(_) => Error::ManaAllotmentsNotUniqueSorted, - UnpackSetError::Item(e) => e, - UnpackSetError::Prefix(p) => Error::InvalidManaAllotmentCount(p.into()), - } -} - fn verify_mana_allotments( allotments: &BTreeSet, protocol_params: &ProtocolParameters, diff --git a/sdk/src/types/block/output/feature/block_issuer.rs b/sdk/src/types/block/output/feature/block_issuer.rs index 61e6b62933..8c15301c0d 100644 --- a/sdk/src/types/block/output/feature/block_issuer.rs +++ b/sdk/src/types/block/output/feature/block_issuer.rs @@ -11,7 +11,7 @@ use packable::{ error::{UnpackError, UnpackErrorExt}, packer::Packer, prefix::BTreeSetPrefix, - set::UnpackSetError, + set::{UnpackOrderedSetError, UnpackSetError}, unpacker::Unpacker, Packable, }; @@ -107,14 +107,17 @@ pub(crate) type BlockIssuerKeyCount = #[packable(unpack_error = Error, with = map_block_issuer_keys_set_error)] pub struct BlockIssuerKeys(BTreeSetPrefix); -fn map_block_issuer_keys_set_error(error: UnpackSetError) -> Error +fn map_block_issuer_keys_set_error(error: UnpackOrderedSetError) -> Error where >::Error: From

, { match error { - UnpackSetError::DuplicateItem(_) => Error::BlockIssuerKeysNotUniqueSorted, - UnpackSetError::Item(e) => e, - UnpackSetError::Prefix(p) => Error::InvalidBlockIssuerKeyCount(p.into()), + UnpackOrderedSetError::Set(e) => match e { + UnpackSetError::DuplicateItem(_) => Error::BlockIssuerKeysNotUniqueSorted, + UnpackSetError::Item(e) => e, + UnpackSetError::Prefix(p) => Error::InvalidBlockIssuerKeyCount(p.into()), + }, + UnpackOrderedSetError::Unordered => Error::BlockIssuerKeysNotUniqueSorted, } } diff --git a/sdk/src/types/block/output/feature/mod.rs b/sdk/src/types/block/output/feature/mod.rs index 69dd640f8d..6ca513170f 100644 --- a/sdk/src/types/block/output/feature/mod.rs +++ b/sdk/src/types/block/output/feature/mod.rs @@ -12,7 +12,12 @@ use alloc::{collections::BTreeSet, vec::Vec}; use bitflags::bitflags; use derive_more::{Deref, From}; -use packable::{bounded::BoundedU8, prefix::BTreeSetPrefix, set::UnpackSetError, Packable}; +use packable::{ + bounded::BoundedU8, + prefix::BTreeSetPrefix, + set::{UnpackOrderedSetError, UnpackSetError}, + Packable, +}; #[cfg(feature = "irc_27")] pub use self::metadata::irc_27::{Attribute, Irc27Metadata}; @@ -227,14 +232,17 @@ pub(crate) type FeatureCount = BoundedU8<0, { Features::COUNT_MAX }>; #[packable(unpack_error = Error, with = map_features_set_error)] pub struct Features(BTreeSetPrefix); -fn map_features_set_error(error: UnpackSetError) -> Error +fn map_features_set_error(error: UnpackOrderedSetError) -> Error where >::Error: From

, { match error { - UnpackSetError::DuplicateItem(_) => Error::FeaturesNotUniqueSorted, - UnpackSetError::Item(e) => e, - UnpackSetError::Prefix(p) => Error::InvalidFeatureCount(p.into()), + UnpackOrderedSetError::Set(e) => match e { + UnpackSetError::DuplicateItem(_) => Error::FeaturesNotUniqueSorted, + UnpackSetError::Item(e) => e, + UnpackSetError::Prefix(p) => Error::InvalidFeatureCount(p.into()), + }, + UnpackOrderedSetError::Unordered => Error::FeaturesNotUniqueSorted, } } diff --git a/sdk/src/types/block/output/native_token.rs b/sdk/src/types/block/output/native_token.rs index 92e88e5e21..dd4d71cb6b 100644 --- a/sdk/src/types/block/output/native_token.rs +++ b/sdk/src/types/block/output/native_token.rs @@ -7,7 +7,12 @@ use alloc::{ }; use derive_more::{Deref, DerefMut, From}; -use packable::{bounded::BoundedU8, prefix::BTreeSetPrefix, set::UnpackSetError, Packable}; +use packable::{ + bounded::BoundedU8, + prefix::BTreeSetPrefix, + set::{UnpackOrderedSetError, UnpackSetError}, + Packable, +}; use primitive_types::U256; use crate::types::block::{output::TokenId, Error}; @@ -160,14 +165,17 @@ pub(crate) type NativeTokenCount = BoundedU8<0, { NativeTokens::COUNT_MAX }>; #[packable(unpack_error = Error, with = map_native_tokens_set_error)] pub struct NativeTokens(BTreeSetPrefix); -fn map_native_tokens_set_error(error: UnpackSetError) -> Error +fn map_native_tokens_set_error(error: UnpackOrderedSetError) -> Error where >::Error: From

, { match error { - UnpackSetError::DuplicateItem(_) => Error::NativeTokensNotUniqueSorted, - UnpackSetError::Item(e) => e, - UnpackSetError::Prefix(p) => Error::InvalidNativeTokenCount(p.into()), + UnpackOrderedSetError::Set(e) => match e { + UnpackSetError::DuplicateItem(_) => Error::NativeTokensNotUniqueSorted, + UnpackSetError::Item(e) => e, + UnpackSetError::Prefix(p) => Error::InvalidNativeTokenCount(p.into()), + }, + UnpackOrderedSetError::Unordered => Error::NativeTokensNotUniqueSorted, } } diff --git a/sdk/src/types/block/output/unlock_condition/mod.rs b/sdk/src/types/block/output/unlock_condition/mod.rs index dc84b72704..2e2a5b5000 100644 --- a/sdk/src/types/block/output/unlock_condition/mod.rs +++ b/sdk/src/types/block/output/unlock_condition/mod.rs @@ -18,7 +18,7 @@ use packable::{ error::{UnpackError, UnpackErrorExt}, packer::Packer, prefix::BTreeSetPrefix, - set::UnpackSetError, + set::{UnpackOrderedSetError, UnpackSetError}, unpacker::Unpacker, Packable, }; @@ -311,14 +311,17 @@ pub(crate) type UnlockConditionCount = BoundedU8<0, { UnlockConditions::COUNT_MA #[packable(unpack_visitor = ProtocolParameters)] pub struct UnlockConditions(BTreeSetPrefix); -fn map_unlock_conditions_set_error(error: UnpackSetError) -> Error +fn map_unlock_conditions_set_error(error: UnpackOrderedSetError) -> Error where >::Error: From

, { match error { - UnpackSetError::DuplicateItem(_) => Error::UnlockConditionsNotUniqueSorted, - UnpackSetError::Item(e) => e, - UnpackSetError::Prefix(p) => Error::InvalidUnlockConditionCount(p.into()), + UnpackOrderedSetError::Set(e) => match e { + UnpackSetError::DuplicateItem(_) => Error::UnlockConditionsNotUniqueSorted, + UnpackSetError::Item(e) => e, + UnpackSetError::Prefix(p) => Error::InvalidUnlockConditionCount(p.into()), + }, + UnpackOrderedSetError::Unordered => Error::UnlockConditionsNotUniqueSorted, } } diff --git a/sdk/src/types/block/parent.rs b/sdk/src/types/block/parent.rs index 9c060beac2..dfec9f2a19 100644 --- a/sdk/src/types/block/parent.rs +++ b/sdk/src/types/block/parent.rs @@ -7,7 +7,12 @@ use alloc::{collections::BTreeSet, vec::Vec}; use core::ops::RangeInclusive; use derive_more::Deref; -use packable::{bounded::BoundedU8, prefix::BTreeSetPrefix, set::UnpackSetError, Packable}; +use packable::{ + bounded::BoundedU8, + prefix::BTreeSetPrefix, + set::{UnpackOrderedSetError, UnpackSetError}, + Packable, +}; use crate::types::block::{BlockId, Error}; @@ -23,11 +28,14 @@ use crate::types::block::{BlockId, Error}; #[packable(unpack_error = Error, with = map_parents_set_error)] pub struct Parents(BTreeSetPrefix>); -fn map_parents_set_error(error: UnpackSetError) -> Error { +fn map_parents_set_error(error: UnpackOrderedSetError) -> Error { match error { - UnpackSetError::DuplicateItem(_) => Error::ParentsNotUniqueSorted, - UnpackSetError::Item(e) => e, - UnpackSetError::Prefix(_) => Error::InvalidParentCount, + UnpackOrderedSetError::Set(e) => match e { + UnpackSetError::DuplicateItem(_) => Error::ParentsNotUniqueSorted, + UnpackSetError::Item(e) => e, + UnpackSetError::Prefix(_) => Error::InvalidParentCount, + }, + UnpackOrderedSetError::Unordered => Error::ParentsNotUniqueSorted, } } diff --git a/sdk/src/types/fuzz/Cargo.toml b/sdk/src/types/fuzz/Cargo.toml index 43f256a55c..44190bc39e 100644 --- a/sdk/src/types/fuzz/Cargo.toml +++ b/sdk/src/types/fuzz/Cargo.toml @@ -12,7 +12,7 @@ cargo-fuzz = true iota-types = { path = "..", default-features = false } libfuzzer-sys = { version = "0.4.7", default-features = false } -packable = { version = "0.8.2", default-features = false } +packable = { version = "0.8.3", default-features = false } # Prevent this from interfering with workspaces [workspace] From 75f6e672c9085ae7c3e0bc037eb119bd67140766 Mon Sep 17 00:00:00 2001 From: DaughterOfMars Date: Tue, 19 Sep 2023 11:24:59 -0400 Subject: [PATCH 4/9] error message Co-authored-by: Thoralf-M <46689931+Thoralf-M@users.noreply.github.com> --- sdk/src/types/block/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/src/types/block/error.rs b/sdk/src/types/block/error.rs index 6f974a055f..64d8742992 100644 --- a/sdk/src/types/block/error.rs +++ b/sdk/src/types/block/error.rs @@ -366,7 +366,7 @@ impl fmt::Display for Error { write!(f, "unallowed feature at index {index} with kind {kind}") } Self::DisallowedUnlockCondition { index, kind } => { - write!(f, "unallowed unlock condition at index {index} with kind {kind}") + write!(f, "disallowed unlock condition at index {index} with kind {kind}") } Self::TooManyUnlockConditions => write!(f, "too many unlock conditions"), Self::UnlockConditionsNotUniqueSorted => write!(f, "unlock conditions are not unique and/or sorted"), From 272d52e4ec62926d45e2b382a805d112b259f801 Mon Sep 17 00:00:00 2001 From: Alex Coats Date: Fri, 29 Sep 2023 08:42:27 -0400 Subject: [PATCH 5/9] clippy --- sdk/src/types/block/output/delegation.rs | 2 +- sdk/src/types/block/output/feature/mod.rs | 4 ++-- sdk/src/types/block/output/unlock_condition/mod.rs | 4 ++-- .../block/output/unlock_condition/storage_deposit_return.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sdk/src/types/block/output/delegation.rs b/sdk/src/types/block/output/delegation.rs index 5c926cd55f..2aa5058a83 100644 --- a/sdk/src/types/block/output/delegation.rs +++ b/sdk/src/types/block/output/delegation.rs @@ -359,7 +359,7 @@ impl DelegationOutput { // Transition, just without full ValidationContext. pub(crate) fn transition_inner(current_state: &Self, next_state: &Self) -> Result<(), StateTransitionError> { - if !(current_state.delegation_id.is_null() && !next_state.delegation_id().is_null()) { + if !current_state.delegation_id.is_null() | next_state.delegation_id().is_null() { return Err(StateTransitionError::NonDelayedClaimingTransition); } diff --git a/sdk/src/types/block/output/feature/mod.rs b/sdk/src/types/block/output/feature/mod.rs index 6ca513170f..af9fe89890 100644 --- a/sdk/src/types/block/output/feature/mod.rs +++ b/sdk/src/types/block/output/feature/mod.rs @@ -74,7 +74,7 @@ impl Ord for Feature { impl core::borrow::Borrow for Feature { fn borrow(&self) -> &u8 { - &self.ord_kind() + self.ord_kind() } } @@ -97,7 +97,7 @@ impl Feature { *self.ord_kind() } - fn ord_kind<'a>(&'a self) -> &'a u8 { + fn ord_kind(&self) -> &u8 { match self { Self::Sender(_) => &SenderFeature::KIND, Self::Issuer(_) => &IssuerFeature::KIND, diff --git a/sdk/src/types/block/output/unlock_condition/mod.rs b/sdk/src/types/block/output/unlock_condition/mod.rs index 2e2a5b5000..0b304d7c68 100644 --- a/sdk/src/types/block/output/unlock_condition/mod.rs +++ b/sdk/src/types/block/output/unlock_condition/mod.rs @@ -63,7 +63,7 @@ impl Ord for UnlockCondition { } impl core::borrow::Borrow for UnlockCondition { fn borrow(&self) -> &u8 { - &self.ord_kind() + self.ord_kind() } } @@ -87,7 +87,7 @@ impl UnlockCondition { *self.ord_kind() } - fn ord_kind<'a>(&'a self) -> &'a u8 { + fn ord_kind(&self) -> &u8 { match self { Self::Address(_) => &AddressUnlockCondition::KIND, Self::StorageDepositReturn(_) => &StorageDepositReturnUnlockCondition::KIND, diff --git a/sdk/src/types/block/output/unlock_condition/storage_deposit_return.rs b/sdk/src/types/block/output/unlock_condition/storage_deposit_return.rs index e4517f9018..b80a158c0f 100644 --- a/sdk/src/types/block/output/unlock_condition/storage_deposit_return.rs +++ b/sdk/src/types/block/output/unlock_condition/storage_deposit_return.rs @@ -137,7 +137,7 @@ pub(crate) mod dto { } impl PartialOrd for StorageDepositReturnUnlockConditionDto { fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(&other)) + Some(self.cmp(other)) } } } From b7463953e8fd434ce5eb0959133fceed9271255e Mon Sep 17 00:00:00 2001 From: Alex Coats Date: Fri, 29 Sep 2023 11:54:47 -0400 Subject: [PATCH 6/9] comment --- sdk/src/types/block/rand/parents.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/src/types/block/rand/parents.rs b/sdk/src/types/block/rand/parents.rs index ec1332dca6..c55d66788c 100644 --- a/sdk/src/types/block/rand/parents.rs +++ b/sdk/src/types/block/rand/parents.rs @@ -6,7 +6,7 @@ use crate::types::block::{ rand::{block::rand_block_ids, number::rand_number_range}, }; -/// Generates random strong parents. +/// Generates random parents. pub fn rand_parents() -> Parents { Parents::from_set(rand_block_ids( rand_number_range(Parents::::COUNT_RANGE).into(), From cd957b22c09797b14a390634972fcd7dfed15cfe Mon Sep 17 00:00:00 2001 From: Alex Coats Date: Fri, 29 Sep 2023 12:13:45 -0400 Subject: [PATCH 7/9] custom hash impls --- sdk/src/types/block/output/feature/mod.rs | 7 ++++++- sdk/src/types/block/output/native_token.rs | 7 ++++++- sdk/src/types/block/output/unlock_condition/mod.rs | 7 ++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/sdk/src/types/block/output/feature/mod.rs b/sdk/src/types/block/output/feature/mod.rs index af9fe89890..8ceec04251 100644 --- a/sdk/src/types/block/output/feature/mod.rs +++ b/sdk/src/types/block/output/feature/mod.rs @@ -35,7 +35,7 @@ pub use self::{ use crate::types::block::{create_bitflags, Error}; /// -#[derive(Clone, Eq, PartialEq, Hash, From, Packable)] +#[derive(Clone, Eq, PartialEq, From, Packable)] #[packable(unpack_error = Error)] #[packable(tag_type = u8, with_error = Error::InvalidFeatureKind)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(untagged))] @@ -71,6 +71,11 @@ impl Ord for Feature { self.kind().cmp(&other.kind()) } } +impl core::hash::Hash for Feature { + fn hash(&self, state: &mut H) { + self.kind().hash(state); + } +} impl core::borrow::Borrow for Feature { fn borrow(&self) -> &u8 { diff --git a/sdk/src/types/block/output/native_token.rs b/sdk/src/types/block/output/native_token.rs index cc57884814..4fc4fd6bb7 100644 --- a/sdk/src/types/block/output/native_token.rs +++ b/sdk/src/types/block/output/native_token.rs @@ -29,7 +29,7 @@ impl From for TokenId { } /// -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Packable)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Packable)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[packable(unpack_error = Error)] pub struct NativeToken { @@ -75,6 +75,11 @@ impl Ord for NativeToken { self.token_id.cmp(&other.token_id) } } +impl core::hash::Hash for NativeToken { + fn hash(&self, state: &mut H) { + self.token_id.hash(state); + } +} impl core::borrow::Borrow for NativeToken { fn borrow(&self) -> &TokenId { diff --git a/sdk/src/types/block/output/unlock_condition/mod.rs b/sdk/src/types/block/output/unlock_condition/mod.rs index 0b304d7c68..1f5dccb3ec 100644 --- a/sdk/src/types/block/output/unlock_condition/mod.rs +++ b/sdk/src/types/block/output/unlock_condition/mod.rs @@ -33,7 +33,7 @@ pub use self::{ use crate::types::block::{address::Address, create_bitflags, protocol::ProtocolParameters, slot::SlotIndex, Error}; /// -#[derive(Clone, Eq, PartialEq, Hash, From)] +#[derive(Clone, Eq, PartialEq, From)] pub enum UnlockCondition { /// An address unlock condition. Address(AddressUnlockCondition), @@ -61,6 +61,11 @@ impl Ord for UnlockCondition { self.kind().cmp(&other.kind()) } } +impl core::hash::Hash for UnlockCondition { + fn hash(&self, state: &mut H) { + self.kind().hash(state); + } +} impl core::borrow::Borrow for UnlockCondition { fn borrow(&self) -> &u8 { self.ord_kind() From bf618b604f37e371c127aaaa02e24a20b7ee31b1 Mon Sep 17 00:00:00 2001 From: Alex Coats Date: Fri, 29 Sep 2023 13:01:51 -0400 Subject: [PATCH 8/9] error on dups --- sdk/src/types/block/core/parent.rs | 20 +++++++++---------- sdk/src/types/block/mana/mod.rs | 14 ++++++------- .../block/output/feature/block_issuer.rs | 14 ++++++------- sdk/src/types/block/output/feature/mod.rs | 14 ++++++------- sdk/src/types/block/output/native_token.rs | 14 ++++++------- .../block/output/unlock_condition/mod.rs | 14 ++++++------- 6 files changed, 45 insertions(+), 45 deletions(-) diff --git a/sdk/src/types/block/core/parent.rs b/sdk/src/types/block/core/parent.rs index 5502929225..cf9bea57f4 100644 --- a/sdk/src/types/block/core/parent.rs +++ b/sdk/src/types/block/core/parent.rs @@ -44,19 +44,19 @@ impl Parents { pub const COUNT_RANGE: RangeInclusive = MIN..=MAX; /// Creates new [`Parents`] from a vec. - pub fn from_vec(inner: Vec) -> Result { - Ok(Self( - inner - .into_iter() - .collect::>() - .try_into() - .map_err(|_| Error::InvalidParentCount)?, - )) + pub fn from_vec(parents: Vec) -> Result { + let mut set = BTreeSet::new(); + for t in parents { + if !set.insert(t) { + return Err(Error::ParentsNotUniqueSorted); + } + } + Ok(Self(set.try_into().map_err(|_| Error::InvalidParentCount)?)) } /// Creates new [`Parents`] from an ordered set. - pub fn from_set(inner: BTreeSet) -> Result { - Ok(Self(inner.try_into().map_err(|_| Error::InvalidParentCount)?)) + pub fn from_set(parents: BTreeSet) -> Result { + Ok(Self(parents.try_into().map_err(|_| Error::InvalidParentCount)?)) } /// Gets the underlying set. diff --git a/sdk/src/types/block/mana/mod.rs b/sdk/src/types/block/mana/mod.rs index 8f9a64e114..fe32f3eed1 100644 --- a/sdk/src/types/block/mana/mod.rs +++ b/sdk/src/types/block/mana/mod.rs @@ -55,13 +55,13 @@ impl ManaAllotments { /// Creates a new [`ManaAllotments`] from a vec. pub fn from_vec(allotments: Vec) -> Result { - Ok(Self( - allotments - .into_iter() - .collect::>() - .try_into() - .map_err(Error::InvalidManaAllotmentCount)?, - )) + let mut set = BTreeSet::new(); + for t in allotments { + if !set.insert(t) { + return Err(Error::ManaAllotmentsNotUniqueSorted); + } + } + Ok(Self(set.try_into().map_err(Error::InvalidManaAllotmentCount)?)) } /// Creates a new [`ManaAllotments`] from an ordered set. diff --git a/sdk/src/types/block/output/feature/block_issuer.rs b/sdk/src/types/block/output/feature/block_issuer.rs index 8c15301c0d..f747d14eb9 100644 --- a/sdk/src/types/block/output/feature/block_issuer.rs +++ b/sdk/src/types/block/output/feature/block_issuer.rs @@ -158,13 +158,13 @@ impl BlockIssuerKeys { /// Creates a new [`BlockIssuerKeys`] from a vec. pub fn from_vec(block_issuer_keys: Vec) -> Result { - Ok(Self( - block_issuer_keys - .into_iter() - .collect::>() - .try_into() - .map_err(Error::InvalidBlockIssuerKeyCount)?, - )) + let mut set = BTreeSet::new(); + for t in block_issuer_keys { + if !set.insert(t) { + return Err(Error::BlockIssuerKeysNotUniqueSorted); + } + } + Ok(Self(set.try_into().map_err(Error::InvalidBlockIssuerKeyCount)?)) } /// Creates a new [`BlockIssuerKeys`] from an ordered set. diff --git a/sdk/src/types/block/output/feature/mod.rs b/sdk/src/types/block/output/feature/mod.rs index 8ceec04251..a049e4bcb4 100644 --- a/sdk/src/types/block/output/feature/mod.rs +++ b/sdk/src/types/block/output/feature/mod.rs @@ -284,13 +284,13 @@ impl Features { /// Creates a new [`Features`] from a vec. pub fn from_vec(features: Vec) -> Result { - Ok(Self( - features - .into_iter() - .collect::>() - .try_into() - .map_err(Error::InvalidFeatureCount)?, - )) + let mut set = BTreeSet::new(); + for t in features { + if !set.insert(t) { + return Err(Error::FeaturesNotUniqueSorted); + } + } + Ok(Self(set.try_into().map_err(Error::InvalidFeatureCount)?)) } /// Creates a new [`Features`] from an ordered set. diff --git a/sdk/src/types/block/output/native_token.rs b/sdk/src/types/block/output/native_token.rs index 4fc4fd6bb7..22840eca5b 100644 --- a/sdk/src/types/block/output/native_token.rs +++ b/sdk/src/types/block/output/native_token.rs @@ -228,13 +228,13 @@ impl NativeTokens { /// Creates a new [`NativeTokens`] from a vec. pub fn from_vec(native_tokens: Vec) -> Result { - Ok(Self( - native_tokens - .into_iter() - .collect::>() - .try_into() - .map_err(Error::InvalidNativeTokenCount)?, - )) + let mut set = BTreeSet::new(); + for t in native_tokens { + if !set.insert(t) { + return Err(Error::NativeTokensNotUniqueSorted); + } + } + Ok(Self(set.try_into().map_err(Error::InvalidNativeTokenCount)?)) } /// Creates a new [`NativeTokens`] from an ordered set. diff --git a/sdk/src/types/block/output/unlock_condition/mod.rs b/sdk/src/types/block/output/unlock_condition/mod.rs index 1f5dccb3ec..ae531f5eec 100644 --- a/sdk/src/types/block/output/unlock_condition/mod.rs +++ b/sdk/src/types/block/output/unlock_condition/mod.rs @@ -363,13 +363,13 @@ impl UnlockConditions { /// Creates a new [`UnlockConditions`] from a vec. pub fn from_vec(unlock_conditions: Vec) -> Result { - Ok(Self( - unlock_conditions - .into_iter() - .collect::>() - .try_into() - .map_err(Error::InvalidUnlockConditionCount)?, - )) + let mut set = BTreeSet::new(); + for t in unlock_conditions { + if !set.insert(t) { + return Err(Error::UnlockConditionsNotUniqueSorted); + } + } + Ok(Self(set.try_into().map_err(Error::InvalidUnlockConditionCount)?)) } /// Creates a new [`UnlockConditions`] from an ordered set. From f54ef4757ad9d219039f81b8da222a9a78b1b7b6 Mon Sep 17 00:00:00 2001 From: Alex Coats Date: Thu, 5 Oct 2023 09:04:02 -0400 Subject: [PATCH 9/9] fail on unordered too and fix tests --- sdk/src/types/block/core/parent.rs | 5 +++++ sdk/tests/types/parents.rs | 12 ++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/sdk/src/types/block/core/parent.rs b/sdk/src/types/block/core/parent.rs index cf9bea57f4..10e3b86f57 100644 --- a/sdk/src/types/block/core/parent.rs +++ b/sdk/src/types/block/core/parent.rs @@ -47,6 +47,11 @@ impl Parents { pub fn from_vec(parents: Vec) -> Result { let mut set = BTreeSet::new(); for t in parents { + if let Some(last) = set.last() { + if t.lt(last) { + return Err(Error::ParentsNotUniqueSorted); + } + } if !set.insert(t) { return Err(Error::ParentsNotUniqueSorted); } diff --git a/sdk/tests/types/parents.rs b/sdk/tests/types/parents.rs index 17e24d8abb..bea783524c 100644 --- a/sdk/tests/types/parents.rs +++ b/sdk/tests/types/parents.rs @@ -67,9 +67,9 @@ fn new_not_sorted() { let inner = rand_block_ids(8); let reversed = inner.iter().copied().rev().collect::>(); - let parents = basic::StrongParents::from_vec(reversed).unwrap(); + let parents = basic::StrongParents::from_vec(reversed); - assert_eq!(parents.as_set(), &inner); + assert!(matches!(parents, Err(Error::ParentsNotUniqueSorted))); } #[test] @@ -77,9 +77,9 @@ fn new_not_unique() { let inner = rand_block_ids(7); let non_unique = inner.iter().chain(&inner).copied().collect::>(); - let parents = basic::StrongParents::from_vec(non_unique).unwrap(); + let parents = basic::StrongParents::from_vec(non_unique); - assert_eq!(parents.as_set(), &inner); + assert!(matches!(parents, Err(Error::ParentsNotUniqueSorted))); } #[test] @@ -152,7 +152,7 @@ fn unpack_invalid_not_sorted() { assert!(matches!( parents, Err(UnpackError::Packable(Error::ParentsNotUniqueSorted)) - ),); + )); } #[test] @@ -167,5 +167,5 @@ fn unpack_invalid_not_unique() { assert!(matches!( parents, Err(UnpackError::Packable(Error::ParentsNotUniqueSorted)) - ),); + )); }