diff --git a/bindings/core/src/method_handler/client.rs b/bindings/core/src/method_handler/client.rs index e49965ea7b..89be3ec625 100644 --- a/bindings/core/src/method_handler/client.rs +++ b/bindings/core/src/method_handler/client.rs @@ -12,7 +12,7 @@ use iota_sdk::{ dto::OutputDto, AccountOutput, BasicOutput, FoundryOutput, NftOutput, Output, OutputBuilderAmount, }, payload::Payload, - rent::StorageScore, + rent::{RentStructure, StorageScore}, 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_parameters().await?) + OutputBuilderAmount::MinimumStorageDeposit(client.get_rent_parameters().await?.into()) }, 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_parameters().await?) + OutputBuilderAmount::MinimumStorageDeposit(client.get_rent_parameters().await?.into()) }, 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_parameters().await?) + OutputBuilderAmount::MinimumStorageDeposit(client.get_rent_parameters().await?.into()) }, 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_parameters().await?) + OutputBuilderAmount::MinimumStorageDeposit(client.get_rent_parameters().await?.into()) }, mana, native_tokens, @@ -296,9 +296,8 @@ 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_params = client.get_rent_parameters().await?; - - let minimum_storage_deposit = output.rent_cost(rent_params); + let rent_struct = client.get_rent_parameters().await?.into(); + let minimum_storage_deposit = output.rent_cost(rent_struct); 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 5668e7399f..0ee053e16b 100644 --- a/bindings/core/src/method_handler/utils.rs +++ b/bindings/core/src/method_handler/utils.rs @@ -81,7 +81,7 @@ pub(crate) fn call_utils_method_internal(method: UtilsMethod) -> Result { let out = Output::try_from_dto(output)?; - Response::MinimumRequiredStorageDeposit(out.rent_cost(rent).to_string()) + Response::MinimumRequiredStorageDeposit(out.rent_cost(rent.into()).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 c2ca40a0ef..06f70dee86 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_params = account.client().get_rent_parameters().await?; + let rent_struct = account.client().get_rent_parameters().await?.into(); 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_params) + let outputs = [BasicOutputBuilder::new_with_minimum_storage_deposit(rent_struct) .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 c8c5fee712..986f65e519 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_params = client.get_rent_parameters().await?; + let rent_struct = client.get_rent_parameters().await?.into(); 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_params, AccountId::null()) + let account_output = AccountOutputBuilder::new_with_minimum_storage_deposit(rent_struct, 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 5c9cf7bc0e..b2a86f1c3b 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_params = client.get_rent_parameters().await?; + let rent_struct = client.get_rent_parameters().await?.into(); 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_params, NftId::null()) + let nft_output = NftOutputBuilder::new_with_minimum_storage_deposit(rent_struct, 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 2d945ddead..bbc201f5fc 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_params = account.client().get_rent_parameters().await?; + let rent_struct = account.client().get_rent_parameters().await?.into(); 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_params) + .with_minimum_storage_deposit(rent_struct) .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 71d45c5d60..f80599dca0 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_params = client.get_rent_parameters().await?; + let rent_struct = client.get_rent_parameters().await?.into(); let address = Address::try_from_bech32("rms1qpllaj0pyveqfkwxmnngz2c488hfdtmfrj3wfkgxtk4gtyrax0jaxzt70zy")?; - let nft_output_builder = NftOutputBuilder::new_with_minimum_storage_deposit(rent_params, NftId::null()) + let nft_output_builder = NftOutputBuilder::new_with_minimum_storage_deposit(rent_struct, 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 7bd6723994..e5ee0cf8a3 100644 --- a/sdk/examples/how_tos/outputs/unlock_conditions.rs +++ b/sdk/examples/how_tos/outputs/unlock_conditions.rs @@ -35,17 +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_params = client.get_rent_parameters().await?; + let rent_struct = client.get_rent_parameters().await?.into(); 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_params) + let basic_output_builder = BasicOutputBuilder::new_with_minimum_storage_deposit(rent_struct) .add_unlock_condition(AddressUnlockCondition::new(address)); - 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 account_output_builder = AccountOutputBuilder::new_with_minimum_storage_deposit(rent_struct, AccountId::null()); + let foundry_output_builder = FoundryOutputBuilder::new_with_minimum_storage_deposit(rent_struct, 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 1eb84aca1b..194c881192 100644 --- a/sdk/src/client/api/block_builder/input_selection/remainder.rs +++ b/sdk/src/client/api/block_builder/input_selection/remainder.rs @@ -16,6 +16,7 @@ use crate::{ types::block::{ address::{Address, Ed25519Address}, output::{unlock_condition::AddressUnlockCondition, BasicOutputBuilder, NativeTokensBuilder, Output}, + rent::RentStructure, }, }; @@ -65,7 +66,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_parameters()) + BasicOutputBuilder::new_with_minimum_storage_deposit(self.protocol_parameters.rent_parameters().into()) .add_unlock_condition(AddressUnlockCondition::new(Address::from(Ed25519Address::from( [0; 32], )))); @@ -144,7 +145,7 @@ impl InputSelection { log::debug!("Created remainder output of {diff} for {remainder_address:?}"); remainder.verify_storage_deposit( - self.protocol_parameters.rent_parameters(), + self.protocol_parameters.rent_parameters().into(), 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 da937b21c6..881c759936 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 @@ -13,7 +13,7 @@ use crate::{ unlock_condition::StorageDepositReturnUnlockCondition, AccountOutputBuilder, AccountTransition, FoundryOutputBuilder, NftOutputBuilder, Output, OutputId, }, - rent::StorageScore, + rent::{RentStructure, StorageScore}, slot::SlotIndex, }, }; @@ -241,7 +241,7 @@ impl InputSelection { for output in outputs { let diff = amount_selection.missing_amount(); let amount = output.amount(); - let rent = output.rent_cost(self.protocol_parameters.rent_parameters()); + let rent = output.rent_cost(self.protocol_parameters.rent_parameters().into()); let new_amount = if amount >= diff + rent { amount - diff } else { rent }; diff --git a/sdk/src/types/block/address/account.rs b/sdk/src/types/block/address/account.rs index 2406576371..e8b5c35b0d 100644 --- a/sdk/src/types/block/address/account.rs +++ b/sdk/src/types/block/address/account.rs @@ -5,7 +5,11 @@ use core::str::FromStr; use derive_more::{AsRef, Deref, From}; -use crate::types::block::{output::AccountId, Error}; +use crate::types::block::{ + output::AccountId, + rent::{RentStructure, StorageScore}, + Error, +}; /// An account address. #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, From, AsRef, Deref, packable::Packable)] @@ -57,6 +61,12 @@ impl core::fmt::Debug for AccountAddress { } } +impl StorageScore for AccountAddress { + fn score(&self, _rent_struct: RentStructure) -> u64 { + 0 + } +} + #[cfg(feature = "serde")] mod dto { use serde::{Deserialize, Serialize}; diff --git a/sdk/src/types/block/address/ed25519.rs b/sdk/src/types/block/address/ed25519.rs index 2adc90ed19..b63e070515 100644 --- a/sdk/src/types/block/address/ed25519.rs +++ b/sdk/src/types/block/address/ed25519.rs @@ -6,7 +6,10 @@ use core::str::FromStr; use crypto::signatures::ed25519::PublicKey; use derive_more::{AsRef, Deref, From}; -use crate::types::block::Error; +use crate::types::block::{ + rent::{RentStructure, StorageScore}, + Error, +}; /// An Ed25519 address. #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, From, AsRef, Deref, packable::Packable)] @@ -24,6 +27,11 @@ impl Ed25519Address { pub fn new(address: [u8; Self::LENGTH]) -> Self { Self::from(address) } + + /// Creates a dummy [`Ed25519Address`] used to calculate a storage score for implicit account addresses. + pub(crate) fn dummy() -> Self { + Self([0; Self::LENGTH]) + } } impl FromStr for Ed25519Address { @@ -46,6 +54,12 @@ impl core::fmt::Debug for Ed25519Address { } } +impl StorageScore for Ed25519Address { + fn score(&self, _rent_struct: RentStructure) -> u64 { + 0 + } +} + #[cfg(feature = "serde")] pub(crate) mod dto { use serde::{Deserialize, Serialize}; diff --git a/sdk/src/types/block/address/mod.rs b/sdk/src/types/block/address/mod.rs index ba72388413..d82e23b60e 100644 --- a/sdk/src/types/block/address/mod.rs +++ b/sdk/src/types/block/address/mod.rs @@ -215,19 +215,13 @@ impl From<&Self> for Address { } } -/// 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 { +impl StorageScore for Address { + fn score(&self, rent_struct: 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 + Self::Account(account) => account.score(rent_struct), + Self::Ed25519(ed25519) => ed25519.score(rent_struct), + Self::Nft(nft) => nft.score(rent_struct), + // TODO: other address types once merged } } } diff --git a/sdk/src/types/block/address/nft.rs b/sdk/src/types/block/address/nft.rs index 93ca2ac0a8..10f622be7e 100644 --- a/sdk/src/types/block/address/nft.rs +++ b/sdk/src/types/block/address/nft.rs @@ -5,7 +5,11 @@ use core::str::FromStr; use derive_more::{AsRef, Deref, From}; -use crate::types::block::{output::NftId, Error}; +use crate::types::block::{ + output::NftId, + rent::{RentStructure, StorageScore}, + Error, +}; /// An NFT address. #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, From, AsRef, Deref, packable::Packable)] @@ -57,6 +61,12 @@ impl core::fmt::Debug for NftAddress { } } +impl StorageScore for NftAddress { + fn score(&self, _rent_struct: RentStructure) -> u64 { + 0 + } +} + #[cfg(feature = "serde")] pub(crate) mod dto { use serde::{Deserialize, Serialize}; diff --git a/sdk/src/types/block/output/account.rs b/sdk/src/types/block/output/account.rs index 9bff1abb0d..af3578f733 100644 --- a/sdk/src/types/block/output/account.rs +++ b/sdk/src/types/block/output/account.rs @@ -13,9 +13,14 @@ use packable::{ Packable, PackableExt, }; +use super::{ + feature::{BlockIssuerFeature, BlockIssuerKey, BlockIssuerKeys, Ed25519BlockIssuerKey}, + storage_score_offset_output, + unlock_condition::{GovernorAddressUnlockCondition, StateControllerAddressUnlockCondition}, +}; use crate::types::{ block::{ - address::{AccountAddress, Address}, + address::{AccountAddress, Address, Ed25519Address}, output::{ feature::{verify_allowed_features, Feature, FeatureFlags, Features}, unlock_condition::{ @@ -25,7 +30,7 @@ use crate::types::{ NativeTokens, Output, OutputBuilderAmount, OutputId, StateTransitionError, StateTransitionVerifier, }, protocol::ProtocolParameters, - rent::{RentParameters, StorageScore}, + rent::{RentParameters, RentStructure, StorageScore}, semantic::{TransactionFailureReason, ValidationContext}, unlock::Unlock, Error, @@ -33,8 +38,6 @@ use crate::types::{ ValidationParams, }; -use super::storage_score_offset_output; - impl_id!(pub AccountId, 32, "Unique identifier of an account, which is the BLAKE2b-256 hash of the Output ID that created it."); #[cfg(feature = "serde")] @@ -114,8 +117,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_params: RentParameters, account_id: AccountId) -> Self { - Self::new(OutputBuilderAmount::MinimumStorageDeposit(rent_params), account_id) + pub fn new_with_minimum_storage_deposit(rent_struct: RentStructure, account_id: AccountId) -> Self { + Self::new(OutputBuilderAmount::MinimumStorageDeposit(rent_struct), account_id) } fn new(amount: OutputBuilderAmount, account_id: AccountId) -> Self { @@ -142,8 +145,8 @@ impl AccountOutputBuilder { /// Sets the amount to the minimum storage deposit. #[inline(always)] - pub fn with_minimum_storage_deposit(mut self, rent_params: RentParameters) -> Self { - self.amount = OutputBuilderAmount::MinimumStorageDeposit(rent_params); + pub fn with_minimum_storage_deposit(mut self, rent_struct: RentStructure) -> Self { + self.amount = OutputBuilderAmount::MinimumStorageDeposit(rent_struct); self } @@ -320,8 +323,8 @@ impl AccountOutputBuilder { output.amount = match self.amount { OutputBuilderAmount::Amount(amount) => amount, - OutputBuilderAmount::MinimumStorageDeposit(rent_params) => { - Output::Account(output.clone()).rent_cost(rent_params) + OutputBuilderAmount::MinimumStorageDeposit(rent_struct) => { + Output::Account(output.clone()).rent_cost(rent_struct) } }; @@ -415,10 +418,10 @@ impl AccountOutput { /// The amount will be set to the minimum storage deposit. #[inline(always)] pub fn build_with_minimum_storage_deposit( - rent_params: RentParameters, + rent_struct: RentStructure, account_id: AccountId, ) -> AccountOutputBuilder { - AccountOutputBuilder::new_with_minimum_storage_deposit(rent_params, account_id) + AccountOutputBuilder::new_with_minimum_storage_deposit(rent_struct, account_id) } /// @@ -618,6 +621,19 @@ impl AccountOutput { Ok(()) } + + /// Creates a dummy [`AccountOutput`] used to calculate a storage score for implicit account addresses. + pub(crate) fn dummy() -> Self { + // Unwrap: cannot fail for provided dummy data. + AccountOutputBuilder::new_with_amount(0, AccountId::null()) + .add_unlock_condition(GovernorAddressUnlockCondition::new(Ed25519Address::dummy())) + .add_unlock_condition(StateControllerAddressUnlockCondition::new(Ed25519Address::dummy())) + .add_feature( + BlockIssuerFeature::new(0, vec![BlockIssuerKey::Ed25519(Ed25519BlockIssuerKey::dummy())]).unwrap(), + ) + .finish() + .unwrap() + } } impl StateTransitionVerifier for AccountOutput { @@ -729,13 +745,13 @@ impl Packable for AccountOutput { } impl StorageScore for AccountOutput { - fn score(&self, rent_params: RentParameters) -> u64 { - storage_score_offset_output(rent_params) - + self.packed_len() as u64 * rent_params.storage_score_factor_data() as u64 - + self.native_tokens().score(rent_params) - + self.unlock_conditions().score(rent_params) - + self.features().score(rent_params) - + self.immutable_features().score(rent_params) + fn score(&self, rent_struct: RentStructure) -> u64 { + storage_score_offset_output(rent_struct) + + self.packed_len() as u64 * rent_struct.storage_score_factor_data() as u64 + + self.native_tokens().score(rent_struct) + + self.unlock_conditions().score(rent_struct) + + self.features().score(rent_struct) + + self.immutable_features().score(rent_struct) } } @@ -869,8 +885,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_params) => { - AccountOutputBuilder::new_with_minimum_storage_deposit(rent_params, *account_id) + OutputBuilderAmount::MinimumStorageDeposit(rent_struct) => { + AccountOutputBuilder::new_with_minimum_storage_deposit(rent_struct, *account_id) } } .with_mana(mana); @@ -991,7 +1007,7 @@ mod tests { test_split_dto(builder); let builder = - AccountOutput::build_with_minimum_storage_deposit(protocol_parameters.rent_parameters(), account_id) + AccountOutput::build_with_minimum_storage_deposit(protocol_parameters.rent_parameters().into(), 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 ef8abec914..ff8de7c29a 100644 --- a/sdk/src/types/block/output/basic.rs +++ b/sdk/src/types/block/output/basic.rs @@ -5,10 +5,10 @@ use alloc::collections::BTreeSet; use packable::{Packable, PackableExt}; -use super::storage_score_offset_output; +use super::{storage_score_offset_output, AddressUnlockCondition}; use crate::types::{ block::{ - address::Address, + address::{Address, Ed25519Address}, output::{ feature::{verify_allowed_features, Feature, FeatureFlags, Features}, unlock_condition::{ @@ -18,7 +18,7 @@ use crate::types::{ NativeTokens, Output, OutputBuilderAmount, OutputId, }, protocol::ProtocolParameters, - rent::{RentParameters, StorageScore}, + rent::{RentParameters, RentStructure, StorageScore}, semantic::{TransactionFailureReason, ValidationContext}, unlock::Unlock, Error, @@ -47,8 +47,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_params: RentParameters) -> Self { - Self::new(OutputBuilderAmount::MinimumStorageDeposit(rent_params)) + pub fn new_with_minimum_storage_deposit(rent_struct: RentStructure) -> Self { + Self::new(OutputBuilderAmount::MinimumStorageDeposit(rent_struct)) } fn new(amount: OutputBuilderAmount) -> Self { @@ -70,8 +70,8 @@ impl BasicOutputBuilder { /// Sets the amount to the minimum storage deposit. #[inline(always)] - pub fn with_minimum_storage_deposit(mut self, rent_params: RentParameters) -> Self { - self.amount = OutputBuilderAmount::MinimumStorageDeposit(rent_params); + pub fn with_minimum_storage_deposit(mut self, rent_struct: RentStructure) -> Self { + self.amount = OutputBuilderAmount::MinimumStorageDeposit(rent_struct); self } @@ -173,8 +173,8 @@ impl BasicOutputBuilder { output.amount = match self.amount { OutputBuilderAmount::Amount(amount) => amount, - OutputBuilderAmount::MinimumStorageDeposit(rent_params) => { - Output::Basic(output.clone()).rent_cost(rent_params) + OutputBuilderAmount::MinimumStorageDeposit(rent_struct) => { + Output::Basic(output.clone()).rent_cost(rent_struct) } }; @@ -255,8 +255,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_params: RentParameters) -> BasicOutputBuilder { - BasicOutputBuilder::new_with_minimum_storage_deposit(rent_params) + pub fn build_with_minimum_storage_deposit(rent_struct: RentStructure) -> BasicOutputBuilder { + BasicOutputBuilder::new_with_minimum_storage_deposit(rent_struct) } /// @@ -323,15 +323,23 @@ impl BasicOutput { None } + + /// Creates a dummy [`BasicOutput`] used to calculate a storage score for implicit account addresses. + pub(crate) fn dummy() -> Self { + BasicOutputBuilder::new_with_amount(0) + .add_unlock_condition(AddressUnlockCondition::new(Ed25519Address::dummy())) + .finish() + .unwrap() + } } impl StorageScore for BasicOutput { - fn score(&self, rent_params: RentParameters) -> u64 { - storage_score_offset_output(rent_params) - + self.packed_len() as u64 * rent_params.storage_score_factor_data() as u64 - + self.native_tokens().score(rent_params) - + self.unlock_conditions().score(rent_params) - + self.features().score(rent_params) + fn score(&self, rent_struct: RentStructure) -> u64 { + storage_score_offset_output(rent_struct) + + self.packed_len() as u64 * rent_struct.storage_score_factor_data() as u64 + + self.native_tokens().score(rent_struct) + + self.unlock_conditions().score(rent_struct) + + self.features().score(rent_struct) } } @@ -440,8 +448,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_params) => { - BasicOutputBuilder::new_with_minimum_storage_deposit(rent_params) + OutputBuilderAmount::MinimumStorageDeposit(rent_struct) => { + BasicOutputBuilder::new_with_minimum_storage_deposit(rent_struct) } } .with_mana(mana); @@ -529,7 +537,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_parameters()) + let builder = BasicOutput::build_with_minimum_storage_deposit(protocol_parameters.rent_parameters().into()) .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 17fb61d5cf..ae2d4f00c6 100644 --- a/sdk/src/types/block/output/delegation.rs +++ b/sdk/src/types/block/output/delegation.rs @@ -10,6 +10,7 @@ use packable::{ Packable, PackableExt, }; +use super::storage_score_offset_output; use crate::types::{ block::{ address::{AccountAddress, Address}, @@ -22,7 +23,7 @@ use crate::types::{ OutputBuilderAmount, OutputId, StateTransitionError, StateTransitionVerifier, }, protocol::ProtocolParameters, - rent::{RentParameters, StorageScore}, + rent::{RentParameters, RentStructure, StorageScore}, semantic::{TransactionFailureReason, ValidationContext}, slot::EpochIndex, unlock::Unlock, @@ -31,8 +32,6 @@ use crate::types::{ ValidationParams, }; -use super::storage_score_offset_output; - impl_id!(pub DelegationId, 32, "Unique identifier of the Delegation Output, which is the BLAKE2b-256 hash of the Output ID that created it."); #[cfg(feature = "serde")] @@ -82,13 +81,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_params: RentParameters, + rent_struct: RentStructure, delegated_amount: u64, delegation_id: DelegationId, validator_address: AccountAddress, ) -> Self { Self::new( - OutputBuilderAmount::MinimumStorageDeposit(rent_params), + OutputBuilderAmount::MinimumStorageDeposit(rent_struct), delegated_amount, delegation_id, validator_address, @@ -119,8 +118,8 @@ impl DelegationOutputBuilder { } /// Sets the amount to the minimum storage deposit. - pub fn with_minimum_storage_deposit(mut self, rent_params: RentParameters) -> Self { - self.amount = OutputBuilderAmount::MinimumStorageDeposit(rent_params); + pub fn with_minimum_storage_deposit(mut self, rent_struct: RentStructure) -> Self { + self.amount = OutputBuilderAmount::MinimumStorageDeposit(rent_struct); self } @@ -197,8 +196,8 @@ impl DelegationOutputBuilder { output.amount = match self.amount { OutputBuilderAmount::Amount(amount) => amount, - OutputBuilderAmount::MinimumStorageDeposit(rent_params) => { - Output::Delegation(output.clone()).rent_cost(rent_params) + OutputBuilderAmount::MinimumStorageDeposit(rent_struct) => { + Output::Delegation(output.clone()).rent_cost(rent_struct) } }; @@ -279,13 +278,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_params: RentParameters, + rent_struct: RentStructure, delegated_amount: u64, delegation_id: DelegationId, validator_address: AccountAddress, ) -> DelegationOutputBuilder { DelegationOutputBuilder::new_with_minimum_storage_deposit( - rent_params, + rent_struct, delegated_amount, delegation_id, validator_address, @@ -459,11 +458,11 @@ impl Packable for DelegationOutput { } impl StorageScore for DelegationOutput { - fn score(&self, rent_params: RentParameters) -> u64 { - storage_score_offset_output(rent_params) - + rent_params.storage_score_offset_delegation() - + self.packed_len() as u64 * rent_params.storage_score_factor_data() as u64 - + self.unlock_conditions().score(rent_params) + fn score(&self, rent_struct: RentStructure) -> u64 { + storage_score_offset_output(rent_struct) + + rent_struct.storage_score_offset_delegation() + + self.packed_len() as u64 * rent_struct.storage_score_factor_data() as u64 + + self.unlock_conditions().score(rent_struct) } } @@ -573,9 +572,9 @@ pub(crate) mod dto { *delegation_id, *validator_address, ), - OutputBuilderAmount::MinimumStorageDeposit(rent_params) => { + OutputBuilderAmount::MinimumStorageDeposit(rent_struct) => { DelegationOutputBuilder::new_with_minimum_storage_deposit( - rent_params, + rent_struct, 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 941b161a9a..3bb6e8dc0b 100644 --- a/sdk/src/types/block/output/feature/block_issuer.rs +++ b/sdk/src/types/block/output/feature/block_issuer.rs @@ -17,7 +17,7 @@ use packable::{ }; use crate::types::block::{ - rent::{RentParameters, StorageScore}, + rent::{RentParameters, RentStructure, StorageScore}, slot::SlotIndex, Error, }; @@ -62,9 +62,9 @@ impl BlockIssuerKey { } impl StorageScore for BlockIssuerKey { - fn score(&self, rent_params: RentParameters) -> u64 { + fn score(&self, rent_struct: RentStructure) -> u64 { match self { - Self::Ed25519(key) => key.score(rent_params), + Self::Ed25519(key) => key.score(rent_struct), } } } @@ -85,6 +85,12 @@ impl Ed25519BlockIssuerKey { pub fn try_from_bytes(bytes: [u8; Self::PUBLIC_KEY_LENGTH]) -> Result { Ok(Self(ed25519::PublicKey::try_from_bytes(bytes)?)) } + + /// Creates a dummy [`Ed25519BlockIssuerKey`] used to calculate a storage score for implicit account addresses. + pub(crate) fn dummy() -> Self { + // Unwrap: we provide a valid byte array + Self(ed25519::PublicKey::try_from_bytes([0; Self::PUBLIC_KEY_LENGTH]).unwrap()) + } } impl core::fmt::Debug for Ed25519BlockIssuerKey { @@ -112,8 +118,8 @@ impl Packable for Ed25519BlockIssuerKey { } impl StorageScore for Ed25519BlockIssuerKey { - fn score(&self, rent_params: RentParameters) -> u64 { - rent_params.storage_score_offset_ed25519_block_issuer_key() + fn score(&self, rent_struct: RentStructure) -> u64 { + rent_struct.storage_score_offset_ed25519_block_issuer_key() } } @@ -200,8 +206,8 @@ impl BlockIssuerKeys { } impl StorageScore for BlockIssuerKeys { - fn score(&self, rent_params: RentParameters) -> u64 { - (*self).iter().map(|key| key.score(rent_params)).sum() + fn score(&self, rent_struct: RentStructure) -> u64 { + (*self).iter().map(|key| key.score(rent_struct)).sum() } } diff --git a/sdk/src/types/block/output/feature/issuer.rs b/sdk/src/types/block/output/feature/issuer.rs index a966736e5e..be5448177a 100644 --- a/sdk/src/types/block/output/feature/issuer.rs +++ b/sdk/src/types/block/output/feature/issuer.rs @@ -5,7 +5,7 @@ use derive_more::From; use crate::types::block::{ address::Address, - rent::{RentParameters, StorageScore}, + rent::{RentParameters, RentStructure, StorageScore}, }; /// Identifies the validated issuer of the UTXO state machine. @@ -30,9 +30,8 @@ impl IssuerFeature { } impl StorageScore for IssuerFeature { - fn score(&self, rent_params: RentParameters) -> u64 { - // self.0.score(rent_params) - todo!() + fn score(&self, rent_struct: RentStructure) -> u64 { + self.0.score(rent_struct) } } diff --git a/sdk/src/types/block/output/feature/mod.rs b/sdk/src/types/block/output/feature/mod.rs index 16acf0340b..2130daecfa 100644 --- a/sdk/src/types/block/output/feature/mod.rs +++ b/sdk/src/types/block/output/feature/mod.rs @@ -28,7 +28,11 @@ pub use self::{ staking::StakingFeature, tag::TagFeature, }; -use crate::types::block::{create_bitflags, Error, rent::StorageScore}; +use crate::types::block::{ + create_bitflags, + rent::{RentStructure, StorageScore}, + Error, +}; /// #[derive(Clone, Eq, PartialEq, Hash, From, Packable)] @@ -197,6 +201,19 @@ impl Feature { } } +impl StorageScore for Feature { + fn score(&self, rent_struct: RentStructure) -> u64 { + match self { + Self::Sender(sender) => todo!(), + Self::Issuer(issuer) => todo!(), + Self::Metadata(metadata) => todo!(), + Self::Tag(tag) => todo!(), + Self::BlockIssuer(block_issuer) => todo!(), + Self::Staking(staking) => todo!(), + } + } +} + create_bitflags!( /// A bitflags-based representation of the set of active [`Feature`]s. pub FeatureFlags, @@ -314,8 +331,8 @@ impl Features { } impl StorageScore for Features { - fn score(&self, rent_params: crate::types::block::rent::RentParameters) -> u64 { - todo!() + fn score(&self, rent_struct: RentStructure) -> u64 { + self.0.iter().map(|f| f.score(rent_struct)).sum() } } diff --git a/sdk/src/types/block/output/foundry.rs b/sdk/src/types/block/output/foundry.rs index e83aa3c15e..d13d641c86 100644 --- a/sdk/src/types/block/output/foundry.rs +++ b/sdk/src/types/block/output/foundry.rs @@ -12,6 +12,7 @@ use packable::{ }; use primitive_types::U256; +use super::storage_score_offset_output; use crate::types::{ block::{ address::{AccountAddress, Address}, @@ -26,7 +27,7 @@ use crate::types::{ TokenId, TokenScheme, }, protocol::ProtocolParameters, - rent::{RentParameters, StorageScore}, + rent::{RentParameters, RentStructure, StorageScore}, semantic::{TransactionFailureReason, ValidationContext}, unlock::Unlock, Error, @@ -34,8 +35,6 @@ use crate::types::{ ValidationParams, }; -use super::storage_score_offset_output; - impl_id!(pub FoundryId, 38, "Defines the unique identifier of a foundry."); #[cfg(feature = "serde")] @@ -106,12 +105,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_params: RentParameters, + rent_struct: RentStructure, serial_number: u32, token_scheme: TokenScheme, ) -> Self { Self::new( - OutputBuilderAmount::MinimumStorageDeposit(rent_params), + OutputBuilderAmount::MinimumStorageDeposit(rent_struct), serial_number, token_scheme, ) @@ -138,8 +137,8 @@ impl FoundryOutputBuilder { /// Sets the amount to the minimum storage deposit. #[inline(always)] - pub fn with_minimum_storage_deposit(mut self, rent_params: RentParameters) -> Self { - self.amount = OutputBuilderAmount::MinimumStorageDeposit(rent_params); + pub fn with_minimum_storage_deposit(mut self, rent_struct: RentStructure) -> Self { + self.amount = OutputBuilderAmount::MinimumStorageDeposit(rent_struct); self } @@ -285,8 +284,8 @@ impl FoundryOutputBuilder { output.amount = match self.amount { OutputBuilderAmount::Amount(amount) => amount, - OutputBuilderAmount::MinimumStorageDeposit(rent_params) => { - Output::Foundry(output.clone()).rent_cost(rent_params) + OutputBuilderAmount::MinimumStorageDeposit(rent_struct) => { + Output::Foundry(output.clone()).rent_cost(rent_struct) } }; @@ -368,11 +367,11 @@ impl FoundryOutput { /// The amount will be set to the minimum storage deposit. #[inline(always)] pub fn build_with_minimum_storage_deposit( - rent_params: RentParameters, + rent_struct: RentStructure, serial_number: u32, token_scheme: TokenScheme, ) -> FoundryOutputBuilder { - FoundryOutputBuilder::new_with_minimum_storage_deposit(rent_params, serial_number, token_scheme) + FoundryOutputBuilder::new_with_minimum_storage_deposit(rent_struct, serial_number, token_scheme) } /// @@ -661,14 +660,14 @@ impl Packable for FoundryOutput { } impl StorageScore for FoundryOutput { - fn score(&self, rent_params: RentParameters) -> u64 { - storage_score_offset_output(rent_params) - + self.packed_len() as u64 * rent_params.storage_score_factor_data() as u64 - + self.native_tokens().score(rent_params) - + self.token_scheme().score(rent_params) - + self.unlock_conditions().score(rent_params) - + self.features().score(rent_params) - + self.immutable_features().score(rent_params) + fn score(&self, rent_struct: RentStructure) -> u64 { + storage_score_offset_output(rent_struct) + + self.packed_len() as u64 * rent_struct.storage_score_factor_data() as u64 + + self.native_tokens().score(rent_struct) + + self.token_scheme().score(rent_struct) + + self.unlock_conditions().score(rent_struct) + + self.features().score(rent_struct) + + self.immutable_features().score(rent_struct) } } @@ -773,8 +772,8 @@ pub(crate) mod dto { OutputBuilderAmount::Amount(amount) => { FoundryOutputBuilder::new_with_amount(amount, serial_number, token_scheme) } - OutputBuilderAmount::MinimumStorageDeposit(rent_params) => { - FoundryOutputBuilder::new_with_minimum_storage_deposit(rent_params, serial_number, token_scheme) + OutputBuilderAmount::MinimumStorageDeposit(rent_struct) => { + FoundryOutputBuilder::new_with_minimum_storage_deposit(rent_struct, serial_number, token_scheme) } }; @@ -861,7 +860,7 @@ mod tests { test_split_dto(builder); let builder = FoundryOutput::build_with_minimum_storage_deposit( - protocol_parameters.rent_parameters(), + protocol_parameters.rent_parameters().into(), 123, rand_token_scheme(), ) diff --git a/sdk/src/types/block/output/mod.rs b/sdk/src/types/block/output/mod.rs index b9d982f4c1..3a5e3d1ab2 100644 --- a/sdk/src/types/block/output/mod.rs +++ b/sdk/src/types/block/output/mod.rs @@ -59,7 +59,7 @@ pub use self::{ use super::{ address::Ed25519Address, protocol::ProtocolParameters, - rent::{RentParameters, StorageScore}, + rent::{RentStructure, StorageScore}, BlockId, }; use crate::types::block::{address::Address, semantic::ValidationContext, slot::SlotIndex, Error}; @@ -76,7 +76,7 @@ pub const OUTPUT_INDEX_RANGE: RangeInclusive = 0..=OUTPUT_INDEX_MAX; // [0. #[derive(Copy, Clone)] pub enum OutputBuilderAmount { Amount(u64), - MinimumStorageDeposit(RentParameters), + MinimumStorageDeposit(RentStructure), } /// Contains the generic [`Output`] with associated [`OutputMetadata`]. @@ -386,8 +386,8 @@ impl Output { /// 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_params: RentParameters, token_supply: u64) -> Result<(), Error> { - let required_output_amount = self.rent_cost(rent_params); + pub fn verify_storage_deposit(&self, rent_struct: RentStructure, token_supply: u64) -> Result<(), Error> { + let required_output_amount = self.rent_cost(rent_struct); if self.amount() < required_output_amount { return Err(Error::InsufficientStorageDepositAmount { @@ -409,7 +409,7 @@ impl Output { }); } - let minimum_deposit = minimum_storage_deposit(return_condition.return_address(), rent_params, token_supply); + let minimum_deposit = minimum_storage_deposit(return_condition.return_address(), rent_struct, token_supply); // `Minimum Storage Deposit` ≤ `Return Amount` if return_condition.amount() < minimum_deposit { @@ -503,47 +503,47 @@ 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_parameters: RentParameters, token_supply: u64) -> u64 { +fn minimum_storage_deposit(address: &Address, rent_struct: RentStructure, 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_parameters) + BasicOutputBuilder::new_with_minimum_storage_deposit(rent_struct) .add_unlock_condition(AddressUnlockCondition::new(*address)) .finish_with_params(token_supply) .unwrap() .amount() } -fn storage_score_offset_output(rent_params: RentParameters) -> u64 { +fn storage_score_offset_output(rent_struct: RentStructure) -> u64 { // included output id, block id, and slot booked data size - rent_params.storage_score_offset_output() - + rent_params.storage_score_factor_data() as u64 + rent_struct.storage_score_offset_output() + + rent_struct.storage_score_factor_data() as u64 * (size_of::() as u64 + size_of::() as u64 + size_of::() as u64) } impl StorageScore for Output { - fn score(&self, rent_params: RentParameters) -> u64 { + fn score(&self, rent_struct: RentStructure) -> u64 { // +1 score for the output kind - rent_params.storage_score_factor_data() as u64 * size_of::() as u64 + rent_struct.storage_score_factor_data() as u64 * size_of::() as u64 + match self { - Self::Basic(basic) => basic.score(rent_params), - Self::Account(account) => account.score(rent_params), - Self::Foundry(foundry) => foundry.score(rent_params), - Self::Nft(nft) => nft.score(rent_params), - Self::Delegation(delegation) => delegation.score(rent_params), + Self::Basic(basic) => basic.score(rent_struct), + Self::Account(account) => account.score(rent_struct), + Self::Foundry(foundry) => foundry.score(rent_struct), + Self::Nft(nft) => nft.score(rent_struct), + Self::Delegation(delegation) => delegation.score(rent_struct), } } } pub struct MinimumStorageDepositBasicOutput { - rent_params: RentParameters, + rent_struct: RentStructure, token_supply: u64, builder: BasicOutputBuilder, } impl MinimumStorageDepositBasicOutput { - pub fn new(rent_params: RentParameters, token_supply: u64) -> Self { + pub fn new(rent_struct: RentStructure, token_supply: u64) -> Self { Self { - rent_params, + rent_struct, token_supply, builder: BasicOutputBuilder::new_with_amount(Output::AMOUNT_MIN).add_unlock_condition( AddressUnlockCondition::new(Address::from(Ed25519Address::from([0; Ed25519Address::LENGTH]))), @@ -581,7 +581,7 @@ impl MinimumStorageDepositBasicOutput { Ok(self .builder .finish_output(self.token_supply)? - .rent_cost(self.rent_params)) + .rent_cost(self.rent_struct)) } } #[cfg(feature = "serde")] diff --git a/sdk/src/types/block/output/native_token.rs b/sdk/src/types/block/output/native_token.rs index 5fbd795139..06bd001e86 100644 --- a/sdk/src/types/block/output/native_token.rs +++ b/sdk/src/types/block/output/native_token.rs @@ -12,7 +12,11 @@ use iterator_sorted::is_unique_sorted; use packable::{bounded::BoundedU8, prefix::BoxedSlicePrefix, Packable}; use primitive_types::U256; -use crate::types::block::{output::foundry::FoundryId, Error, rent::StorageScore}; +use crate::types::block::{ + output::foundry::FoundryId, + rent::{RentStructure, StorageScore}, + Error, +}; impl_id!(pub TokenId, 38, "Unique identifiers of native tokens. The TokenId of native tokens minted by a specific foundry is the same as the FoundryId."); @@ -73,6 +77,12 @@ impl Ord for NativeToken { } } +impl StorageScore for NativeToken { + fn score(&self, rent_struct: RentStructure) -> u64 { + todo!("native token score") + } +} + #[inline] fn verify_amount(amount: &U256, _: &()) -> Result<(), Error> { if VERIFY && amount.is_zero() { @@ -248,8 +258,8 @@ impl NativeTokens { } impl StorageScore for NativeTokens { - fn score(&self, rent_params: crate::types::block::rent::RentParameters) -> u64 { - todo!() + fn score(&self, rent_struct: RentStructure) -> u64 { + self.0.iter().map(|nt| nt.score(rent_struct)).sum() } } diff --git a/sdk/src/types/block/output/nft.rs b/sdk/src/types/block/output/nft.rs index 9ac1154e48..1acd52c40b 100644 --- a/sdk/src/types/block/output/nft.rs +++ b/sdk/src/types/block/output/nft.rs @@ -10,6 +10,7 @@ use packable::{ Packable, PackableExt, }; +use super::storage_score_offset_output; use crate::types::{ block::{ address::{Address, NftAddress}, @@ -19,10 +20,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, RentParameters, StateTransitionError, - StateTransitionVerifier, StorageScore, + NativeTokens, Output, OutputBuilderAmount, OutputId, StateTransitionError, StateTransitionVerifier, + StorageScore, }, protocol::ProtocolParameters, + rent::RentStructure, semantic::{TransactionFailureReason, ValidationContext}, unlock::Unlock, Error, @@ -30,8 +32,6 @@ use crate::types::{ ValidationParams, }; -use super::storage_score_offset_output; - impl_id!(pub NftId, 32, "Unique identifier of an NFT, which is the BLAKE2b-256 hash of the Output ID that created it."); #[cfg(feature = "serde")] @@ -77,8 +77,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_params: RentParameters, nft_id: NftId) -> Self { - Self::new(OutputBuilderAmount::MinimumStorageDeposit(rent_params), nft_id) + pub fn new_with_minimum_storage_deposit(rent_struct: RentStructure, nft_id: NftId) -> Self { + Self::new(OutputBuilderAmount::MinimumStorageDeposit(rent_struct), nft_id) } fn new(amount: OutputBuilderAmount, nft_id: NftId) -> Self { @@ -102,8 +102,8 @@ impl NftOutputBuilder { /// Sets the amount to the minimum storage deposit. #[inline(always)] - pub fn with_minimum_storage_deposit(mut self, rent_params: RentParameters) -> Self { - self.amount = OutputBuilderAmount::MinimumStorageDeposit(rent_params); + pub fn with_minimum_storage_deposit(mut self, rent_struct: RentStructure) -> Self { + self.amount = OutputBuilderAmount::MinimumStorageDeposit(rent_struct); self } @@ -245,8 +245,8 @@ impl NftOutputBuilder { output.amount = match self.amount { OutputBuilderAmount::Amount(amount) => amount, - OutputBuilderAmount::MinimumStorageDeposit(rent_params) => { - Output::Nft(output.clone()).rent_cost(rent_params) + OutputBuilderAmount::MinimumStorageDeposit(rent_struct) => { + Output::Nft(output.clone()).rent_cost(rent_struct) } }; @@ -329,8 +329,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_params: RentParameters, nft_id: NftId) -> NftOutputBuilder { - NftOutputBuilder::new_with_minimum_storage_deposit(rent_params, nft_id) + pub fn build_with_minimum_storage_deposit(rent_struct: RentStructure, nft_id: NftId) -> NftOutputBuilder { + NftOutputBuilder::new_with_minimum_storage_deposit(rent_struct, nft_id) } /// @@ -523,13 +523,13 @@ impl Packable for NftOutput { } impl StorageScore for NftOutput { - fn score(&self, rent_params: RentParameters) -> u64 { - storage_score_offset_output(rent_params) - + self.packed_len() as u64 * rent_params.storage_score_factor_data() as u64 - + self.native_tokens().score(rent_params) - + self.unlock_conditions().score(rent_params) - + self.features().score(rent_params) - + self.immutable_features().score(rent_params) + fn score(&self, rent_struct: RentStructure) -> u64 { + storage_score_offset_output(rent_struct) + + self.packed_len() as u64 * rent_struct.storage_score_factor_data() as u64 + + self.native_tokens().score(rent_struct) + + self.unlock_conditions().score(rent_struct) + + self.features().score(rent_struct) + + self.immutable_features().score(rent_struct) } } @@ -630,8 +630,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_params) => { - NftOutputBuilder::new_with_minimum_storage_deposit(rent_params, *nft_id) + OutputBuilderAmount::MinimumStorageDeposit(rent_struct) => { + NftOutputBuilder::new_with_minimum_storage_deposit(rent_struct, *nft_id) } } .with_mana(mana); @@ -725,7 +725,7 @@ mod tests { test_split_dto(builder); let builder = - NftOutput::build_with_minimum_storage_deposit(protocol_parameters.rent_parameters(), NftId::null()) + NftOutput::build_with_minimum_storage_deposit(protocol_parameters.rent_parameters().into(), 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/output/token_scheme/mod.rs b/sdk/src/types/block/output/token_scheme/mod.rs index 42b428dbc0..4e63076c89 100644 --- a/sdk/src/types/block/output/token_scheme/mod.rs +++ b/sdk/src/types/block/output/token_scheme/mod.rs @@ -4,7 +4,10 @@ mod simple; pub use self::simple::SimpleTokenScheme; -use crate::types::block::{Error, rent::{StorageScore, RentParameters}}; +use crate::types::block::{ + rent::{RentParameters, RentStructure, StorageScore}, + Error, +}; /// #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, derive_more::From, packable::Packable)] @@ -47,9 +50,9 @@ impl TokenScheme { } impl StorageScore for TokenScheme { - fn score(&self, rent_params: RentParameters) -> u64 { + fn score(&self, rent_struct: RentStructure) -> u64 { match self { - Self::Simple(simple) => simple.score(rent_params) + Self::Simple(simple) => simple.score(rent_struct), } } } diff --git a/sdk/src/types/block/output/token_scheme/simple.rs b/sdk/src/types/block/output/token_scheme/simple.rs index 34131f2127..e1250b93bb 100644 --- a/sdk/src/types/block/output/token_scheme/simple.rs +++ b/sdk/src/types/block/output/token_scheme/simple.rs @@ -9,7 +9,10 @@ use packable::{ }; use primitive_types::U256; -use crate::types::block::{Error, rent::{StorageScore, RentParameters}}; +use crate::types::block::{ + rent::{RentParameters, RentStructure, StorageScore}, + Error, +}; /// #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] @@ -104,7 +107,7 @@ impl Packable for SimpleTokenScheme { } impl StorageScore for SimpleTokenScheme { - fn score(&self, rent_params: RentParameters) -> u64 { + fn score(&self, rent_struct: RentStructure) -> u64 { 0 } } diff --git a/sdk/src/types/block/output/unlock_condition/mod.rs b/sdk/src/types/block/output/unlock_condition/mod.rs index 3e89809900..f6cc8d19a8 100644 --- a/sdk/src/types/block/output/unlock_condition/mod.rs +++ b/sdk/src/types/block/output/unlock_condition/mod.rs @@ -30,7 +30,14 @@ pub use self::{ state_controller_address::StateControllerAddressUnlockCondition, storage_deposit_return::StorageDepositReturnUnlockCondition, timelock::TimelockUnlockCondition, }; -use crate::types::block::{address::Address, create_bitflags, protocol::ProtocolParameters, slot::SlotIndex, Error, rent::StorageScore}; +use crate::types::block::{ + address::Address, + create_bitflags, + protocol::ProtocolParameters, + rent::{RentStructure, StorageScore}, + slot::SlotIndex, + Error, +}; /// #[derive(Clone, Eq, PartialEq, Hash, From)] @@ -65,17 +72,23 @@ impl Ord for UnlockCondition { impl core::fmt::Debug for UnlockCondition { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - Self::Address(unlock_condition) => unlock_condition.fmt(f), - Self::StorageDepositReturn(unlock_condition) => unlock_condition.fmt(f), - Self::Timelock(unlock_condition) => unlock_condition.fmt(f), - Self::Expiration(unlock_condition) => unlock_condition.fmt(f), - Self::StateControllerAddress(unlock_condition) => unlock_condition.fmt(f), - Self::GovernorAddress(unlock_condition) => unlock_condition.fmt(f), - Self::ImmutableAccountAddress(unlock_condition) => unlock_condition.fmt(f), + Self::Address(address) => address.fmt(f), + Self::StorageDepositReturn(storage_deposit_return) => storage_deposit_return.fmt(f), + Self::Timelock(timelock) => timelock.fmt(f), + Self::Expiration(expiration) => expiration.fmt(f), + Self::StateControllerAddress(state_controller_address) => state_controller_address.fmt(f), + Self::GovernorAddress(governor_address) => governor_address.fmt(f), + Self::ImmutableAccountAddress(immutable_account_address) => immutable_account_address.fmt(f), } } } +impl StorageScore for UnlockCondition { + fn score(&self, rent_struct: RentStructure) -> u64 { + todo!("unlock condition score") + } +} + impl UnlockCondition { /// Return the output kind of an `Output`. pub fn kind(&self) -> u8 { @@ -445,8 +458,8 @@ impl UnlockConditions { } impl StorageScore for UnlockConditions { - fn score(&self, rent_params: crate::types::block::rent::RentParameters) -> u64 { - todo!() + fn score(&self, rent_struct: RentStructure) -> u64 { + self.0.iter().map(|uc| uc.score(rent_struct)).sum() } } diff --git a/sdk/src/types/block/payload/transaction/essence/regular.rs b/sdk/src/types/block/payload/transaction/essence/regular.rs index 71e0d332cc..3cb42a19b3 100644 --- a/sdk/src/types/block/payload/transaction/essence/regular.rs +++ b/sdk/src/types/block/payload/transaction/essence/regular.rs @@ -14,6 +14,7 @@ use crate::types::{ output::{InputsCommitment, NativeTokens, Output, OUTPUT_COUNT_RANGE}, payload::{OptionalPayload, Payload}, protocol::ProtocolParameters, + rent::RentStructure, slot::SlotIndex, Error, }, @@ -395,7 +396,7 @@ fn verify_outputs(outputs: &[Output], visitor: &ProtocolPara } } - output.verify_storage_deposit(visitor.rent_parameters(), visitor.token_supply())?; + output.verify_storage_deposit(visitor.rent_parameters().into(), visitor.token_supply())?; } } diff --git a/sdk/src/types/block/rent.rs b/sdk/src/types/block/rent.rs index 4608ad809f..f89c8e8c27 100644 --- a/sdk/src/types/block/rent.rs +++ b/sdk/src/types/block/rent.rs @@ -5,6 +5,7 @@ use core::{mem::size_of, ops::Deref}; use packable::Packable; +use super::output::{AccountOutput, BasicOutput}; use crate::types::block::{ address::{Address, Ed25519Address}, output::{ @@ -26,13 +27,38 @@ const DEFAULT_STORAGE_SCORE_OFFSET_DELEGATION: StorageScoreOffset = 100; type StorageScoreFactor = u8; type StorageScoreOffset = u64; -// Includes the rent parameters and the additional factors/offsets computed from these parameters. #[derive(Copy, -// Clone, Debug, Eq, PartialEq)] +// 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 RentStructure { + /// Creates a new [`RentStructure`]. Computes the score offset for implicit account creation addresses. + pub fn new(rent_parameters: RentParameters) -> Self { + let mut rent_structure = Self { + rent_parameters, + storage_score_offset_implicit_account_creation_address: 0, + }; + + // set the storage score offset for implicit account creation addresses as + // the difference between the storage score of the dummy account and the storage + // score of the dummy basic output minus the storage score of the dummy address. + let dummy_basic_output_score = BasicOutput::dummy().score(rent_structure); + let dummy_address_score = Ed25519Address::dummy().score(rent_structure); + let basic_score_without_address = dummy_basic_output_score + .checked_sub(dummy_address_score) + .expect("underflow"); + let dummy_account_output_score = AccountOutput::dummy().score(rent_structure); + + rent_structure.storage_score_offset_implicit_account_creation_address = dummy_account_output_score + .checked_sub(basic_score_without_address) + .expect("underflow"); + rent_structure + } +} + impl Deref for RentStructure { type Target = RentParameters; @@ -41,6 +67,12 @@ impl Deref for RentStructure { } } +impl From for RentStructure { + fn from(rent_parameters: RentParameters) -> Self { + Self::new(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. @@ -171,20 +203,20 @@ impl RentParameters { } } -/// A trait to facilitate the rent cost computation for block outputs, which is central to dust protection. +/// A trait to facilitate the rent cost computation for implementing types, which is central to dust protection. pub trait StorageScore { - /// Computes the storage score given a [`RentParameters`]. Different fields in a type lead to different storage + /// Computes the storage score given a [`RentStructure`]. Different fields in a type lead to different storage /// requirements for the ledger state. - fn score(&self, rent_params: RentParameters) -> u64; + fn score(&self, rent_struct: RentStructure) -> u64; - /// Computes the storage cost given a [`RentParameters`]. - fn rent_cost(&self, rent_params: RentParameters) -> u64 { - rent_params.storage_cost as u64 * self.score(rent_params) + /// Computes the rent cost given a [`RentStructure`]. + fn rent_cost(&self, rent_struct: RentStructure) -> u64 { + rent_struct.storage_cost as u64 * self.score(rent_struct) } } impl StorageScore for [T; N] { - fn score(&self, rent_params: RentParameters) -> u64 { - self.iter().map(|elem| elem.score(rent_params)).sum() + fn score(&self, rent_struct: RentStructure) -> u64 { + self.iter().map(|elem| elem.score(rent_struct)).sum() } } diff --git a/sdk/src/wallet/account/operations/balance.rs b/sdk/src/wallet/account/operations/balance.rs index 77f6fb02b7..65eb8be704 100644 --- a/sdk/src/wallet/account/operations/balance.rs +++ b/sdk/src/wallet/account/operations/balance.rs @@ -8,7 +8,7 @@ use crate::{ types::block::{ address::Bech32Address, output::{unlock_condition::UnlockCondition, FoundryId, NativeTokensBuilder, Output}, - rent::StorageScore, + rent::{RentStructure, StorageScore}, ConvertTo, }, wallet::{ @@ -63,7 +63,7 @@ where account_details: &AccountDetails, ) -> Result { let network_id = self.client().get_network_id().await?; - let rent_params = self.client().get_rent_parameters().await?; + let rent_struct = self.client().get_rent_parameters().await?.into(); let mut balance = Balance::default(); let mut total_rent_amount = 0; let mut total_native_tokens = NativeTokensBuilder::default(); @@ -89,7 +89,7 @@ where } let output = &data.output; - let rent = output.rent_cost(rent_params); + let rent = output.rent_cost(rent_struct); // 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 6ca1cc10b2..56c4e6e55c 100644 --- a/sdk/src/wallet/account/operations/output_claiming.rs +++ b/sdk/src/wallet/account/operations/output_claiming.rs @@ -14,6 +14,7 @@ use crate::{ BasicOutputBuilder, MinimumStorageDepositBasicOutput, NativeTokens, NativeTokensBuilder, NftOutputBuilder, Output, OutputId, }, + rent::RentStructure, slot::SlotIndex, }, wallet::account::{ @@ -203,7 +204,7 @@ where log::debug!("[OUTPUT_CLAIMING] claim_outputs_internal"); let slot_index = self.client().get_slot_index().await?; - let rent_params = self.client().get_rent_parameters().await?; + let rent_struct = self.client().get_rent_parameters().await?.into(); let token_supply = self.client().get_token_supply().await?; let account_details = self.details().await; @@ -276,7 +277,7 @@ where .finish_output(token_supply)? } else { NftOutputBuilder::from(nft_output) - .with_minimum_storage_deposit(rent_params) + .with_minimum_storage_deposit(rent_struct) .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 +302,7 @@ where required_amount_for_nfts } else { required_amount_for_nfts - + MinimumStorageDepositBasicOutput::new(rent_params, token_supply) + + MinimumStorageDepositBasicOutput::new(rent_struct, token_supply) .with_native_tokens(option_native_token) .finish()? }; @@ -321,7 +322,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_params, token_supply) + + MinimumStorageDepositBasicOutput::new(rent_struct, 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 31efd75e59..ad0f7d05eb 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_params = self.client().get_rent_parameters().await?; + let rent_struct = self.client().get_rent_parameters().await?.into(); 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_params, AccountId::null()) + AccountOutputBuilder::new_with_minimum_storage_deposit(rent_struct, 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 373ac38201..ce3f7e2ace 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 @@ -15,6 +15,7 @@ use crate::{ feature::MetadataFeature, unlock_condition::ImmutableAccountAddressUnlockCondition, AccountId, AccountOutputBuilder, FoundryId, FoundryOutputBuilder, Output, SimpleTokenScheme, TokenId, TokenScheme, }, + rent::RentStructure, }, wallet::account::{ types::{Transaction, TransactionDto}, @@ -131,7 +132,7 @@ where options: impl Into> + Send, ) -> crate::wallet::Result { log::debug!("[TRANSACTION] create_native_token"); - let rent_params = self.client().get_rent_parameters().await?; + let rent_struct = self.client().get_rent_parameters().await?.into(); let token_supply = self.client().get_token_supply().await?; let (account_id, account_output) = self @@ -158,7 +159,7 @@ where new_account_output_builder.finish_output(token_supply)?, { let mut foundry_builder = FoundryOutputBuilder::new_with_minimum_storage_deposit( - rent_params, + rent_struct, 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 f07f6e79a4..c574ed781f 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 @@ -13,6 +13,7 @@ use crate::{ unlock_condition::AddressUnlockCondition, NftId, NftOutputBuilder, }, + rent::RentStructure, ConvertTo, }, wallet::{ @@ -160,7 +161,7 @@ where I::IntoIter: Send, { log::debug!("[TRANSACTION] prepare_mint_nfts"); - let rent_params = self.client().get_rent_parameters().await?; + let rent_struct = self.client().get_rent_parameters().await?.into(); let token_supply = self.client().get_token_supply().await?; let account_addresses = self.addresses().await?; let mut outputs = Vec::new(); @@ -189,7 +190,7 @@ where }; // NftId needs to be set to 0 for the creation - let mut nft_builder = NftOutputBuilder::new_with_minimum_storage_deposit(rent_params, NftId::null()) + let mut nft_builder = NftOutputBuilder::new_with_minimum_storage_deposit(rent_struct, 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 83dee32dcd..2b1dad9068 100644 --- a/sdk/src/wallet/account/operations/transaction/high_level/send.rs +++ b/sdk/src/wallet/account/operations/transaction/high_level/send.rs @@ -14,6 +14,7 @@ use crate::{ }, BasicOutputBuilder, MinimumStorageDepositBasicOutput, }, + rent::RentStructure, slot::SlotIndex, ConvertTo, }, @@ -139,7 +140,7 @@ where { log::debug!("[TRANSACTION] prepare_send"); let options = options.into(); - let rent_params = self.client().get_rent_parameters().await?; + let rent_struct = self.client().get_rent_parameters().await?.into(); let token_supply = self.client().get_token_supply().await?; let account_addresses = self.addresses().await?; @@ -170,7 +171,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_params) + let output = BasicOutputBuilder::new_with_minimum_storage_deposit(rent_struct) .add_unlock_condition(AddressUnlockCondition::new(address)) .finish_output(token_supply)?; @@ -187,7 +188,7 @@ where }); // Since it does need a storage deposit, calculate how much that should be - let storage_deposit_amount = MinimumStorageDepositBasicOutput::new(rent_params, token_supply) + let storage_deposit_amount = MinimumStorageDepositBasicOutput::new(rent_struct, 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 571344c1a7..7654b8693d 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 @@ -15,6 +15,7 @@ use crate::{ }, BasicOutputBuilder, MinimumStorageDepositBasicOutput, NativeToken, NativeTokens, TokenId, }, + rent::RentStructure, slot::SlotIndex, ConvertTo, }, @@ -129,7 +130,7 @@ where I::IntoIter: Send, { log::debug!("[TRANSACTION] prepare_send_native_tokens"); - let rent_params = self.client().get_rent_parameters().await?; + let rent_struct = self.client().get_rent_parameters().await?.into(); let token_supply = self.client().get_token_supply().await?; let account_addresses = self.addresses().await?; @@ -171,7 +172,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_params, token_supply) + let storage_deposit_amount = MinimumStorageDepositBasicOutput::new(rent_struct, 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 0e0545827c..d21ead4c00 100644 --- a/sdk/src/wallet/account/operations/transaction/mod.rs +++ b/sdk/src/wallet/account/operations/transaction/mod.rs @@ -73,7 +73,7 @@ where // Check if the outputs have enough amount to cover the storage deposit for output in &outputs { output.verify_storage_deposit( - protocol_parameters.rent_parameters(), + protocol_parameters.rent_parameters().into(), protocol_parameters.token_supply(), )?; } diff --git a/sdk/src/wallet/account/operations/transaction/prepare_output.rs b/sdk/src/wallet/account/operations/transaction/prepare_output.rs index 5d25537914..e258a14bce 100644 --- a/sdk/src/wallet/account/operations/transaction/prepare_output.rs +++ b/sdk/src/wallet/account/operations/transaction/prepare_output.rs @@ -16,7 +16,7 @@ use crate::{ BasicOutputBuilder, MinimumStorageDepositBasicOutput, NativeToken, NftId, NftOutputBuilder, Output, UnlockCondition, }, - rent::{RentParameters, StorageScore}, + rent::{RentParameters, RentStructure, StorageScore}, slot::SlotIndex, Error, }, @@ -47,12 +47,12 @@ where self.client().bech32_hrp_matches(params.recipient_address.hrp()).await?; - let rent_params = self.client().get_rent_parameters().await?; + let rent_struct = self.client().get_rent_parameters().await?.into(); 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_params) + .create_initial_output_builder(params.recipient_address, nft_id, rent_struct) .await?; if let Some(assets) = ¶ms.assets { @@ -103,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_params) + .with_minimum_storage_deposit(rent_struct) .finish_output(token_supply)?; let mut second_output_builder = if nft_id.is_some() { @@ -113,9 +113,9 @@ where }; let min_storage_deposit_basic_output = - MinimumStorageDepositBasicOutput::new(rent_params, token_supply).finish()?; + MinimumStorageDepositBasicOutput::new(rent_struct, token_supply).finish()?; - let min_required_storage_deposit = first_output.rent_cost(rent_params); + let min_required_storage_deposit = first_output.rent_cost(rent_struct); if params.amount > min_required_storage_deposit { second_output_builder = second_output_builder.with_amount(params.amount); @@ -147,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_params) + .with_minimum_storage_deposit(rent_struct) .finish_output(token_supply)? .amount(); @@ -177,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.rent_cost(rent_params); + available_base_coin += existing_nft_output_data.output.rent_cost(rent_struct); } if final_amount > available_base_coin { @@ -237,13 +237,13 @@ where &self, recipient_address: Bech32Address, nft_id: Option, - rent_params: RentParameters, + rent_struct: RentStructure, ) -> 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_params, *nft_id)), + OutputBuilder::Nft(NftOutputBuilder::new_with_minimum_storage_deposit(rent_struct, *nft_id)), None, ) } else { @@ -264,7 +264,7 @@ where } } else { ( - OutputBuilder::Basic(BasicOutputBuilder::new_with_minimum_storage_deposit(rent_params)), + OutputBuilder::Basic(BasicOutputBuilder::new_with_minimum_storage_deposit(rent_struct)), None, ) }; @@ -427,13 +427,13 @@ impl OutputBuilder { } self } - fn with_minimum_storage_deposit(mut self, rent_params: RentParameters) -> Self { + fn with_minimum_storage_deposit(mut self, rent_struct: RentStructure) -> Self { match self { Self::Basic(b) => { - self = Self::Basic(b.with_minimum_storage_deposit(rent_params)); + self = Self::Basic(b.with_minimum_storage_deposit(rent_struct)); } Self::Nft(b) => { - self = Self::Nft(b.with_minimum_storage_deposit(rent_params)); + self = Self::Nft(b.with_minimum_storage_deposit(rent_struct)); } } self diff --git a/sdk/src/wallet/account/operations/transaction/prepare_transaction.rs b/sdk/src/wallet/account/operations/transaction/prepare_transaction.rs index d493791b05..9bbbc66398 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_params = self.client().get_rent_parameters().await?; + let rent_struct = self.client().get_rent_parameters().await?.into(); 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_params, token_supply)?; + output.verify_storage_deposit(rent_struct, 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 d9fcb96d05..e03739b902 100644 --- a/sdk/tests/client/input_selection/account_outputs.rs +++ b/sdk/tests/client/input_selection/account_outputs.rs @@ -2312,17 +2312,19 @@ fn new_state_metadata() { let protocol_parameters = protocol_parameters(); let account_id_1 = AccountId::from_str(ACCOUNT_ID_1).unwrap(); - let account_output = - 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(), - )) - .add_unlock_condition(GovernorAddressUnlockCondition::new( - Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), - )) - .finish_output(protocol_parameters.token_supply()) - .unwrap(); + let account_output = AccountOutputBuilder::new_with_minimum_storage_deposit( + protocol_parameters.rent_parameters().into(), + account_id_1, + ) + .with_state_metadata([1, 2, 3]) + .add_unlock_condition(StateControllerAddressUnlockCondition::new( + Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), + )) + .add_unlock_condition(GovernorAddressUnlockCondition::new( + Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), + )) + .finish_output(protocol_parameters.token_supply()) + .unwrap(); let inputs = [InputSigningData { output: account_output.clone(), @@ -2332,7 +2334,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_parameters()) + .with_minimum_storage_deposit(protocol_parameters.rent_parameters().into()) .with_state_metadata([3, 4, 5]) .with_state_index(account_output.as_account().state_index() + 1) .finish_output(protocol_parameters.token_supply()) @@ -2358,17 +2360,19 @@ fn new_state_metadata_but_same_state_index() { let protocol_parameters = protocol_parameters(); let account_id_1 = AccountId::from_str(ACCOUNT_ID_1).unwrap(); - let account_output = - 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(), - )) - .add_unlock_condition(GovernorAddressUnlockCondition::new( - Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), - )) - .finish_output(protocol_parameters.token_supply()) - .unwrap(); + let account_output = AccountOutputBuilder::new_with_minimum_storage_deposit( + protocol_parameters.rent_parameters().into(), + account_id_1, + ) + .with_state_metadata([1, 2, 3]) + .add_unlock_condition(StateControllerAddressUnlockCondition::new( + Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), + )) + .add_unlock_condition(GovernorAddressUnlockCondition::new( + Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), + )) + .finish_output(protocol_parameters.token_supply()) + .unwrap(); let inputs = [InputSigningData { output: account_output.clone(), @@ -2378,7 +2382,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_parameters()) + .with_minimum_storage_deposit(protocol_parameters.rent_parameters().into()) .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 fa5a9b6cae..7c608f356d 100644 --- a/sdk/tests/client/input_selection/nft_outputs.rs +++ b/sdk/tests/client/input_selection/nft_outputs.rs @@ -1198,7 +1198,7 @@ fn changed_immutable_metadata() { let metadata = [1, 2, 3]; let nft_output = - NftOutputBuilder::new_with_minimum_storage_deposit(protocol_parameters.rent_parameters(), nft_id_1) + NftOutputBuilder::new_with_minimum_storage_deposit(protocol_parameters.rent_parameters().into(), nft_id_1) .with_immutable_features(MetadataFeature::try_from(metadata)) .add_unlock_condition(AddressUnlockCondition::new( Address::try_from_bech32(BECH32_ADDRESS_ED25519_0).unwrap(), @@ -1224,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_parameters()) + .with_minimum_storage_deposit(protocol_parameters.rent_parameters().into()) .with_immutable_features(MetadataFeature::try_from(metadata)) .finish_output(protocol_parameters.token_supply()) .unwrap(); diff --git a/sdk/tests/types/output/account.rs b/sdk/tests/types/output/account.rs index 277b04f3e1..76a677b5ab 100644 --- a/sdk/tests/types/output/account.rs +++ b/sdk/tests/types/output/account.rs @@ -72,7 +72,7 @@ fn builder() { let metadata = rand_metadata_feature(); let output = builder - .with_minimum_storage_deposit(protocol_parameters.rent_parameters()) + .with_minimum_storage_deposit(protocol_parameters.rent_parameters().into()) .add_unlock_condition(rand_state_controller_address_unlock_condition_different_from( &account_id, )) @@ -84,7 +84,7 @@ fn builder() { assert_eq!( output.amount(), - Output::Account(output.clone()).rent_cost(protocol_parameters.rent_parameters()) + Output::Account(output.clone()).rent_cost(protocol_parameters.rent_parameters().into()) ); 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 5f219c3851..f8989304ba 100644 --- a/sdk/tests/types/output/basic.rs +++ b/sdk/tests/types/output/basic.rs @@ -51,7 +51,7 @@ fn builder() { let metadata = rand_metadata_feature(); let output = builder - .with_minimum_storage_deposit(protocol_parameters.rent_parameters()) + .with_minimum_storage_deposit(protocol_parameters.rent_parameters().into()) .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())) @@ -59,7 +59,7 @@ fn builder() { assert_eq!( output.amount(), - Output::Basic(output.clone()).rent_cost(protocol_parameters.rent_parameters()) + Output::Basic(output.clone()).rent_cost(protocol_parameters.rent_parameters().into()) ); 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 80703ecf6c..db44ee9bd7 100644 --- a/sdk/tests/types/output/foundry.rs +++ b/sdk/tests/types/output/foundry.rs @@ -52,14 +52,14 @@ fn builder() { assert!(output.immutable_features().is_empty()); let output = builder - .with_minimum_storage_deposit(protocol_parameters.rent_parameters()) + .with_minimum_storage_deposit(protocol_parameters.rent_parameters().into()) .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_parameters()) + Output::Foundry(output).rent_cost(protocol_parameters.rent_parameters().into()) ); } diff --git a/sdk/tests/types/output/nft.rs b/sdk/tests/types/output/nft.rs index da0c1867d0..f6eafaf5c8 100644 --- a/sdk/tests/types/output/nft.rs +++ b/sdk/tests/types/output/nft.rs @@ -53,14 +53,14 @@ fn builder() { assert!(output.immutable_features().is_empty()); let output = builder - .with_minimum_storage_deposit(protocol_parameters.rent_parameters()) + .with_minimum_storage_deposit(protocol_parameters.rent_parameters().into()) .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_parameters()) + Output::Nft(output).rent_cost(protocol_parameters.rent_parameters().into()) ); } diff --git a/sdk/tests/wallet/claim_outputs.rs b/sdk/tests/wallet/claim_outputs.rs index f72f74dd60..cb599df340 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_params = account_0.client().get_rent_parameters().await?; + let rent_struct = account_0.client().get_rent_parameters().await?.into(); // TODO more fitting value let expiration_slot = account_0.client().get_slot_index().await? + 86400; - let output = BasicOutputBuilder::new_with_minimum_storage_deposit(rent_params) + let output = BasicOutputBuilder::new_with_minimum_storage_deposit(rent_struct) .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_params = account_0.client().get_rent_parameters().await?; + let rent_strcut = account_0.client().get_rent_parameters().await?.into(); let token_supply = account_0.client().get_token_supply().await?; let tx = account_0 .send_outputs( [ - BasicOutputBuilder::new_with_minimum_storage_deposit(rent_params) + BasicOutputBuilder::new_with_minimum_storage_deposit(rent_strcut) .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_params) + BasicOutputBuilder::new_with_minimum_storage_deposit(rent_strcut) .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 ceb92acd42..58560f6556 100644 --- a/sdk/tests/wallet/output_preparation.rs +++ b/sdk/tests/wallet/output_preparation.rs @@ -409,8 +409,8 @@ async fn output_preparation() -> Result<()> { None, ) .await?; - let rent_params = wallet.client().get_rent_parameters().await?; - let minimum_storage_deposit = output.rent_cost(rent_params); + let rent_struct = wallet.client().get_rent_parameters().await?.into(); + let minimum_storage_deposit = output.rent_cost(rent_struct); assert_eq!(output.amount(), minimum_storage_deposit); assert_eq!(output.amount(), 187900); let sdr = output.unlock_conditions().unwrap().storage_deposit_return().unwrap(); @@ -433,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_params = account.client().get_rent_parameters().await?; + let rent_struct = account.client().get_rent_parameters().await?.into(); let token_supply = account.client().get_token_supply().await?; let recipient_address_bech32 = String::from("rms1qpszqzadsym6wpppd6z037dvlejmjuke7s24hm95s9fg9vpua7vluaw60xu"); @@ -455,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_params, token_supply)?; + output.verify_storage_deposit(rent_struct, token_supply)?; assert_eq!(output.amount(), 50601); // address and sdr unlock condition assert_eq!(output.unlock_conditions().unwrap().len(), 2); @@ -476,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_params, token_supply)?; + output.verify_storage_deposit(rent_struct, token_supply)?; assert_eq!(output.amount(), 85199); // address and sdr unlock condition assert_eq!(output.unlock_conditions().unwrap().len(), 2); @@ -501,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_params, token_supply)?; + output.verify_storage_deposit(rent_struct, token_supply)?; assert_eq!(output.amount(), 85199); // address and sdr unlock condition assert_eq!(output.unlock_conditions().unwrap().len(), 2); @@ -526,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_params, token_supply)?; + output.verify_storage_deposit(rent_struct, 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 @@ -614,11 +614,11 @@ async fn prepare_output_remainder_dust() -> Result<()> { let addresses = &accounts[1].addresses().await?; let address = addresses[0].address(); - let rent_params = account.client().get_rent_parameters().await?; + let rent_struct = account.client().get_rent_parameters().await?.into(); let token_supply = account.client().get_token_supply().await?; let balance = account.sync(None).await?; - let minimum_required_storage_deposit = MinimumStorageDepositBasicOutput::new(rent_params, token_supply).finish()?; + let minimum_required_storage_deposit = MinimumStorageDepositBasicOutput::new(rent_struct, 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_params, token_supply)?; + output.verify_storage_deposit(rent_struct, 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_params, token_supply)?; + output.verify_storage_deposit(rent_struct, 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_params, token_supply)?; + output.verify_storage_deposit(rent_struct, 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_params = wallet.client().get_rent_parameters().await?; - let minimum_storage_deposit = Output::Nft(nft.clone()).rent_cost(rent_params); + let rent_struct = wallet.client().get_rent_parameters().await?.into(); + let minimum_storage_deposit = Output::Nft(nft.clone()).rent_cost(rent_struct); assert_eq!(nft.amount(), minimum_storage_deposit); assert_eq!(nft.amount(), 52300);