From bacea90c7ada7c230fdd51c1fd3e55fa568ccd02 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Sun, 5 Nov 2023 23:05:09 +0100 Subject: [PATCH 01/25] Add MultiAddress --- sdk/src/client/utils.rs | 1 + sdk/src/types/block/address/bech32.rs | 20 +++-- sdk/src/types/block/address/mod.rs | 24 ++--- sdk/src/types/block/address/multi.rs | 122 ++++++++++++++++++++++++++ sdk/src/types/block/error.rs | 3 + sdk/tests/types/address/mod.rs | 1 + sdk/tests/types/address/multi.rs | 57 ++++++++++++ 7 files changed, 207 insertions(+), 21 deletions(-) create mode 100644 sdk/src/types/block/address/multi.rs create mode 100644 sdk/tests/types/address/multi.rs diff --git a/sdk/src/client/utils.rs b/sdk/src/client/utils.rs index ce7f79de12..da07957425 100644 --- a/sdk/src/client/utils.rs +++ b/sdk/src/client/utils.rs @@ -33,6 +33,7 @@ pub fn bech32_to_hex(bech32: impl ConvertTo) -> Result { Address::Nft(nft) => nft.to_string(), Address::Anchor(anchor) => anchor.to_string(), Address::ImplicitAccountCreation(implicit) => implicit.to_string(), + Address::Multi(multi) => multi.to_string(), Address::Restricted(restricted) => restricted.to_string(), }) } diff --git a/sdk/src/types/block/address/bech32.rs b/sdk/src/types/block/address/bech32.rs index dd0caa2e1e..18701195e0 100644 --- a/sdk/src/types/block/address/bech32.rs +++ b/sdk/src/types/block/address/bech32.rs @@ -7,6 +7,7 @@ use alloc::{ }; use core::str::FromStr; +use crypto::hashes::{blake2b::Blake2b256, Digest}; use derive_more::{AsRef, Deref, Display}; use packable::{ error::{UnpackError, UnpackErrorExt}, @@ -15,7 +16,10 @@ use packable::{ Packable, PackableExt, }; -use crate::types::block::{address::Address, ConvertTo, Error}; +use crate::types::block::{ + address::{Address, MultiAddress}, + ConvertTo, Error, +}; #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deref, Display)] #[repr(transparent)] @@ -172,11 +176,15 @@ impl Bech32Address { impl core::fmt::Display for Bech32Address { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!( - f, - "{}", - bech32::encode::(self.hrp.0, &self.inner.pack_to_vec(),).unwrap() - ) + let bytes = if self.inner.is_multi() { + let mut bytes = vec![MultiAddress::KIND]; + bytes.extend(Blake2b256::digest(self.inner.pack_to_vec())); + bytes + } else { + self.inner.pack_to_vec() + }; + + write!(f, "{}", bech32::encode::(self.hrp.0, &bytes,).unwrap()) } } diff --git a/sdk/src/types/block/address/mod.rs b/sdk/src/types/block/address/mod.rs index fa6dda1a84..608670d26f 100644 --- a/sdk/src/types/block/address/mod.rs +++ b/sdk/src/types/block/address/mod.rs @@ -6,6 +6,7 @@ mod anchor; mod bech32; mod ed25519; mod implicit_account_creation; +mod multi; mod nft; mod restricted; @@ -14,12 +15,14 @@ use alloc::boxed::Box; use derive_more::{Display, From}; use packable::Packable; +pub(crate) use self::multi::WeightedAddressCount; pub use self::{ account::AccountAddress, anchor::AnchorAddress, bech32::{Bech32Address, Hrp}, ed25519::Ed25519Address, implicit_account_creation::ImplicitAccountCreationAddress, + multi::MultiAddress, nft::NftAddress, restricted::{AddressCapabilities, AddressCapabilityFlag, RestrictedAddress}, }; @@ -32,7 +35,7 @@ use crate::types::block::{ }; /// A generic address supporting different address kinds. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, From, Display, Packable)] +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, Display, Packable)] #[packable(tag_type = u8, with_error = Error::InvalidAddressKind)] #[packable(unpack_error = Error)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(untagged))] @@ -52,6 +55,9 @@ pub enum Address { /// An implicit account creation address. #[packable(tag = ImplicitAccountCreationAddress::KIND)] ImplicitAccountCreation(ImplicitAccountCreationAddress), + /// A multi address. + #[packable(tag = MultiAddress::KIND)] + Multi(MultiAddress), /// An address with restricted capabilities. #[packable(tag = RestrictedAddress::KIND)] #[from(ignore)] @@ -64,19 +70,6 @@ impl From for Address { } } -impl core::fmt::Debug for Address { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - Self::Ed25519(address) => address.fmt(f), - Self::Account(address) => address.fmt(f), - Self::Nft(address) => address.fmt(f), - Self::Anchor(address) => address.fmt(f), - Self::ImplicitAccountCreation(address) => address.fmt(f), - Self::Restricted(address) => address.fmt(f), - } - } -} - impl Address { /// Returns the address kind of an [`Address`]. pub fn kind(&self) -> u8 { @@ -86,11 +79,12 @@ impl Address { Self::Nft(_) => NftAddress::KIND, Self::Anchor(_) => AnchorAddress::KIND, Self::ImplicitAccountCreation(_) => ImplicitAccountCreationAddress::KIND, + Self::Multi(_) => MultiAddress::KIND, Self::Restricted(_) => RestrictedAddress::KIND, } } - crate::def_is_as_opt!(Address: Ed25519, Account, Nft, Anchor, ImplicitAccountCreation, Restricted); + crate::def_is_as_opt!(Address: Ed25519, Account, Nft, Anchor, ImplicitAccountCreation, Multi, Restricted); /// Tries to create an [`Address`] from a bech32 encoded string. pub fn try_from_bech32(address: impl AsRef) -> Result { diff --git a/sdk/src/types/block/address/multi.rs b/sdk/src/types/block/address/multi.rs new file mode 100644 index 0000000000..13a17e1d00 --- /dev/null +++ b/sdk/src/types/block/address/multi.rs @@ -0,0 +1,122 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use core::{fmt, ops::RangeInclusive, str::FromStr}; + +use derive_more::{AsRef, Display, From}; +use packable::{bounded::BoundedU8, prefix::BoxedSlicePrefix, Packable}; + +use crate::types::block::{address::Address, Error}; + +pub(crate) type WeightedAddressCount = + BoundedU8<{ *MultiAddress::ADDRESSES_COUNT.start() }, { *MultiAddress::ADDRESSES_COUNT.end() }>; + +// context_inputs: BoxedSlicePrefix, + +/// An address with an assigned weight. +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, AsRef, Packable)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct WeightedAddress { + address: Address, + weight: u8, +} + +/// An address that consists of addresses with weights and a threshold value. +/// The Multi Address can be unlocked if the cumulative weight of all unlocked addresses is equal to or exceeds the +/// threshold. +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Packable)] +#[packable(unpack_error = Error)] +pub struct MultiAddress { + // #[packable(verify_with = verify_context_inputs_packable)] + #[packable(unpack_error_with = |e| e.unwrap_item_err_or_else(|p| Error::InvalidWeightedAddressCount(p.into())))] + addresses: BoxedSlicePrefix, + /// The threshold that needs to be reached by the unlocked addresses in order to unlock the multi address. + threshold: u16, +} + +impl MultiAddress { + /// The [`Address`](crate::types::block::address::Address) kind of an [`MultiAddress`]. + pub const KIND: u8 = 40; + /// The allowed range of inner [`Address`]es. + pub const ADDRESSES_COUNT: RangeInclusive = 1..=10; + + /// Creates a new [`MultiAddress`]. + #[inline(always)] + pub fn new(addresses: Vec, threshold: u16) -> Result { + Ok(Self { + addresses: BoxedSlicePrefix::::try_from( + addresses.into_boxed_slice(), + ) + .map_err(Error::InvalidWeightedAddressCount)?, + threshold, + }) + } + + // /// Returns the [`AccountId`] of an [`MultiAddress`]. + // #[inline(always)] + // pub fn account_id(&self) -> &AccountId { + // &self.0 + // } + + // /// Consumes an [`MultiAddress`] and returns its [`AccountId`]. + // #[inline(always)] + // pub fn into_account_id(self) -> AccountId { + // self.0 + // } +} + +// impl FromStr for MultiAddress { +// type Err = Error; + +// fn from_str(s: &str) -> Result { +// Ok(Self::new(AccountId::from_str(s)?)) +// } +// } + +impl fmt::Display for MultiAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + panic!() + } +} + +// impl core::fmt::Debug for MultiAddress { +// fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { +// write!(f, "MultiAddress({self})") +// } +// } + +#[cfg(feature = "serde")] +mod dto { + use serde::{Deserialize, Serialize}; + + use super::*; + + #[derive(Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + struct MultiAddressDto { + #[serde(rename = "type")] + kind: u8, + addresses: Vec, + threshold: u16, + } + + impl From<&MultiAddress> for MultiAddressDto { + fn from(value: &MultiAddress) -> Self { + Self { + kind: MultiAddress::KIND, + addresses: value.addresses.to_vec(), + threshold: value.threshold, + } + } + } + + impl TryFrom for MultiAddress { + type Error = Error; + + fn try_from(value: MultiAddressDto) -> Result { + Self::new(value.addresses, value.threshold) + } + } + + crate::impl_serde_typed_dto!(MultiAddress, MultiAddressDto, "multi address"); +} diff --git a/sdk/src/types/block/error.rs b/sdk/src/types/block/error.rs index 33b23839be..ba6231f353 100644 --- a/sdk/src/types/block/error.rs +++ b/sdk/src/types/block/error.rs @@ -11,6 +11,7 @@ use primitive_types::U256; use super::slot::EpochIndex; use crate::types::block::{ + address::WeightedAddressCount, context_input::RewardContextInputIndex, input::UtxoInput, mana::ManaAllotmentCount, @@ -72,6 +73,7 @@ pub enum Error { }, InvalidContextInputKind(u8), InvalidContextInputCount(>::Error), + InvalidWeightedAddressCount(>::Error), InvalidFeatureCount(>::Error), InvalidFeatureKind(u8), InvalidFoundryOutputSupply { @@ -253,6 +255,7 @@ impl fmt::Display for Error { "storage deposit return of {deposit} exceeds the original output amount of {amount}" ), Self::InvalidContextInputCount(count) => write!(f, "invalid context input count: {count}"), + Self::InvalidWeightedAddressCount(count) => write!(f, "invalid weighted address count: {count}"), Self::InvalidContextInputKind(k) => write!(f, "invalid context input kind: {k}"), Self::InvalidFeatureCount(count) => write!(f, "invalid feature count: {count}"), Self::InvalidFeatureKind(k) => write!(f, "invalid feature kind: {k}"), diff --git a/sdk/tests/types/address/mod.rs b/sdk/tests/types/address/mod.rs index a19cff845d..fff6ac7d57 100644 --- a/sdk/tests/types/address/mod.rs +++ b/sdk/tests/types/address/mod.rs @@ -4,6 +4,7 @@ mod account; mod bech32; mod ed25519; +mod multi; mod nft; mod restricted; diff --git a/sdk/tests/types/address/multi.rs b/sdk/tests/types/address/multi.rs new file mode 100644 index 0000000000..ea767e5f69 --- /dev/null +++ b/sdk/tests/types/address/multi.rs @@ -0,0 +1,57 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use iota_sdk::types::block::address::{Address, ToBech32Ext}; + +#[test] +fn bech32() { + // Test from https://github.com/iotaledger/tips/blob/tip52/tips/TIP-0052/tip-0052.md#bech32 + + let multi_address_json = serde_json::json!({ + "type": 40, + "addresses": [ + { + "address": { + "type": 0, + "pubKeyHash": "0x52fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c649" + }, + "weight": 1 + }, + { + "address": { + "type": 0, + "pubKeyHash": "0x53fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c649" + }, + "weight": 1 + }, + { + "address": { + "type": 0, + "pubKeyHash": "0x54fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c649" + }, + "weight": 1 + }, + { + "address": { + "type": 8, + "accountId": "0x55fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c649" + }, + "weight": 2 + }, + { + "address": { + "type": 16, + "nftId": "0x56fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c649" + }, + "weight": 3 + } + ], + "threshold": 2 + }); + let multi_address = serde_json::from_value::
(multi_address_json).unwrap(); + + assert_eq!( + multi_address.to_bech32_unchecked("iota"), + "iota19qq0ezu97zl76wqnpdxxleuf55gk0eqhscjtdgqm5sqwav6gcarz6vvesnk" + ); +} From 7082a82ec3e03a17958e4428b1553a246f9eae40 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Sun, 5 Nov 2023 23:07:59 +0100 Subject: [PATCH 02/25] Fix common feature set CI --- sdk/src/types/block/address/multi.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/src/types/block/address/multi.rs b/sdk/src/types/block/address/multi.rs index 13a17e1d00..62aaacb704 100644 --- a/sdk/src/types/block/address/multi.rs +++ b/sdk/src/types/block/address/multi.rs @@ -1,6 +1,7 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use alloc::vec; use core::{fmt, ops::RangeInclusive, str::FromStr}; use derive_more::{AsRef, Display, From}; From 9f3b33844af30e970f0123ea2ff81b7bf6b81715 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 6 Nov 2023 10:43:20 +0100 Subject: [PATCH 03/25] Add verify_weight --- sdk/src/types/block/address/multi.rs | 28 ++++++++++++++++++++++++++++ sdk/src/types/block/error.rs | 2 ++ 2 files changed, 30 insertions(+) diff --git a/sdk/src/types/block/address/multi.rs b/sdk/src/types/block/address/multi.rs index 62aaacb704..7fa715b701 100644 --- a/sdk/src/types/block/address/multi.rs +++ b/sdk/src/types/block/address/multi.rs @@ -19,9 +19,37 @@ pub(crate) type WeightedAddressCount = #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct WeightedAddress { address: Address, + #[packable(verify_with = verify_weight)] weight: u8, } +impl WeightedAddress { + /// Creates a new [`WeightedAddress`]. + pub fn new(address: Address, weight: u8) -> Result { + verify_weight::(&weight, &())?; + + Ok(Self { address, weight }) + } + + /// Returns the address of the [`WeightedAddress`]. + pub fn address(&self) -> &Address { + &self.address + } + + /// Returns the weight of the [`WeightedAddress`]. + pub fn weight(&self) -> u8 { + self.weight + } +} + +fn verify_weight(weight: &u8, _visitor: &()) -> Result<(), Error> { + if VERIFY && *weight == 0 { + return Err(Error::InvalidAddressWeight(*weight)); + } else { + Ok(()) + } +} + /// An address that consists of addresses with weights and a threshold value. /// The Multi Address can be unlocked if the cumulative weight of all unlocked addresses is equal to or exceeds the /// threshold. diff --git a/sdk/src/types/block/error.rs b/sdk/src/types/block/error.rs index ba6231f353..7d0df38636 100644 --- a/sdk/src/types/block/error.rs +++ b/sdk/src/types/block/error.rs @@ -71,6 +71,7 @@ pub enum Error { deposit: u64, required: u64, }, + InvalidAddressWeight(u8), InvalidContextInputKind(u8), InvalidContextInputCount(>::Error), InvalidWeightedAddressCount(>::Error), @@ -256,6 +257,7 @@ impl fmt::Display for Error { ), Self::InvalidContextInputCount(count) => write!(f, "invalid context input count: {count}"), Self::InvalidWeightedAddressCount(count) => write!(f, "invalid weighted address count: {count}"), + Self::InvalidAddressWeight(w) => write!(f, "invalid address weight: {w}"), Self::InvalidContextInputKind(k) => write!(f, "invalid context input kind: {k}"), Self::InvalidFeatureCount(count) => write!(f, "invalid feature count: {count}"), Self::InvalidFeatureKind(k) => write!(f, "invalid feature kind: {k}"), From 42d24c9583c176039e19fdbc4b0b4acf4ed17f4f Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 6 Nov 2023 10:54:26 +0100 Subject: [PATCH 04/25] Add verify_address --- sdk/src/types/block/address/multi.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sdk/src/types/block/address/multi.rs b/sdk/src/types/block/address/multi.rs index 7fa715b701..7f96fb20ca 100644 --- a/sdk/src/types/block/address/multi.rs +++ b/sdk/src/types/block/address/multi.rs @@ -18,6 +18,7 @@ pub(crate) type WeightedAddressCount = #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, AsRef, Packable)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct WeightedAddress { + #[packable(verify_with = verify_address)] address: Address, #[packable(verify_with = verify_weight)] weight: u8, @@ -26,6 +27,7 @@ pub struct WeightedAddress { impl WeightedAddress { /// Creates a new [`WeightedAddress`]. pub fn new(address: Address, weight: u8) -> Result { + verify_address::(&address, &())?; verify_weight::(&weight, &())?; Ok(Self { address, weight }) @@ -42,6 +44,14 @@ impl WeightedAddress { } } +fn verify_address(address: &Address, _visitor: &()) -> Result<(), Error> { + if VERIFY && !address.is_ed25519() && !address.is_account() && !address.is_nft() { + return Err(Error::InvalidAddressKind(address.kind())); + } else { + Ok(()) + } +} + fn verify_weight(weight: &u8, _visitor: &()) -> Result<(), Error> { if VERIFY && *weight == 0 { return Err(Error::InvalidAddressWeight(*weight)); From 8de4baa6f9ce07d87709ce08d151e4f48528d6e5 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 6 Nov 2023 10:56:53 +0100 Subject: [PATCH 05/25] Docs --- sdk/src/types/block/address/multi.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/src/types/block/address/multi.rs b/sdk/src/types/block/address/multi.rs index 7f96fb20ca..6d250fdcce 100644 --- a/sdk/src/types/block/address/multi.rs +++ b/sdk/src/types/block/address/multi.rs @@ -12,14 +12,14 @@ use crate::types::block::{address::Address, Error}; pub(crate) type WeightedAddressCount = BoundedU8<{ *MultiAddress::ADDRESSES_COUNT.start() }, { *MultiAddress::ADDRESSES_COUNT.end() }>; -// context_inputs: BoxedSlicePrefix, - /// An address with an assigned weight. #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, AsRef, Packable)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct WeightedAddress { + /// The unlocked address. #[packable(verify_with = verify_address)] address: Address, + /// The weight of the unlocked address. #[packable(verify_with = verify_weight)] weight: u8, } @@ -66,7 +66,7 @@ fn verify_weight(weight: &u8, _visitor: &()) -> Result<(), E #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Packable)] #[packable(unpack_error = Error)] pub struct MultiAddress { - // #[packable(verify_with = verify_context_inputs_packable)] + /// The weighted unlocked addresses. #[packable(unpack_error_with = |e| e.unwrap_item_err_or_else(|p| Error::InvalidWeightedAddressCount(p.into())))] addresses: BoxedSlicePrefix, /// The threshold that needs to be reached by the unlocked addresses in order to unlock the multi address. From 414b4cf40e08b6b29184de8e04da52dec6c8c112 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 6 Nov 2023 10:59:43 +0100 Subject: [PATCH 06/25] Add verify_threshold --- sdk/src/types/block/address/multi.rs | 11 +++++++++++ sdk/src/types/block/error.rs | 2 ++ 2 files changed, 13 insertions(+) diff --git a/sdk/src/types/block/address/multi.rs b/sdk/src/types/block/address/multi.rs index 6d250fdcce..66090bc2ca 100644 --- a/sdk/src/types/block/address/multi.rs +++ b/sdk/src/types/block/address/multi.rs @@ -70,6 +70,7 @@ pub struct MultiAddress { #[packable(unpack_error_with = |e| e.unwrap_item_err_or_else(|p| Error::InvalidWeightedAddressCount(p.into())))] addresses: BoxedSlicePrefix, /// The threshold that needs to be reached by the unlocked addresses in order to unlock the multi address. + #[packable(verify_with = verify_threshold)] threshold: u16, } @@ -82,6 +83,8 @@ impl MultiAddress { /// Creates a new [`MultiAddress`]. #[inline(always)] pub fn new(addresses: Vec, threshold: u16) -> Result { + verify_threshold::(&threshold, &())?; + Ok(Self { addresses: BoxedSlicePrefix::::try_from( addresses.into_boxed_slice(), @@ -104,6 +107,14 @@ impl MultiAddress { // } } +fn verify_threshold(threshold: &u16, _visitor: &()) -> Result<(), Error> { + if VERIFY && *threshold == 0 { + return Err(Error::InvalidAddressWeightThreshold(*threshold)); + } else { + Ok(()) + } +} + // impl FromStr for MultiAddress { // type Err = Error; diff --git a/sdk/src/types/block/error.rs b/sdk/src/types/block/error.rs index 7d0df38636..7638bd16b4 100644 --- a/sdk/src/types/block/error.rs +++ b/sdk/src/types/block/error.rs @@ -72,6 +72,7 @@ pub enum Error { required: u64, }, InvalidAddressWeight(u8), + InvalidAddressWeightThreshold(u16), InvalidContextInputKind(u8), InvalidContextInputCount(>::Error), InvalidWeightedAddressCount(>::Error), @@ -258,6 +259,7 @@ impl fmt::Display for Error { Self::InvalidContextInputCount(count) => write!(f, "invalid context input count: {count}"), Self::InvalidWeightedAddressCount(count) => write!(f, "invalid weighted address count: {count}"), Self::InvalidAddressWeight(w) => write!(f, "invalid address weight: {w}"), + Self::InvalidAddressWeightThreshold(t) => write!(f, "invalid address weight threshold: {t}"), Self::InvalidContextInputKind(k) => write!(f, "invalid context input kind: {k}"), Self::InvalidFeatureCount(count) => write!(f, "invalid feature count: {count}"), Self::InvalidFeatureKind(k) => write!(f, "invalid feature kind: {k}"), From 899c820b4132012c3800d5db328b3d488310f31b Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 6 Nov 2023 12:50:48 +0100 Subject: [PATCH 07/25] Add impl Packable for MultiAddress --- sdk/src/types/block/address/multi.rs | 42 ++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/sdk/src/types/block/address/multi.rs b/sdk/src/types/block/address/multi.rs index 66090bc2ca..24de69c4bc 100644 --- a/sdk/src/types/block/address/multi.rs +++ b/sdk/src/types/block/address/multi.rs @@ -5,7 +5,14 @@ use alloc::vec; use core::{fmt, ops::RangeInclusive, str::FromStr}; use derive_more::{AsRef, Display, From}; -use packable::{bounded::BoundedU8, prefix::BoxedSlicePrefix, Packable}; +use packable::{ + bounded::BoundedU8, + error::{UnpackError, UnpackErrorExt}, + packer::Packer, + prefix::BoxedSlicePrefix, + unpacker::Unpacker, + Packable, +}; use crate::types::block::{address::Address, Error}; @@ -63,14 +70,11 @@ fn verify_weight(weight: &u8, _visitor: &()) -> Result<(), E /// An address that consists of addresses with weights and a threshold value. /// The Multi Address can be unlocked if the cumulative weight of all unlocked addresses is equal to or exceeds the /// threshold. -#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Packable)] -#[packable(unpack_error = Error)] +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct MultiAddress { /// The weighted unlocked addresses. - #[packable(unpack_error_with = |e| e.unwrap_item_err_or_else(|p| Error::InvalidWeightedAddressCount(p.into())))] addresses: BoxedSlicePrefix, /// The threshold that needs to be reached by the unlocked addresses in order to unlock the multi address. - #[packable(verify_with = verify_threshold)] threshold: u16, } @@ -107,6 +111,34 @@ impl MultiAddress { // } } +impl Packable for MultiAddress { + type UnpackError = Error; + type UnpackVisitor = (); + + #[inline] + fn pack(&self, packer: &mut P) -> Result<(), P::Error> { + self.addresses.pack(packer)?; + self.threshold.pack(packer)?; + + Ok(()) + } + + #[inline] + fn unpack( + unpacker: &mut U, + visitor: &Self::UnpackVisitor, + ) -> Result> { + let addresses = + BoxedSlicePrefix::::unpack::<_, VERIFY>(unpacker, visitor) + .map_packable_err(|e| e.unwrap_item_err_or_else(|e| Error::InvalidWeightedAddressCount(e.into())))?; + let threshold = u16::unpack::<_, VERIFY>(unpacker, visitor).coerce()?; + + verify_threshold::(&threshold, &()).map_err(UnpackError::Packable)?; + + Ok(Self { addresses, threshold }) + } +} + fn verify_threshold(threshold: &u16, _visitor: &()) -> Result<(), Error> { if VERIFY && *threshold == 0 { return Err(Error::InvalidAddressWeightThreshold(*threshold)); From 095f948f2c20946b470578ad034da64a193c79af Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 6 Nov 2023 12:54:10 +0100 Subject: [PATCH 08/25] Bring back Address Debug --- sdk/src/types/block/address/mod.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/sdk/src/types/block/address/mod.rs b/sdk/src/types/block/address/mod.rs index 608670d26f..626eeb43ca 100644 --- a/sdk/src/types/block/address/mod.rs +++ b/sdk/src/types/block/address/mod.rs @@ -35,7 +35,7 @@ use crate::types::block::{ }; /// A generic address supporting different address kinds. -#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, Display, Packable)] +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, From, Display, Packable)] #[packable(tag_type = u8, with_error = Error::InvalidAddressKind)] #[packable(unpack_error = Error)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(untagged))] @@ -70,6 +70,20 @@ impl From for Address { } } +impl core::fmt::Debug for Address { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Ed25519(address) => address.fmt(f), + Self::Account(address) => address.fmt(f), + Self::Nft(address) => address.fmt(f), + Self::Anchor(address) => address.fmt(f), + Self::ImplicitAccountCreation(address) => address.fmt(f), + Self::Multi(address) => address.fmt(f), + Self::Restricted(address) => address.fmt(f), + } + } +} + impl Address { /// Returns the address kind of an [`Address`]. pub fn kind(&self) -> u8 { From 42cd39014cd14488af307dfeb00e2afb5e1d6f4b Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 6 Nov 2023 12:57:16 +0100 Subject: [PATCH 09/25] Nit --- sdk/src/types/block/error.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/src/types/block/error.rs b/sdk/src/types/block/error.rs index 7638bd16b4..dc6c168f65 100644 --- a/sdk/src/types/block/error.rs +++ b/sdk/src/types/block/error.rs @@ -73,9 +73,9 @@ pub enum Error { }, InvalidAddressWeight(u8), InvalidAddressWeightThreshold(u16), + InvalidWeightedAddressCount(>::Error), InvalidContextInputKind(u8), InvalidContextInputCount(>::Error), - InvalidWeightedAddressCount(>::Error), InvalidFeatureCount(>::Error), InvalidFeatureKind(u8), InvalidFoundryOutputSupply { @@ -257,9 +257,9 @@ impl fmt::Display for Error { "storage deposit return of {deposit} exceeds the original output amount of {amount}" ), Self::InvalidContextInputCount(count) => write!(f, "invalid context input count: {count}"), - Self::InvalidWeightedAddressCount(count) => write!(f, "invalid weighted address count: {count}"), Self::InvalidAddressWeight(w) => write!(f, "invalid address weight: {w}"), Self::InvalidAddressWeightThreshold(t) => write!(f, "invalid address weight threshold: {t}"), + Self::InvalidWeightedAddressCount(count) => write!(f, "invalid weighted address count: {count}"), Self::InvalidContextInputKind(k) => write!(f, "invalid context input kind: {k}"), Self::InvalidFeatureCount(count) => write!(f, "invalid feature count: {count}"), Self::InvalidFeatureKind(k) => write!(f, "invalid feature kind: {k}"), From 581f222ffe2f331ad914dbd60fe40e89be18884a Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 6 Nov 2023 13:07:45 +0100 Subject: [PATCH 10/25] Add verify_cumulative_weight --- sdk/src/types/block/address/multi.rs | 35 +++++++++++++++++++++------- sdk/src/types/block/error.rs | 14 +++++++++++ 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/sdk/src/types/block/address/multi.rs b/sdk/src/types/block/address/multi.rs index 24de69c4bc..ca6366152e 100644 --- a/sdk/src/types/block/address/multi.rs +++ b/sdk/src/types/block/address/multi.rs @@ -89,13 +89,13 @@ impl MultiAddress { pub fn new(addresses: Vec, threshold: u16) -> Result { verify_threshold::(&threshold, &())?; - Ok(Self { - addresses: BoxedSlicePrefix::::try_from( - addresses.into_boxed_slice(), - ) - .map_err(Error::InvalidWeightedAddressCount)?, - threshold, - }) + let addresses = + BoxedSlicePrefix::::try_from(addresses.into_boxed_slice()) + .map_err(Error::InvalidWeightedAddressCount)?; + + verify_cumulative_weight::(&addresses, &threshold, &())?; + + Ok(Self { addresses, threshold }) } // /// Returns the [`AccountId`] of an [`MultiAddress`]. @@ -133,7 +133,8 @@ impl Packable for MultiAddress { .map_packable_err(|e| e.unwrap_item_err_or_else(|e| Error::InvalidWeightedAddressCount(e.into())))?; let threshold = u16::unpack::<_, VERIFY>(unpacker, visitor).coerce()?; - verify_threshold::(&threshold, &()).map_err(UnpackError::Packable)?; + verify_threshold::(&threshold, &()).map_err(UnpackError::Packable)?; + verify_cumulative_weight::(&addresses, &threshold, &()).map_err(UnpackError::Packable)?; Ok(Self { addresses, threshold }) } @@ -147,6 +148,24 @@ fn verify_threshold(threshold: &u16, _visitor: &()) -> Resul } } +fn verify_cumulative_weight( + addresses: &[WeightedAddress], + threshold: &u16, + _visitor: &(), +) -> Result<(), Error> { + if VERIFY { + let cumulative_weight = addresses.iter().map(|address| address.weight as u16).sum::(); + + if cumulative_weight < *threshold { + return Err(Error::InvalidCumulativeAddressWeight { + cumulative_weight, + threshold: *threshold, + }); + } + } + Ok(()) +} + // impl FromStr for MultiAddress { // type Err = Error; diff --git a/sdk/src/types/block/error.rs b/sdk/src/types/block/error.rs index dc6c168f65..61cfa77c3f 100644 --- a/sdk/src/types/block/error.rs +++ b/sdk/src/types/block/error.rs @@ -73,6 +73,10 @@ pub enum Error { }, InvalidAddressWeight(u8), InvalidAddressWeightThreshold(u16), + InvalidCumulativeAddressWeight { + cumulative_weight: u16, + threshold: u16, + }, InvalidWeightedAddressCount(>::Error), InvalidContextInputKind(u8), InvalidContextInputCount(>::Error), @@ -259,6 +263,16 @@ impl fmt::Display for Error { Self::InvalidContextInputCount(count) => write!(f, "invalid context input count: {count}"), Self::InvalidAddressWeight(w) => write!(f, "invalid address weight: {w}"), Self::InvalidAddressWeightThreshold(t) => write!(f, "invalid address weight threshold: {t}"), + Self::InvalidAddressWeightThreshold(t) => write!(f, "invalid address weight threshold: {t}"), + Self::InvalidCumulativeAddressWeight { + cumulative_weight, + threshold, + } => { + write!( + f, + "invalid cumulative address weight: cumulative weight {cumulative_weight} < threshold {threshold}" + ) + } Self::InvalidWeightedAddressCount(count) => write!(f, "invalid weighted address count: {count}"), Self::InvalidContextInputKind(k) => write!(f, "invalid context input kind: {k}"), Self::InvalidFeatureCount(count) => write!(f, "invalid feature count: {count}"), From 642a92218e021e5ff639fd4c8839cccdc0901eaf Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 6 Nov 2023 13:09:17 +0100 Subject: [PATCH 11/25] Add getters --- sdk/src/types/block/address/multi.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/sdk/src/types/block/address/multi.rs b/sdk/src/types/block/address/multi.rs index ca6366152e..5687bd9b8a 100644 --- a/sdk/src/types/block/address/multi.rs +++ b/sdk/src/types/block/address/multi.rs @@ -98,17 +98,17 @@ impl MultiAddress { Ok(Self { addresses, threshold }) } - // /// Returns the [`AccountId`] of an [`MultiAddress`]. - // #[inline(always)] - // pub fn account_id(&self) -> &AccountId { - // &self.0 - // } - - // /// Consumes an [`MultiAddress`] and returns its [`AccountId`]. - // #[inline(always)] - // pub fn into_account_id(self) -> AccountId { - // self.0 - // } + /// Returns the addresses of a [`MultiAddress`]. + #[inline(always)] + pub fn addresses(&self) -> &[WeightedAddress] { + &self.addresses + } + + /// Returns the threshold of a [`MultiAddress`]. + #[inline(always)] + pub fn threshold(&self) -> u16 { + self.threshold + } } impl Packable for MultiAddress { From 971830e7ba08f3d0938e0b38feb8f712444eac23 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 6 Nov 2023 13:15:40 +0100 Subject: [PATCH 12/25] Add verify_addresses --- sdk/src/types/block/address/multi.rs | 13 +++++++++++++ sdk/src/types/block/error.rs | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/sdk/src/types/block/address/multi.rs b/sdk/src/types/block/address/multi.rs index 5687bd9b8a..0b7755b05c 100644 --- a/sdk/src/types/block/address/multi.rs +++ b/sdk/src/types/block/address/multi.rs @@ -5,6 +5,7 @@ use alloc::vec; use core::{fmt, ops::RangeInclusive, str::FromStr}; use derive_more::{AsRef, Display, From}; +use iterator_sorted::is_unique_sorted; use packable::{ bounded::BoundedU8, error::{UnpackError, UnpackErrorExt}, @@ -87,6 +88,7 @@ impl MultiAddress { /// Creates a new [`MultiAddress`]. #[inline(always)] pub fn new(addresses: Vec, threshold: u16) -> Result { + verify_addresses::(&addresses, &())?; verify_threshold::(&threshold, &())?; let addresses = @@ -131,6 +133,9 @@ impl Packable for MultiAddress { let addresses = BoxedSlicePrefix::::unpack::<_, VERIFY>(unpacker, visitor) .map_packable_err(|e| e.unwrap_item_err_or_else(|e| Error::InvalidWeightedAddressCount(e.into())))?; + + verify_addresses::(&addresses, &()).map_err(UnpackError::Packable)?; + let threshold = u16::unpack::<_, VERIFY>(unpacker, visitor).coerce()?; verify_threshold::(&threshold, &()).map_err(UnpackError::Packable)?; @@ -140,6 +145,14 @@ impl Packable for MultiAddress { } } +fn verify_addresses(addresses: &[WeightedAddress], _visitor: &()) -> Result<(), Error> { + if VERIFY && !is_unique_sorted(addresses.iter().map(WeightedAddress::address)) { + return Err(Error::WeightedAddressesNotUniqueSorted); + } else { + Ok(()) + } +} + fn verify_threshold(threshold: &u16, _visitor: &()) -> Result<(), Error> { if VERIFY && *threshold == 0 { return Err(Error::InvalidAddressWeightThreshold(*threshold)); diff --git a/sdk/src/types/block/error.rs b/sdk/src/types/block/error.rs index 61cfa77c3f..3cf67ed214 100644 --- a/sdk/src/types/block/error.rs +++ b/sdk/src/types/block/error.rs @@ -78,6 +78,7 @@ pub enum Error { threshold: u16, }, InvalidWeightedAddressCount(>::Error), + WeightedAddressesNotUniqueSorted, InvalidContextInputKind(u8), InvalidContextInputCount(>::Error), InvalidFeatureCount(>::Error), @@ -274,6 +275,9 @@ impl fmt::Display for Error { ) } Self::InvalidWeightedAddressCount(count) => write!(f, "invalid weighted address count: {count}"), + Self::WeightedAddressesNotUniqueSorted => { + write!(f, "weighted addresses are not unique and/or sorted") + } Self::InvalidContextInputKind(k) => write!(f, "invalid context input kind: {k}"), Self::InvalidFeatureCount(count) => write!(f, "invalid feature count: {count}"), Self::InvalidFeatureKind(k) => write!(f, "invalid feature kind: {k}"), From 2f628dfe938f0cdfe5a71855bc0fdb8f49ee36ed Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 6 Nov 2023 13:31:03 +0100 Subject: [PATCH 13/25] Error nits --- sdk/src/types/block/address/multi.rs | 4 ++-- sdk/src/types/block/error.rs | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/sdk/src/types/block/address/multi.rs b/sdk/src/types/block/address/multi.rs index 0b7755b05c..f71bef96b1 100644 --- a/sdk/src/types/block/address/multi.rs +++ b/sdk/src/types/block/address/multi.rs @@ -155,7 +155,7 @@ fn verify_addresses(addresses: &[WeightedAddress], _visitor: fn verify_threshold(threshold: &u16, _visitor: &()) -> Result<(), Error> { if VERIFY && *threshold == 0 { - return Err(Error::InvalidAddressWeightThreshold(*threshold)); + return Err(Error::InvalidMultiAddressThreshold(*threshold)); } else { Ok(()) } @@ -170,7 +170,7 @@ fn verify_cumulative_weight( let cumulative_weight = addresses.iter().map(|address| address.weight as u16).sum::(); if cumulative_weight < *threshold { - return Err(Error::InvalidCumulativeAddressWeight { + return Err(Error::InvalidMultiAddressCumulativeWeight { cumulative_weight, threshold: *threshold, }); diff --git a/sdk/src/types/block/error.rs b/sdk/src/types/block/error.rs index 3cf67ed214..c54d7cc549 100644 --- a/sdk/src/types/block/error.rs +++ b/sdk/src/types/block/error.rs @@ -72,8 +72,8 @@ pub enum Error { required: u64, }, InvalidAddressWeight(u8), - InvalidAddressWeightThreshold(u16), - InvalidCumulativeAddressWeight { + InvalidMultiAddressThreshold(u16), + InvalidMultiAddressCumulativeWeight { cumulative_weight: u16, threshold: u16, }, @@ -263,15 +263,14 @@ impl fmt::Display for Error { ), Self::InvalidContextInputCount(count) => write!(f, "invalid context input count: {count}"), Self::InvalidAddressWeight(w) => write!(f, "invalid address weight: {w}"), - Self::InvalidAddressWeightThreshold(t) => write!(f, "invalid address weight threshold: {t}"), - Self::InvalidAddressWeightThreshold(t) => write!(f, "invalid address weight threshold: {t}"), - Self::InvalidCumulativeAddressWeight { + Self::InvalidMultiAddressThreshold(t) => write!(f, "invalid multi address threshold: {t}"), + Self::InvalidMultiAddressCumulativeWeight { cumulative_weight, threshold, } => { write!( f, - "invalid cumulative address weight: cumulative weight {cumulative_weight} < threshold {threshold}" + "invalid multi address cumulative weight {cumulative_weight} < threshold {threshold}" ) } Self::InvalidWeightedAddressCount(count) => write!(f, "invalid weighted address count: {count}"), From 8a802f9b9405fbbb8d3efa67fa821ae7a3b116d1 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 6 Nov 2023 13:35:44 +0100 Subject: [PATCH 14/25] Allow anchor --- sdk/src/types/block/address/multi.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sdk/src/types/block/address/multi.rs b/sdk/src/types/block/address/multi.rs index f71bef96b1..fb2e6387a1 100644 --- a/sdk/src/types/block/address/multi.rs +++ b/sdk/src/types/block/address/multi.rs @@ -53,11 +53,15 @@ impl WeightedAddress { } fn verify_address(address: &Address, _visitor: &()) -> Result<(), Error> { - if VERIFY && !address.is_ed25519() && !address.is_account() && !address.is_nft() { - return Err(Error::InvalidAddressKind(address.kind())); - } else { - Ok(()) + if VERIFY { + if !matches!( + address, + Address::Ed25519(_) | Address::Account(_) | Address::Nft(_) | Address::Anchor(_) + ) { + return Err(Error::InvalidAddressKind(address.kind())); + } } + Ok(()) } fn verify_weight(weight: &u8, _visitor: &()) -> Result<(), Error> { From 05bb493265ebde8a726ab7ab1e9a9a7ec3fbfd0c Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 6 Nov 2023 13:45:09 +0100 Subject: [PATCH 15/25] Nits --- sdk/src/types/block/address/multi.rs | 19 ++----------------- sdk/tests/types/address/multi.rs | 2 ++ 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/sdk/src/types/block/address/multi.rs b/sdk/src/types/block/address/multi.rs index fb2e6387a1..9f39746c79 100644 --- a/sdk/src/types/block/address/multi.rs +++ b/sdk/src/types/block/address/multi.rs @@ -1,10 +1,9 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use alloc::vec; use core::{fmt, ops::RangeInclusive, str::FromStr}; -use derive_more::{AsRef, Display, From}; +use derive_more::{AsRef, From}; use iterator_sorted::is_unique_sorted; use packable::{ bounded::BoundedU8, @@ -183,26 +182,12 @@ fn verify_cumulative_weight( Ok(()) } -// impl FromStr for MultiAddress { -// type Err = Error; - -// fn from_str(s: &str) -> Result { -// Ok(Self::new(AccountId::from_str(s)?)) -// } -// } - impl fmt::Display for MultiAddress { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - panic!() + todo!() } } -// impl core::fmt::Debug for MultiAddress { -// fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { -// write!(f, "MultiAddress({self})") -// } -// } - #[cfg(feature = "serde")] mod dto { use serde::{Deserialize, Serialize}; diff --git a/sdk/tests/types/address/multi.rs b/sdk/tests/types/address/multi.rs index ea767e5f69..cf3b68941a 100644 --- a/sdk/tests/types/address/multi.rs +++ b/sdk/tests/types/address/multi.rs @@ -50,6 +50,8 @@ fn bech32() { }); let multi_address = serde_json::from_value::
(multi_address_json).unwrap(); + println!("{multi_address:?}"); + assert_eq!( multi_address.to_bech32_unchecked("iota"), "iota19qq0ezu97zl76wqnpdxxleuf55gk0eqhscjtdgqm5sqwav6gcarz6vvesnk" From b50808c5a10d60ca95702294586b7ad1b0687283 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 6 Nov 2023 13:46:54 +0100 Subject: [PATCH 16/25] MultiAddress Display --- sdk/src/types/block/address/multi.rs | 6 +++--- sdk/tests/types/address/multi.rs | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/sdk/src/types/block/address/multi.rs b/sdk/src/types/block/address/multi.rs index 9f39746c79..232729b05b 100644 --- a/sdk/src/types/block/address/multi.rs +++ b/sdk/src/types/block/address/multi.rs @@ -1,7 +1,7 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use core::{fmt, ops::RangeInclusive, str::FromStr}; +use core::{fmt, ops::RangeInclusive}; use derive_more::{AsRef, From}; use iterator_sorted::is_unique_sorted; @@ -11,7 +11,7 @@ use packable::{ packer::Packer, prefix::BoxedSlicePrefix, unpacker::Unpacker, - Packable, + Packable, PackableExt, }; use crate::types::block::{address::Address, Error}; @@ -184,7 +184,7 @@ fn verify_cumulative_weight( impl fmt::Display for MultiAddress { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - todo!() + write!(f, "{}", prefix_hex::encode(self.pack_to_vec())) } } diff --git a/sdk/tests/types/address/multi.rs b/sdk/tests/types/address/multi.rs index cf3b68941a..ea767e5f69 100644 --- a/sdk/tests/types/address/multi.rs +++ b/sdk/tests/types/address/multi.rs @@ -50,8 +50,6 @@ fn bech32() { }); let multi_address = serde_json::from_value::
(multi_address_json).unwrap(); - println!("{multi_address:?}"); - assert_eq!( multi_address.to_bech32_unchecked("iota"), "iota19qq0ezu97zl76wqnpdxxleuf55gk0eqhscjtdgqm5sqwav6gcarz6vvesnk" From ebd4dab62bf70f1b0d656bf2242bf3f92b49ab4c Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 6 Nov 2023 13:52:47 +0100 Subject: [PATCH 17/25] Fix common feature sets CI --- sdk/src/types/block/address/bech32.rs | 1 + sdk/src/types/block/address/multi.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/sdk/src/types/block/address/bech32.rs b/sdk/src/types/block/address/bech32.rs index 18701195e0..676e5e0dd5 100644 --- a/sdk/src/types/block/address/bech32.rs +++ b/sdk/src/types/block/address/bech32.rs @@ -3,6 +3,7 @@ use alloc::{ string::{String, ToString}, + vec, vec::Vec, }; use core::str::FromStr; diff --git a/sdk/src/types/block/address/multi.rs b/sdk/src/types/block/address/multi.rs index 232729b05b..adeebd11ea 100644 --- a/sdk/src/types/block/address/multi.rs +++ b/sdk/src/types/block/address/multi.rs @@ -1,6 +1,7 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use alloc::vec::Vec; use core::{fmt, ops::RangeInclusive}; use derive_more::{AsRef, From}; From e427afe6ff0558ae02ba55351a9499e3b55de636 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 6 Nov 2023 14:28:01 +0100 Subject: [PATCH 18/25] Update sdk/src/types/block/address/multi.rs Co-authored-by: DaughterOfMars --- sdk/src/types/block/address/multi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/src/types/block/address/multi.rs b/sdk/src/types/block/address/multi.rs index adeebd11ea..88dbdcb12f 100644 --- a/sdk/src/types/block/address/multi.rs +++ b/sdk/src/types/block/address/multi.rs @@ -84,7 +84,7 @@ pub struct MultiAddress { } impl MultiAddress { - /// The [`Address`](crate::types::block::address::Address) kind of an [`MultiAddress`]. + /// The [`Address`](crate::types::block::address::Address) kind of a [`MultiAddress`]. pub const KIND: u8 = 40; /// The allowed range of inner [`Address`]es. pub const ADDRESSES_COUNT: RangeInclusive = 1..=10; From 0fd38b9882c195fd9ac93e5a0175ab0b328c0d6f Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 6 Nov 2023 14:39:59 +0100 Subject: [PATCH 19/25] Use chain --- sdk/src/types/block/address/bech32.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sdk/src/types/block/address/bech32.rs b/sdk/src/types/block/address/bech32.rs index 676e5e0dd5..f499259101 100644 --- a/sdk/src/types/block/address/bech32.rs +++ b/sdk/src/types/block/address/bech32.rs @@ -3,7 +3,6 @@ use alloc::{ string::{String, ToString}, - vec, vec::Vec, }; use core::str::FromStr; @@ -178,9 +177,9 @@ impl Bech32Address { impl core::fmt::Display for Bech32Address { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let bytes = if self.inner.is_multi() { - let mut bytes = vec![MultiAddress::KIND]; - bytes.extend(Blake2b256::digest(self.inner.pack_to_vec())); - bytes + std::iter::once(MultiAddress::KIND) + .chain(Blake2b256::digest(self.inner.pack_to_vec())) + .collect() } else { self.inner.pack_to_vec() }; From 9ec9d411a87ca0628087b885e295fde84a3f97ce Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Mon, 6 Nov 2023 21:12:16 +0100 Subject: [PATCH 20/25] Use impl IntoIterator --- sdk/src/types/block/address/multi.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sdk/src/types/block/address/multi.rs b/sdk/src/types/block/address/multi.rs index 88dbdcb12f..c2aa911676 100644 --- a/sdk/src/types/block/address/multi.rs +++ b/sdk/src/types/block/address/multi.rs @@ -91,13 +91,14 @@ impl MultiAddress { /// Creates a new [`MultiAddress`]. #[inline(always)] - pub fn new(addresses: Vec, threshold: u16) -> Result { + pub fn new(addresses: impl IntoIterator, threshold: u16) -> Result { + let addresses = addresses.into_iter().collect::>(); + verify_addresses::(&addresses, &())?; verify_threshold::(&threshold, &())?; - let addresses = - BoxedSlicePrefix::::try_from(addresses.into_boxed_slice()) - .map_err(Error::InvalidWeightedAddressCount)?; + let addresses = BoxedSlicePrefix::::try_from(addresses) + .map_err(Error::InvalidWeightedAddressCount)?; verify_cumulative_weight::(&addresses, &threshold, &())?; From e2d3ddf1d74e4ee455716730d055396048ed6c08 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Tue, 7 Nov 2023 13:41:48 +0100 Subject: [PATCH 21/25] Impl better display --- sdk/src/types/block/address/multi.rs | 17 +++++++++++++---- sdk/tests/types/address/multi.rs | 2 ++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/sdk/src/types/block/address/multi.rs b/sdk/src/types/block/address/multi.rs index c2aa911676..48633ffaf5 100644 --- a/sdk/src/types/block/address/multi.rs +++ b/sdk/src/types/block/address/multi.rs @@ -4,7 +4,7 @@ use alloc::vec::Vec; use core::{fmt, ops::RangeInclusive}; -use derive_more::{AsRef, From}; +use derive_more::{AsRef, Display, From}; use iterator_sorted::is_unique_sorted; use packable::{ bounded::BoundedU8, @@ -12,7 +12,7 @@ use packable::{ packer::Packer, prefix::BoxedSlicePrefix, unpacker::Unpacker, - Packable, PackableExt, + Packable, }; use crate::types::block::{address::Address, Error}; @@ -21,7 +21,8 @@ pub(crate) type WeightedAddressCount = BoundedU8<{ *MultiAddress::ADDRESSES_COUNT.start() }, { *MultiAddress::ADDRESSES_COUNT.end() }>; /// An address with an assigned weight. -#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, AsRef, Packable)] +#[derive(Clone, Debug, Display, Eq, PartialEq, Ord, PartialOrd, Hash, From, AsRef, Packable)] +#[display(fmt = "{address}")] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct WeightedAddress { /// The unlocked address. @@ -186,7 +187,15 @@ fn verify_cumulative_weight( impl fmt::Display for MultiAddress { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", prefix_hex::encode(self.pack_to_vec())) + write!( + f, + "[{}]", + self.addresses() + .iter() + .map(|address| address.to_string()) + .collect::>() + .join(", ") + ) } } diff --git a/sdk/tests/types/address/multi.rs b/sdk/tests/types/address/multi.rs index ea767e5f69..43b8efaf50 100644 --- a/sdk/tests/types/address/multi.rs +++ b/sdk/tests/types/address/multi.rs @@ -50,6 +50,8 @@ fn bech32() { }); let multi_address = serde_json::from_value::
(multi_address_json).unwrap(); + println!("{}", multi_address); + assert_eq!( multi_address.to_bech32_unchecked("iota"), "iota19qq0ezu97zl76wqnpdxxleuf55gk0eqhscjtdgqm5sqwav6gcarz6vvesnk" From d353eb691bc8180e3b401f4cfa79f6856a013e50 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Tue, 7 Nov 2023 13:58:41 +0100 Subject: [PATCH 22/25] Remove print --- sdk/tests/types/address/multi.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/sdk/tests/types/address/multi.rs b/sdk/tests/types/address/multi.rs index 43b8efaf50..ea767e5f69 100644 --- a/sdk/tests/types/address/multi.rs +++ b/sdk/tests/types/address/multi.rs @@ -50,8 +50,6 @@ fn bech32() { }); let multi_address = serde_json::from_value::
(multi_address_json).unwrap(); - println!("{}", multi_address); - assert_eq!( multi_address.to_bech32_unchecked("iota"), "iota19qq0ezu97zl76wqnpdxxleuf55gk0eqhscjtdgqm5sqwav6gcarz6vvesnk" From d317490c15ad062ae170a30c58ccc0ef21196f6e Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Tue, 7 Nov 2023 14:19:03 +0100 Subject: [PATCH 23/25] Fix common features set --- sdk/src/types/block/address/bech32.rs | 2 +- sdk/src/types/block/address/multi.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/src/types/block/address/bech32.rs b/sdk/src/types/block/address/bech32.rs index f499259101..7a7d33263b 100644 --- a/sdk/src/types/block/address/bech32.rs +++ b/sdk/src/types/block/address/bech32.rs @@ -177,7 +177,7 @@ impl Bech32Address { impl core::fmt::Display for Bech32Address { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let bytes = if self.inner.is_multi() { - std::iter::once(MultiAddress::KIND) + core::iter::once(MultiAddress::KIND) .chain(Blake2b256::digest(self.inner.pack_to_vec())) .collect() } else { diff --git a/sdk/src/types/block/address/multi.rs b/sdk/src/types/block/address/multi.rs index 48633ffaf5..4402dce12c 100644 --- a/sdk/src/types/block/address/multi.rs +++ b/sdk/src/types/block/address/multi.rs @@ -1,7 +1,7 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use alloc::vec::Vec; +use alloc::{boxed::Box, string::ToString, vec::Vec}; use core::{fmt, ops::RangeInclusive}; use derive_more::{AsRef, Display, From}; From 00a53093cdfb0fefc097793c436b837fa0119b15 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Tue, 7 Nov 2023 14:25:04 +0100 Subject: [PATCH 24/25] Update sdk/src/types/block/address/bech32.rs Co-authored-by: /alex/ --- sdk/src/types/block/address/bech32.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/src/types/block/address/bech32.rs b/sdk/src/types/block/address/bech32.rs index 7a7d33263b..70fba15297 100644 --- a/sdk/src/types/block/address/bech32.rs +++ b/sdk/src/types/block/address/bech32.rs @@ -184,7 +184,7 @@ impl core::fmt::Display for Bech32Address { self.inner.pack_to_vec() }; - write!(f, "{}", bech32::encode::(self.hrp.0, &bytes,).unwrap()) + write!(f, "{}", bech32::encode::(self.hrp.0, &bytes).unwrap()) } } From 5d2bcd9cd58fe69349bd01721193f2c724966e9f Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Tue, 7 Nov 2023 14:28:31 +0100 Subject: [PATCH 25/25] document unwrap --- sdk/src/types/block/address/bech32.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/src/types/block/address/bech32.rs b/sdk/src/types/block/address/bech32.rs index 70fba15297..cbc8d26d5b 100644 --- a/sdk/src/types/block/address/bech32.rs +++ b/sdk/src/types/block/address/bech32.rs @@ -184,6 +184,7 @@ impl core::fmt::Display for Bech32Address { self.inner.pack_to_vec() }; + // PANIC: unwrap is fine as the Bech32Address has been validated at construction. write!(f, "{}", bech32::encode::(self.hrp.0, &bytes).unwrap()) } }