Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GovernorMetadataFeature #1571

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions bindings/nodejs/lib/types/block/output/output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,32 +243,25 @@ 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.
* @param mana The amount of stored mana.
* @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,
mana: u64,
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;
}
}

Expand Down
3 changes: 0 additions & 3 deletions bindings/python/iota_sdk/types/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 :
Expand Down Expand Up @@ -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(
Expand Down
10 changes: 6 additions & 4 deletions sdk/src/types/block/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -86,9 +86,9 @@ pub enum Error {
InvalidBech32Hrp(Bech32HrpError),
InvalidCapabilitiesCount(<u8 as TryFrom<usize>>::Error),
InvalidSignedBlockLength(usize),
InvalidStateMetadataLength(<StateMetadataLength as TryFrom<usize>>::Error),
InvalidManaValue(u64),
InvalidMetadataFeatureLength(<MetadataFeatureLength as TryFrom<usize>>::Error),
InvalidGovernorMetadataFeatureLength(<GovernorMetadataFeatureLength as TryFrom<usize>>::Error),
InvalidNativeTokenCount(<NativeTokenCount as TryFrom<usize>>::Error),
InvalidNetworkName(FromUtf8Error),
InvalidManaDecayFactors,
Expand Down Expand Up @@ -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"),
Expand Down
76 changes: 22 additions & 54 deletions sdk/src/types/block/output/anchor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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
}
}
}

Expand Down Expand Up @@ -100,7 +102,6 @@ pub struct AnchorOutputBuilder {
native_tokens: BTreeSet<NativeToken>,
anchor_id: AnchorId,
state_index: Option<u32>,
state_metadata: Vec<u8>,
unlock_conditions: BTreeSet<UnlockCondition>,
features: BTreeSet<Feature>,
immutable_features: BTreeSet<Feature>,
Expand All @@ -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(),
Expand Down Expand Up @@ -181,13 +181,6 @@ impl AnchorOutputBuilder {
self
}

///
#[inline(always)]
pub fn with_state_metadata(mut self, state_metadata: impl Into<Vec<u8>>) -> 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<UnlockCondition>) -> Self {
Expand Down Expand Up @@ -276,12 +269,6 @@ impl AnchorOutputBuilder {
pub fn finish(self) -> Result<AnchorOutput, Error> {
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)?;
Expand All @@ -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,
Expand Down Expand Up @@ -345,16 +331,13 @@ 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(),
}
}
}

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 {
Expand All @@ -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<u8, StateMetadataLength>,
/// Define how the output can be unlocked in a transaction.
unlock_conditions: UnlockConditions,
/// Features of the output.
Expand All @@ -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);

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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)?;
Expand All @@ -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::<u8, StateMetadataLength>::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)?;
Expand Down Expand Up @@ -672,7 +653,6 @@ impl Packable for AnchorOutput {
native_tokens,
anchor_id,
state_index,
state_metadata,
unlock_conditions,
features,
immutable_features,
Expand Down Expand Up @@ -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};

Expand All @@ -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.
Expand All @@ -742,8 +721,6 @@ pub(crate) mod dto {
pub native_tokens: Vec<NativeToken>,
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<UnlockConditionDto>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub features: Vec<Feature>,
Expand All @@ -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(),
Expand All @@ -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, &params)?);
Expand All @@ -797,7 +772,6 @@ pub(crate) mod dto {
native_tokens: Option<Vec<NativeToken>>,
anchor_id: &AnchorId,
state_index: Option<u32>,
state_metadata: Option<Vec<u8>>,
unlock_conditions: Vec<UnlockConditionDto>,
features: Option<Vec<Feature>>,
immutable_features: Option<Vec<Feature>>,
Expand All @@ -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, &params))
Expand Down Expand Up @@ -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()),
Expand All @@ -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()),
Expand Down
2 changes: 1 addition & 1 deletion sdk/src/types/block/output/feature/block_issuer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
Loading
Loading