diff --git a/sdk/src/types/block/mod.rs b/sdk/src/types/block/mod.rs index 5a61a6b7d9..0fccfd5c75 100644 --- a/sdk/src/types/block/mod.rs +++ b/sdk/src/types/block/mod.rs @@ -56,4 +56,4 @@ pub use self::{ issuer_id::IssuerId, }; -pub(crate) const PROTOCOL_VERSION: u8 = 3; +pub const PROTOCOL_VERSION: u8 = 3; diff --git a/sdk/src/types/block/output/account.rs b/sdk/src/types/block/output/account.rs index fb0abff849..ea95d5f13b 100644 --- a/sdk/src/types/block/output/account.rs +++ b/sdk/src/types/block/output/account.rs @@ -884,18 +884,16 @@ pub(crate) mod dto { #[cfg(test)] mod tests { - use packable::PackableExt; use super::*; use crate::types::{ block::{ - address::AccountAddress, output::{dto::OutputDto, FoundryId, SimpleTokenScheme, TokenId}, protocol::protocol_parameters, rand::{ address::rand_account_address, output::{ - feature::{rand_allowed_features, rand_issuer_feature, rand_metadata_feature, rand_sender_feature}, + feature::rand_allowed_features, rand_account_id, rand_account_output, unlock_condition::{ rand_governor_address_unlock_condition_different_from, @@ -907,87 +905,6 @@ mod tests { TryFromDto, }; - #[test] - fn builder() { - let protocol_parameters = protocol_parameters(); - let account_id = rand_account_id(); - let foundry_id = FoundryId::build(&AccountAddress::from(account_id), 0, SimpleTokenScheme::KIND); - let gov_address_1 = rand_governor_address_unlock_condition_different_from(&account_id); - let gov_address_2 = rand_governor_address_unlock_condition_different_from(&account_id); - let state_address_1 = rand_state_controller_address_unlock_condition_different_from(&account_id); - let state_address_2 = rand_state_controller_address_unlock_condition_different_from(&account_id); - let sender_1 = rand_sender_feature(); - let sender_2 = rand_sender_feature(); - let issuer_1 = rand_issuer_feature(); - let issuer_2 = rand_issuer_feature(); - let amount = 500_000; - - let mut builder = AccountOutput::build_with_amount(amount, account_id) - .add_native_token(NativeToken::new(TokenId::from(foundry_id), 1000).unwrap()) - .add_unlock_condition(gov_address_1) - .add_unlock_condition(state_address_1) - .add_feature(sender_1) - .replace_feature(sender_2) - .replace_immutable_feature(issuer_1) - .add_immutable_feature(issuer_2); - - let output = builder.clone().finish().unwrap(); - assert_eq!(output.amount(), amount); - assert_eq!(output.unlock_conditions().governor_address(), Some(&gov_address_1)); - assert_eq!( - output.unlock_conditions().state_controller_address(), - Some(&state_address_1) - ); - assert_eq!(output.features().sender(), Some(&sender_2)); - assert_eq!(output.immutable_features().issuer(), Some(&issuer_1)); - - builder = builder - .clear_unlock_conditions() - .clear_features() - .clear_immutable_features() - .replace_unlock_condition(gov_address_2) - .replace_unlock_condition(state_address_2); - let output = builder.clone().finish().unwrap(); - assert_eq!(output.unlock_conditions().governor_address(), Some(&gov_address_2)); - assert_eq!( - output.unlock_conditions().state_controller_address(), - Some(&state_address_2) - ); - assert!(output.features().is_empty()); - assert!(output.immutable_features().is_empty()); - - let metadata = rand_metadata_feature(); - - let output = builder - .with_minimum_storage_deposit(protocol_parameters.rent_structure()) - .add_unlock_condition(rand_state_controller_address_unlock_condition_different_from( - &account_id, - )) - .add_unlock_condition(rand_governor_address_unlock_condition_different_from(&account_id)) - .with_features([Feature::from(metadata.clone()), sender_1.into()]) - .with_immutable_features([Feature::from(metadata.clone()), issuer_1.into()]) - .finish_with_params(ValidationParams::default().with_protocol_parameters(protocol_parameters.clone())) - .unwrap(); - - assert_eq!( - output.amount(), - Output::Account(output.clone()).rent_cost(protocol_parameters.rent_structure()) - ); - assert_eq!(output.features().metadata(), Some(&metadata)); - assert_eq!(output.features().sender(), Some(&sender_1)); - assert_eq!(output.immutable_features().metadata(), Some(&metadata)); - assert_eq!(output.immutable_features().issuer(), Some(&issuer_1)); - } - - #[test] - fn pack_unpack() { - let protocol_parameters = protocol_parameters(); - let output = rand_account_output(protocol_parameters.token_supply()); - let bytes = output.pack_to_vec(); - let output_unpacked = AccountOutput::unpack_verified(bytes, &protocol_parameters).unwrap(); - assert_eq!(output, output_unpacked); - } - #[test] fn to_from_dto() { let protocol_parameters = protocol_parameters(); diff --git a/sdk/src/types/block/output/basic.rs b/sdk/src/types/block/output/basic.rs index f8734b6b4e..51a26add26 100644 --- a/sdk/src/types/block/output/basic.rs +++ b/sdk/src/types/block/output/basic.rs @@ -24,7 +24,7 @@ use crate::types::{ ValidationParams, }; -/// +/// Builder for a [`BasicOutput`]. #[derive(Clone)] #[must_use] pub struct BasicOutputBuilder { @@ -215,15 +215,18 @@ impl From<&BasicOutput> for BasicOutputBuilder { #[packable(unpack_error = Error)] #[packable(unpack_visitor = ProtocolParameters)] pub struct BasicOutput { - // Amount of IOTA tokens held by the output. + /// Amount of IOTA tokens to deposit with this output. #[packable(verify_with = verify_output_amount_packable)] amount: u64, + /// Amount of stored Mana held by this output. mana: u64, - // Native tokens held by the output. + /// Native tokens held by this output. native_tokens: NativeTokens, + /// Define how the output can be unlocked in a transaction. #[packable(verify_with = verify_unlock_conditions_packable)] unlock_conditions: UnlockConditions, #[packable(verify_with = verify_features_packable)] + /// Features of the output. features: Features, } @@ -286,7 +289,7 @@ impl BasicOutput { /// #[inline(always)] pub fn address(&self) -> &Address { - // An BasicOutput must have an AddressUnlockCondition. + // A BasicOutput must have an AddressUnlockCondition. self.unlock_conditions .address() .map(|unlock_condition| unlock_condition.address()) @@ -339,16 +342,16 @@ fn verify_unlock_conditions_packable( verify_unlock_conditions::(unlock_conditions) } -fn verify_features(blocks: &Features) -> Result<(), Error> { +fn verify_features(features: &Features) -> Result<(), Error> { if VERIFY { - verify_allowed_features(blocks, BasicOutput::ALLOWED_FEATURES) + verify_allowed_features(features, BasicOutput::ALLOWED_FEATURES) } else { Ok(()) } } -fn verify_features_packable(blocks: &Features, _: &ProtocolParameters) -> Result<(), Error> { - verify_features::(blocks) +fn verify_features_packable(features: &Features, _: &ProtocolParameters) -> Result<(), Error> { + verify_features::(features) } #[cfg(feature = "serde")] @@ -366,18 +369,15 @@ pub(crate) mod dto { utils::serde::string, }; - /// Describes a basic output. #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct BasicOutputDto { #[serde(rename = "type")] pub kind: u8, - // Amount of IOTA tokens held by the output. #[serde(with = "string")] pub amount: u64, #[serde(with = "string")] pub mana: u64, - // Native tokens held by the output. #[serde(skip_serializing_if = "Vec::is_empty", default)] pub native_tokens: Vec, pub unlock_conditions: Vec, @@ -455,7 +455,6 @@ pub(crate) mod dto { #[cfg(test)] mod tests { - use packable::PackableExt; use super::*; use crate::types::{ @@ -465,70 +464,13 @@ mod tests { rand::{ address::rand_account_address, output::{ - feature::{rand_allowed_features, rand_metadata_feature, rand_sender_feature}, - rand_basic_output, - unlock_condition::rand_address_unlock_condition, + feature::rand_allowed_features, rand_basic_output, unlock_condition::rand_address_unlock_condition, }, }, }, TryFromDto, }; - #[test] - fn builder() { - let protocol_parameters = protocol_parameters(); - let foundry_id = FoundryId::build(&rand_account_address(), 0, SimpleTokenScheme::KIND); - let address_1 = rand_address_unlock_condition(); - let address_2 = rand_address_unlock_condition(); - let sender_1 = rand_sender_feature(); - let sender_2 = rand_sender_feature(); - let amount = 500_000; - - let mut builder = BasicOutput::build_with_amount(amount) - .add_native_token(NativeToken::new(TokenId::from(foundry_id), 1000).unwrap()) - .add_unlock_condition(address_1) - .add_feature(sender_1) - .replace_feature(sender_2); - - let output = builder.clone().finish().unwrap(); - assert_eq!(output.amount(), amount); - assert_eq!(output.unlock_conditions().address(), Some(&address_1)); - assert_eq!(output.features().sender(), Some(&sender_2)); - - builder = builder - .clear_unlock_conditions() - .clear_features() - .replace_unlock_condition(address_2); - let output = builder.clone().finish().unwrap(); - assert_eq!(output.unlock_conditions().address(), Some(&address_2)); - assert!(output.features().is_empty()); - - let metadata = rand_metadata_feature(); - - let output = builder - .with_minimum_storage_deposit(protocol_parameters.rent_structure()) - .add_unlock_condition(rand_address_unlock_condition()) - .with_features([Feature::from(metadata.clone()), sender_1.into()]) - .finish_with_params(ValidationParams::default().with_protocol_parameters(protocol_parameters.clone())) - .unwrap(); - - assert_eq!( - output.amount(), - Output::Basic(output.clone()).rent_cost(protocol_parameters.rent_structure()) - ); - assert_eq!(output.features().metadata(), Some(&metadata)); - assert_eq!(output.features().sender(), Some(&sender_1)); - } - - #[test] - fn pack_unpack() { - let protocol_parameters = protocol_parameters(); - let output = rand_basic_output(protocol_parameters.token_supply()); - let bytes = output.pack_to_vec(); - let output_unpacked = BasicOutput::unpack_verified(bytes, &protocol_parameters).unwrap(); - assert_eq!(output, output_unpacked); - } - #[test] fn to_from_dto() { let protocol_parameters = protocol_parameters(); diff --git a/sdk/src/types/block/output/foundry.rs b/sdk/src/types/block/output/foundry.rs index 7a3249a711..cc6118bdf6 100644 --- a/sdk/src/types/block/output/foundry.rs +++ b/sdk/src/types/block/output/foundry.rs @@ -32,7 +32,7 @@ use crate::types::{ ValidationParams, }; -/// +/// Builder for a [`FoundryOutput`]. #[derive(Clone)] #[must_use] pub struct FoundryOutputBuilder { @@ -280,15 +280,19 @@ impl From<&FoundryOutput> for FoundryOutputBuilder { /// Describes a foundry output that is controlled by an account. #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct FoundryOutput { - // Amount of IOTA tokens held by the output. + /// Amount of IOTA tokens to deposit with this output. amount: u64, - // Native tokens held by the output. + /// Native tokens held by this output. native_tokens: NativeTokens, - // The serial number of the foundry with respect to the controlling account. + /// The serial number of the foundry with respect to the controlling account. serial_number: u32, + /// Define the supply control scheme of the native tokens controlled by the foundry. token_scheme: TokenScheme, + /// Define how the output can be unlocked in a transaction. unlock_conditions: UnlockConditions, + /// Features of the output. features: Features, + /// Immutable features of the output. immutable_features: Features, } @@ -627,19 +631,15 @@ pub(crate) mod dto { utils::serde::string, }; - /// Describes a foundry output that is controlled by an account. #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct FoundryOutputDto { #[serde(rename = "type")] pub kind: u8, - // Amount of IOTA tokens held by the output. #[serde(with = "string")] pub amount: u64, - // Native tokens held by the output. #[serde(skip_serializing_if = "Vec::is_empty", default)] pub native_tokens: Vec, - // The serial number of the foundry with respect to the controlling account. pub serial_number: u32, pub token_scheme: TokenScheme, pub unlock_conditions: Vec, @@ -739,7 +739,6 @@ pub(crate) mod dto { #[cfg(test)] mod tests { - use packable::PackableExt; use super::*; use crate::types::{ @@ -760,63 +759,6 @@ mod tests { TryFromDto, }; - #[test] - fn builder() { - let protocol_parameters = protocol_parameters(); - let foundry_id = FoundryId::build(&rand_account_address(), 0, SimpleTokenScheme::KIND); - let account_1 = ImmutableAccountAddressUnlockCondition::new(rand_account_address()); - let account_2 = ImmutableAccountAddressUnlockCondition::new(rand_account_address()); - let metadata_1 = rand_metadata_feature(); - let metadata_2 = rand_metadata_feature(); - let amount = 500_000; - - let mut builder = FoundryOutput::build_with_amount(amount, 234, rand_token_scheme()) - .with_serial_number(85) - .add_native_token(NativeToken::new(TokenId::from(foundry_id), 1000).unwrap()) - .with_unlock_conditions([account_1]) - .add_feature(metadata_1.clone()) - .replace_feature(metadata_2.clone()) - .with_immutable_features([metadata_2.clone()]) - .replace_immutable_feature(metadata_1.clone()); - - let output = builder.clone().finish().unwrap(); - assert_eq!(output.amount(), amount); - assert_eq!(output.serial_number(), 85); - assert_eq!(output.unlock_conditions().immutable_account_address(), Some(&account_1)); - assert_eq!(output.features().metadata(), Some(&metadata_2)); - assert_eq!(output.immutable_features().metadata(), Some(&metadata_1)); - - builder = builder - .clear_unlock_conditions() - .clear_features() - .clear_immutable_features() - .replace_unlock_condition(account_2); - let output = builder.clone().finish().unwrap(); - assert_eq!(output.unlock_conditions().immutable_account_address(), Some(&account_2)); - assert!(output.features().is_empty()); - assert!(output.immutable_features().is_empty()); - - let output = builder - .with_minimum_storage_deposit(protocol_parameters.rent_structure()) - .add_unlock_condition(ImmutableAccountAddressUnlockCondition::new(rand_account_address())) - .finish_with_params(&protocol_parameters) - .unwrap(); - - assert_eq!( - output.amount(), - Output::Foundry(output).rent_cost(protocol_parameters.rent_structure()) - ); - } - - #[test] - fn pack_unpack() { - let protocol_parameters = protocol_parameters(); - let output = rand_foundry_output(protocol_parameters.token_supply()); - let bytes = output.pack_to_vec(); - let output_unpacked = FoundryOutput::unpack_verified(bytes, &protocol_parameters).unwrap(); - assert_eq!(output, output_unpacked); - } - #[test] fn to_from_dto() { let protocol_parameters = protocol_parameters(); diff --git a/sdk/src/types/block/output/nft.rs b/sdk/src/types/block/output/nft.rs index 98133eeb5c..8a23602906 100644 --- a/sdk/src/types/block/output/nft.rs +++ b/sdk/src/types/block/output/nft.rs @@ -30,7 +30,7 @@ use crate::types::{ ValidationParams, }; -/// +/// Builder for an [`NftOutput`]. #[derive(Clone)] #[must_use] pub struct NftOutputBuilder { @@ -263,15 +263,19 @@ impl From<&NftOutput> for NftOutputBuilder { /// Describes an NFT output, a globally unique token with metadata attached. #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct NftOutput { - // Amount of IOTA tokens held by the output. + /// Amount of IOTA tokens to deposit with this output. amount: u64, + /// Amount of stored Mana held by this output. mana: u64, - // Native tokens held by the output. + /// Native tokens held by this output. native_tokens: NativeTokens, - // Unique identifier of the NFT. + /// Unique identifier of the NFT. nft_id: NftId, + /// Define how the output can be unlocked in a transaction. unlock_conditions: UnlockConditions, + /// Features of the output. features: Features, + /// Immutable features of the output. immutable_features: Features, } @@ -521,21 +525,17 @@ pub(crate) mod dto { utils::serde::string, }; - /// Describes an NFT output, a globally unique token with metadata attached. #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NftOutputDto { #[serde(rename = "type")] pub kind: u8, - // Amount of IOTA tokens held by the output. #[serde(with = "string")] pub amount: u64, #[serde(with = "string")] pub mana: u64, - // Native tokens held by the output. #[serde(skip_serializing_if = "Vec::is_empty", default)] pub native_tokens: Vec, - // Unique identifier of the NFT. pub nft_id: NftId, pub unlock_conditions: Vec, #[serde(skip_serializing_if = "Vec::is_empty", default)] @@ -624,7 +624,6 @@ pub(crate) mod dto { #[cfg(test)] mod tests { - use packable::PackableExt; use super::*; use crate::types::{ @@ -634,72 +633,13 @@ mod tests { rand::{ address::rand_account_address, output::{ - feature::{rand_allowed_features, rand_issuer_feature, rand_sender_feature}, - rand_nft_output, - unlock_condition::rand_address_unlock_condition, + feature::rand_allowed_features, rand_nft_output, unlock_condition::rand_address_unlock_condition, }, }, }, TryFromDto, }; - #[test] - fn builder() { - let protocol_parameters = protocol_parameters(); - let foundry_id = FoundryId::build(&rand_account_address(), 0, SimpleTokenScheme::KIND); - let address_1 = rand_address_unlock_condition(); - let address_2 = rand_address_unlock_condition(); - let sender_1 = rand_sender_feature(); - let sender_2 = rand_sender_feature(); - let issuer_1 = rand_issuer_feature(); - let issuer_2 = rand_issuer_feature(); - let amount = 500_000; - - let mut builder = NftOutput::build_with_amount(amount, NftId::null()) - .add_native_token(NativeToken::new(TokenId::from(foundry_id), 1000).unwrap()) - .add_unlock_condition(address_1) - .add_feature(sender_1) - .replace_feature(sender_2) - .replace_immutable_feature(issuer_1) - .add_immutable_feature(issuer_2); - - let output = builder.clone().finish().unwrap(); - assert_eq!(output.amount(), amount); - assert_eq!(output.unlock_conditions().address(), Some(&address_1)); - assert_eq!(output.features().sender(), Some(&sender_2)); - assert_eq!(output.immutable_features().issuer(), Some(&issuer_1)); - - builder = builder - .clear_unlock_conditions() - .clear_features() - .clear_immutable_features() - .replace_unlock_condition(address_2); - let output = builder.clone().finish().unwrap(); - assert_eq!(output.unlock_conditions().address(), Some(&address_2)); - assert!(output.features().is_empty()); - assert!(output.immutable_features().is_empty()); - - let output = builder - .with_minimum_storage_deposit(protocol_parameters.rent_structure()) - .add_unlock_condition(rand_address_unlock_condition()) - .finish_with_params(protocol_parameters.token_supply()) - .unwrap(); - - assert_eq!( - output.amount(), - Output::Nft(output).rent_cost(protocol_parameters.rent_structure()) - ); - } - - #[test] - fn pack_unpack() { - let protocol_parameters = protocol_parameters(); - let output = rand_nft_output(protocol_parameters.token_supply()); - let bytes = output.pack_to_vec(); - let output_unpacked = NftOutput::unpack_verified(bytes, &protocol_parameters).unwrap(); - assert_eq!(output, output_unpacked); - } - #[test] fn to_from_dto() { let protocol_parameters = protocol_parameters(); diff --git a/sdk/src/types/block/slot/commitment.rs b/sdk/src/types/block/slot/commitment.rs index 37229aa63d..6f3b03b54a 100644 --- a/sdk/src/types/block/slot/commitment.rs +++ b/sdk/src/types/block/slot/commitment.rs @@ -24,7 +24,7 @@ pub struct SlotCommitment { /// The commitment ID of the previous slot. #[cfg_attr(feature = "serde", serde(rename = "previousCommitmentId"))] previous_slot_commitment_id: SlotCommitmentId, - /// A BLAKE2b-256 hash of concatenating multiple sparse merkle tree roots of a slot. + /// The digest of multiple sparse merkle tree roots of this slot. roots_id: RootsId, /// The sum of previous slot commitment cumulative weight and weight of issuers of accepted blocks within this /// slot. It is just an indication of "committed into" this slot, and can not strictly be used for evaluating @@ -56,6 +56,11 @@ impl SlotCommitment { } } + /// Returns the protocol version of the [`SlotCommitment`]. + pub fn protocol_version(&self) -> u8 { + self.protocol_version + } + /// Returns the index of the [`SlotCommitment`]. pub fn index(&self) -> SlotIndex { self.index @@ -66,7 +71,7 @@ impl SlotCommitment { &self.previous_slot_commitment_id } - /// Returns the [`RootsId`] of the [`SlotCommitment`]. + /// Returns the roots ID of the [`SlotCommitment`]. pub fn roots_id(&self) -> &RootsId { &self.roots_id } @@ -76,47 +81,22 @@ impl SlotCommitment { self.cumulative_weight } - /// Derives the [`SlotCommitmentId`] of the [`SlotCommitment`]. + /// Returns the reference mana cost of the [`SlotCommitment`]. + pub fn reference_mana_cost(&self) -> u64 { + self.reference_mana_cost + } + + /// Computes the [`SlotCommitmentId`] of the [`SlotCommitment`]. pub fn id(&self) -> SlotCommitmentId { let mut bytes = [0u8; SlotCommitmentId::LENGTH]; let mut packer = SlicePacker::new(&mut bytes); - let hash: [u8; 32] = Blake2b256::digest(self.pack_to_vec()).into(); + let content = self.pack_to_vec(); + let content_hash: [u8; 32] = Blake2b256::digest(content).into(); // PANIC: packing to an array of bytes can't fail. - hash.pack(&mut packer).unwrap(); + content_hash.pack(&mut packer).unwrap(); self.index.pack(&mut packer).unwrap(); SlotCommitmentId::from(bytes) } } - -#[cfg(test)] -mod test { - use core::str::FromStr; - - use super::SlotCommitment; - use crate::types::block::{ - slot::{RootsId, SlotCommitmentId, SlotIndex}, - PROTOCOL_VERSION, - }; - - #[test] - fn test() { - let commitment = SlotCommitment::new( - PROTOCOL_VERSION, - SlotIndex::new(10), - SlotCommitmentId::from_str( - "0x20e07a0ea344707d69a08b90be7ad14eec8326cf2b8b86c8ec23720fab8dcf8ec43a30e4a8cc3f1f", - ) - .unwrap(), - RootsId::from_str("0xcf077d276686ba64c0404b9eb2d15556782113c5a1985f262b70f9964d3bbd7f").unwrap(), - 5, - 10, - ); - // TODO: Independently verify this value - assert_eq!( - &commitment.id().to_string(), - "0x2f3ad38aa65d20ede9dcd6a045dccdd3332cf38192c4875308bb77116e8650880a00000000000000" - ) - } -} diff --git a/sdk/src/types/block/slot/roots_id.rs b/sdk/src/types/block/slot/roots_id.rs index bb3e7907ae..d9873d7303 100644 --- a/sdk/src/types/block/slot/roots_id.rs +++ b/sdk/src/types/block/slot/roots_id.rs @@ -1,7 +1,7 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -impl_id!(pub RootsId, 32, "A BLAKE2b-256 hash of concatenating multiple sparse merkle tree roots of a slot."); +impl_id!(pub RootsId, 32, "The digest of multiple sparse merkle tree roots of a slot."); #[cfg(feature = "serde")] string_serde_impl!(RootsId); diff --git a/sdk/src/types/block/unlock/account.rs b/sdk/src/types/block/unlock/account.rs index 4a301733b9..3da5fd0e43 100644 --- a/sdk/src/types/block/unlock/account.rs +++ b/sdk/src/types/block/unlock/account.rs @@ -41,7 +41,6 @@ mod dto { use super::*; - /// Points to the unlock of a consumed account output. #[derive(Serialize, Deserialize)] struct AccountUnlockDto { #[serde(rename = "type")] diff --git a/sdk/src/types/block/unlock/nft.rs b/sdk/src/types/block/unlock/nft.rs index 97d766a4f5..e77bdb0746 100644 --- a/sdk/src/types/block/unlock/nft.rs +++ b/sdk/src/types/block/unlock/nft.rs @@ -42,7 +42,6 @@ pub(crate) mod dto { use super::*; - /// Points to the unlock of a consumed NFT output. #[derive(Serialize, Deserialize)] struct NftUnlockDto { #[serde(rename = "type")] diff --git a/sdk/src/types/block/unlock/reference.rs b/sdk/src/types/block/unlock/reference.rs index b56599d146..8a72d608c1 100644 --- a/sdk/src/types/block/unlock/reference.rs +++ b/sdk/src/types/block/unlock/reference.rs @@ -39,8 +39,6 @@ pub(crate) mod dto { use super::*; - /// References a previous unlock in order to substitute the duplication of the same unlock data for inputs which - /// unlock through the same data. #[derive(Serialize, Deserialize)] struct ReferenceUnlockDto { #[serde(rename = "type")] diff --git a/sdk/src/types/block/unlock/signature.rs b/sdk/src/types/block/unlock/signature.rs index b2676d6a64..ba1aaff388 100644 --- a/sdk/src/types/block/unlock/signature.rs +++ b/sdk/src/types/block/unlock/signature.rs @@ -33,7 +33,6 @@ pub(crate) mod dto { use super::*; - /// Defines an unlock containing signature(s) unlocking input(s). #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct SignatureUnlockDto { #[serde(rename = "type")] diff --git a/sdk/tests/types/mod.rs b/sdk/tests/types/mod.rs index acd0635120..bef7ff7f10 100644 --- a/sdk/tests/types/mod.rs +++ b/sdk/tests/types/mod.rs @@ -8,6 +8,7 @@ mod block_id; mod ed25519_signature; mod foundry_id; mod input; +mod output; mod output_id; mod parents; mod payload; diff --git a/sdk/tests/types/output/account.rs b/sdk/tests/types/output/account.rs new file mode 100644 index 0000000000..d62f730398 --- /dev/null +++ b/sdk/tests/types/output/account.rs @@ -0,0 +1,102 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use iota_sdk::types::{ + block::{ + address::AccountAddress, + output::{AccountOutput, Feature, FoundryId, NativeToken, Output, Rent, SimpleTokenScheme, TokenId}, + protocol::protocol_parameters, + rand::output::{ + feature::{rand_issuer_feature, rand_metadata_feature, rand_sender_feature}, + rand_account_id, rand_account_output, + unlock_condition::{ + rand_governor_address_unlock_condition_different_from, + rand_state_controller_address_unlock_condition_different_from, + }, + }, + }, + ValidationParams, +}; +use packable::PackableExt; + +#[test] +fn builder() { + let protocol_parameters = protocol_parameters(); + let account_id = rand_account_id(); + let foundry_id = FoundryId::build(&AccountAddress::from(account_id), 0, SimpleTokenScheme::KIND); + let gov_address_1 = rand_governor_address_unlock_condition_different_from(&account_id); + let gov_address_2 = rand_governor_address_unlock_condition_different_from(&account_id); + let state_address_1 = rand_state_controller_address_unlock_condition_different_from(&account_id); + let state_address_2 = rand_state_controller_address_unlock_condition_different_from(&account_id); + let sender_1 = rand_sender_feature(); + let sender_2 = rand_sender_feature(); + let issuer_1 = rand_issuer_feature(); + let issuer_2 = rand_issuer_feature(); + let amount = 500_000; + + let mut builder = AccountOutput::build_with_amount(amount, account_id) + .add_native_token(NativeToken::new(TokenId::from(foundry_id), 1000).unwrap()) + .add_unlock_condition(gov_address_1) + .add_unlock_condition(state_address_1) + .add_feature(sender_1) + .replace_feature(sender_2) + .replace_immutable_feature(issuer_1) + .add_immutable_feature(issuer_2); + + let output = builder.clone().finish().unwrap(); + assert_eq!(output.amount(), amount); + assert_eq!(output.unlock_conditions().governor_address(), Some(&gov_address_1)); + assert_eq!( + output.unlock_conditions().state_controller_address(), + Some(&state_address_1) + ); + assert_eq!(output.features().sender(), Some(&sender_2)); + assert_eq!(output.immutable_features().issuer(), Some(&issuer_1)); + + builder = builder + .clear_unlock_conditions() + .clear_features() + .clear_immutable_features() + .replace_unlock_condition(gov_address_2) + .replace_unlock_condition(state_address_2); + let output = builder.clone().finish().unwrap(); + assert_eq!(output.unlock_conditions().governor_address(), Some(&gov_address_2)); + assert_eq!( + output.unlock_conditions().state_controller_address(), + Some(&state_address_2) + ); + assert!(output.features().is_empty()); + assert!(output.immutable_features().is_empty()); + + let metadata = rand_metadata_feature(); + + let output = builder + .with_minimum_storage_deposit(protocol_parameters.rent_structure()) + .add_unlock_condition(rand_state_controller_address_unlock_condition_different_from( + &account_id, + )) + .add_unlock_condition(rand_governor_address_unlock_condition_different_from(&account_id)) + .with_features([Feature::from(metadata.clone()), sender_1.into()]) + .with_immutable_features([Feature::from(metadata.clone()), issuer_1.into()]) + .finish_with_params(ValidationParams::default().with_protocol_parameters(protocol_parameters.clone())) + .unwrap(); + + assert_eq!( + output.amount(), + Output::Account(output.clone()).rent_cost(protocol_parameters.rent_structure()) + ); + assert_eq!(output.features().metadata(), Some(&metadata)); + assert_eq!(output.features().sender(), Some(&sender_1)); + assert_eq!(output.immutable_features().metadata(), Some(&metadata)); + assert_eq!(output.immutable_features().issuer(), Some(&issuer_1)); +} + +#[test] +fn pack_unpack() { + let protocol_parameters = protocol_parameters(); + let output = rand_account_output(protocol_parameters.token_supply()); + let bytes = output.pack_to_vec(); + let output_unpacked = AccountOutput::unpack_verified(bytes, &protocol_parameters).unwrap(); + + assert_eq!(output, output_unpacked); +} diff --git a/sdk/tests/types/output/basic.rs b/sdk/tests/types/output/basic.rs new file mode 100644 index 0000000000..ae0dbea7a9 --- /dev/null +++ b/sdk/tests/types/output/basic.rs @@ -0,0 +1,75 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use iota_sdk::types::{ + block::{ + output::{BasicOutput, Feature, FoundryId, NativeToken, Output, Rent, SimpleTokenScheme, TokenId}, + protocol::protocol_parameters, + rand::{ + address::rand_account_address, + output::{ + feature::{rand_metadata_feature, rand_sender_feature}, + rand_basic_output, + unlock_condition::rand_address_unlock_condition, + }, + }, + }, + ValidationParams, +}; +use packable::PackableExt; + +#[test] +fn builder() { + let protocol_parameters = protocol_parameters(); + let foundry_id = FoundryId::build(&rand_account_address(), 0, SimpleTokenScheme::KIND); + let address_1 = rand_address_unlock_condition(); + let address_2 = rand_address_unlock_condition(); + let sender_1 = rand_sender_feature(); + let sender_2 = rand_sender_feature(); + let amount = 500_000; + + let mut builder = BasicOutput::build_with_amount(amount) + .add_native_token(NativeToken::new(TokenId::from(foundry_id), 1000).unwrap()) + .add_unlock_condition(address_1) + .add_feature(sender_1) + .replace_feature(sender_2); + + let output = builder.clone().finish().unwrap(); + assert_eq!(output.amount(), amount); + assert_eq!(output.unlock_conditions().address(), Some(&address_1)); + assert_eq!(output.features().sender(), Some(&sender_2)); + + builder = builder + .clear_unlock_conditions() + .clear_features() + .replace_unlock_condition(address_2); + let output = builder.clone().finish().unwrap(); + assert_eq!(output.unlock_conditions().address(), Some(&address_2)); + assert!(output.features().is_empty()); + + let metadata = rand_metadata_feature(); + + let output = builder + .with_minimum_storage_deposit(protocol_parameters.rent_structure()) + .add_unlock_condition(rand_address_unlock_condition()) + .with_features([Feature::from(metadata.clone()), sender_1.into()]) + .finish_with_params(ValidationParams::default().with_protocol_parameters(protocol_parameters.clone())) + .unwrap(); + + assert_eq!( + output.amount(), + Output::Basic(output.clone()).rent_cost(protocol_parameters.rent_structure()) + ); + assert_eq!(output.features().metadata(), Some(&metadata)); + assert_eq!(output.features().sender(), Some(&sender_1)); +} + +#[test] +fn pack_unpack() { + let protocol_parameters = protocol_parameters(); + let output = rand_basic_output(protocol_parameters.token_supply()); + let bytes = output.pack_to_vec(); + let output_unpacked = BasicOutput::unpack_verified(bytes, &protocol_parameters).unwrap(); + + assert_eq!(output, output_unpacked); +} diff --git a/sdk/tests/types/output/foundry.rs b/sdk/tests/types/output/foundry.rs new file mode 100644 index 0000000000..896351ef4a --- /dev/null +++ b/sdk/tests/types/output/foundry.rs @@ -0,0 +1,73 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use iota_sdk::types::block::{ + output::{ + unlock_condition::ImmutableAccountAddressUnlockCondition, FoundryId, FoundryOutput, NativeToken, Output, Rent, + SimpleTokenScheme, TokenId, + }, + protocol::protocol_parameters, + rand::{ + address::rand_account_address, + output::{feature::rand_metadata_feature, rand_foundry_output, rand_token_scheme}, + }, +}; +use packable::PackableExt; + +#[test] +fn builder() { + let protocol_parameters = protocol_parameters(); + let foundry_id = FoundryId::build(&rand_account_address(), 0, SimpleTokenScheme::KIND); + let account_1 = ImmutableAccountAddressUnlockCondition::new(rand_account_address()); + let account_2 = ImmutableAccountAddressUnlockCondition::new(rand_account_address()); + let metadata_1 = rand_metadata_feature(); + let metadata_2 = rand_metadata_feature(); + let amount = 500_000; + + let mut builder = FoundryOutput::build_with_amount(amount, 234, rand_token_scheme()) + .with_serial_number(85) + .add_native_token(NativeToken::new(TokenId::from(foundry_id), 1000).unwrap()) + .with_unlock_conditions([account_1]) + .add_feature(metadata_1.clone()) + .replace_feature(metadata_2.clone()) + .with_immutable_features([metadata_2.clone()]) + .replace_immutable_feature(metadata_1.clone()); + + let output = builder.clone().finish().unwrap(); + assert_eq!(output.amount(), amount); + assert_eq!(output.serial_number(), 85); + assert_eq!(output.unlock_conditions().immutable_account_address(), Some(&account_1)); + assert_eq!(output.features().metadata(), Some(&metadata_2)); + assert_eq!(output.immutable_features().metadata(), Some(&metadata_1)); + + builder = builder + .clear_unlock_conditions() + .clear_features() + .clear_immutable_features() + .replace_unlock_condition(account_2); + let output = builder.clone().finish().unwrap(); + assert_eq!(output.unlock_conditions().immutable_account_address(), Some(&account_2)); + assert!(output.features().is_empty()); + assert!(output.immutable_features().is_empty()); + + let output = builder + .with_minimum_storage_deposit(protocol_parameters.rent_structure()) + .add_unlock_condition(ImmutableAccountAddressUnlockCondition::new(rand_account_address())) + .finish_with_params(&protocol_parameters) + .unwrap(); + + assert_eq!( + output.amount(), + Output::Foundry(output).rent_cost(protocol_parameters.rent_structure()) + ); +} + +#[test] +fn pack_unpack() { + let protocol_parameters = protocol_parameters(); + let output = rand_foundry_output(protocol_parameters.token_supply()); + let bytes = output.pack_to_vec(); + let output_unpacked = FoundryOutput::unpack_verified(bytes, &protocol_parameters).unwrap(); + + assert_eq!(output, output_unpacked); +} diff --git a/sdk/tests/types/output/mod.rs b/sdk/tests/types/output/mod.rs new file mode 100644 index 0000000000..7c0be69755 --- /dev/null +++ b/sdk/tests/types/output/mod.rs @@ -0,0 +1,7 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +mod account; +mod basic; +mod foundry; +mod nft; diff --git a/sdk/tests/types/output/nft.rs b/sdk/tests/types/output/nft.rs new file mode 100644 index 0000000000..4617325d0d --- /dev/null +++ b/sdk/tests/types/output/nft.rs @@ -0,0 +1,74 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use iota_sdk::types::block::{ + output::{FoundryId, NativeToken, NftId, NftOutput, Output, Rent, SimpleTokenScheme, TokenId}, + protocol::protocol_parameters, + rand::{ + address::rand_account_address, + output::{ + feature::{rand_issuer_feature, rand_sender_feature}, + rand_nft_output, + unlock_condition::rand_address_unlock_condition, + }, + }, +}; +use packable::PackableExt; + +#[test] +fn builder() { + let protocol_parameters = protocol_parameters(); + let foundry_id = FoundryId::build(&rand_account_address(), 0, SimpleTokenScheme::KIND); + let address_1 = rand_address_unlock_condition(); + let address_2 = rand_address_unlock_condition(); + let sender_1 = rand_sender_feature(); + let sender_2 = rand_sender_feature(); + let issuer_1 = rand_issuer_feature(); + let issuer_2 = rand_issuer_feature(); + let amount = 500_000; + + let mut builder = NftOutput::build_with_amount(amount, NftId::null()) + .add_native_token(NativeToken::new(TokenId::from(foundry_id), 1000).unwrap()) + .add_unlock_condition(address_1) + .add_feature(sender_1) + .replace_feature(sender_2) + .replace_immutable_feature(issuer_1) + .add_immutable_feature(issuer_2); + + let output = builder.clone().finish().unwrap(); + assert_eq!(output.amount(), amount); + assert_eq!(output.unlock_conditions().address(), Some(&address_1)); + assert_eq!(output.features().sender(), Some(&sender_2)); + assert_eq!(output.immutable_features().issuer(), Some(&issuer_1)); + + builder = builder + .clear_unlock_conditions() + .clear_features() + .clear_immutable_features() + .replace_unlock_condition(address_2); + let output = builder.clone().finish().unwrap(); + assert_eq!(output.unlock_conditions().address(), Some(&address_2)); + assert!(output.features().is_empty()); + assert!(output.immutable_features().is_empty()); + + let output = builder + .with_minimum_storage_deposit(protocol_parameters.rent_structure()) + .add_unlock_condition(rand_address_unlock_condition()) + .finish_with_params(protocol_parameters.token_supply()) + .unwrap(); + + assert_eq!( + output.amount(), + Output::Nft(output).rent_cost(protocol_parameters.rent_structure()) + ); +} + +#[test] +fn pack_unpack() { + let protocol_parameters = protocol_parameters(); + let output = rand_nft_output(protocol_parameters.token_supply()); + let bytes = output.pack_to_vec(); + let output_unpacked = NftOutput::unpack_verified(bytes, &protocol_parameters).unwrap(); + + assert_eq!(output, output_unpacked); +} diff --git a/sdk/tests/types/slot.rs b/sdk/tests/types/slot.rs index 72840df33e..69a611fa40 100644 --- a/sdk/tests/types/slot.rs +++ b/sdk/tests/types/slot.rs @@ -7,18 +7,16 @@ use packable::PackableExt; #[test] fn slot_commitment_id() { // Test from https://github.com/iotaledger/tips-draft/blob/tip46/tips/TIP-0046/tip-0046.md#slot-commitment-id-1 - let slot_commitment_json = r#" - { - "version":3, - "index":"10", - "previousCommitmentId":"0x4b024b3e47280d05272a7d136f0c464e4e136b734e6c427749413e286162077560652c007e37241a", - "rootsId":"0x75614402763f5f045c040334631b791b4d755d626d504b134a505c001c516549", - "cumulativeWeight":"100", - "referenceManaCost":"6000" - } - "#; + let slot_commitment_json = serde_json::json!({ + "version":3, + "index":"10", + "previousCommitmentId":"0x4b024b3e47280d05272a7d136f0c464e4e136b734e6c427749413e286162077560652c007e37241a", + "rootsId":"0x75614402763f5f045c040334631b791b4d755d626d504b134a505c001c516549", + "cumulativeWeight":"100", + "referenceManaCost":"6000" + }); - let slot_commitment = serde_json::from_str::(slot_commitment_json).unwrap(); + let slot_commitment = serde_json::from_value::(slot_commitment_json).unwrap(); let slot_commitment_bytes = slot_commitment.pack_to_vec(); assert_eq!( diff --git a/sdk/tests/types/transaction_id.rs b/sdk/tests/types/transaction_id.rs index d5d8160f49..cd7f60a0e3 100644 --- a/sdk/tests/types/transaction_id.rs +++ b/sdk/tests/types/transaction_id.rs @@ -57,59 +57,57 @@ fn pack_unpack_valid() { #[test] fn transaction_id() { // Test from https://github.com/iotaledger/tips-draft/blob/tip46/tips/TIP-0046/tip-0046.md#transaction-id - let transaction_payload_json = r#" - { - "type":6, - "essence":{ - "type":2, - "networkId":"3650798313638353144", - "creationSlot":"28", - "contextInputs":[], - "inputs":[ - { - "type":0, - "transactionId":"0x24ff9b3038506fb1b406306a496001c3e24e2be07c838317922bf21d686a078f", - "transactionOutputIndex":10 - } - ], - "inputsCommitment":"0xb70c6f86a1ea03a59a71d73dcd07e2082bbdf0ce971faa21748348bca22fb023", - "outputs":[ - { - "type":3, - "amount":"10000", - "mana":"0", - "unlockConditions":[ - { + let transaction_payload_json = serde_json::json!({ + "type":6, + "essence":{ + "type":2, + "networkId":"3650798313638353144", + "creationSlot":"28", + "contextInputs":[], + "inputs":[ + { + "type":0, + "transactionId":"0x24ff9b3038506fb1b406306a496001c3e24e2be07c838317922bf21d686a078f", + "transactionOutputIndex":10 + } + ], + "inputsCommitment":"0xb70c6f86a1ea03a59a71d73dcd07e2082bbdf0ce971faa21748348bca22fb023", + "outputs":[ + { + "type":3, + "amount":"10000", + "mana":"0", + "unlockConditions":[ + { + "type":0, + "address":{ "type":0, - "address":{ - "type":0, - "pubKeyHash":"0xd9f84458286dc41cd34789dec566cd096cf47de991aa36a97aebfaea14128f6d" - } + "pubKeyHash":"0xd9f84458286dc41cd34789dec566cd096cf47de991aa36a97aebfaea14128f6d" } - ] - } - ], - "allotments":[], - "payload":{ - "type":5, - "tag":"0x1d7b3e11697264111e130b0e", - "data":"0x1d7b3e11697264111e130b0e" + } + ] } - }, - "unlocks":[ - { + ], + "allotments":[], + "payload":{ + "type":5, + "tag":"0x1d7b3e11697264111e130b0e", + "data":"0x1d7b3e11697264111e130b0e" + } + }, + "unlocks":[ + { + "type":0, + "signature":{ "type":0, - "signature":{ - "type":0, - "publicKey":"0x803361fe1effc899dca7f931d8ad07c01ba23aaa93f986adb04d4c17cf6368d8", - "signature":"0xccddbac3aaac413e0193e16da3449f30c183d0e7eaa7f303dc12ae0dbc9fb890e449a52f9056e7d952ea796fd3e5645f60d9eb98ed91cb3261720fb528d2a105" - } + "publicKey":"0x803361fe1effc899dca7f931d8ad07c01ba23aaa93f986adb04d4c17cf6368d8", + "signature":"0xccddbac3aaac413e0193e16da3449f30c183d0e7eaa7f303dc12ae0dbc9fb890e449a52f9056e7d952ea796fd3e5645f60d9eb98ed91cb3261720fb528d2a105" } - ] - } - "#; + } + ] + }); - let transaction_payload_dto = serde_json::from_str::(transaction_payload_json).unwrap(); + let transaction_payload_dto = serde_json::from_value::(transaction_payload_json).unwrap(); let transaction_payload = TransactionPayload::try_from_dto(transaction_payload_dto).unwrap(); let transaction_payload_bytes = Payload::from(transaction_payload.clone()).pack_to_vec();