diff --git a/bindings/nodejs/lib/types/block/output/output.ts b/bindings/nodejs/lib/types/block/output/output.ts index fb2fe10cb1..8d54898472 100644 --- a/bindings/nodejs/lib/types/block/output/output.ts +++ b/bindings/nodejs/lib/types/block/output/output.ts @@ -243,10 +243,6 @@ class AnchorOutput extends ImmutableFeaturesOutput { * The amount of (stored) Mana held by the output. */ readonly mana: u64; - /** - * Metadata that can only be changed by the state controller. - */ - readonly stateMetadata?: HexEncodedString; /** * @param amount The amount of the output. @@ -254,7 +250,6 @@ class AnchorOutput extends ImmutableFeaturesOutput { * @param anchorId The anchor ID as hex-encoded string. * @param stateIndex A counter that must increase by 1 every time the anchor output is state transitioned. * @param unlockConditions The unlock conditions of the output. - * @param stateMetadata Metadata that can only be changed by the state controller. */ constructor( amount: u64, @@ -262,13 +257,11 @@ class AnchorOutput extends ImmutableFeaturesOutput { anchorId: AnchorId, stateIndex: number, unlockConditions: UnlockCondition[], - stateMetadata?: HexEncodedString, ) { super(OutputType.Account, amount, unlockConditions); this.anchorId = anchorId; this.stateIndex = stateIndex; this.mana = mana; - this.stateMetadata = stateMetadata; } } diff --git a/bindings/python/iota_sdk/types/output.py b/bindings/python/iota_sdk/types/output.py index 92769c780a..f9272c282a 100644 --- a/bindings/python/iota_sdk/types/output.py +++ b/bindings/python/iota_sdk/types/output.py @@ -145,8 +145,6 @@ class AnchorOutput: Features that add utility to the output but do not impose unlocking conditions. immutable_features : Features that add utility to the output but do not impose unlocking conditions. These features need to be kept in future transitions of the UTXO state machine. - state_metadata : - Metadata that can only be changed by the state controller. native_tokens : Native tokens added to the new output. type : @@ -175,7 +173,6 @@ class AnchorOutput: metadata=config( decoder=deserialize_features )) - state_metadata: Optional[HexStr] = None native_tokens: Optional[List[NativeToken]] = None type: int = field( default_factory=lambda: int( diff --git a/sdk/src/types/block/error.rs b/sdk/src/types/block/error.rs index 33b23839be..5c6618ccc3 100644 --- a/sdk/src/types/block/error.rs +++ b/sdk/src/types/block/error.rs @@ -17,8 +17,8 @@ use crate::types::block::{ output::{ feature::{BlockIssuerKeyCount, FeatureCount}, unlock_condition::UnlockConditionCount, - AccountId, AnchorId, ChainId, MetadataFeatureLength, NativeTokenCount, NftId, OutputIndex, StateMetadataLength, - TagFeatureLength, + AccountId, AnchorId, ChainId, GovernorMetadataFeatureLength, MetadataFeatureLength, NativeTokenCount, NftId, + OutputIndex, TagFeatureLength, }, payload::{ContextInputCount, InputCount, OutputCount, TagLength, TaggedDataLength}, protocol::ProtocolParametersHash, @@ -86,9 +86,9 @@ pub enum Error { InvalidBech32Hrp(Bech32HrpError), InvalidCapabilitiesCount(>::Error), InvalidSignedBlockLength(usize), - InvalidStateMetadataLength(>::Error), InvalidManaValue(u64), InvalidMetadataFeatureLength(>::Error), + InvalidGovernorMetadataFeatureLength(>::Error), InvalidNativeTokenCount(>::Error), InvalidNetworkName(FromUtf8Error), InvalidManaDecayFactors, @@ -265,11 +265,13 @@ impl fmt::Display for Error { Self::InvalidInputCount(count) => write!(f, "invalid input count: {count}"), Self::InvalidInputOutputIndex(index) => write!(f, "invalid input or output index: {index}"), Self::InvalidSignedBlockLength(length) => write!(f, "invalid signed block length {length}"), - Self::InvalidStateMetadataLength(length) => write!(f, "invalid state metadata length: {length}"), Self::InvalidManaValue(mana) => write!(f, "invalid mana value: {mana}"), Self::InvalidMetadataFeatureLength(length) => { write!(f, "invalid metadata feature length: {length}") } + Self::InvalidGovernorMetadataFeatureLength(length) => { + write!(f, "invalid governor metadata feature length: {length}") + } Self::InvalidNativeTokenCount(count) => write!(f, "invalid native token count: {count}"), Self::InvalidNetworkName(err) => write!(f, "invalid network name: {err}"), Self::InvalidManaDecayFactors => write!(f, "invalid mana decay factors"), diff --git a/sdk/src/types/block/output/anchor.rs b/sdk/src/types/block/output/anchor.rs index c54fd11901..8c76ced9f7 100644 --- a/sdk/src/types/block/output/anchor.rs +++ b/sdk/src/types/block/output/anchor.rs @@ -5,10 +5,8 @@ use alloc::{collections::BTreeSet, vec::Vec}; use hashbrown::HashMap; use packable::{ - bounded::BoundedU16, error::{UnpackError, UnpackErrorExt}, packer::Packer, - prefix::BoxedSlicePrefix, unpacker::Unpacker, Packable, }; @@ -50,7 +48,11 @@ impl From<&OutputId> for AnchorId { impl AnchorId { /// pub fn or_from_output_id(self, output_id: &OutputId) -> Self { - if self.is_null() { Self::from(output_id) } else { self } + if self.is_null() { + Self::from(output_id) + } else { + self + } } } @@ -100,7 +102,6 @@ pub struct AnchorOutputBuilder { native_tokens: BTreeSet, anchor_id: AnchorId, state_index: Option, - state_metadata: Vec, unlock_conditions: BTreeSet, features: BTreeSet, immutable_features: BTreeSet, @@ -125,7 +126,6 @@ impl AnchorOutputBuilder { native_tokens: BTreeSet::new(), anchor_id, state_index: None, - state_metadata: Vec::new(), unlock_conditions: BTreeSet::new(), features: BTreeSet::new(), immutable_features: BTreeSet::new(), @@ -181,13 +181,6 @@ impl AnchorOutputBuilder { self } - /// - #[inline(always)] - pub fn with_state_metadata(mut self, state_metadata: impl Into>) -> Self { - self.state_metadata = state_metadata.into(); - self - } - /// Adds an [`UnlockCondition`] to the builder, if one does not already exist of that type. #[inline(always)] pub fn add_unlock_condition(mut self, unlock_condition: impl Into) -> Self { @@ -276,12 +269,6 @@ impl AnchorOutputBuilder { pub fn finish(self) -> Result { let state_index = self.state_index.unwrap_or(0); - let state_metadata = self - .state_metadata - .into_boxed_slice() - .try_into() - .map_err(Error::InvalidStateMetadataLength)?; - verify_index_counter(&self.anchor_id, state_index)?; let unlock_conditions = UnlockConditions::from_set(self.unlock_conditions)?; @@ -302,7 +289,6 @@ impl AnchorOutputBuilder { native_tokens: NativeTokens::from_set(self.native_tokens)?, anchor_id: self.anchor_id, state_index, - state_metadata, unlock_conditions, features, immutable_features, @@ -345,7 +331,6 @@ impl From<&AnchorOutput> for AnchorOutputBuilder { native_tokens: output.native_tokens.iter().copied().collect(), anchor_id: output.anchor_id, state_index: Some(output.state_index), - state_metadata: output.state_metadata.to_vec(), unlock_conditions: output.unlock_conditions.iter().cloned().collect(), features: output.features.iter().cloned().collect(), immutable_features: output.immutable_features.iter().cloned().collect(), @@ -353,8 +338,6 @@ impl From<&AnchorOutput> for AnchorOutputBuilder { } } -pub(crate) type StateMetadataLength = BoundedU16<0, { AnchorOutput::STATE_METADATA_LENGTH_MAX }>; - /// Describes an anchor in the ledger that can be controlled by the state and governance controllers. #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct AnchorOutput { @@ -367,8 +350,6 @@ pub struct AnchorOutput { anchor_id: AnchorId, /// A counter that must increase by 1 every time the anchor is state transitioned. state_index: u32, - /// Metadata that can only be changed by the state controller. - state_metadata: BoxedSlicePrefix, /// Define how the output can be unlocked in a transaction. unlock_conditions: UnlockConditions, /// Features of the output. @@ -380,13 +361,13 @@ pub struct AnchorOutput { impl AnchorOutput { /// The [`Output`](crate::types::block::output::Output) kind of an [`AnchorOutput`]. pub const KIND: u8 = 2; - /// Maximum possible length in bytes of the state metadata. - pub const STATE_METADATA_LENGTH_MAX: u16 = 8192; /// The set of allowed [`UnlockCondition`]s for an [`AnchorOutput`]. pub const ALLOWED_UNLOCK_CONDITIONS: UnlockConditionFlags = UnlockConditionFlags::STATE_CONTROLLER_ADDRESS.union(UnlockConditionFlags::GOVERNOR_ADDRESS); /// The set of allowed [`Feature`]s for an [`AnchorOutput`]. - pub const ALLOWED_FEATURES: FeatureFlags = FeatureFlags::SENDER.union(FeatureFlags::METADATA); + pub const ALLOWED_FEATURES: FeatureFlags = FeatureFlags::SENDER + .union(FeatureFlags::METADATA) + .union(FeatureFlags::GOVERNOR_METADATA); /// The set of allowed immutable [`Feature`]s for an [`AnchorOutput`]. pub const ALLOWED_IMMUTABLE_FEATURES: FeatureFlags = FeatureFlags::ISSUER.union(FeatureFlags::METADATA); @@ -441,12 +422,6 @@ impl AnchorOutput { self.state_index } - /// - #[inline(always)] - pub fn state_metadata(&self) -> &[u8] { - &self.state_metadata - } - /// #[inline(always)] pub fn unlock_conditions(&self) -> &UnlockConditions { @@ -485,6 +460,16 @@ impl AnchorOutput { .unwrap() } + // /// Returns the governor metadata of the [`AnchorOutput`]. + // #[inline(always)] + // pub fn governor_metadata(&self) -> &[u8] { + // // An AnchorOutput must have a GovernorAddressUnlockCondition. + // self.unlock_conditions + // .governor_address() + // .map(|unlock_condition| unlock_condition.address()) + // .unwrap() + // } + /// #[inline(always)] pub fn chain_id(&self) -> ChainId { @@ -552,9 +537,8 @@ impl AnchorOutput { } } else if next_state.state_index == current_state.state_index { // Governance transition. - if current_state.amount != next_state.amount - || current_state.native_tokens != next_state.native_tokens - || current_state.state_metadata != next_state.state_metadata + if current_state.amount != next_state.amount || current_state.native_tokens != next_state.native_tokens + // || current_state.state_metadata != next_state.state_metadata { return Err(StateTransitionError::MutatedFieldWithoutRights); } @@ -619,7 +603,6 @@ impl Packable for AnchorOutput { self.native_tokens.pack(packer)?; self.anchor_id.pack(packer)?; self.state_index.pack(packer)?; - self.state_metadata.pack(packer)?; self.unlock_conditions.pack(packer)?; self.features.pack(packer)?; self.immutable_features.pack(packer)?; @@ -640,8 +623,6 @@ impl Packable for AnchorOutput { let native_tokens = NativeTokens::unpack::<_, VERIFY>(unpacker, &())?; let anchor_id = AnchorId::unpack::<_, VERIFY>(unpacker, &()).coerce()?; let state_index = u32::unpack::<_, VERIFY>(unpacker, &()).coerce()?; - let state_metadata = BoxedSlicePrefix::::unpack::<_, VERIFY>(unpacker, &()) - .map_packable_err(|err| Error::InvalidStateMetadataLength(err.into_prefix_err().into()))?; if VERIFY { verify_index_counter(&anchor_id, state_index).map_err(UnpackError::Packable)?; @@ -672,7 +653,6 @@ impl Packable for AnchorOutput { native_tokens, anchor_id, state_index, - state_metadata, unlock_conditions, features, immutable_features, @@ -715,7 +695,6 @@ fn verify_unlock_conditions(unlock_conditions: &UnlockConditions, anchor_id: &An #[cfg(feature = "serde")] pub(crate) mod dto { - use alloc::boxed::Box; use serde::{Deserialize, Serialize}; @@ -725,7 +704,7 @@ pub(crate) mod dto { block::{output::unlock_condition::dto::UnlockConditionDto, Error}, TryFromDto, }, - utils::serde::{prefix_hex_bytes, string}, + utils::serde::string, }; /// Describes an anchor in the ledger that can be controlled by the state and governance controllers. @@ -742,8 +721,6 @@ pub(crate) mod dto { pub native_tokens: Vec, pub anchor_id: AnchorId, pub state_index: u32, - #[serde(skip_serializing_if = "<[_]>::is_empty", default, with = "prefix_hex_bytes")] - pub state_metadata: Box<[u8]>, pub unlock_conditions: Vec, #[serde(skip_serializing_if = "Vec::is_empty", default)] pub features: Vec, @@ -760,7 +737,6 @@ pub(crate) mod dto { native_tokens: value.native_tokens().to_vec(), anchor_id: *value.anchor_id(), state_index: value.state_index(), - state_metadata: value.state_metadata().into(), unlock_conditions: value.unlock_conditions().iter().map(Into::into).collect::<_>(), features: value.features().to_vec(), immutable_features: value.immutable_features().to_vec(), @@ -778,8 +754,7 @@ pub(crate) mod dto { .with_state_index(dto.state_index) .with_native_tokens(dto.native_tokens) .with_features(dto.features) - .with_immutable_features(dto.immutable_features) - .with_state_metadata(dto.state_metadata); + .with_immutable_features(dto.immutable_features); for u in dto.unlock_conditions { builder = builder.add_unlock_condition(UnlockCondition::try_from_dto_with_params(u, ¶ms)?); @@ -797,7 +772,6 @@ pub(crate) mod dto { native_tokens: Option>, anchor_id: &AnchorId, state_index: Option, - state_metadata: Option>, unlock_conditions: Vec, features: Option>, immutable_features: Option>, @@ -820,10 +794,6 @@ pub(crate) mod dto { builder = builder.with_state_index(state_index); } - if let Some(state_metadata) = state_metadata { - builder = builder.with_state_metadata(state_metadata); - } - let unlock_conditions = unlock_conditions .into_iter() .map(|u| UnlockCondition::try_from_dto_with_params(u, ¶ms)) @@ -878,7 +848,6 @@ mod tests { Some(output.native_tokens().to_vec()), output.anchor_id(), output.state_index().into(), - output.state_metadata().to_owned().into(), output.unlock_conditions().iter().map(Into::into).collect(), Some(output.features().to_vec()), Some(output.immutable_features().to_vec()), @@ -898,7 +867,6 @@ mod tests { Some(builder.native_tokens.iter().copied().collect()), &builder.anchor_id, builder.state_index, - builder.state_metadata.to_owned().into(), builder.unlock_conditions.iter().map(Into::into).collect(), Some(builder.features.iter().cloned().collect()), Some(builder.immutable_features.iter().cloned().collect()), diff --git a/sdk/src/types/block/output/feature/block_issuer.rs b/sdk/src/types/block/output/feature/block_issuer.rs index dde9271ab8..900085ed5e 100644 --- a/sdk/src/types/block/output/feature/block_issuer.rs +++ b/sdk/src/types/block/output/feature/block_issuer.rs @@ -184,7 +184,7 @@ pub struct BlockIssuerFeature { impl BlockIssuerFeature { /// The [`Feature`](crate::types::block::output::Feature) kind of a [`BlockIssuerFeature`]. - pub const KIND: u8 = 4; + pub const KIND: u8 = 5; /// Creates a new [`BlockIssuerFeature`]. #[inline(always)] diff --git a/sdk/src/types/block/output/feature/governor_metadata.rs b/sdk/src/types/block/output/feature/governor_metadata.rs new file mode 100644 index 0000000000..478d3f9fab --- /dev/null +++ b/sdk/src/types/block/output/feature/governor_metadata.rs @@ -0,0 +1,139 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use alloc::{boxed::Box, string::String, vec::Vec}; +use core::{ops::RangeInclusive, str::FromStr}; + +use packable::{bounded::BoundedU16, prefix::BoxedSlicePrefix}; + +use crate::types::block::Error; + +pub(crate) type GovernorMetadataFeatureLength = + BoundedU16<{ *GovernorMetadataFeature::LENGTH_RANGE.start() }, { *GovernorMetadataFeature::LENGTH_RANGE.end() }>; + +/// Defines governor metadata, arbitrary binary data, that will be stored in the anchor output. +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, packable::Packable)] +#[packable(unpack_error = Error, with = |err| Error::InvalidGovernorMetadataFeatureLength(err.into_prefix_err().into()))] +pub struct GovernorMetadataFeature( + // Binary data. + pub(crate) BoxedSlicePrefix, +); + +macro_rules! impl_from_vec { + ($type:ty) => { + impl TryFrom<$type> for GovernorMetadataFeature { + type Error = Error; + + fn try_from(value: $type) -> Result { + Vec::::from(value).try_into() + } + } + }; +} +impl_from_vec!(&str); +impl_from_vec!(String); +impl_from_vec!(&[u8]); + +impl TryFrom<[u8; N]> for GovernorMetadataFeature { + type Error = Error; + + fn try_from(value: [u8; N]) -> Result { + value.to_vec().try_into() + } +} + +impl TryFrom> for GovernorMetadataFeature { + type Error = Error; + + fn try_from(data: Vec) -> Result { + data.into_boxed_slice().try_into() + } +} + +impl TryFrom> for GovernorMetadataFeature { + type Error = Error; + + fn try_from(data: Box<[u8]>) -> Result { + data.try_into() + .map(Self) + .map_err(Error::InvalidGovernorMetadataFeatureLength) + } +} + +impl FromStr for GovernorMetadataFeature { + type Err = Error; + + fn from_str(s: &str) -> Result { + Self::new(prefix_hex::decode::>(s).map_err(Error::Hex)?) + } +} + +impl GovernorMetadataFeature { + /// The [`Feature`](crate::types::block::output::Feature) kind of [`GovernorMetadataFeature`]. + pub const KIND: u8 = 3; + /// Valid lengths for a [`GovernorMetadataFeature`]. + pub const LENGTH_RANGE: RangeInclusive = 1..=8192; + + /// Creates a new [`GovernorMetadataFeature`]. + #[inline(always)] + pub fn new(data: impl Into>) -> Result { + Self::try_from(data.into()) + } + + /// Returns the data. + #[inline(always)] + pub fn data(&self) -> &[u8] { + &self.0 + } +} + +impl core::fmt::Display for GovernorMetadataFeature { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", prefix_hex::encode(self.data())) + } +} + +impl core::fmt::Debug for GovernorMetadataFeature { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "GovernorMetadataFeature({self})") + } +} + +#[cfg(feature = "serde")] +pub(crate) mod dto { + use alloc::borrow::Cow; + + use serde::{Deserialize, Serialize}; + + use super::*; + use crate::utils::serde::cow_boxed_slice_prefix_hex_bytes; + + #[derive(Serialize, Deserialize)] + struct GovernorMetadataFeatureDto<'a> { + #[serde(rename = "type")] + kind: u8, + #[serde(with = "cow_boxed_slice_prefix_hex_bytes")] + data: Cow<'a, BoxedSlicePrefix>, + } + + impl<'a> From<&'a GovernorMetadataFeature> for GovernorMetadataFeatureDto<'a> { + fn from(value: &'a GovernorMetadataFeature) -> Self { + Self { + kind: GovernorMetadataFeature::KIND, + data: Cow::Borrowed(&value.0), + } + } + } + + impl<'a> From> for GovernorMetadataFeature { + fn from(value: GovernorMetadataFeatureDto<'a>) -> Self { + Self(value.data.into_owned()) + } + } + + crate::impl_serde_typed_dto!( + GovernorMetadataFeature, + GovernorMetadataFeatureDto<'_>, + "governor metadata feature" + ); +} diff --git a/sdk/src/types/block/output/feature/mod.rs b/sdk/src/types/block/output/feature/mod.rs index 897db63ef9..2f515597ab 100644 --- a/sdk/src/types/block/output/feature/mod.rs +++ b/sdk/src/types/block/output/feature/mod.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 mod block_issuer; +mod governor_metadata; mod issuer; mod metadata; mod sender; @@ -19,9 +20,13 @@ use packable::{bounded::BoundedU8, prefix::BoxedSlicePrefix, Packable}; pub use self::metadata::irc_27::{Attribute, Irc27Metadata}; #[cfg(feature = "irc_30")] pub use self::metadata::irc_30::Irc30Metadata; -pub(crate) use self::{block_issuer::BlockIssuerKeyCount, metadata::MetadataFeatureLength, tag::TagFeatureLength}; +pub(crate) use self::{ + block_issuer::BlockIssuerKeyCount, governor_metadata::GovernorMetadataFeatureLength, + metadata::MetadataFeatureLength, tag::TagFeatureLength, +}; pub use self::{ block_issuer::{BlockIssuerFeature, BlockIssuerKey, BlockIssuerKeys, Ed25519BlockIssuerKey}, + governor_metadata::GovernorMetadataFeature, issuer::IssuerFeature, metadata::MetadataFeature, sender::SenderFeature, @@ -45,6 +50,9 @@ pub enum Feature { /// A metadata feature. #[packable(tag = MetadataFeature::KIND)] Metadata(MetadataFeature), + /// A governor metadata feature. + #[packable(tag = GovernorMetadataFeature::KIND)] + GovernorMetadata(GovernorMetadataFeature), /// A tag feature. #[packable(tag = TagFeature::KIND)] Tag(TagFeature), @@ -74,6 +82,7 @@ impl core::fmt::Debug for Feature { Self::Sender(feature) => feature.fmt(f), Self::Issuer(feature) => feature.fmt(f), Self::Metadata(feature) => feature.fmt(f), + Self::GovernorMetadata(feature) => feature.fmt(f), Self::Tag(feature) => feature.fmt(f), Self::BlockIssuer(feature) => feature.fmt(f), Self::Staking(feature) => feature.fmt(f), @@ -88,6 +97,7 @@ impl Feature { Self::Sender(_) => SenderFeature::KIND, Self::Issuer(_) => IssuerFeature::KIND, Self::Metadata(_) => MetadataFeature::KIND, + Self::GovernorMetadata(_) => GovernorMetadataFeature::KIND, Self::Tag(_) => TagFeature::KIND, Self::BlockIssuer(_) => BlockIssuerFeature::KIND, Self::Staking(_) => StakingFeature::KIND, @@ -100,13 +110,14 @@ impl Feature { Self::Sender(_) => FeatureFlags::SENDER, Self::Issuer(_) => FeatureFlags::ISSUER, Self::Metadata(_) => FeatureFlags::METADATA, + Self::GovernorMetadata(_) => FeatureFlags::GOVERNOR_METADATA, Self::Tag(_) => FeatureFlags::TAG, Self::BlockIssuer(_) => FeatureFlags::BLOCK_ISSUER, Self::Staking(_) => FeatureFlags::STAKING, } } - crate::def_is_as_opt!(Feature: Sender, Issuer, Metadata, Tag, BlockIssuer, Staking); + crate::def_is_as_opt!(Feature: Sender, Issuer, Metadata, GovernorMetadata, Tag, BlockIssuer, Staking); } create_bitflags!( @@ -117,6 +128,7 @@ create_bitflags!( (SENDER, SenderFeature), (ISSUER, IssuerFeature), (METADATA, MetadataFeature), + (GOVERNOR_METADATA, GovernorMetadataFeature), (TAG, TagFeature), (BLOCK_ISSUER, BlockIssuerFeature), (STAKING, StakingFeature), @@ -158,8 +170,8 @@ impl IntoIterator for Features { } impl Features { - /// - pub const COUNT_MAX: u8 = 5; + /// Maximum number of unique features. + pub const COUNT_MAX: u8 = 7; /// Creates a new [`Features`] from a vec. pub fn from_vec(features: Vec) -> Result { @@ -209,6 +221,12 @@ impl Features { self.get(MetadataFeature::KIND).map(Feature::as_metadata) } + /// Gets a reference to a [`GovernorMetadataFeature`], if any. + pub fn governor_metadata(&self) -> Option<&GovernorMetadataFeature> { + self.get(GovernorMetadataFeature::KIND) + .map(Feature::as_governor_metadata) + } + /// Gets a reference to a [`TagFeature`], if any. pub fn tag(&self) -> Option<&TagFeature> { self.get(TagFeature::KIND).map(Feature::as_tag) @@ -261,6 +279,7 @@ mod test { FeatureFlags::SENDER, FeatureFlags::ISSUER, FeatureFlags::METADATA, + FeatureFlags::GOVERNOR_METADATA, FeatureFlags::TAG, FeatureFlags::BLOCK_ISSUER, FeatureFlags::STAKING diff --git a/sdk/src/types/block/output/feature/staking.rs b/sdk/src/types/block/output/feature/staking.rs index a94b2634fe..a19aed69b6 100644 --- a/sdk/src/types/block/output/feature/staking.rs +++ b/sdk/src/types/block/output/feature/staking.rs @@ -18,7 +18,7 @@ pub struct StakingFeature { impl StakingFeature { /// The [`Feature`](crate::types::block::output::Feature) kind of [`StakingFeature`]. - pub const KIND: u8 = 5; + pub const KIND: u8 = 6; /// Creates a new [`StakingFeature`]. pub fn new( diff --git a/sdk/src/types/block/output/feature/tag.rs b/sdk/src/types/block/output/feature/tag.rs index 1a53dcb375..4541c84b14 100644 --- a/sdk/src/types/block/output/feature/tag.rs +++ b/sdk/src/types/block/output/feature/tag.rs @@ -37,7 +37,7 @@ impl TryFrom> for TagFeature { impl TagFeature { /// The [`Feature`](crate::types::block::output::Feature) kind of an [`TagFeature`]. - pub const KIND: u8 = 3; + pub const KIND: u8 = 4; /// Valid lengths for an [`TagFeature`]. pub const LENGTH_RANGE: RangeInclusive = 1..=64; diff --git a/sdk/src/types/block/output/mod.rs b/sdk/src/types/block/output/mod.rs index d6bd867569..34c299bc9e 100644 --- a/sdk/src/types/block/output/mod.rs +++ b/sdk/src/types/block/output/mod.rs @@ -52,8 +52,7 @@ pub use self::{ unlock_condition::{UnlockCondition, UnlockConditions}, }; pub(crate) use self::{ - anchor::StateMetadataLength, - feature::{MetadataFeatureLength, TagFeatureLength}, + feature::{GovernorMetadataFeatureLength, MetadataFeatureLength, TagFeatureLength}, native_token::NativeTokenCount, output_id::OutputIndex, unlock_condition::AddressUnlockCondition, diff --git a/sdk/src/types/block/rand/output/feature.rs b/sdk/src/types/block/rand/output/feature.rs index 85ebdbb374..dd8c8dcf22 100644 --- a/sdk/src/types/block/rand/output/feature.rs +++ b/sdk/src/types/block/rand/output/feature.rs @@ -6,7 +6,7 @@ use alloc::{collections::BTreeSet, vec::Vec}; use crate::types::block::{ output::feature::{ BlockIssuerFeature, BlockIssuerKey, BlockIssuerKeys, Ed25519BlockIssuerKey, Feature, FeatureFlags, - IssuerFeature, MetadataFeature, SenderFeature, StakingFeature, TagFeature, + GovernorMetadataFeature, IssuerFeature, MetadataFeature, SenderFeature, StakingFeature, TagFeature, }, rand::{ address::rand_address, @@ -32,6 +32,12 @@ pub fn rand_metadata_feature() -> MetadataFeature { MetadataFeature::new(bytes).unwrap() } +/// Generates a random [`GovernorMetadataFeature`]. +pub fn rand_governor_metadata_feature() -> GovernorMetadataFeature { + let bytes = rand_bytes(rand_number_range(GovernorMetadataFeature::LENGTH_RANGE) as usize); + GovernorMetadataFeature::new(bytes).unwrap() +} + /// Generates a random [`TagFeature`]. pub fn rand_tag_feature() -> TagFeature { let bytes = rand_bytes(rand_number_range(TagFeature::LENGTH_RANGE) as usize); @@ -83,6 +89,7 @@ fn rand_feature_from_flag(flag: &FeatureFlags) -> Feature { FeatureFlags::SENDER => Feature::Sender(rand_sender_feature()), FeatureFlags::ISSUER => Feature::Issuer(rand_issuer_feature()), FeatureFlags::METADATA => Feature::Metadata(rand_metadata_feature()), + FeatureFlags::GOVERNOR_METADATA => Feature::GovernorMetadata(rand_governor_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()), diff --git a/sdk/src/wallet/operations/transaction/high_level/create_account.rs b/sdk/src/wallet/operations/transaction/high_level/create_account.rs index 4b9b318f20..fea5c33a62 100644 --- a/sdk/src/wallet/operations/transaction/high_level/create_account.rs +++ b/sdk/src/wallet/operations/transaction/high_level/create_account.rs @@ -45,7 +45,6 @@ where /// address: None, /// immutable_metadata: Some(b"some immutable account metadata".to_vec()), /// metadata: Some(b"some account metadata".to_vec()), - /// state_metadata: Some(b"some account state metadata".to_vec()), /// }; /// /// let transaction = account.create_account_output(params, None).await?;