diff --git a/bindings/core/src/method/utils.rs b/bindings/core/src/method/utils.rs index 938b33b293..552aeef331 100644 --- a/bindings/core/src/method/utils.rs +++ b/bindings/core/src/method/utils.rs @@ -4,12 +4,13 @@ use derivative::Derivative; use iota_sdk::types::block::{ address::{Bech32Address, Hrp}, - output::{dto::OutputDto, AccountId, NftId, OutputId, RentStructure}, + output::{dto::OutputDto, AccountId, NftId, OutputId}, payload::transaction::{ dto::{TransactionEssenceDto, TransactionPayloadDto}, TransactionId, }, protocol::ProtocolParameters, + rent::RentParameters, signature::Ed25519Signature, slot::SlotCommitment, BlockWrapperDto, @@ -131,7 +132,7 @@ pub enum UtilsMethod { /// Computes the input commitment from the output objects that are used as inputs to fund the transaction. ComputeInputsCommitment { inputs: Vec }, /// Computes the required storage deposit of an output. - ComputeStorageDeposit { output: OutputDto, rent: RentStructure }, + ComputeStorageDeposit { output: OutputDto, rent: RentParameters }, /// Checks if the given mnemonic is valid. /// Expected response: [`Ok`](crate::Response::Ok) VerifyMnemonic { diff --git a/bindings/core/src/method_handler/client.rs b/bindings/core/src/method_handler/client.rs index f5823aa006..53a3da32a2 100644 --- a/bindings/core/src/method_handler/client.rs +++ b/bindings/core/src/method_handler/client.rs @@ -10,9 +10,9 @@ use iota_sdk::{ block::{ output::{ dto::OutputDto, AccountOutput, BasicOutput, FoundryOutput, NftOutput, Output, OutputBuilderAmount, - StorageScore, }, payload::Payload, + rent::StorageCost, BlockWrapper, BlockWrapperDto, }, TryFromDto, @@ -72,7 +72,7 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM if let Some(amount) = amount { OutputBuilderAmount::Amount(amount) } else { - OutputBuilderAmount::MinimumStorageDeposit(client.get_rent_structure().await?) + OutputBuilderAmount::MinimumStorageDeposit(client.get_rent_parameters().await?) }, mana, native_tokens, @@ -99,7 +99,7 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM if let Some(amount) = amount { OutputBuilderAmount::Amount(amount) } else { - OutputBuilderAmount::MinimumStorageDeposit(client.get_rent_structure().await?) + OutputBuilderAmount::MinimumStorageDeposit(client.get_rent_parameters().await?) }, mana, native_tokens, @@ -123,7 +123,7 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM if let Some(amount) = amount { OutputBuilderAmount::Amount(amount) } else { - OutputBuilderAmount::MinimumStorageDeposit(client.get_rent_structure().await?) + OutputBuilderAmount::MinimumStorageDeposit(client.get_rent_parameters().await?) }, native_tokens, serial_number, @@ -149,7 +149,7 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM if let Some(amount) = amount { OutputBuilderAmount::Amount(amount) } else { - OutputBuilderAmount::MinimumStorageDeposit(client.get_rent_structure().await?) + OutputBuilderAmount::MinimumStorageDeposit(client.get_rent_parameters().await?) }, mana, native_tokens, @@ -296,9 +296,9 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM } ClientMethod::MinimumRequiredStorageDeposit { output } => { let output = Output::try_from_dto_with_params(output, client.get_token_supply().await?)?; - let rent_structure = client.get_rent_structure().await?; + let rent_params = client.get_rent_parameters().await?; - let minimum_storage_deposit = output.storage_score(rent_structure); + let minimum_storage_deposit = output.storage_cost(rent_params); Response::MinimumRequiredStorageDeposit(minimum_storage_deposit.to_string()) } diff --git a/bindings/core/src/method_handler/utils.rs b/bindings/core/src/method_handler/utils.rs index 050f2389a3..c4809711c5 100644 --- a/bindings/core/src/method_handler/utils.rs +++ b/bindings/core/src/method_handler/utils.rs @@ -8,8 +8,9 @@ use iota_sdk::{ block::{ address::{AccountAddress, Address, ToBech32Ext}, input::UtxoInput, - output::{AccountId, FoundryId, InputsCommitment, NftId, Output, OutputId, StorageScore, TokenId}, + output::{AccountId, FoundryId, InputsCommitment, NftId, Output, OutputId, TokenId}, payload::{transaction::TransactionEssence, TransactionPayload}, + rent::StorageCost, BlockWrapper, }, TryFromDto, @@ -80,7 +81,7 @@ pub(crate) fn call_utils_method_internal(method: UtilsMethod) -> Result { let out = Output::try_from_dto(output)?; - Response::MinimumRequiredStorageDeposit(out.storage_score(rent).to_string()) + Response::MinimumRequiredStorageDeposit(out.storage_cost(rent).to_string()) } UtilsMethod::VerifyMnemonic { mnemonic } => { let mnemonic = Mnemonic::from(mnemonic); diff --git a/cli/src/command/account.rs b/cli/src/command/account.rs index 5ea180908c..c2ca40a0ef 100644 --- a/cli/src/command/account.rs +++ b/cli/src/command/account.rs @@ -721,12 +721,12 @@ pub async fn send_native_token_command( let address = address.convert()?; let transaction = if gift_storage_deposit.unwrap_or(false) { // Send native tokens together with the required storage deposit - let rent_structure = account.client().get_rent_structure().await?; + let rent_params = account.client().get_rent_parameters().await?; let token_supply = account.client().get_token_supply().await?; account.client().bech32_hrp_matches(address.hrp()).await?; - let outputs = [BasicOutputBuilder::new_with_minimum_storage_deposit(rent_structure) + let outputs = [BasicOutputBuilder::new_with_minimum_storage_deposit(rent_params) .add_unlock_condition(AddressUnlockCondition::new(address)) .with_native_tokens([NativeToken::new( TokenId::from_str(&token_id)?, diff --git a/sdk/examples/client/output/build_account_output.rs b/sdk/examples/client/output/build_account_output.rs index a4c22583b5..c8c5fee712 100644 --- a/sdk/examples/client/output/build_account_output.rs +++ b/sdk/examples/client/output/build_account_output.rs @@ -35,7 +35,7 @@ async fn main() -> Result<()> { .await?; let token_supply = client.get_token_supply().await?; - let rent_structure = client.get_rent_structure().await?; + let rent_params = client.get_rent_parameters().await?; let address = std::env::args() .nth(1) @@ -43,7 +43,7 @@ async fn main() -> Result<()> { let address = Address::try_from_bech32(address)?; // Account id needs to be null the first time - let account_output = AccountOutputBuilder::new_with_minimum_storage_deposit(rent_structure, AccountId::null()) + let account_output = AccountOutputBuilder::new_with_minimum_storage_deposit(rent_params, AccountId::null()) .with_state_metadata(metadata) .add_feature(SenderFeature::new(address)) .add_feature(MetadataFeature::new(metadata)?) diff --git a/sdk/examples/client/output/build_nft_output.rs b/sdk/examples/client/output/build_nft_output.rs index e75aa942ba..5c9cf7bc0e 100644 --- a/sdk/examples/client/output/build_nft_output.rs +++ b/sdk/examples/client/output/build_nft_output.rs @@ -35,7 +35,7 @@ async fn main() -> Result<()> { .await?; let token_supply = client.get_token_supply().await?; - let rent_structure = client.get_rent_structure().await?; + let rent_params = client.get_rent_parameters().await?; let address = std::env::args() .nth(1) @@ -56,7 +56,7 @@ async fn main() -> Result<()> { .to_string(); // NftId needs to be null the first time - let nft_output = NftOutputBuilder::new_with_minimum_storage_deposit(rent_structure, NftId::null()) + let nft_output = NftOutputBuilder::new_with_minimum_storage_deposit(rent_params, NftId::null()) .add_unlock_condition(AddressUnlockCondition::new(address)) .add_feature(SenderFeature::new(address)) .add_feature(MetadataFeature::new(MUTABLE_METADATA)?) diff --git a/sdk/examples/how_tos/account/state_transition.rs b/sdk/examples/how_tos/account/state_transition.rs index f17919de50..2d945ddead 100644 --- a/sdk/examples/how_tos/account/state_transition.rs +++ b/sdk/examples/how_tos/account/state_transition.rs @@ -51,13 +51,13 @@ async fn main() -> Result<()> { ); let token_supply = account.client().get_token_supply().await?; - let rent_structure = account.client().get_rent_structure().await?; + let rent_params = account.client().get_rent_parameters().await?; let account_output = account_output_data.output.as_account(); let updated_account_output = AccountOutputBuilder::from(account_output) // Minimum required storage deposit will change if the new metadata has a different size, so we will update // the amount - .with_minimum_storage_deposit(rent_structure) + .with_minimum_storage_deposit(rent_params) .with_state_metadata(NEW_STATE_METADATA.as_bytes().to_vec()) .with_state_index(account_output.state_index() + 1) .finish_output(token_supply)?; diff --git a/sdk/examples/how_tos/outputs/features.rs b/sdk/examples/how_tos/outputs/features.rs index ca8367531e..71d45c5d60 100644 --- a/sdk/examples/how_tos/outputs/features.rs +++ b/sdk/examples/how_tos/outputs/features.rs @@ -32,11 +32,11 @@ async fn main() -> Result<()> { let client = Client::builder().with_node(&node_url)?.finish().await?; let token_supply = client.get_token_supply().await?; - let rent_structure = client.get_rent_structure().await?; + let rent_params = client.get_rent_parameters().await?; let address = Address::try_from_bech32("rms1qpllaj0pyveqfkwxmnngz2c488hfdtmfrj3wfkgxtk4gtyrax0jaxzt70zy")?; - let nft_output_builder = NftOutputBuilder::new_with_minimum_storage_deposit(rent_structure, NftId::null()) + let nft_output_builder = NftOutputBuilder::new_with_minimum_storage_deposit(rent_params, NftId::null()) .add_unlock_condition(AddressUnlockCondition::new(address)); let outputs = [ diff --git a/sdk/examples/how_tos/outputs/unlock_conditions.rs b/sdk/examples/how_tos/outputs/unlock_conditions.rs index 9d28bff850..7bd6723994 100644 --- a/sdk/examples/how_tos/outputs/unlock_conditions.rs +++ b/sdk/examples/how_tos/outputs/unlock_conditions.rs @@ -35,19 +35,17 @@ async fn main() -> Result<()> { let client = Client::builder().with_node(&node_url)?.finish().await?; let token_supply = client.get_token_supply().await?; - let rent_structure = client.get_rent_structure().await?; + let rent_params = client.get_rent_parameters().await?; let address = Address::try_from_bech32("rms1qpllaj0pyveqfkwxmnngz2c488hfdtmfrj3wfkgxtk4gtyrax0jaxzt70zy")?; let account_address = Address::try_from_bech32("rms1pr59qm43mjtvhcajfmupqf23x29llam88yecn6pyul80rx099krmv2fnnux")?; let token_scheme = TokenScheme::Simple(SimpleTokenScheme::new(50, 0, 100)?); - let basic_output_builder = BasicOutputBuilder::new_with_minimum_storage_deposit(rent_structure) + let basic_output_builder = BasicOutputBuilder::new_with_minimum_storage_deposit(rent_params) .add_unlock_condition(AddressUnlockCondition::new(address)); - let account_output_builder = - AccountOutputBuilder::new_with_minimum_storage_deposit(rent_structure, AccountId::null()); - let foundry_output_builder = - FoundryOutputBuilder::new_with_minimum_storage_deposit(rent_structure, 1, token_scheme); + let account_output_builder = AccountOutputBuilder::new_with_minimum_storage_deposit(rent_params, AccountId::null()); + let foundry_output_builder = FoundryOutputBuilder::new_with_minimum_storage_deposit(rent_params, 1, token_scheme); let outputs = [ //// most simple output diff --git a/sdk/src/client/api/block_builder/input_selection/remainder.rs b/sdk/src/client/api/block_builder/input_selection/remainder.rs index 5b2e39caa9..1eb84aca1b 100644 --- a/sdk/src/client/api/block_builder/input_selection/remainder.rs +++ b/sdk/src/client/api/block_builder/input_selection/remainder.rs @@ -65,7 +65,7 @@ impl InputSelection { let native_tokens_remainder = native_tokens_diff.is_some(); let mut remainder_builder = - BasicOutputBuilder::new_with_minimum_storage_deposit(self.protocol_parameters.rent_structure()) + BasicOutputBuilder::new_with_minimum_storage_deposit(self.protocol_parameters.rent_parameters()) .add_unlock_condition(AddressUnlockCondition::new(Address::from(Ed25519Address::from( [0; 32], )))); @@ -144,7 +144,7 @@ impl InputSelection { log::debug!("Created remainder output of {diff} for {remainder_address:?}"); remainder.verify_storage_deposit( - self.protocol_parameters.rent_structure(), + self.protocol_parameters.rent_parameters(), self.protocol_parameters.token_supply(), )?; diff --git a/sdk/src/client/api/block_builder/input_selection/requirement/amount.rs b/sdk/src/client/api/block_builder/input_selection/requirement/amount.rs index 75ee4d2aa6..a030f94101 100644 --- a/sdk/src/client/api/block_builder/input_selection/requirement/amount.rs +++ b/sdk/src/client/api/block_builder/input_selection/requirement/amount.rs @@ -11,8 +11,9 @@ use crate::{ input::INPUT_COUNT_MAX, output::{ unlock_condition::StorageDepositReturnUnlockCondition, AccountOutputBuilder, AccountTransition, - FoundryOutputBuilder, NftOutputBuilder, Output, OutputId, StorageScore, + FoundryOutputBuilder, NftOutputBuilder, Output, OutputId, }, + rent::StorageCost, slot::SlotIndex, }, }; @@ -240,7 +241,7 @@ impl InputSelection { for output in outputs { let diff = amount_selection.missing_amount(); let amount = output.amount(); - let rent = output.storage_score(self.protocol_parameters.rent_structure()); + let rent = output.storage_cost(self.protocol_parameters.rent_parameters()); let new_amount = if amount >= diff + rent { amount - diff } else { rent }; diff --git a/sdk/src/client/core.rs b/sdk/src/client/core.rs index e2ca71c2f4..f8efcb9ca1 100644 --- a/sdk/src/client/core.rs +++ b/sdk/src/client/core.rs @@ -24,7 +24,7 @@ use crate::{ node_manager::NodeManager, Error, }, - types::block::{address::Hrp, output::RentStructure, protocol::ProtocolParameters}, + types::block::{address::Hrp, protocol::ProtocolParameters, rent::RentParameters}, }; /// An IOTA node client. @@ -153,8 +153,8 @@ impl ClientInner { } /// Gets the rent structure of the node we're connecting to. - pub async fn get_rent_structure(&self) -> Result { - Ok(self.get_network_info().await?.protocol_parameters.rent_structure()) + pub async fn get_rent_parameters(&self) -> Result { + Ok(self.get_network_info().await?.protocol_parameters.rent_parameters()) } /// Gets the token supply of the node we're connecting to. diff --git a/sdk/src/types/block/address/mod.rs b/sdk/src/types/block/address/mod.rs index a870d399b7..0c63d3bc57 100644 --- a/sdk/src/types/block/address/mod.rs +++ b/sdk/src/types/block/address/mod.rs @@ -14,12 +14,16 @@ pub use self::{ ed25519::Ed25519Address, nft::NftAddress, }; -use crate::types::block::{ - output::{Output, OutputId}, - semantic::{TransactionFailureReason, ValidationContext}, - signature::Signature, - unlock::Unlock, - ConvertTo, Error, +use crate::{ + types::block::{ + output::{Output, OutputId}, + rent::{RentParameters, RentStructure, StorageCost}, + semantic::{TransactionFailureReason, ValidationContext}, + signature::Signature, + unlock::Unlock, + ConvertTo, Error, + }, + wallet::storage::Storage, }; /// A generic address supporting different address kinds. @@ -210,3 +214,20 @@ impl From<&Self> for Address { *value } } + +/// A trait to facilitate the rent cost computation for addresses, which is central to dust protection. +pub trait AddressStorageCost { + /// Computes the storage score of an address given a [`RentStructure`]. + fn storage_cost(&self, rent_structure: RentStructure) -> u64; +} + +impl AddressStorageCost for Address { + fn storage_cost(&self, rent_structure: RentStructure) -> u64 { + match self { + Self::Account(_) | Self::Ed25519(_) | Self::Nft(_) => 0, + // TODO: implicit account address and restricted address + // Address::ImplicitAccountCreation(_) => + // rent_stucture.storage_score_offset_implicit_account_creation_address + } + } +} diff --git a/sdk/src/types/block/mod.rs b/sdk/src/types/block/mod.rs index de471203d6..d2040863a4 100644 --- a/sdk/src/types/block/mod.rs +++ b/sdk/src/types/block/mod.rs @@ -31,6 +31,8 @@ pub mod protocol; /// A module that provides utilities for random generation of types. #[cfg(feature = "rand")] pub mod rand; +/// A module that provides types and rules for rent cost computation. +pub mod rent; /// A module that provides types and rules for semantic validation. pub mod semantic; /// A module that provides types and syntactic validations of signatures. diff --git a/sdk/src/types/block/output/account.rs b/sdk/src/types/block/output/account.rs index 4face4d474..108f64d7f9 100644 --- a/sdk/src/types/block/output/account.rs +++ b/sdk/src/types/block/output/account.rs @@ -22,10 +22,10 @@ use crate::types::{ verify_allowed_unlock_conditions, UnlockCondition, UnlockConditionFlags, UnlockConditions, }, verify_output_amount_min, verify_output_amount_packable, verify_output_amount_supply, ChainId, NativeToken, - NativeTokens, Output, OutputBuilderAmount, OutputId, RentStructure, StateTransitionError, - StateTransitionVerifier, StorageScore, + NativeTokens, Output, OutputBuilderAmount, OutputId, StateTransitionError, StateTransitionVerifier, }, protocol::ProtocolParameters, + rent::{RentParameters, StorageCost}, semantic::{TransactionFailureReason, ValidationContext}, unlock::Unlock, Error, @@ -112,8 +112,8 @@ impl AccountOutputBuilder { /// Creates an [`AccountOutputBuilder`] with a provided rent structure. /// The amount will be set to the minimum storage deposit. - pub fn new_with_minimum_storage_deposit(rent_structure: RentStructure, account_id: AccountId) -> Self { - Self::new(OutputBuilderAmount::MinimumStorageDeposit(rent_structure), account_id) + pub fn new_with_minimum_storage_deposit(rent_params: RentParameters, account_id: AccountId) -> Self { + Self::new(OutputBuilderAmount::MinimumStorageDeposit(rent_params), account_id) } fn new(amount: OutputBuilderAmount, account_id: AccountId) -> Self { @@ -140,8 +140,8 @@ impl AccountOutputBuilder { /// Sets the amount to the minimum storage deposit. #[inline(always)] - pub fn with_minimum_storage_deposit(mut self, rent_structure: RentStructure) -> Self { - self.amount = OutputBuilderAmount::MinimumStorageDeposit(rent_structure); + pub fn with_minimum_storage_deposit(mut self, rent_params: RentParameters) -> Self { + self.amount = OutputBuilderAmount::MinimumStorageDeposit(rent_params); self } @@ -318,8 +318,8 @@ impl AccountOutputBuilder { output.amount = match self.amount { OutputBuilderAmount::Amount(amount) => amount, - OutputBuilderAmount::MinimumStorageDeposit(rent_structure) => { - Output::Account(output.clone()).storage_score(rent_structure) + OutputBuilderAmount::MinimumStorageDeposit(rent_params) => { + Output::Account(output.clone()).storage_cost(rent_params) } }; @@ -413,10 +413,10 @@ impl AccountOutput { /// The amount will be set to the minimum storage deposit. #[inline(always)] pub fn build_with_minimum_storage_deposit( - rent_structure: RentStructure, + rent_params: RentParameters, account_id: AccountId, ) -> AccountOutputBuilder { - AccountOutputBuilder::new_with_minimum_storage_deposit(rent_structure, account_id) + AccountOutputBuilder::new_with_minimum_storage_deposit(rent_params, account_id) } /// @@ -856,8 +856,8 @@ pub(crate) mod dto { let params = params.into(); let mut builder = match amount { OutputBuilderAmount::Amount(amount) => AccountOutputBuilder::new_with_amount(amount, *account_id), - OutputBuilderAmount::MinimumStorageDeposit(rent_structure) => { - AccountOutputBuilder::new_with_minimum_storage_deposit(rent_structure, *account_id) + OutputBuilderAmount::MinimumStorageDeposit(rent_params) => { + AccountOutputBuilder::new_with_minimum_storage_deposit(rent_params, *account_id) } } .with_mana(mana); @@ -978,7 +978,7 @@ mod tests { test_split_dto(builder); let builder = - AccountOutput::build_with_minimum_storage_deposit(protocol_parameters.rent_structure(), account_id) + AccountOutput::build_with_minimum_storage_deposit(protocol_parameters.rent_parameters(), account_id) .add_native_token(NativeToken::new(TokenId::from(foundry_id), 1000).unwrap()) .add_unlock_condition(gov_address) .add_unlock_condition(state_address) diff --git a/sdk/src/types/block/output/basic.rs b/sdk/src/types/block/output/basic.rs index 76fbfade2d..c21f892655 100644 --- a/sdk/src/types/block/output/basic.rs +++ b/sdk/src/types/block/output/basic.rs @@ -14,9 +14,10 @@ use crate::types::{ verify_allowed_unlock_conditions, UnlockCondition, UnlockConditionFlags, UnlockConditions, }, verify_output_amount_min, verify_output_amount_packable, verify_output_amount_supply, NativeToken, - NativeTokens, Output, OutputBuilderAmount, OutputId, RentStructure, StorageScore, + NativeTokens, Output, OutputBuilderAmount, OutputId, }, protocol::ProtocolParameters, + rent::{RentParameters, StorageCost}, semantic::{TransactionFailureReason, ValidationContext}, unlock::Unlock, Error, @@ -45,8 +46,8 @@ impl BasicOutputBuilder { /// Creates an [`BasicOutputBuilder`] with a provided rent structure. /// The amount will be set to the minimum storage deposit. #[inline(always)] - pub fn new_with_minimum_storage_deposit(rent_structure: RentStructure) -> Self { - Self::new(OutputBuilderAmount::MinimumStorageDeposit(rent_structure)) + pub fn new_with_minimum_storage_deposit(rent_params: RentParameters) -> Self { + Self::new(OutputBuilderAmount::MinimumStorageDeposit(rent_params)) } fn new(amount: OutputBuilderAmount) -> Self { @@ -68,8 +69,8 @@ impl BasicOutputBuilder { /// Sets the amount to the minimum storage deposit. #[inline(always)] - pub fn with_minimum_storage_deposit(mut self, rent_structure: RentStructure) -> Self { - self.amount = OutputBuilderAmount::MinimumStorageDeposit(rent_structure); + pub fn with_minimum_storage_deposit(mut self, rent_params: RentParameters) -> Self { + self.amount = OutputBuilderAmount::MinimumStorageDeposit(rent_params); self } @@ -171,8 +172,8 @@ impl BasicOutputBuilder { output.amount = match self.amount { OutputBuilderAmount::Amount(amount) => amount, - OutputBuilderAmount::MinimumStorageDeposit(rent_structure) => { - Output::Basic(output.clone()).storage_score(rent_structure) + OutputBuilderAmount::MinimumStorageDeposit(rent_params) => { + Output::Basic(output.clone()).storage_cost(rent_params) } }; @@ -253,8 +254,8 @@ impl BasicOutput { /// Creates a new [`BasicOutputBuilder`] with a provided rent structure. /// The amount will be set to the minimum storage deposit. #[inline(always)] - pub fn build_with_minimum_storage_deposit(rent_structure: RentStructure) -> BasicOutputBuilder { - BasicOutputBuilder::new_with_minimum_storage_deposit(rent_structure) + pub fn build_with_minimum_storage_deposit(rent_params: RentParameters) -> BasicOutputBuilder { + BasicOutputBuilder::new_with_minimum_storage_deposit(rent_params) } /// @@ -428,8 +429,8 @@ pub(crate) mod dto { let params = params.into(); let mut builder = match amount { OutputBuilderAmount::Amount(amount) => BasicOutputBuilder::new_with_amount(amount), - OutputBuilderAmount::MinimumStorageDeposit(rent_structure) => { - BasicOutputBuilder::new_with_minimum_storage_deposit(rent_structure) + OutputBuilderAmount::MinimumStorageDeposit(rent_params) => { + BasicOutputBuilder::new_with_minimum_storage_deposit(rent_params) } } .with_mana(mana); @@ -517,7 +518,7 @@ mod tests { .with_features(rand_allowed_features(BasicOutput::ALLOWED_FEATURES)); test_split_dto(builder); - let builder = BasicOutput::build_with_minimum_storage_deposit(protocol_parameters.rent_structure()) + let builder = BasicOutput::build_with_minimum_storage_deposit(protocol_parameters.rent_parameters()) .add_native_token(NativeToken::new(TokenId::from(foundry_id), 1000).unwrap()) .add_unlock_condition(address) .with_features(rand_allowed_features(BasicOutput::ALLOWED_FEATURES)); diff --git a/sdk/src/types/block/output/delegation.rs b/sdk/src/types/block/output/delegation.rs index 252fe9a2b4..0e3c9970b1 100644 --- a/sdk/src/types/block/output/delegation.rs +++ b/sdk/src/types/block/output/delegation.rs @@ -19,9 +19,10 @@ use crate::types::{ verify_allowed_unlock_conditions, UnlockCondition, UnlockConditionFlags, UnlockConditions, }, verify_output_amount_min, verify_output_amount_packable, verify_output_amount_supply, Output, - OutputBuilderAmount, OutputId, RentStructure, StateTransitionError, StateTransitionVerifier, StorageScore, + OutputBuilderAmount, OutputId, StateTransitionError, StateTransitionVerifier, }, protocol::ProtocolParameters, + rent::{RentParameters, StorageCost}, semantic::{TransactionFailureReason, ValidationContext}, slot::EpochIndex, unlock::Unlock, @@ -79,13 +80,13 @@ impl DelegationOutputBuilder { /// Creates a [`DelegationOutputBuilder`] with a provided rent structure. /// The amount will be set to the minimum storage deposit. pub fn new_with_minimum_storage_deposit( - rent_structure: RentStructure, + rent_params: RentParameters, delegated_amount: u64, delegation_id: DelegationId, validator_address: AccountAddress, ) -> Self { Self::new( - OutputBuilderAmount::MinimumStorageDeposit(rent_structure), + OutputBuilderAmount::MinimumStorageDeposit(rent_params), delegated_amount, delegation_id, validator_address, @@ -116,8 +117,8 @@ impl DelegationOutputBuilder { } /// Sets the amount to the minimum storage deposit. - pub fn with_minimum_storage_deposit(mut self, rent_structure: RentStructure) -> Self { - self.amount = OutputBuilderAmount::MinimumStorageDeposit(rent_structure); + pub fn with_minimum_storage_deposit(mut self, rent_params: RentParameters) -> Self { + self.amount = OutputBuilderAmount::MinimumStorageDeposit(rent_params); self } @@ -194,8 +195,8 @@ impl DelegationOutputBuilder { output.amount = match self.amount { OutputBuilderAmount::Amount(amount) => amount, - OutputBuilderAmount::MinimumStorageDeposit(rent_structure) => { - Output::Delegation(output.clone()).storage_score(rent_structure) + OutputBuilderAmount::MinimumStorageDeposit(rent_params) => { + Output::Delegation(output.clone()).storage_cost(rent_params) } }; @@ -276,13 +277,13 @@ impl DelegationOutput { /// Creates a new [`DelegationOutputBuilder`] with a provided rent structure. /// The amount will be set to the minimum storage deposit. pub fn build_with_minimum_storage_deposit( - rent_structure: RentStructure, + rent_params: RentParameters, delegated_amount: u64, delegation_id: DelegationId, validator_address: AccountAddress, ) -> DelegationOutputBuilder { DelegationOutputBuilder::new_with_minimum_storage_deposit( - rent_structure, + rent_params, delegated_amount, delegation_id, validator_address, @@ -561,9 +562,9 @@ pub(crate) mod dto { *delegation_id, *validator_address, ), - OutputBuilderAmount::MinimumStorageDeposit(rent_structure) => { + OutputBuilderAmount::MinimumStorageDeposit(rent_params) => { DelegationOutputBuilder::new_with_minimum_storage_deposit( - rent_structure, + rent_params, delegated_amount, *delegation_id, *validator_address, diff --git a/sdk/src/types/block/output/feature/block_issuer.rs b/sdk/src/types/block/output/feature/block_issuer.rs index c7682bdf33..1a561e4bd5 100644 --- a/sdk/src/types/block/output/feature/block_issuer.rs +++ b/sdk/src/types/block/output/feature/block_issuer.rs @@ -16,7 +16,11 @@ use packable::{ Packable, }; -use crate::types::block::{slot::SlotIndex, Error}; +use crate::types::block::{ + rent::{RentParameters, StorageCost}, + slot::SlotIndex, + Error, +}; #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, From, packable::Packable)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(untagged))] @@ -57,6 +61,19 @@ impl BlockIssuerKey { } } +impl StorageCost for BlockIssuerKey { + fn offset_score(&self, rent_params: RentParameters) -> u64 { + // TODO: ?? + 0 + } + + fn score(&self, rent_params: RentParameters) -> u64 { + match self { + Self::Ed25519(key) => key.score(rent_params), + } + } +} + /// An Ed25519 block issuer key. #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deref, AsRef, From)] #[as_ref(forward)] @@ -99,6 +116,12 @@ impl Packable for Ed25519BlockIssuerKey { } } +impl StorageCost for Ed25519BlockIssuerKey { + fn score(&self, rent_params: RentParameters) -> u64 { + rent_params.storage_score_offset_ed25519_block_issuer_key() + } +} + pub(crate) type BlockIssuerKeyCount = BoundedU8<{ *BlockIssuerKeys::COUNT_RANGE.start() }, { *BlockIssuerKeys::COUNT_RANGE.end() }>; @@ -181,6 +204,12 @@ impl BlockIssuerKeys { } } +impl StorageCost for BlockIssuerKeys { + fn score(&self, rent_params: RentParameters) -> u64 { + (*self).iter().map(|key| key.score(rent_params)).sum() + } +} + /// This feature defines the block issuer 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)] diff --git a/sdk/src/types/block/output/foundry.rs b/sdk/src/types/block/output/foundry.rs index f7bb861cd9..6da68682cf 100644 --- a/sdk/src/types/block/output/foundry.rs +++ b/sdk/src/types/block/output/foundry.rs @@ -22,10 +22,11 @@ use crate::types::{ verify_allowed_unlock_conditions, UnlockCondition, UnlockConditionFlags, UnlockConditions, }, verify_output_amount_min, verify_output_amount_packable, verify_output_amount_supply, ChainId, NativeToken, - NativeTokens, Output, OutputBuilderAmount, OutputId, RentStructure, StateTransitionError, - StateTransitionVerifier, StorageScore, TokenId, TokenScheme, + NativeTokens, Output, OutputBuilderAmount, OutputId, StateTransitionError, StateTransitionVerifier, + TokenId, TokenScheme, }, protocol::ProtocolParameters, + rent::{RentParameters, StorageCost}, semantic::{TransactionFailureReason, ValidationContext}, unlock::Unlock, Error, @@ -103,12 +104,12 @@ impl FoundryOutputBuilder { /// Creates a [`FoundryOutputBuilder`] with a provided rent structure. /// The amount will be set to the minimum storage deposit. pub fn new_with_minimum_storage_deposit( - rent_structure: RentStructure, + rent_params: RentParameters, serial_number: u32, token_scheme: TokenScheme, ) -> Self { Self::new( - OutputBuilderAmount::MinimumStorageDeposit(rent_structure), + OutputBuilderAmount::MinimumStorageDeposit(rent_params), serial_number, token_scheme, ) @@ -135,8 +136,8 @@ impl FoundryOutputBuilder { /// Sets the amount to the minimum storage deposit. #[inline(always)] - pub fn with_minimum_storage_deposit(mut self, rent_structure: RentStructure) -> Self { - self.amount = OutputBuilderAmount::MinimumStorageDeposit(rent_structure); + pub fn with_minimum_storage_deposit(mut self, rent_params: RentParameters) -> Self { + self.amount = OutputBuilderAmount::MinimumStorageDeposit(rent_params); self } @@ -282,8 +283,8 @@ impl FoundryOutputBuilder { output.amount = match self.amount { OutputBuilderAmount::Amount(amount) => amount, - OutputBuilderAmount::MinimumStorageDeposit(rent_structure) => { - Output::Foundry(output.clone()).storage_score(rent_structure) + OutputBuilderAmount::MinimumStorageDeposit(rent_params) => { + Output::Foundry(output.clone()).storage_cost(rent_params) } }; @@ -365,11 +366,11 @@ impl FoundryOutput { /// The amount will be set to the minimum storage deposit. #[inline(always)] pub fn build_with_minimum_storage_deposit( - rent_structure: RentStructure, + rent_params: RentParameters, serial_number: u32, token_scheme: TokenScheme, ) -> FoundryOutputBuilder { - FoundryOutputBuilder::new_with_minimum_storage_deposit(rent_structure, serial_number, token_scheme) + FoundryOutputBuilder::new_with_minimum_storage_deposit(rent_params, serial_number, token_scheme) } /// @@ -758,8 +759,8 @@ pub(crate) mod dto { OutputBuilderAmount::Amount(amount) => { FoundryOutputBuilder::new_with_amount(amount, serial_number, token_scheme) } - OutputBuilderAmount::MinimumStorageDeposit(rent_structure) => { - FoundryOutputBuilder::new_with_minimum_storage_deposit(rent_structure, serial_number, token_scheme) + OutputBuilderAmount::MinimumStorageDeposit(rent_params) => { + FoundryOutputBuilder::new_with_minimum_storage_deposit(rent_params, serial_number, token_scheme) } }; @@ -846,7 +847,7 @@ mod tests { test_split_dto(builder); let builder = FoundryOutput::build_with_minimum_storage_deposit( - protocol_parameters.rent_structure(), + protocol_parameters.rent_parameters(), 123, rand_token_scheme(), ) diff --git a/sdk/src/types/block/output/mod.rs b/sdk/src/types/block/output/mod.rs index 2b964ca9a2..e4f142110e 100644 --- a/sdk/src/types/block/output/mod.rs +++ b/sdk/src/types/block/output/mod.rs @@ -7,7 +7,6 @@ mod inputs_commitment; mod metadata; mod native_token; mod output_id; -mod rent; mod state_transition; mod token_scheme; @@ -24,7 +23,7 @@ pub mod nft; /// pub mod unlock_condition; -use core::ops::RangeInclusive; +use core::{mem::size_of, ops::RangeInclusive}; use derive_more::From; use packable::{ @@ -39,7 +38,7 @@ pub(crate) use self::{ feature::{MetadataFeatureLength, TagFeatureLength}, native_token::NativeTokenCount, output_id::OutputIndex, - unlock_condition::AddressUnlockCondition, + unlock_condition::{AddressUnlockCondition, ExpirationUnlockCondition, StorageDepositReturnUnlockCondition}, }; pub use self::{ account::{AccountId, AccountOutput, AccountOutputBuilder, AccountTransition}, @@ -53,12 +52,16 @@ pub use self::{ native_token::{NativeToken, NativeTokens, NativeTokensBuilder, TokenId}, nft::{NftId, NftOutput, NftOutputBuilder}, output_id::OutputId, - rent::{MinimumStorageDepositBasicOutput, RentStructure, StorageScore}, state_transition::{StateTransitionError, StateTransitionVerifier}, token_scheme::{SimpleTokenScheme, TokenScheme}, unlock_condition::{UnlockCondition, UnlockConditions}, }; -use super::protocol::ProtocolParameters; +use super::{ + address::Ed25519Address, + protocol::ProtocolParameters, + rent::{RentParameters, StorageCost}, + BlockId, +}; use crate::types::block::{address::Address, semantic::ValidationContext, slot::SlotIndex, Error}; /// The maximum number of outputs of a transaction. @@ -73,7 +76,7 @@ pub const OUTPUT_INDEX_RANGE: RangeInclusive = 0..=OUTPUT_INDEX_MAX; // [0. #[derive(Copy, Clone)] pub enum OutputBuilderAmount { Amount(u64), - MinimumStorageDeposit(RentStructure), + MinimumStorageDeposit(RentParameters), } /// Contains the generic [`Output`] with associated [`OutputMetadata`]. @@ -380,11 +383,11 @@ impl Output { } /// Verifies if a valid storage deposit was made. Each [`Output`] has to have an amount that covers its associated - /// byte cost, given by [`RentStructure`]. + /// storage score, given by [`RentParameters`]. /// If there is a [`StorageDepositReturnUnlockCondition`](unlock_condition::StorageDepositReturnUnlockCondition), /// its amount is also checked. - pub fn verify_storage_deposit(&self, rent_structure: RentStructure, token_supply: u64) -> Result<(), Error> { - let required_output_amount = self.storage_score(rent_structure); + pub fn verify_storage_deposit(&self, rent_params: RentParameters, token_supply: u64) -> Result<(), Error> { + let required_output_amount = self.storage_cost(rent_params); if self.amount() < required_output_amount { return Err(Error::InsufficientStorageDepositAmount { @@ -406,8 +409,7 @@ impl Output { }); } - let minimum_deposit = - minimum_storage_deposit(return_condition.return_address(), rent_structure, token_supply); + let minimum_deposit = minimum_storage_deposit(return_condition.return_address(), rent_params, token_supply); // `Minimum Storage Deposit` ≤ `Return Amount` if return_condition.amount() < minimum_deposit { @@ -468,12 +470,6 @@ impl Packable for Output { } } -impl StorageScore for Output { - fn weighted_bytes(&self, rent_structure: RentStructure) -> u64 { - self.packed_len() as u64 * rent_structure.storage_score_factor_data() as u64 - } -} - pub(crate) fn verify_output_amount_min(amount: u64) -> Result<(), Error> { if amount < Output::AMOUNT_MIN { Err(Error::InvalidOutputAmount(amount)) @@ -507,16 +503,79 @@ pub(crate) fn verify_output_amount_packable( /// Computes the minimum amount that a storage deposit has to match to allow creating a return [`Output`] back to the /// sender [`Address`]. -fn minimum_storage_deposit(address: &Address, rent_structure: RentStructure, token_supply: u64) -> u64 { +fn minimum_storage_deposit(address: &Address, rent_parameters: RentParameters, token_supply: u64) -> u64 { // PANIC: This can never fail because the amount will always be within the valid range. Also, the actual value is // not important, we are only interested in the storage requirements of the type. - BasicOutputBuilder::new_with_minimum_storage_deposit(rent_structure) + BasicOutputBuilder::new_with_minimum_storage_deposit(rent_parameters) .add_unlock_condition(AddressUnlockCondition::new(*address)) .finish_with_params(token_supply) .unwrap() .amount() } +impl StorageCost for Output { + fn offset_score(&self, rent_params: RentParameters) -> u64 { + // included output id, block id, and slot booked data size + rent_params.storage_score_offset_output() + + (size_of::() as u64 + size_of::() as u64 + size_of::() as u64) + * rent_params.storage_score_factor_data() as u64 + } + + fn score(&self, rent_params: RentParameters) -> u64 { + self.packed_len() as u64 * rent_params.storage_score_factor_data() as u64 + } +} + +pub struct MinimumStorageDepositBasicOutput { + rent_params: RentParameters, + token_supply: u64, + builder: BasicOutputBuilder, +} + +impl MinimumStorageDepositBasicOutput { + pub fn new(rent_params: RentParameters, token_supply: u64) -> Self { + Self { + rent_params, + token_supply, + builder: BasicOutputBuilder::new_with_amount(Output::AMOUNT_MIN).add_unlock_condition( + AddressUnlockCondition::new(Address::from(Ed25519Address::from([0; Ed25519Address::LENGTH]))), + ), + } + } + + pub fn with_native_tokens(mut self, native_tokens: impl Into>) -> Self { + if let Some(native_tokens) = native_tokens.into() { + self.builder = self.builder.with_native_tokens(native_tokens); + } + self + } + + pub fn with_storage_deposit_return(mut self) -> Result { + self.builder = self + .builder + .add_unlock_condition(StorageDepositReturnUnlockCondition::new( + Address::from(Ed25519Address::from([0; Ed25519Address::LENGTH])), + Output::AMOUNT_MIN, + self.token_supply, + )?); + Ok(self) + } + + pub fn with_expiration(mut self) -> Result { + self.builder = self.builder.add_unlock_condition(ExpirationUnlockCondition::new( + Address::from(Ed25519Address::from([0; Ed25519Address::LENGTH])), + 1, + )?); + Ok(self) + } + + pub fn finish(self) -> Result { + Ok(self + .builder + .finish_output(self.token_supply)? + .storage_cost(self.rent_params)) + } +} #[cfg(feature = "serde")] pub mod dto { use alloc::format; diff --git a/sdk/src/types/block/output/nft.rs b/sdk/src/types/block/output/nft.rs index 695411b096..2bf755dbe0 100644 --- a/sdk/src/types/block/output/nft.rs +++ b/sdk/src/types/block/output/nft.rs @@ -19,8 +19,8 @@ use crate::types::{ verify_allowed_unlock_conditions, UnlockCondition, UnlockConditionFlags, UnlockConditions, }, verify_output_amount_min, verify_output_amount_packable, verify_output_amount_supply, ChainId, NativeToken, - NativeTokens, Output, OutputBuilderAmount, OutputId, RentStructure, StateTransitionError, - StateTransitionVerifier, StorageScore, + NativeTokens, Output, OutputBuilderAmount, OutputId, RentParameters, StateTransitionError, + StateTransitionVerifier, StorageCost, }, protocol::ProtocolParameters, semantic::{TransactionFailureReason, ValidationContext}, @@ -75,8 +75,8 @@ impl NftOutputBuilder { /// Creates an [`NftOutputBuilder`] with a provided rent structure. /// The amount will be set to the minimum storage deposit. - pub fn new_with_minimum_storage_deposit(rent_structure: RentStructure, nft_id: NftId) -> Self { - Self::new(OutputBuilderAmount::MinimumStorageDeposit(rent_structure), nft_id) + pub fn new_with_minimum_storage_deposit(rent_params: RentParameters, nft_id: NftId) -> Self { + Self::new(OutputBuilderAmount::MinimumStorageDeposit(rent_params), nft_id) } fn new(amount: OutputBuilderAmount, nft_id: NftId) -> Self { @@ -100,8 +100,8 @@ impl NftOutputBuilder { /// Sets the amount to the minimum storage deposit. #[inline(always)] - pub fn with_minimum_storage_deposit(mut self, rent_structure: RentStructure) -> Self { - self.amount = OutputBuilderAmount::MinimumStorageDeposit(rent_structure); + pub fn with_minimum_storage_deposit(mut self, rent_params: RentParameters) -> Self { + self.amount = OutputBuilderAmount::MinimumStorageDeposit(rent_params); self } @@ -243,8 +243,8 @@ impl NftOutputBuilder { output.amount = match self.amount { OutputBuilderAmount::Amount(amount) => amount, - OutputBuilderAmount::MinimumStorageDeposit(rent_structure) => { - Output::Nft(output.clone()).storage_score(rent_structure) + OutputBuilderAmount::MinimumStorageDeposit(rent_params) => { + Output::Nft(output.clone()).storage_cost(rent_params) } }; @@ -327,8 +327,8 @@ impl NftOutput { /// Creates a new [`NftOutputBuilder`] with a provided rent structure. /// The amount will be set to the minimum storage deposit. #[inline(always)] - pub fn build_with_minimum_storage_deposit(rent_structure: RentStructure, nft_id: NftId) -> NftOutputBuilder { - NftOutputBuilder::new_with_minimum_storage_deposit(rent_structure, nft_id) + pub fn build_with_minimum_storage_deposit(rent_params: RentParameters, nft_id: NftId) -> NftOutputBuilder { + NftOutputBuilder::new_with_minimum_storage_deposit(rent_params, nft_id) } /// @@ -617,8 +617,8 @@ pub(crate) mod dto { let params = params.into(); let mut builder = match amount { OutputBuilderAmount::Amount(amount) => NftOutputBuilder::new_with_amount(amount, *nft_id), - OutputBuilderAmount::MinimumStorageDeposit(rent_structure) => { - NftOutputBuilder::new_with_minimum_storage_deposit(rent_structure, *nft_id) + OutputBuilderAmount::MinimumStorageDeposit(rent_params) => { + NftOutputBuilder::new_with_minimum_storage_deposit(rent_params, *nft_id) } } .with_mana(mana); @@ -712,7 +712,7 @@ mod tests { test_split_dto(builder); let builder = - NftOutput::build_with_minimum_storage_deposit(protocol_parameters.rent_structure(), NftId::null()) + NftOutput::build_with_minimum_storage_deposit(protocol_parameters.rent_parameters(), NftId::null()) .add_native_token(NativeToken::new(TokenId::from(foundry_id), 1000).unwrap()) .add_unlock_condition(rand_address_unlock_condition()) .with_features(rand_allowed_features(NftOutput::ALLOWED_FEATURES)) diff --git a/sdk/src/types/block/payload/transaction/essence/regular.rs b/sdk/src/types/block/payload/transaction/essence/regular.rs index c21dfb0ddd..71e0d332cc 100644 --- a/sdk/src/types/block/payload/transaction/essence/regular.rs +++ b/sdk/src/types/block/payload/transaction/essence/regular.rs @@ -395,7 +395,7 @@ fn verify_outputs(outputs: &[Output], visitor: &ProtocolPara } } - output.verify_storage_deposit(visitor.rent_structure(), visitor.token_supply())?; + output.verify_storage_deposit(visitor.rent_parameters(), visitor.token_supply())?; } } diff --git a/sdk/src/types/block/protocol.rs b/sdk/src/types/block/protocol.rs index 7f07da3a8f..2fb9564ba5 100644 --- a/sdk/src/types/block/protocol.rs +++ b/sdk/src/types/block/protocol.rs @@ -13,7 +13,7 @@ use super::{ mana::{ManaStructure, RewardsParameters}, slot::SlotIndex, }; -use crate::types::block::{helper::network_name_to_id, output::RentStructure, ConvertTo, Error, PROTOCOL_VERSION}; +use crate::types::block::{helper::network_name_to_id, rent::RentParameters, ConvertTo, Error, PROTOCOL_VERSION}; /// Defines the parameters of the protocol at a particular version. #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Packable, Getters, CopyGetters)] @@ -38,7 +38,7 @@ pub struct ProtocolParameters { /// The HRP prefix used for Bech32 addresses in the network. pub(crate) bech32_hrp: Hrp, /// The rent structure used by given node/network. - pub(crate) rent_structure: RentStructure, + pub(crate) rent_parameters: RentParameters, /// The work score structure used by the node/network. pub(crate) work_score_structure: WorkScoreStructure, /// TokenSupply defines the current token supply on the network. @@ -100,7 +100,7 @@ impl Default for ProtocolParameters { // Unwrap: Known to be valid network_name: String::from("iota-core-testnet").try_into().unwrap(), bech32_hrp: Hrp::from_str_unchecked("smr"), - rent_structure: Default::default(), + rent_parameters: Default::default(), work_score_structure: Default::default(), token_supply: 1_813_620_509_061_365, genesis_unix_timestamp: 1582328545, @@ -128,7 +128,7 @@ impl ProtocolParameters { version: u8, network_name: impl Into, bech32_hrp: impl ConvertTo, - rent_structure: RentStructure, + rent_parameters: RentParameters, token_supply: u64, genesis_unix_timestamp: u64, slot_duration_in_seconds: u8, @@ -138,7 +138,7 @@ impl ProtocolParameters { version, network_name: >::try_from(network_name.into()).map_err(Error::InvalidStringPrefix)?, bech32_hrp: bech32_hrp.convert()?, - rent_structure, + rent_parameters, token_supply, genesis_unix_timestamp, slot_duration_in_seconds, @@ -318,7 +318,7 @@ pub fn protocol_parameters() -> ProtocolParameters { 2, "testnet", "rms", - crate::types::block::output::RentStructure::new(500, 1, 10, 1, 1, 1), + crate::types::block::rent::RentParameters::new(500, 1, 10, 1, 1, 1), 1_813_620_509_061_365, 1582328545, 10, diff --git a/sdk/src/types/block/output/rent.rs b/sdk/src/types/block/rent.rs similarity index 55% rename from sdk/src/types/block/output/rent.rs rename to sdk/src/types/block/rent.rs index 5c8f792a35..1a48a92761 100644 --- a/sdk/src/types/block/output/rent.rs +++ b/sdk/src/types/block/rent.rs @@ -1,7 +1,7 @@ // Copyright 2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use core::mem::size_of; +use core::{mem::size_of, ops::Deref}; use packable::Packable; @@ -15,30 +15,44 @@ use crate::types::block::{ BlockId, Error, }; -// TODO: fill in the real values -const DEFAULT_STORAGE_COST: u32 = 100; +// TODO: fill in the real values and/or verify +const DEFAULT_STORAGE_COST: u64 = 500; const DEFAULT_STORAGE_SCORE_FACTOR_DATA: StorageScoreFactor = 1; -const DEFAULT_STORAGE_SCORE_OFFSET_OUTPUT: StorageScoreOffset = 1; -const DEFAULT_STORAGE_SCORE_OFFSET_ED25519_BLOCK_ISSUER_KEY: StorageScoreOffset = 1; -const DEFAULT_STORAGE_SCORE_STAKING_FEATURE: StorageScoreOffset = 1; -const DEFAULT_STORAGE_SCORE_OFFSET_DELEGATION: StorageScoreOffset = 1; +const DEFAULT_STORAGE_SCORE_OFFSET_OUTPUT: StorageScoreOffset = 10; +const DEFAULT_STORAGE_SCORE_OFFSET_ED25519_BLOCK_ISSUER_KEY: StorageScoreOffset = 50; +const DEFAULT_STORAGE_SCORE_OFFSET_STAKING_FEATURE: StorageScoreOffset = 100; +const DEFAULT_STORAGE_SCORE_OFFSET_DELEGATION: StorageScoreOffset = 100; -// Defines the type of the storage score factor. type StorageScoreFactor = u8; -// Defines the type of storage score. type StorageScoreOffset = u64; -/// Specifies the current parameters for the byte cost computation. +// Includes the rent parameters and the additional factors/offsets computed from these parameters. #[derive(Copy, +// Clone, Debug, Eq, PartialEq)] +pub struct RentStructure { + rent_parameters: RentParameters, + storage_score_offset_implicit_account_creation_address: u64, +} + +impl Deref for RentStructure { + type Target = RentParameters; + + fn deref(&self) -> &Self::Target { + &self.rent_parameters + } +} + +// Defines the parameters of storage score calculations on objects which take node resources. +// This structure defines the minimum base token deposit required on an object. This deposit does not +// generate Mana, which serves as a rent payment in Mana for storing the object. #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Packable)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(rename_all = "camelCase") )] -pub struct RentStructure { - // TODO: what actual primitive type is their `BaseToken` type def? +pub struct RentParameters { /// Defines the number of IOTA tokens required per unit of storage score. - storage_cost: u32, + storage_cost: u64, /// Defines the factor to be used for data only fields. storage_score_factor_data: StorageScoreFactor, /// Defines the offset to be used for key/lookup generating fields. @@ -51,23 +65,23 @@ pub struct RentStructure { storage_score_offset_delegation: StorageScoreOffset, } -impl Default for RentStructure { +impl Default for RentParameters { fn default() -> Self { Self { storage_cost: DEFAULT_STORAGE_COST, storage_score_factor_data: DEFAULT_STORAGE_SCORE_FACTOR_DATA, storage_score_offset_output: DEFAULT_STORAGE_SCORE_OFFSET_OUTPUT, storage_score_offset_ed25519_block_issuer_key: DEFAULT_STORAGE_SCORE_OFFSET_ED25519_BLOCK_ISSUER_KEY, - storage_score_offset_staking_feature: DEFAULT_STORAGE_SCORE_STAKING_FEATURE, + storage_score_offset_staking_feature: DEFAULT_STORAGE_SCORE_OFFSET_STAKING_FEATURE, storage_score_offset_delegation: DEFAULT_STORAGE_SCORE_OFFSET_DELEGATION, } } } -impl RentStructure { - /// Creates a new [`RentStructure`]. +impl RentParameters { + /// Creates a new [`RentParameters`]. pub fn new( - storage_cost: u32, + storage_cost: u64, storage_score_factor_data: StorageScoreFactor, storage_score_offset_output: StorageScoreOffset, storage_score_offset_ed25519_block_issuer_key: StorageScoreOffset, @@ -85,7 +99,7 @@ impl RentStructure { } /// Sets the storage cost for the storage deposit. - pub fn with_storage_cost(mut self, storage_cost: u32) -> Self { + pub fn with_storage_cost(mut self, storage_cost: u64) -> Self { self.storage_cost = storage_cost; self } @@ -126,115 +140,59 @@ impl RentStructure { self } - /// Returns the TODO of the [`RentStructure`]. - pub const fn storage_cost(&self) -> u32 { + /// Returns the TODO of the [`RentParameters`]. + pub const fn storage_cost(&self) -> u64 { self.storage_cost } - /// Returns the TODO of the [`RentStructure`]. + /// Returns the TODO of the [`RentParameters`]. pub const fn storage_score_factor_data(&self) -> StorageScoreFactor { self.storage_score_factor_data } - /// Returns the TODO of the [`RentStructure`]. + /// Returns the TODO of the [`RentParameters`]. pub const fn storage_score_offset_output(&self) -> StorageScoreOffset { self.storage_score_offset_output } - /// Returns the TODO the [`RentStructure`]. + /// Returns the TODO the [`RentParameters`]. pub const fn storage_score_offset_ed25519_block_issuer_key(&self) -> StorageScoreOffset { self.storage_score_offset_ed25519_block_issuer_key } - /// Returns the TODO the [`RentStructure`]. + /// Returns the TODO the [`RentParameters`]. pub const fn storage_score_offset_staking_feature(&self) -> StorageScoreOffset { self.storage_score_offset_staking_feature } - /// Returns the TODO the [`RentStructure`]. + /// Returns the TODO the [`RentParameters`]. pub const fn storage_score_offset_delegation(&self) -> StorageScoreOffset { self.storage_score_offset_delegation } } -/// A trait to facilitate the computation of the storage score of block outputs, which is central to dust protection. -pub trait StorageScore { - /// Computes the byte offset given a [`RentStructure`]. - fn byte_offset(&self, rent_structure: RentStructure) -> u32 { - // TODO: verify this - // The ID of the output. - size_of::() as u32 * rent_structure.storage_score_offset_output as u32 - // The ID of the block in which the transaction payload that created this output was included. - + size_of::() as u32 * rent_structure.storage_score_factor_data as u32 - // The index of the slot in which the transaction that created it was booked. - + size_of::() as u32 * rent_structure.storage_score_factor_data as u32 - // The index of the slot in which the transaction was created. - + size_of::() as u32 * rent_structure.storage_score_factor_data as u32 - } - - /// Different fields in a type lead to different storage requirements for the ledger state. - fn weighted_bytes(&self, config: RentStructure) -> u64; - - /// Computes the storage score given a [`RentStructure`]. - fn storage_score(&self, rent_structure: RentStructure) -> u64 { - rent_structure.storage_cost as u64 - * (self.weighted_bytes(rent_structure) + self.byte_offset(rent_structure) as u64) +/// A trait to facilitate the rent cost computation for block outputs, which is central to dust protection. +pub trait StorageCost { + /// Computes the offset storage score given a [`RentParameters`]. Defaults to 0. + fn offset_score(&self, rent_params: RentParameters) -> u64 { + 0 } -} -impl StorageScore for [T; N] { - fn weighted_bytes(&self, config: RentStructure) -> u64 { - self.iter().map(|elem| elem.weighted_bytes(config)).sum() - } -} + /// Computes the storage score given a [`RentParameters`]. Different fields in a type lead to different storage + /// requirements for the ledger state. + fn score(&self, rent_params: RentParameters) -> u64; -pub struct MinimumStorageDepositBasicOutput { - config: RentStructure, - token_supply: u64, - builder: BasicOutputBuilder, -} - -impl MinimumStorageDepositBasicOutput { - pub fn new(config: RentStructure, token_supply: u64) -> Self { - Self { - config, - token_supply, - builder: BasicOutputBuilder::new_with_amount(Output::AMOUNT_MIN).add_unlock_condition( - AddressUnlockCondition::new(Address::from(Ed25519Address::from([0; Ed25519Address::LENGTH]))), - ), - } - } - - pub fn with_native_tokens(mut self, native_tokens: impl Into>) -> Self { - if let Some(native_tokens) = native_tokens.into() { - self.builder = self.builder.with_native_tokens(native_tokens); - } - self - } - - pub fn with_storage_deposit_return(mut self) -> Result { - self.builder = self - .builder - .add_unlock_condition(StorageDepositReturnUnlockCondition::new( - Address::from(Ed25519Address::from([0; Ed25519Address::LENGTH])), - Output::AMOUNT_MIN, - self.token_supply, - )?); - Ok(self) + /// Computes the storage cost given a [`RentParameters`]. + fn storage_cost(&self, rent_params: RentParameters) -> u64 { + rent_params.storage_cost as u64 * (self.offset_score(rent_params) + self.score(rent_params)) } +} - pub fn with_expiration(mut self) -> Result { - self.builder = self.builder.add_unlock_condition(ExpirationUnlockCondition::new( - Address::from(Ed25519Address::from([0; Ed25519Address::LENGTH])), - 1, - )?); - Ok(self) +impl StorageCost for [T; N] { + fn offset_score(&self, rent_params: RentParameters) -> u64 { + self.get(0).expect("zero sized array").score(rent_params) } - - pub fn finish(self) -> Result { - Ok(self - .builder - .finish_output(self.token_supply)? - .storage_score(self.config)) + fn score(&self, rent_params: RentParameters) -> u64 { + self.iter().map(|elem| elem.score(rent_params)).sum() } } diff --git a/sdk/src/wallet/account/mod.rs b/sdk/src/wallet/account/mod.rs index f8ab9a8e44..d07026b9df 100644 --- a/sdk/src/wallet/account/mod.rs +++ b/sdk/src/wallet/account/mod.rs @@ -613,7 +613,7 @@ fn serialize() { 2, "testnet", "rms", - crate::types::block::output::RentStructure::new(500, 1, 10, 1, 1, 1), + crate::types::block::rent::RentParameters::new(500, 1, 10, 1, 1, 1), 1_813_620_509_061_365, 1582328545, 10, diff --git a/sdk/src/wallet/account/operations/balance.rs b/sdk/src/wallet/account/operations/balance.rs index 85689610d3..5b622c01d1 100644 --- a/sdk/src/wallet/account/operations/balance.rs +++ b/sdk/src/wallet/account/operations/balance.rs @@ -7,7 +7,8 @@ use crate::{ client::secret::SecretManage, types::block::{ address::Bech32Address, - output::{unlock_condition::UnlockCondition, FoundryId, NativeTokensBuilder, Output, StorageScore}, + output::{unlock_condition::UnlockCondition, FoundryId, NativeTokensBuilder, Output}, + rent::StorageCost, ConvertTo, }, wallet::{ @@ -62,7 +63,7 @@ where account_details: &AccountDetails, ) -> Result { let network_id = self.client().get_network_id().await?; - let rent_structure = self.client().get_rent_structure().await?; + let rent_params = self.client().get_rent_parameters().await?; let mut balance = Balance::default(); let mut total_rent_amount = 0; let mut total_native_tokens = NativeTokensBuilder::default(); @@ -88,7 +89,7 @@ where } let output = &data.output; - let rent = output.storage_score(rent_structure); + let rent = output.storage_cost(rent_params); // Add account and foundry outputs here because they can't have a // [`StorageDepositReturnUnlockCondition`] or time related unlock conditions diff --git a/sdk/src/wallet/account/operations/output_claiming.rs b/sdk/src/wallet/account/operations/output_claiming.rs index 66192f018a..6ca1cc10b2 100644 --- a/sdk/src/wallet/account/operations/output_claiming.rs +++ b/sdk/src/wallet/account/operations/output_claiming.rs @@ -203,7 +203,7 @@ where log::debug!("[OUTPUT_CLAIMING] claim_outputs_internal"); let slot_index = self.client().get_slot_index().await?; - let rent_structure = self.client().get_rent_structure().await?; + let rent_params = self.client().get_rent_parameters().await?; let token_supply = self.client().get_token_supply().await?; let account_details = self.details().await; @@ -276,7 +276,7 @@ where .finish_output(token_supply)? } else { NftOutputBuilder::from(nft_output) - .with_minimum_storage_deposit(rent_structure) + .with_minimum_storage_deposit(rent_params) .with_nft_id(nft_output.nft_id_non_null(&output_data.output_id)) .with_unlock_conditions([AddressUnlockCondition::new(first_account_address.address.inner)]) // Set native tokens empty, we will collect them from all inputs later @@ -301,7 +301,7 @@ where required_amount_for_nfts } else { required_amount_for_nfts - + MinimumStorageDepositBasicOutput::new(rent_structure, token_supply) + + MinimumStorageDepositBasicOutput::new(rent_params, token_supply) .with_native_tokens(option_native_token) .finish()? }; @@ -321,7 +321,7 @@ where // Recalculate every time, because new inputs can also add more native tokens, which would increase // the required storage deposit required_amount = required_amount_for_nfts - + MinimumStorageDepositBasicOutput::new(rent_structure, token_supply) + + MinimumStorageDepositBasicOutput::new(rent_params, token_supply) .with_native_tokens(option_native_token) .finish()?; diff --git a/sdk/src/wallet/account/operations/transaction/high_level/create_account.rs b/sdk/src/wallet/account/operations/transaction/high_level/create_account.rs index 6a5a4b2310..31efd75e59 100644 --- a/sdk/src/wallet/account/operations/transaction/high_level/create_account.rs +++ b/sdk/src/wallet/account/operations/transaction/high_level/create_account.rs @@ -74,7 +74,7 @@ where options: impl Into> + Send, ) -> crate::wallet::Result { log::debug!("[TRANSACTION] prepare_create_account_output"); - let rent_structure = self.client().get_rent_structure().await?; + let rent_params = self.client().get_rent_parameters().await?; let token_supply = self.client().get_token_supply().await?; let controller_address = match params.as_ref().and_then(|options| options.address.as_ref()) { @@ -93,7 +93,7 @@ where }; let mut account_output_builder = - AccountOutputBuilder::new_with_minimum_storage_deposit(rent_structure, AccountId::null()) + AccountOutputBuilder::new_with_minimum_storage_deposit(rent_params, AccountId::null()) .with_state_index(0) .with_foundry_counter(0) .add_unlock_condition(StateControllerAddressUnlockCondition::new(controller_address)) diff --git a/sdk/src/wallet/account/operations/transaction/high_level/minting/create_native_token.rs b/sdk/src/wallet/account/operations/transaction/high_level/minting/create_native_token.rs index 8e7c71997e..373ac38201 100644 --- a/sdk/src/wallet/account/operations/transaction/high_level/minting/create_native_token.rs +++ b/sdk/src/wallet/account/operations/transaction/high_level/minting/create_native_token.rs @@ -131,7 +131,7 @@ where options: impl Into> + Send, ) -> crate::wallet::Result { log::debug!("[TRANSACTION] create_native_token"); - let rent_structure = self.client().get_rent_structure().await?; + let rent_params = self.client().get_rent_parameters().await?; let token_supply = self.client().get_token_supply().await?; let (account_id, account_output) = self @@ -158,7 +158,7 @@ where new_account_output_builder.finish_output(token_supply)?, { let mut foundry_builder = FoundryOutputBuilder::new_with_minimum_storage_deposit( - rent_structure, + rent_params, account_output.foundry_counter() + 1, TokenScheme::Simple(SimpleTokenScheme::new( params.circulating_supply, diff --git a/sdk/src/wallet/account/operations/transaction/high_level/minting/mint_nfts.rs b/sdk/src/wallet/account/operations/transaction/high_level/minting/mint_nfts.rs index 18996d33ee..f07f6e79a4 100644 --- a/sdk/src/wallet/account/operations/transaction/high_level/minting/mint_nfts.rs +++ b/sdk/src/wallet/account/operations/transaction/high_level/minting/mint_nfts.rs @@ -160,7 +160,7 @@ where I::IntoIter: Send, { log::debug!("[TRANSACTION] prepare_mint_nfts"); - let rent_structure = self.client().get_rent_structure().await?; + let rent_params = self.client().get_rent_parameters().await?; let token_supply = self.client().get_token_supply().await?; let account_addresses = self.addresses().await?; let mut outputs = Vec::new(); @@ -189,7 +189,7 @@ where }; // NftId needs to be set to 0 for the creation - let mut nft_builder = NftOutputBuilder::new_with_minimum_storage_deposit(rent_structure, NftId::null()) + let mut nft_builder = NftOutputBuilder::new_with_minimum_storage_deposit(rent_params, NftId::null()) // Address which will own the nft .add_unlock_condition(AddressUnlockCondition::new(address)); diff --git a/sdk/src/wallet/account/operations/transaction/high_level/send.rs b/sdk/src/wallet/account/operations/transaction/high_level/send.rs index 943aa87b9f..83dee32dcd 100644 --- a/sdk/src/wallet/account/operations/transaction/high_level/send.rs +++ b/sdk/src/wallet/account/operations/transaction/high_level/send.rs @@ -139,7 +139,7 @@ where { log::debug!("[TRANSACTION] prepare_send"); let options = options.into(); - let rent_structure = self.client().get_rent_structure().await?; + let rent_params = self.client().get_rent_parameters().await?; let token_supply = self.client().get_token_supply().await?; let account_addresses = self.addresses().await?; @@ -170,7 +170,7 @@ where .unwrap_or(default_return_address.address); // Get the minimum required amount for an output assuming it does not need a storage deposit. - let output = BasicOutputBuilder::new_with_minimum_storage_deposit(rent_structure) + let output = BasicOutputBuilder::new_with_minimum_storage_deposit(rent_params) .add_unlock_condition(AddressUnlockCondition::new(address)) .finish_output(token_supply)?; @@ -187,7 +187,7 @@ where }); // Since it does need a storage deposit, calculate how much that should be - let storage_deposit_amount = MinimumStorageDepositBasicOutput::new(rent_structure, token_supply) + let storage_deposit_amount = MinimumStorageDepositBasicOutput::new(rent_params, token_supply) .with_storage_deposit_return()? .with_expiration()? .finish()?; diff --git a/sdk/src/wallet/account/operations/transaction/high_level/send_native_tokens.rs b/sdk/src/wallet/account/operations/transaction/high_level/send_native_tokens.rs index 758074730e..571344c1a7 100644 --- a/sdk/src/wallet/account/operations/transaction/high_level/send_native_tokens.rs +++ b/sdk/src/wallet/account/operations/transaction/high_level/send_native_tokens.rs @@ -129,7 +129,7 @@ where I::IntoIter: Send, { log::debug!("[TRANSACTION] prepare_send_native_tokens"); - let rent_structure = self.client().get_rent_structure().await?; + let rent_params = self.client().get_rent_parameters().await?; let token_supply = self.client().get_token_supply().await?; let account_addresses = self.addresses().await?; @@ -171,7 +171,7 @@ where // get minimum required amount for such an output, so we don't lock more than required // We have to check it for every output individually, because different address types and amount of // different native tokens require a different storage deposit - let storage_deposit_amount = MinimumStorageDepositBasicOutput::new(rent_structure, token_supply) + let storage_deposit_amount = MinimumStorageDepositBasicOutput::new(rent_params, token_supply) .with_native_tokens(native_tokens.clone()) .with_storage_deposit_return()? .with_expiration()? diff --git a/sdk/src/wallet/account/operations/transaction/mod.rs b/sdk/src/wallet/account/operations/transaction/mod.rs index c3003ef4f8..0e0545827c 100644 --- a/sdk/src/wallet/account/operations/transaction/mod.rs +++ b/sdk/src/wallet/account/operations/transaction/mod.rs @@ -72,7 +72,10 @@ where // Check if the outputs have enough amount to cover the storage deposit for output in &outputs { - output.verify_storage_deposit(protocol_parameters.rent_structure(), protocol_parameters.token_supply())?; + output.verify_storage_deposit( + protocol_parameters.rent_parameters(), + protocol_parameters.token_supply(), + )?; } self.finish_transaction(outputs, options).await diff --git a/sdk/src/wallet/account/operations/transaction/prepare_output.rs b/sdk/src/wallet/account/operations/transaction/prepare_output.rs index 26b4c075fc..bb6af46d4e 100644 --- a/sdk/src/wallet/account/operations/transaction/prepare_output.rs +++ b/sdk/src/wallet/account/operations/transaction/prepare_output.rs @@ -14,8 +14,9 @@ use crate::{ TimelockUnlockCondition, }, BasicOutputBuilder, MinimumStorageDepositBasicOutput, NativeToken, NftId, NftOutputBuilder, Output, - RentStructure, StorageScore, UnlockCondition, + UnlockCondition, }, + rent::{RentParameters, StorageCost}, slot::SlotIndex, Error, }, @@ -46,12 +47,12 @@ where self.client().bech32_hrp_matches(params.recipient_address.hrp()).await?; - let rent_structure = self.client().get_rent_structure().await?; + let rent_params = self.client().get_rent_parameters().await?; let nft_id = params.assets.as_ref().and_then(|a| a.nft_id); let (mut first_output_builder, existing_nft_output_data) = self - .create_initial_output_builder(params.recipient_address, nft_id, rent_structure) + .create_initial_output_builder(params.recipient_address, nft_id, rent_params) .await?; if let Some(assets) = ¶ms.assets { @@ -102,7 +103,7 @@ where // Build output with minimum required storage deposit so we can use the amount in the next step let first_output = first_output_builder - .with_minimum_storage_deposit(rent_structure) + .with_minimum_storage_deposit(rent_params) .finish_output(token_supply)?; let mut second_output_builder = if nft_id.is_some() { @@ -112,9 +113,9 @@ where }; let min_storage_deposit_basic_output = - MinimumStorageDepositBasicOutput::new(rent_structure, token_supply).finish()?; + MinimumStorageDepositBasicOutput::new(rent_params, token_supply).finish()?; - let min_required_storage_deposit = first_output.storage_score(rent_structure); + let min_required_storage_deposit = first_output.storage_cost(rent_params); if params.amount > min_required_storage_deposit { second_output_builder = second_output_builder.with_amount(params.amount); @@ -146,7 +147,7 @@ where // need to check the min required storage deposit again let min_storage_deposit_new_amount = second_output_builder .clone() - .with_minimum_storage_deposit(rent_structure) + .with_minimum_storage_deposit(rent_params) .finish_output(token_supply)? .amount(); @@ -176,7 +177,7 @@ where // If we're sending an existing NFT, its minimum required storage deposit is not part of the available base_coin // balance, so we add it here if let Some(existing_nft_output_data) = existing_nft_output_data { - available_base_coin += existing_nft_output_data.output.storage_score(rent_structure); + available_base_coin += existing_nft_output_data.output.storage_cost(rent_params); } if final_amount > available_base_coin { @@ -236,16 +237,13 @@ where &self, recipient_address: Bech32Address, nft_id: Option, - rent_structure: RentStructure, + rent_params: RentParameters, ) -> crate::wallet::Result<(OutputBuilder, Option)> { let (mut first_output_builder, existing_nft_output_data) = if let Some(nft_id) = &nft_id { if nft_id.is_null() { // Mint a new NFT output ( - OutputBuilder::Nft(NftOutputBuilder::new_with_minimum_storage_deposit( - rent_structure, - *nft_id, - )), + OutputBuilder::Nft(NftOutputBuilder::new_with_minimum_storage_deposit(rent_params, *nft_id)), None, ) } else { @@ -266,7 +264,7 @@ where } } else { ( - OutputBuilder::Basic(BasicOutputBuilder::new_with_minimum_storage_deposit(rent_structure)), + OutputBuilder::Basic(BasicOutputBuilder::new_with_minimum_storage_deposit(rent_params)), None, ) }; @@ -429,13 +427,13 @@ impl OutputBuilder { } self } - fn with_minimum_storage_deposit(mut self, rent_structure: RentStructure) -> Self { + fn with_minimum_storage_deposit(mut self, rent_params: RentParameters) -> Self { match self { Self::Basic(b) => { - self = Self::Basic(b.with_minimum_storage_deposit(rent_structure)); + self = Self::Basic(b.with_minimum_storage_deposit(rent_params)); } Self::Nft(b) => { - self = Self::Nft(b.with_minimum_storage_deposit(rent_structure)); + self = Self::Nft(b.with_minimum_storage_deposit(rent_params)); } } self diff --git a/sdk/src/wallet/account/operations/transaction/prepare_transaction.rs b/sdk/src/wallet/account/operations/transaction/prepare_transaction.rs index 659fbb028d..d493791b05 100644 --- a/sdk/src/wallet/account/operations/transaction/prepare_transaction.rs +++ b/sdk/src/wallet/account/operations/transaction/prepare_transaction.rs @@ -34,12 +34,12 @@ where let options = options.into(); let outputs = outputs.into(); let prepare_transaction_start_time = Instant::now(); - let rent_structure = self.client().get_rent_structure().await?; + let rent_params = self.client().get_rent_parameters().await?; let token_supply = self.client().get_token_supply().await?; // Check if the outputs have enough amount to cover the storage deposit for output in &outputs { - output.verify_storage_deposit(rent_structure, token_supply)?; + output.verify_storage_deposit(rent_params, token_supply)?; } let is_burn_present = options.as_ref().map(|options| options.burn.is_some()).unwrap_or(false); diff --git a/sdk/tests/client/input_selection/account_outputs.rs b/sdk/tests/client/input_selection/account_outputs.rs index 9e1ce84ec2..d9fcb96d05 100644 --- a/sdk/tests/client/input_selection/account_outputs.rs +++ b/sdk/tests/client/input_selection/account_outputs.rs @@ -2313,7 +2313,7 @@ fn new_state_metadata() { let account_id_1 = AccountId::from_str(ACCOUNT_ID_1).unwrap(); let account_output = - AccountOutputBuilder::new_with_minimum_storage_deposit(protocol_parameters.rent_structure(), account_id_1) + AccountOutputBuilder::new_with_minimum_storage_deposit(protocol_parameters.rent_parameters(), account_id_1) .with_state_metadata([1, 2, 3]) .add_unlock_condition(StateControllerAddressUnlockCondition::new( Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), @@ -2332,7 +2332,7 @@ fn new_state_metadata() { // New account output, with updated state index let updated_account_output = AccountOutputBuilder::from(account_output.as_account()) - .with_minimum_storage_deposit(protocol_parameters.rent_structure()) + .with_minimum_storage_deposit(protocol_parameters.rent_parameters()) .with_state_metadata([3, 4, 5]) .with_state_index(account_output.as_account().state_index() + 1) .finish_output(protocol_parameters.token_supply()) @@ -2359,7 +2359,7 @@ fn new_state_metadata_but_same_state_index() { let account_id_1 = AccountId::from_str(ACCOUNT_ID_1).unwrap(); let account_output = - AccountOutputBuilder::new_with_minimum_storage_deposit(protocol_parameters.rent_structure(), account_id_1) + AccountOutputBuilder::new_with_minimum_storage_deposit(protocol_parameters.rent_parameters(), account_id_1) .with_state_metadata([1, 2, 3]) .add_unlock_condition(StateControllerAddressUnlockCondition::new( Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), @@ -2378,7 +2378,7 @@ fn new_state_metadata_but_same_state_index() { // New account output, without updated state index let updated_account_output = AccountOutputBuilder::from(account_output.as_account()) - .with_minimum_storage_deposit(protocol_parameters.rent_structure()) + .with_minimum_storage_deposit(protocol_parameters.rent_parameters()) .with_state_metadata([3, 4, 5]) .finish_output(protocol_parameters.token_supply()) .unwrap(); diff --git a/sdk/tests/client/input_selection/nft_outputs.rs b/sdk/tests/client/input_selection/nft_outputs.rs index 7033ed3f14..fa5a9b6cae 100644 --- a/sdk/tests/client/input_selection/nft_outputs.rs +++ b/sdk/tests/client/input_selection/nft_outputs.rs @@ -1197,13 +1197,14 @@ fn changed_immutable_metadata() { #[cfg(not(feature = "irc_27"))] let metadata = [1, 2, 3]; - let nft_output = NftOutputBuilder::new_with_minimum_storage_deposit(protocol_parameters.rent_structure(), nft_id_1) - .with_immutable_features(MetadataFeature::try_from(metadata)) - .add_unlock_condition(AddressUnlockCondition::new( - Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), - )) - .finish_output(protocol_parameters.token_supply()) - .unwrap(); + let nft_output = + NftOutputBuilder::new_with_minimum_storage_deposit(protocol_parameters.rent_parameters(), nft_id_1) + .with_immutable_features(MetadataFeature::try_from(metadata)) + .add_unlock_condition(AddressUnlockCondition::new( + Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), + )) + .finish_output(protocol_parameters.token_supply()) + .unwrap(); let inputs = [InputSigningData { output: nft_output.clone(), @@ -1223,7 +1224,7 @@ fn changed_immutable_metadata() { // New nft output with changed immutable metadata feature let updated_nft_output = NftOutputBuilder::from(nft_output.as_nft()) - .with_minimum_storage_deposit(protocol_parameters.rent_structure()) + .with_minimum_storage_deposit(protocol_parameters.rent_parameters()) .with_immutable_features(MetadataFeature::try_from(metadata)) .finish_output(protocol_parameters.token_supply()) .unwrap(); diff --git a/sdk/tests/client/node_api/indexer.rs b/sdk/tests/client/node_api/indexer.rs index c1c221e625..4914476f73 100644 --- a/sdk/tests/client/node_api/indexer.rs +++ b/sdk/tests/client/node_api/indexer.rs @@ -32,7 +32,7 @@ // .await?[0]; // let alias_output = -// AliasOutputBuilder::new_with_minimum_storage_deposit(*protocol_parameters.rent_structure(), AliasId::null()) +// AliasOutputBuilder::new_with_minimum_storage_deposit(*protocol_parameters.rent_parameters(), AliasId::null()) // .with_state_metadata([1, 2, 3]) // .add_unlock_condition(StateControllerAddressUnlockCondition::new(address)) // .add_unlock_condition(GovernorAddressUnlockCondition::new(address)) @@ -64,7 +64,7 @@ // .await?[0]; // let nft_output = -// NftOutputBuilder::new_with_minimum_storage_deposit(*protocol_parameters.rent_structure(), NftId::null()) +// NftOutputBuilder::new_with_minimum_storage_deposit(*protocol_parameters.rent_parameters(), NftId::null()) // .with_unlock_conditions([UnlockCondition::Address(AddressUnlockCondition::new(address))]) // .finish_output(protocol_parameters.token_supply())?; @@ -94,7 +94,7 @@ // .await?[0]; // let alias_output_0 = -// AliasOutputBuilder::new_with_minimum_storage_deposit(*protocol_parameters.rent_structure(), AliasId::null()) +// AliasOutputBuilder::new_with_minimum_storage_deposit(*protocol_parameters.rent_parameters(), AliasId::null()) // .with_state_metadata([1, 2, 3]) // .add_unlock_condition(StateControllerAddressUnlockCondition::new(address)) // .add_unlock_condition(GovernorAddressUnlockCondition::new(address)) @@ -122,7 +122,7 @@ // ); // let foundry_output = FoundryOutputBuilder::new_with_minimum_storage_deposit( -// *protocol_parameters.rent_structure(), +// *protocol_parameters.rent_parameters(), // alias_output_0.as_alias().foundry_counter() + 1, // TokenScheme::Simple(SimpleTokenScheme::new(100, 0, 500)?), // ) diff --git a/sdk/tests/types/block_id.rs b/sdk/tests/types/block_id.rs index 5e21824713..9ab5e30dca 100644 --- a/sdk/tests/types/block_id.rs +++ b/sdk/tests/types/block_id.rs @@ -5,7 +5,7 @@ use core::str::FromStr; use iota_sdk::types::{ block::{ - output::RentStructure, protocol::ProtocolParameters, rand::bytes::rand_bytes_array, BlockHash, BlockId, + protocol::ProtocolParameters, rand::bytes::rand_bytes_array, rent::RentParameters, BlockHash, BlockId, BlockWrapper, BlockWrapperDto, }, TryFromDto, @@ -68,7 +68,7 @@ fn memory_layout() { } fn protocol_parameters() -> ProtocolParameters { - ProtocolParameters::new(3, "test", "rms", RentStructure::default(), 0, 1695275822, 10, 0).unwrap() + ProtocolParameters::new(3, "test", "rms", RentParameters::default(), 0, 1695275822, 10, 0).unwrap() } #[test] diff --git a/sdk/tests/types/output/account.rs b/sdk/tests/types/output/account.rs index a58f9cb0a1..da57929d40 100644 --- a/sdk/tests/types/output/account.rs +++ b/sdk/tests/types/output/account.rs @@ -4,7 +4,7 @@ use iota_sdk::types::{ block::{ address::AccountAddress, - output::{AccountOutput, Feature, FoundryId, NativeToken, Output, SimpleTokenScheme, StorageScore, TokenId}, + output::{AccountOutput, Feature, FoundryId, NativeToken, Output, SimpleTokenScheme, TokenId}, protocol::protocol_parameters, rand::output::{ feature::{rand_issuer_feature, rand_metadata_feature, rand_sender_feature}, @@ -14,6 +14,7 @@ use iota_sdk::types::{ rand_state_controller_address_unlock_condition_different_from, }, }, + rent::StorageCost, }, ValidationParams, }; @@ -71,7 +72,7 @@ fn builder() { let metadata = rand_metadata_feature(); let output = builder - .with_minimum_storage_deposit(protocol_parameters.rent_structure()) + .with_minimum_storage_deposit(protocol_parameters.rent_parameters()) .add_unlock_condition(rand_state_controller_address_unlock_condition_different_from( &account_id, )) @@ -83,7 +84,7 @@ fn builder() { assert_eq!( output.amount(), - Output::Account(output.clone()).storage_score(protocol_parameters.rent_structure()) + Output::Account(output.clone()).storage_cost(protocol_parameters.rent_parameters()) ); assert_eq!(output.features().metadata(), Some(&metadata)); assert_eq!(output.features().sender(), Some(&sender_1)); diff --git a/sdk/tests/types/output/basic.rs b/sdk/tests/types/output/basic.rs index 8b897e9b53..7511b44410 100644 --- a/sdk/tests/types/output/basic.rs +++ b/sdk/tests/types/output/basic.rs @@ -3,7 +3,7 @@ use iota_sdk::types::{ block::{ - output::{BasicOutput, Feature, FoundryId, NativeToken, Output, SimpleTokenScheme, StorageScore, TokenId}, + output::{BasicOutput, Feature, FoundryId, NativeToken, Output, SimpleTokenScheme, TokenId}, protocol::protocol_parameters, rand::{ address::rand_account_address, @@ -13,6 +13,7 @@ use iota_sdk::types::{ unlock_condition::rand_address_unlock_condition, }, }, + rent::StorageCost, }, ValidationParams, }; @@ -50,7 +51,7 @@ fn builder() { let metadata = rand_metadata_feature(); let output = builder - .with_minimum_storage_deposit(protocol_parameters.rent_structure()) + .with_minimum_storage_deposit(protocol_parameters.rent_parameters()) .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())) @@ -58,7 +59,7 @@ fn builder() { assert_eq!( output.amount(), - Output::Basic(output.clone()).storage_score(protocol_parameters.rent_structure()) + Output::Basic(output.clone()).storage_cost(protocol_parameters.rent_parameters()) ); assert_eq!(output.features().metadata(), Some(&metadata)); assert_eq!(output.features().sender(), Some(&sender_1)); diff --git a/sdk/tests/types/output/foundry.rs b/sdk/tests/types/output/foundry.rs index a620b664b6..e5e5bba55b 100644 --- a/sdk/tests/types/output/foundry.rs +++ b/sdk/tests/types/output/foundry.rs @@ -4,13 +4,14 @@ use iota_sdk::types::block::{ output::{ unlock_condition::ImmutableAccountAddressUnlockCondition, FoundryId, FoundryOutput, NativeToken, Output, - SimpleTokenScheme, StorageScore, TokenId, + SimpleTokenScheme, TokenId, }, protocol::protocol_parameters, rand::{ address::rand_account_address, output::{feature::rand_metadata_feature, rand_foundry_output, rand_token_scheme}, }, + rent::StorageCost, }; use packable::PackableExt; @@ -51,14 +52,14 @@ fn builder() { assert!(output.immutable_features().is_empty()); let output = builder - .with_minimum_storage_deposit(protocol_parameters.rent_structure()) + .with_minimum_storage_deposit(protocol_parameters.rent_parameters()) .add_unlock_condition(ImmutableAccountAddressUnlockCondition::new(rand_account_address())) .finish_with_params(&protocol_parameters) .unwrap(); assert_eq!( output.amount(), - Output::Foundry(output).storage_score(protocol_parameters.rent_structure()) + Output::Foundry(output).storage_cost(protocol_parameters.rent_parameters()) ); } diff --git a/sdk/tests/types/output/nft.rs b/sdk/tests/types/output/nft.rs index 4485176a36..8c742cc4ae 100644 --- a/sdk/tests/types/output/nft.rs +++ b/sdk/tests/types/output/nft.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use iota_sdk::types::block::{ - output::{FoundryId, NativeToken, NftId, NftOutput, Output, SimpleTokenScheme, StorageScore, TokenId}, + output::{FoundryId, NativeToken, NftId, NftOutput, Output, SimpleTokenScheme, TokenId}, protocol::protocol_parameters, rand::{ address::rand_account_address, @@ -12,6 +12,7 @@ use iota_sdk::types::block::{ unlock_condition::rand_address_unlock_condition, }, }, + rent::StorageCost, }; use packable::PackableExt; @@ -52,14 +53,14 @@ fn builder() { assert!(output.immutable_features().is_empty()); let output = builder - .with_minimum_storage_deposit(protocol_parameters.rent_structure()) + .with_minimum_storage_deposit(protocol_parameters.rent_parameters()) .add_unlock_condition(rand_address_unlock_condition()) .finish_with_params(protocol_parameters.token_supply()) .unwrap(); assert_eq!( output.amount(), - Output::Nft(output).storage_score(protocol_parameters.rent_structure()) + Output::Nft(output).storage_cost(protocol_parameters.rent_parameters()) ); } diff --git a/sdk/tests/types/protocol.rs b/sdk/tests/types/protocol.rs index 5ec2528475..d612358ecf 100644 --- a/sdk/tests/types/protocol.rs +++ b/sdk/tests/types/protocol.rs @@ -14,13 +14,13 @@ // "version":3, // "networkName":"TestJungle", // "bech32Hrp":"tgl", -// "rentStructure":{ -// "vByteCost":0, -// "vByteFactorData":0, -// "vByteFactorKey":0, -// "vByteFactorBlockIssuerKey":0, -// "vByteFactorStakingFeature":0, -// "vByteFactorDelegation":0 +// "rentParameters":{ +// "storageCost":0, +// "storageScoreFactorData":0, +// "storageScoreOffsetOutput":0, +// "storageScoreOffsetEd25519BlockIssuerKey":0, +// "storageScoreOffsetStakingFeature":0, +// "storageScoreOffsetDelegation":0 // }, // "workScoreStructure":{ // "dataByte":0, diff --git a/sdk/tests/types/rent.rs b/sdk/tests/types/rent.rs index 9f8bb6d9b2..9bffe8047d 100644 --- a/sdk/tests/types/rent.rs +++ b/sdk/tests/types/rent.rs @@ -11,7 +11,7 @@ // fn output_in_range(output: Output, range: std::ops::RangeInclusive) { // let cost = output.storage_score(Default::default()); -// assert!(range.contains(&cost), "{output:#?} has a required byte cost of {cost}"); +// assert!(range.contains(&cost), "{output:#?} has a required storage cost of {cost}"); // } // #[test] diff --git a/sdk/tests/wallet/claim_outputs.rs b/sdk/tests/wallet/claim_outputs.rs index 706811796e..f72f74dd60 100644 --- a/sdk/tests/wallet/claim_outputs.rs +++ b/sdk/tests/wallet/claim_outputs.rs @@ -127,11 +127,11 @@ async fn claim_2_basic_outputs_no_outputs_in_claim_account() -> Result<()> { let account_1 = wallet.create_account().finish().await?; let token_supply = account_0.client().get_token_supply().await?; - let rent_structure = account_0.client().get_rent_structure().await?; + let rent_params = account_0.client().get_rent_parameters().await?; // TODO more fitting value let expiration_slot = account_0.client().get_slot_index().await? + 86400; - let output = BasicOutputBuilder::new_with_minimum_storage_deposit(rent_structure) + let output = BasicOutputBuilder::new_with_minimum_storage_deposit(rent_params) .add_unlock_condition(AddressUnlockCondition::new( *account_1.addresses().await?[0].address().as_ref(), )) @@ -322,13 +322,13 @@ async fn claim_2_native_tokens_no_outputs_in_claim_account() -> Result<()> { .await?; account_0.sync(None).await?; - let rent_structure = account_0.client().get_rent_structure().await?; + let rent_params = account_0.client().get_rent_parameters().await?; let token_supply = account_0.client().get_token_supply().await?; let tx = account_0 .send_outputs( [ - BasicOutputBuilder::new_with_minimum_storage_deposit(rent_structure) + BasicOutputBuilder::new_with_minimum_storage_deposit(rent_params) .add_unlock_condition(AddressUnlockCondition::new( *account_1.addresses().await?[0].address().as_ref(), )) @@ -338,7 +338,7 @@ async fn claim_2_native_tokens_no_outputs_in_claim_account() -> Result<()> { )?) .add_native_token(NativeToken::new(create_tx_0.token_id, native_token_amount)?) .finish_output(token_supply)?, - BasicOutputBuilder::new_with_minimum_storage_deposit(rent_structure) + BasicOutputBuilder::new_with_minimum_storage_deposit(rent_params) .add_unlock_condition(AddressUnlockCondition::new( *account_1.addresses().await?[0].address().as_ref(), )) diff --git a/sdk/tests/wallet/output_preparation.rs b/sdk/tests/wallet/output_preparation.rs index f032b59923..dfe2bc4afa 100644 --- a/sdk/tests/wallet/output_preparation.rs +++ b/sdk/tests/wallet/output_preparation.rs @@ -6,7 +6,8 @@ use std::str::FromStr; use iota_sdk::{ types::block::{ address::{Address, Bech32Address, ToBech32Ext}, - output::{MinimumStorageDepositBasicOutput, NativeToken, NftId, Output, StorageScore, TokenId}, + output::{MinimumStorageDepositBasicOutput, NativeToken, NftId, Output, TokenId}, + rent::StorageCost, slot::SlotIndex, }, wallet::{ @@ -408,8 +409,8 @@ async fn output_preparation() -> Result<()> { None, ) .await?; - let rent_structure = wallet.client().get_rent_structure().await?; - let minimum_storage_deposit = output.storage_score(rent_structure); + let rent_params = wallet.client().get_rent_parameters().await?; + let minimum_storage_deposit = output.storage_cost(rent_params); assert_eq!(output.amount(), minimum_storage_deposit); assert_eq!(output.amount(), 187900); let sdr = output.unlock_conditions().unwrap().storage_deposit_return().unwrap(); @@ -432,7 +433,7 @@ async fn output_preparation_sdr() -> Result<()> { let wallet = make_wallet(storage_path, None, None).await?; let account = &create_accounts_with_funds(&wallet, 1).await?[0]; - let rent_structure = account.client().get_rent_structure().await?; + let rent_params = account.client().get_rent_parameters().await?; let token_supply = account.client().get_token_supply().await?; let recipient_address_bech32 = String::from("rms1qpszqzadsym6wpppd6z037dvlejmjuke7s24hm95s9fg9vpua7vluaw60xu"); @@ -454,7 +455,7 @@ async fn output_preparation_sdr() -> Result<()> { ) .await?; // Check if the output has enough amount to cover the storage deposit - output.verify_storage_deposit(rent_structure, token_supply)?; + output.verify_storage_deposit(rent_params, token_supply)?; assert_eq!(output.amount(), 50601); // address and sdr unlock condition assert_eq!(output.unlock_conditions().unwrap().len(), 2); @@ -475,7 +476,7 @@ async fn output_preparation_sdr() -> Result<()> { ) .await?; // Check if the output has enough amount to cover the storage deposit - output.verify_storage_deposit(rent_structure, token_supply)?; + output.verify_storage_deposit(rent_params, token_supply)?; assert_eq!(output.amount(), 85199); // address and sdr unlock condition assert_eq!(output.unlock_conditions().unwrap().len(), 2); @@ -500,7 +501,7 @@ async fn output_preparation_sdr() -> Result<()> { ) .await?; // Check if the output has enough amount to cover the storage deposit - output.verify_storage_deposit(rent_structure, token_supply)?; + output.verify_storage_deposit(rent_params, token_supply)?; assert_eq!(output.amount(), 85199); // address and sdr unlock condition assert_eq!(output.unlock_conditions().unwrap().len(), 2); @@ -525,7 +526,7 @@ async fn output_preparation_sdr() -> Result<()> { ) .await?; // Check if the output has enough amount to cover the storage deposit - output.verify_storage_deposit(rent_structure, token_supply)?; + output.verify_storage_deposit(rent_params, token_supply)?; // The additional 1 amount will be added, because the storage deposit should be gifted and not returned assert_eq!(output.amount(), 42600); // storage deposit gifted, only address unlock condition @@ -613,12 +614,11 @@ async fn prepare_output_remainder_dust() -> Result<()> { let addresses = &accounts[1].addresses().await?; let address = addresses[0].address(); - let rent_structure = account.client().get_rent_structure().await?; + let rent_params = account.client().get_rent_parameters().await?; let token_supply = account.client().get_token_supply().await?; let balance = account.sync(None).await?; - let minimum_required_storage_deposit = - MinimumStorageDepositBasicOutput::new(rent_structure, token_supply).finish()?; + let minimum_required_storage_deposit = MinimumStorageDepositBasicOutput::new(rent_params, token_supply).finish()?; // Send away most balance so we can test with leaving dust let output = account @@ -659,7 +659,7 @@ async fn prepare_output_remainder_dust() -> Result<()> { .await?; // Check if the output has enough amount to cover the storage deposit - output.verify_storage_deposit(rent_structure, token_supply)?; + output.verify_storage_deposit(rent_params, token_supply)?; // The left over 21299 is too small to keep, so we donate it assert_eq!(output.amount(), balance.base_coin().available()); // storage deposit gifted, only address unlock condition @@ -703,7 +703,7 @@ async fn prepare_output_remainder_dust() -> Result<()> { .await?; // Check if the output has enough amount to cover the storage deposit - output.verify_storage_deposit(rent_structure, token_supply)?; + output.verify_storage_deposit(rent_params, token_supply)?; // We use excess if leftover is too small, so amount == all available balance assert_eq!(output.amount(), 63900); // storage deposit gifted, only address unlock condition @@ -727,7 +727,7 @@ async fn prepare_output_remainder_dust() -> Result<()> { .await?; // Check if the output has enough amount to cover the storage deposit - output.verify_storage_deposit(rent_structure, token_supply)?; + output.verify_storage_deposit(rent_params, token_supply)?; // We use excess if leftover is too small, so amount == all available balance assert_eq!(output.amount(), 63900); // storage deposit returned, address and SDR unlock condition @@ -849,8 +849,8 @@ async fn prepare_existing_nft_output_gift() -> Result<()> { .as_nft() .clone(); - let rent_structure = wallet.client().get_rent_structure().await?; - let minimum_storage_deposit = Output::Nft(nft.clone()).storage_score(rent_structure); + let rent_params = wallet.client().get_rent_parameters().await?; + let minimum_storage_deposit = Output::Nft(nft.clone()).storage_cost(rent_params); assert_eq!(nft.amount(), minimum_storage_deposit); assert_eq!(nft.amount(), 52300);