diff --git a/sdk/src/types/block/address/account.rs b/sdk/src/types/block/address/account.rs index 9961c53b1f..91483b2980 100644 --- a/sdk/src/types/block/address/account.rs +++ b/sdk/src/types/block/address/account.rs @@ -58,8 +58,6 @@ impl core::fmt::Debug for AccountAddress { } mod dto { - use alloc::format; - use serde::{Deserialize, Serialize}; use super::*; diff --git a/sdk/src/types/block/address/ed25519.rs b/sdk/src/types/block/address/ed25519.rs index f4877b10ad..14cfbeab38 100644 --- a/sdk/src/types/block/address/ed25519.rs +++ b/sdk/src/types/block/address/ed25519.rs @@ -47,8 +47,6 @@ impl core::fmt::Debug for Ed25519Address { } mod dto { - use alloc::format; - use serde::{Deserialize, Serialize}; use super::*; diff --git a/sdk/src/types/block/address/nft.rs b/sdk/src/types/block/address/nft.rs index ddbc06b030..959d21018c 100644 --- a/sdk/src/types/block/address/nft.rs +++ b/sdk/src/types/block/address/nft.rs @@ -58,8 +58,6 @@ impl core::fmt::Debug for NftAddress { } mod dto { - use alloc::format; - use serde::{Deserialize, Serialize}; use super::*; diff --git a/sdk/src/types/block/context_input/block_issuance_credit.rs b/sdk/src/types/block/context_input/block_issuance_credit.rs index 3a1b0fcec5..4105f64ea9 100644 --- a/sdk/src/types/block/context_input/block_issuance_credit.rs +++ b/sdk/src/types/block/context_input/block_issuance_credit.rs @@ -26,8 +26,6 @@ impl BlockIssuanceCreditContextInput { } mod dto { - use alloc::format; - use serde::{Deserialize, Serialize}; use super::*; diff --git a/sdk/src/types/block/context_input/commitment.rs b/sdk/src/types/block/context_input/commitment.rs index 2e00488779..b43be8c69c 100644 --- a/sdk/src/types/block/context_input/commitment.rs +++ b/sdk/src/types/block/context_input/commitment.rs @@ -25,8 +25,6 @@ impl CommitmentContextInput { } mod dto { - use alloc::format; - use serde::{Deserialize, Serialize}; use super::*; diff --git a/sdk/src/types/block/context_input/reward.rs b/sdk/src/types/block/context_input/reward.rs index 3c01197ef4..c49e757483 100644 --- a/sdk/src/types/block/context_input/reward.rs +++ b/sdk/src/types/block/context_input/reward.rs @@ -23,8 +23,6 @@ impl RewardContextInput { } mod dto { - use alloc::format; - use serde::{Deserialize, Serialize}; use super::*; diff --git a/sdk/src/types/block/error.rs b/sdk/src/types/block/error.rs index 3e51800325..5a201ff7ac 100644 --- a/sdk/src/types/block/error.rs +++ b/sdk/src/types/block/error.rs @@ -9,7 +9,7 @@ use crypto::Error as CryptoError; use prefix_hex::Error as HexError; use primitive_types::U256; -use super::{mana::AllotmentCount, protocol::ProtocolParametersHash}; +use super::{mana::AllotmentCount, protocol::ProtocolParametersHash, public_key::PublicKeyCount}; use crate::types::block::{ input::UtxoInput, output::{ @@ -94,6 +94,7 @@ pub enum Error { expected: ProtocolParametersHash, actual: ProtocolParametersHash, }, + InvalidPublicKeyCount(>::Error), InvalidReferenceIndex(>::Error), InvalidSignature, InvalidSignatureKind(u8), @@ -132,6 +133,7 @@ pub enum Error { expected: u8, actual: u8, }, + PublicKeysNotUniqueSorted, RemainingBytesAfterBlock, SelfControlledAccountOutput(AccountId), SelfDepositNft(NftId), @@ -249,6 +251,7 @@ impl fmt::Display for Error { "invalid protocol parameters hash: expected {expected} but got {actual}" ) } + Self::InvalidPublicKeyCount(count) => write!(f, "invalid public key count: {count}"), Self::InvalidReferenceIndex(index) => write!(f, "invalid reference index: {index}"), Self::InvalidSignature => write!(f, "invalid signature provided"), Self::InvalidSignatureKind(k) => write!(f, "invalid signature kind: {k}"), @@ -308,6 +311,7 @@ impl fmt::Display for Error { Self::ProtocolVersionMismatch { expected, actual } => { write!(f, "protocol version mismatch: expected {expected} but got {actual}") } + Self::PublicKeysNotUniqueSorted => write!(f, "public keys are not unique and/or sorted"), Self::RemainingBytesAfterBlock => { write!(f, "remaining bytes after block") } diff --git a/sdk/src/types/block/input/utxo.rs b/sdk/src/types/block/input/utxo.rs index 2c5616bf3b..bcd07ff96d 100644 --- a/sdk/src/types/block/input/utxo.rs +++ b/sdk/src/types/block/input/utxo.rs @@ -47,8 +47,6 @@ impl core::fmt::Debug for UtxoInput { } mod dto { - use alloc::format; - use serde::{Deserialize, Serialize}; use super::*; diff --git a/sdk/src/types/block/macro.rs b/sdk/src/types/block/macro.rs index 850537287d..1c1ffe85b2 100644 --- a/sdk/src/types/block/macro.rs +++ b/sdk/src/types/block/macro.rs @@ -177,7 +177,7 @@ macro_rules! impl_serde_typed_dto { fn deserialize>(d: D) -> Result { let dto = <$dto>::deserialize(d)?; if dto.kind != Self::KIND { - return Err(serde::de::Error::custom(format!( + return Err(serde::de::Error::custom(alloc::format!( "invalid {} type: expected {}, found {}", $type_str, Self::KIND, diff --git a/sdk/src/types/block/output/feature/block_issuer.rs b/sdk/src/types/block/output/feature/block_issuer.rs new file mode 100644 index 0000000000..e7f8bad8c2 --- /dev/null +++ b/sdk/src/types/block/output/feature/block_issuer.rs @@ -0,0 +1,97 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use alloc::vec::Vec; + +use crate::types::block::{ + public_key::{PublicKey, PublicKeys}, + slot::SlotIndex, + Error, +}; + +/// This feature defines the public keys with which a signature from the containing +/// account's Block Issuance Credit can be verified in order to burn Mana. +#[derive(Clone, Debug, Eq, PartialEq, Hash, packable::Packable)] +#[packable(unpack_error = Error)] +pub struct BlockIssuerFeature { + /// The slot index at which the Block Issuer Feature expires and can be removed. + expiry_slot: SlotIndex, + /// The Block Issuer Keys. + public_keys: PublicKeys, +} + +impl BlockIssuerFeature { + /// The [`Feature`](crate::types::block::output::Feature) kind of a [`BlockIssuerFeature`]. + pub const KIND: u8 = 4; + + /// Creates a new [`BlockIssuerFeature`]. + #[inline(always)] + pub fn new( + expiry_slot: impl Into, + public_keys: impl IntoIterator, + ) -> Result { + let public_keys = PublicKeys::from_vec(public_keys.into_iter().collect::>())?; + Ok(Self { + expiry_slot: expiry_slot.into(), + public_keys, + }) + } + + /// Returns the Slot Index at which the Block Issuer Feature expires and can be removed. + pub fn expiry_slot(&self) -> SlotIndex { + self.expiry_slot + } + + /// Returns the Block Issuer Keys. + pub fn public_keys(&self) -> &[PublicKey] { + &self.public_keys + } +} + +mod dto { + use alloc::vec::Vec; + + use serde::{Deserialize, Serialize}; + + use super::BlockIssuerFeature; + use crate::types::block::{ + public_key::{dto::PublicKeyDto, PublicKey}, + slot::SlotIndex, + Error, + }; + + #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + struct BlockIssuerFeatureDto { + #[serde(rename = "type")] + kind: u8, + expiry_slot: SlotIndex, + keys: Vec, + } + + impl From<&BlockIssuerFeature> for BlockIssuerFeatureDto { + fn from(value: &BlockIssuerFeature) -> Self { + Self { + kind: BlockIssuerFeature::KIND, + expiry_slot: value.expiry_slot, + keys: value.public_keys.iter().map(|key| key.into()).collect(), + } + } + } + + impl TryFrom for BlockIssuerFeature { + type Error = Error; + + fn try_from(value: BlockIssuerFeatureDto) -> Result { + let keys = value + .keys + .into_iter() + .map(PublicKey::try_from) + .collect::, Error>>()?; + + Self::new(value.expiry_slot, keys) + } + } + + impl_serde_typed_dto!(BlockIssuerFeature, BlockIssuerFeatureDto, "block issuer feature"); +} diff --git a/sdk/src/types/block/output/feature/issuer.rs b/sdk/src/types/block/output/feature/issuer.rs index ed7db8f808..d4fa89a8d5 100644 --- a/sdk/src/types/block/output/feature/issuer.rs +++ b/sdk/src/types/block/output/feature/issuer.rs @@ -27,8 +27,6 @@ impl IssuerFeature { } mod dto { - use alloc::format; - use serde::{Deserialize, Serialize}; use super::*; diff --git a/sdk/src/types/block/output/feature/metadata.rs b/sdk/src/types/block/output/feature/metadata.rs index fdf9444f76..e5903a6d5b 100644 --- a/sdk/src/types/block/output/feature/metadata.rs +++ b/sdk/src/types/block/output/feature/metadata.rs @@ -75,7 +75,7 @@ impl core::fmt::Debug for MetadataFeature { } mod dto { - use alloc::{borrow::Cow, format}; + use alloc::borrow::Cow; use serde::{Deserialize, Serialize}; diff --git a/sdk/src/types/block/output/feature/mod.rs b/sdk/src/types/block/output/feature/mod.rs index b9430bbe7b..6f766fa933 100644 --- a/sdk/src/types/block/output/feature/mod.rs +++ b/sdk/src/types/block/output/feature/mod.rs @@ -1,6 +1,7 @@ // Copyright 2021-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +mod block_issuer; mod issuer; mod metadata; mod sender; @@ -15,7 +16,8 @@ use iterator_sorted::is_unique_sorted; use packable::{bounded::BoundedU8, prefix::BoxedSlicePrefix, Packable}; pub use self::{ - issuer::IssuerFeature, metadata::MetadataFeature, sender::SenderFeature, staking::StakingFeature, tag::TagFeature, + block_issuer::BlockIssuerFeature, issuer::IssuerFeature, metadata::MetadataFeature, sender::SenderFeature, + staking::StakingFeature, tag::TagFeature, }; pub(crate) use self::{metadata::MetadataFeatureLength, tag::TagFeatureLength}; use crate::types::block::{create_bitflags, Error}; @@ -38,6 +40,9 @@ pub enum Feature { /// A tag feature. #[packable(tag = TagFeature::KIND)] Tag(TagFeature), + /// A block issuer feature. + #[packable(tag = BlockIssuerFeature::KIND)] + BlockIssuer(BlockIssuerFeature), /// A staking feature. #[packable(tag = StakingFeature::KIND)] Staking(StakingFeature), @@ -62,6 +67,7 @@ impl core::fmt::Debug for Feature { Self::Issuer(feature) => feature.fmt(f), Self::Metadata(feature) => feature.fmt(f), Self::Tag(feature) => feature.fmt(f), + Self::BlockIssuer(feature) => feature.fmt(f), Self::Staking(feature) => feature.fmt(f), } } @@ -75,6 +81,7 @@ impl Feature { Self::Issuer(_) => IssuerFeature::KIND, Self::Metadata(_) => MetadataFeature::KIND, Self::Tag(_) => TagFeature::KIND, + Self::BlockIssuer(_) => BlockIssuerFeature::KIND, Self::Staking(_) => StakingFeature::KIND, } } @@ -86,6 +93,7 @@ impl Feature { Self::Issuer(_) => FeatureFlags::ISSUER, Self::Metadata(_) => FeatureFlags::METADATA, Self::Tag(_) => FeatureFlags::TAG, + Self::BlockIssuer(_) => FeatureFlags::BLOCK_ISSUER, Self::Staking(_) => FeatureFlags::STAKING, } } @@ -150,6 +158,21 @@ impl Feature { } } + /// Checks whether the feature is a [`BlockIssuerFeature`]. + pub fn is_block_issuer(&self) -> bool { + matches!(self, Self::BlockIssuer(_)) + } + + /// Gets the feature as an actual [`BlockIssuerFeature`]. + /// NOTE: Will panic if the feature is not a [`BlockIssuerFeature`]. + pub fn as_block_issuer(&self) -> &BlockIssuerFeature { + if let Self::BlockIssuer(feature) = self { + feature + } else { + panic!("invalid downcast of non-BlockIssuerFeature"); + } + } + /// Checks whether the feature is a [`StakingFeature`]. pub fn is_staking(&self) -> bool { matches!(self, Self::Staking(_)) @@ -175,6 +198,7 @@ create_bitflags!( (ISSUER, IssuerFeature), (METADATA, MetadataFeature), (TAG, TagFeature), + (BLOCK_ISSUER, BlockIssuerFeature), (STAKING, StakingFeature), ] ); @@ -270,6 +294,11 @@ impl Features { 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) + } + /// Gets a reference to a [`StakingFeature`], if any. pub fn staking(&self) -> Option<&StakingFeature> { self.get(StakingFeature::KIND).map(Feature::as_staking) @@ -311,6 +340,7 @@ mod test { FeatureFlags::ISSUER, FeatureFlags::METADATA, FeatureFlags::TAG, + FeatureFlags::BLOCK_ISSUER, FeatureFlags::STAKING ] ); diff --git a/sdk/src/types/block/output/feature/sender.rs b/sdk/src/types/block/output/feature/sender.rs index 7094ef67c3..6ce4a11f74 100644 --- a/sdk/src/types/block/output/feature/sender.rs +++ b/sdk/src/types/block/output/feature/sender.rs @@ -27,8 +27,6 @@ impl SenderFeature { } mod dto { - use alloc::format; - use serde::{Deserialize, Serialize}; use super::*; diff --git a/sdk/src/types/block/output/feature/staking.rs b/sdk/src/types/block/output/feature/staking.rs index 04acdbd76c..97f52f7e6b 100644 --- a/sdk/src/types/block/output/feature/staking.rs +++ b/sdk/src/types/block/output/feature/staking.rs @@ -50,8 +50,6 @@ impl StakingFeature { } mod dto { - use alloc::format; - use serde::{Deserialize, Serialize}; use super::*; diff --git a/sdk/src/types/block/output/feature/tag.rs b/sdk/src/types/block/output/feature/tag.rs index ffa192664c..bd47355a15 100644 --- a/sdk/src/types/block/output/feature/tag.rs +++ b/sdk/src/types/block/output/feature/tag.rs @@ -67,7 +67,7 @@ impl core::fmt::Debug for TagFeature { } mod dto { - use alloc::{borrow::Cow, format}; + use alloc::borrow::Cow; use serde::{Deserialize, Serialize}; diff --git a/sdk/src/types/block/output/token_scheme/simple.rs b/sdk/src/types/block/output/token_scheme/simple.rs index 0c186ea96e..90df5693a5 100644 --- a/sdk/src/types/block/output/token_scheme/simple.rs +++ b/sdk/src/types/block/output/token_scheme/simple.rs @@ -117,8 +117,6 @@ fn verify_supply(minted_tokens: &U256, melted_tokens: &U256, maximum_supply: &U2 } mod dto { - use alloc::format; - use serde::{Deserialize, Serialize}; use super::*; diff --git a/sdk/src/types/block/output/unlock_condition/address.rs b/sdk/src/types/block/output/unlock_condition/address.rs index 1017ba58a6..b8cae1622d 100644 --- a/sdk/src/types/block/output/unlock_condition/address.rs +++ b/sdk/src/types/block/output/unlock_condition/address.rs @@ -27,8 +27,6 @@ impl AddressUnlockCondition { } mod dto { - use alloc::format; - use serde::{Deserialize, Serialize}; use super::*; diff --git a/sdk/src/types/block/output/unlock_condition/expiration.rs b/sdk/src/types/block/output/unlock_condition/expiration.rs index bc9711937b..ef2d40b075 100644 --- a/sdk/src/types/block/output/unlock_condition/expiration.rs +++ b/sdk/src/types/block/output/unlock_condition/expiration.rs @@ -65,8 +65,6 @@ fn verify_timestamp(timestamp: &u32, _: &()) -> Result<(), E } mod dto { - use alloc::format; - use serde::{Deserialize, Serialize}; use super::*; diff --git a/sdk/src/types/block/output/unlock_condition/governor_address.rs b/sdk/src/types/block/output/unlock_condition/governor_address.rs index 4abc7d6a56..11769d73dd 100644 --- a/sdk/src/types/block/output/unlock_condition/governor_address.rs +++ b/sdk/src/types/block/output/unlock_condition/governor_address.rs @@ -29,8 +29,6 @@ impl GovernorAddressUnlockCondition { } mod dto { - use alloc::format; - use serde::{Deserialize, Serialize}; use super::*; diff --git a/sdk/src/types/block/output/unlock_condition/immutable_account_address.rs b/sdk/src/types/block/output/unlock_condition/immutable_account_address.rs index 4bc11ee4da..86c71de764 100644 --- a/sdk/src/types/block/output/unlock_condition/immutable_account_address.rs +++ b/sdk/src/types/block/output/unlock_condition/immutable_account_address.rs @@ -27,8 +27,6 @@ impl ImmutableAccountAddressUnlockCondition { } mod dto { - use alloc::format; - use serde::{Deserialize, Serialize}; use super::*; diff --git a/sdk/src/types/block/output/unlock_condition/state_controller_address.rs b/sdk/src/types/block/output/unlock_condition/state_controller_address.rs index c820a94214..54d44ccbfc 100644 --- a/sdk/src/types/block/output/unlock_condition/state_controller_address.rs +++ b/sdk/src/types/block/output/unlock_condition/state_controller_address.rs @@ -29,8 +29,6 @@ impl StateControllerAddressUnlockCondition { } mod dto { - use alloc::format; - use serde::{Deserialize, Serialize}; use super::*; diff --git a/sdk/src/types/block/output/unlock_condition/timelock.rs b/sdk/src/types/block/output/unlock_condition/timelock.rs index 42a62e981f..b2d60ef002 100644 --- a/sdk/src/types/block/output/unlock_condition/timelock.rs +++ b/sdk/src/types/block/output/unlock_condition/timelock.rs @@ -39,8 +39,6 @@ fn verify_timestamp(timestamp: &u32, _: &()) -> Result<(), E } mod dto { - use alloc::format; - use serde::{Deserialize, Serialize}; use super::*; diff --git a/sdk/src/types/block/payload/tagged_data/mod.rs b/sdk/src/types/block/payload/tagged_data/mod.rs index 6c06c49498..abef88eb82 100644 --- a/sdk/src/types/block/payload/tagged_data/mod.rs +++ b/sdk/src/types/block/payload/tagged_data/mod.rs @@ -68,8 +68,6 @@ impl core::fmt::Debug for TaggedDataPayload { } mod dto { - use alloc::format; - use serde::{Deserialize, Serialize}; use super::*; diff --git a/sdk/src/types/block/public_key/mod.rs b/sdk/src/types/block/public_key/mod.rs index 38f8b67836..02acb3d8e8 100644 --- a/sdk/src/types/block/public_key/mod.rs +++ b/sdk/src/types/block/public_key/mod.rs @@ -3,12 +3,18 @@ mod ed25519; -use derive_more::From; +use alloc::{boxed::Box, collections::BTreeSet, vec::Vec}; +use core::ops::RangeInclusive; + +use derive_more::{Deref, From}; +use iterator_sorted::is_unique_sorted; +use packable::{bounded::BoundedU8, prefix::BoxedSlicePrefix, Packable}; pub use self::ed25519::Ed25519PublicKey; use crate::types::block::Error; #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, From, packable::Packable)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(untagged))] #[packable(unpack_error = Error)] #[packable(tag_type = u8, with_error = Error::InvalidPublicKeyKind)] pub enum PublicKey { @@ -77,3 +83,77 @@ pub(crate) mod dto { } } } + +pub(crate) type PublicKeyCount = BoundedU8<{ *PublicKeys::COUNT_RANGE.start() }, { *PublicKeys::COUNT_RANGE.end() }>; + +/// Lexicographically ordered list of unique [`PublicKey`] +#[derive(Clone, Debug, Eq, PartialEq, Deref, Packable, Hash)] +#[packable(unpack_error = Error, with = |e| e.unwrap_item_err_or_else(|p| Error::InvalidPublicKeyCount(p.into())))] +pub struct PublicKeys(#[packable(verify_with = verify_public_keys)] BoxedSlicePrefix); + +fn verify_public_keys(public_keys: &[PublicKey], _visitor: &()) -> Result<(), Error> { + if VERIFY && !is_unique_sorted(public_keys.iter()) { + return Err(Error::PublicKeysNotUniqueSorted); + } + + Ok(()) +} + +impl TryFrom> for PublicKeys { + type Error = Error; + + #[inline(always)] + fn try_from(public_keys: Vec) -> Result { + Self::from_vec(public_keys) + } +} + +impl TryFrom> for PublicKeys { + type Error = Error; + + #[inline(always)] + fn try_from(public_keys: BTreeSet) -> Result { + Self::from_set(public_keys) + } +} + +impl IntoIterator for PublicKeys { + type Item = PublicKey; + type IntoIter = alloc::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + Vec::from(Into::>::into(self.0)).into_iter() + } +} + +impl PublicKeys { + /// The minimum number of public_keys of a transaction. + pub const COUNT_MIN: u8 = 1; + /// The maximum number of public_keys of a transaction. + pub const COUNT_MAX: u8 = 128; + /// The range of valid numbers of public_keys. + pub const COUNT_RANGE: RangeInclusive = Self::COUNT_MIN..=Self::COUNT_MAX; // [1..128] + + /// Creates a new [`PublicKeys`] from a vec. + pub fn from_vec(public_keys: Vec) -> Result { + let mut public_keys = BoxedSlicePrefix::::try_from(public_keys.into_boxed_slice()) + .map_err(Error::InvalidPublicKeyCount)?; + + public_keys.sort(); + + // Still need to verify the duplicate public keys. + verify_public_keys::(&public_keys, &())?; + + Ok(Self(public_keys)) + } + + /// Creates a new [`PublicKeys`] from an ordered set. + pub fn from_set(public_keys: BTreeSet) -> Result { + let public_keys = + BoxedSlicePrefix::::try_from(public_keys.into_iter().collect::>()) + .map_err(Error::InvalidPublicKeyCount)?; + + // We don't need to verify the public keys here, because they are already verified by the BTreeSet. + Ok(Self(public_keys)) + } +} diff --git a/sdk/src/types/block/rand/mod.rs b/sdk/src/types/block/rand/mod.rs index 9cac72a1fe..9b5564ef2e 100644 --- a/sdk/src/types/block/rand/mod.rs +++ b/sdk/src/types/block/rand/mod.rs @@ -21,6 +21,8 @@ pub mod output; pub mod parents; /// Module providing random payload generation utilities. pub mod payload; +/// Module providing random public key generation utilities. +pub mod public_key; /// Module providing random signature generation utilities. pub mod signature; /// Module providing random slot generation utilities. diff --git a/sdk/src/types/block/rand/output/feature.rs b/sdk/src/types/block/rand/output/feature.rs index 3b41389a63..fcdc6ad92b 100644 --- a/sdk/src/types/block/rand/output/feature.rs +++ b/sdk/src/types/block/rand/output/feature.rs @@ -5,12 +5,15 @@ use alloc::vec::Vec; use crate::types::block::{ output::feature::{ - Feature, FeatureFlags, IssuerFeature, MetadataFeature, SenderFeature, StakingFeature, TagFeature, + BlockIssuerFeature, Feature, FeatureFlags, IssuerFeature, MetadataFeature, SenderFeature, StakingFeature, + TagFeature, }, + public_key::PublicKeys, rand::{ address::rand_address, bytes::rand_bytes, number::{rand_number, rand_number_range}, + public_key::rand_public_keys, }, }; @@ -36,6 +39,19 @@ pub fn rand_tag_feature() -> TagFeature { TagFeature::new(bytes).unwrap() } +/// Generates a random [`BlockIssuerFeature`]. +pub fn rand_block_issuer_feature() -> BlockIssuerFeature { + BlockIssuerFeature::new( + rand_number::(), + rand_public_keys(rand_number_range( + PublicKeys::COUNT_MIN as usize..=PublicKeys::COUNT_MAX as usize, + )) + .into_iter() + .collect::>(), + ) + .unwrap() +} + /// Generates a random [`StakingFeature`]. pub fn rand_staking_feature() -> StakingFeature { StakingFeature::new(rand_number(), rand_number(), rand_number(), rand_number()) @@ -47,6 +63,7 @@ fn rand_feature_from_flag(flag: &FeatureFlags) -> Feature { FeatureFlags::ISSUER => Feature::Issuer(rand_issuer_feature()), FeatureFlags::METADATA => Feature::Metadata(rand_metadata_feature()), FeatureFlags::TAG => Feature::Tag(rand_tag_feature()), + FeatureFlags::BLOCK_ISSUER => Feature::BlockIssuer(rand_block_issuer_feature()), FeatureFlags::STAKING => Feature::Staking(rand_staking_feature()), _ => unreachable!(), } diff --git a/sdk/src/types/block/rand/public_key.rs b/sdk/src/types/block/rand/public_key.rs new file mode 100644 index 0000000000..6158e3d6b4 --- /dev/null +++ b/sdk/src/types/block/rand/public_key.rs @@ -0,0 +1,26 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use alloc::collections::BTreeSet; + +use crate::types::block::public_key::{Ed25519PublicKey, PublicKey}; + +/// Generates a valid random Ed25519 public key. +pub fn rand_ed25519_public_key() -> Ed25519PublicKey { + let key = crypto::signatures::ed25519::SecretKey::generate().unwrap(); + key.public_key().into() +} + +/// Generates a valid random public key. +pub fn rand_public_key() -> PublicKey { + rand_ed25519_public_key().into() +} + +/// Generates a vector of random valid public keys of a given length. +pub fn rand_public_keys(len: usize) -> BTreeSet { + let mut public_keys: BTreeSet = BTreeSet::new(); + while public_keys.len() < len { + public_keys.insert(rand_public_key()); + } + public_keys +} diff --git a/sdk/src/types/block/signature/ed25519.rs b/sdk/src/types/block/signature/ed25519.rs index 959a5ca5f0..676d481698 100644 --- a/sdk/src/types/block/signature/ed25519.rs +++ b/sdk/src/types/block/signature/ed25519.rs @@ -128,7 +128,7 @@ impl Packable for Ed25519Signature { } mod dto { - use alloc::{format, string::String}; + use alloc::string::String; use serde::{Deserialize, Serialize}; diff --git a/sdk/src/types/block/slot/index.rs b/sdk/src/types/block/slot/index.rs index 8d92a4f5a1..27d62fee89 100644 --- a/sdk/src/types/block/slot/index.rs +++ b/sdk/src/types/block/slot/index.rs @@ -16,5 +16,11 @@ impl SlotIndex { } } +impl From for u64 { + fn from(slot_index: SlotIndex) -> Self { + *slot_index + } +} + #[cfg(feature = "serde")] string_serde_impl!(SlotIndex); diff --git a/sdk/src/types/block/unlock/account.rs b/sdk/src/types/block/unlock/account.rs index e496be7d3b..4a301733b9 100644 --- a/sdk/src/types/block/unlock/account.rs +++ b/sdk/src/types/block/unlock/account.rs @@ -37,8 +37,6 @@ impl AccountUnlock { } mod dto { - use alloc::format; - use serde::{Deserialize, Serialize}; use super::*; diff --git a/sdk/src/types/block/unlock/nft.rs b/sdk/src/types/block/unlock/nft.rs index 30ebd89a05..d7c114a9ca 100644 --- a/sdk/src/types/block/unlock/nft.rs +++ b/sdk/src/types/block/unlock/nft.rs @@ -37,8 +37,6 @@ impl NftUnlock { } mod dto { - use alloc::format; - use serde::{Deserialize, Serialize}; use super::*; diff --git a/sdk/src/types/block/unlock/reference.rs b/sdk/src/types/block/unlock/reference.rs index 2d5a52957b..3499cb0d67 100644 --- a/sdk/src/types/block/unlock/reference.rs +++ b/sdk/src/types/block/unlock/reference.rs @@ -34,8 +34,6 @@ impl ReferenceUnlock { } mod dto { - use alloc::format; - use serde::{Deserialize, Serialize}; use super::*; diff --git a/sdk/src/types/block/unlock/signature.rs b/sdk/src/types/block/unlock/signature.rs index 3047f76113..fdacd2549e 100644 --- a/sdk/src/types/block/unlock/signature.rs +++ b/sdk/src/types/block/unlock/signature.rs @@ -28,8 +28,6 @@ impl SignatureUnlock { } pub(crate) mod dto { - use alloc::format; - use serde::{Deserialize, Serialize}; use super::*;