diff --git a/bindings/core/src/method_handler/client.rs b/bindings/core/src/method_handler/client.rs index db03905021..b14f958664 100644 --- a/bindings/core/src/method_handler/client.rs +++ b/bindings/core/src/method_handler/client.rs @@ -289,7 +289,7 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM let output = Output::try_from_dto_with_params(output, client.get_token_supply().await?)?; let rent_structure = client.get_rent_structure().await?; - let minimum_storage_deposit = output.rent_cost(&rent_structure); + let minimum_storage_deposit = output.rent_cost(rent_structure); 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 88683b0c38..de11e6ac0f 100644 --- a/bindings/core/src/method_handler/utils.rs +++ b/bindings/core/src/method_handler/utils.rs @@ -80,7 +80,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).to_string()) } UtilsMethod::VerifyMnemonic { mnemonic } => { let mnemonic = Mnemonic::from(mnemonic); 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 046886d14a..03f34bd2cc 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_structure()) .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_structure(), self.protocol_parameters.token_supply(), )?; diff --git a/sdk/src/client/api/high_level.rs b/sdk/src/client/api/high_level.rs index 6efc46218f..70118b6c9e 100644 --- a/sdk/src/client/api/high_level.rs +++ b/sdk/src/client/api/high_level.rs @@ -114,21 +114,20 @@ impl Client { Ok(selected_inputs) } - /// Returns the local time checked with the timestamp of the latest milestone, if the difference is larger than 5 + /// Returns the local time checked with the tangle timestamp. If the difference is larger than 5 /// minutes an error is returned to prevent locking outputs by accident for a wrong time. pub async fn get_time_checked(&self) -> Result { let current_time = unix_timestamp_now().as_secs() as u32; let network_info = self.get_network_info().await?; - if let Some(latest_ms_timestamp) = network_info.latest_milestone_timestamp { + if let Some(tangle_time) = network_info.tangle_time { + let tangle_time = tangle_time as u32; // Check the local time is in the range of +-5 minutes of the node to prevent locking funds by accident - if !(latest_ms_timestamp - FIVE_MINUTES_IN_SECONDS..latest_ms_timestamp + FIVE_MINUTES_IN_SECONDS) - .contains(¤t_time) - { + if !(tangle_time - FIVE_MINUTES_IN_SECONDS..tangle_time + FIVE_MINUTES_IN_SECONDS).contains(¤t_time) { return Err(Error::TimeNotSynced { current_time, - milestone_timestamp: latest_ms_timestamp, + tangle_time, }); } } diff --git a/sdk/src/client/builder.rs b/sdk/src/client/builder.rs index 4bc781ad9b..5a7407e629 100644 --- a/sdk/src/client/builder.rs +++ b/sdk/src/client/builder.rs @@ -263,9 +263,9 @@ pub struct NetworkInfo { /// Protocol parameters. #[serde(default)] pub protocol_parameters: ProtocolParameters, - /// The latest cached milestone timestamp. + /// The current tangle time. #[serde(skip)] - pub latest_milestone_timestamp: Option, + pub tangle_time: Option, } impl NetworkInfo { @@ -274,8 +274,8 @@ impl NetworkInfo { self } - pub fn with_latest_milestone_timestamp(mut self, latest_milestone_timestamp: impl Into>) -> Self { - self.latest_milestone_timestamp = latest_milestone_timestamp.into(); + pub fn with_tangle_time(mut self, tangle_time: u64) -> Self { + self.tangle_time = Some(tangle_time.into()); self } } diff --git a/sdk/src/client/core.rs b/sdk/src/client/core.rs index bfcaa919f7..6a18c03192 100644 --- a/sdk/src/client/core.rs +++ b/sdk/src/client/core.rs @@ -14,7 +14,7 @@ use { }; #[cfg(target_family = "wasm")] -use crate::client::constants::CACHE_NETWORK_INFO_TIMEOUT_IN_SECONDS; +use crate::{client::constants::CACHE_NETWORK_INFO_TIMEOUT_IN_SECONDS, types::block::PROTOCOL_VERSION}; use crate::{ client::{ builder::{ClientBuilder, NetworkInfo}, @@ -109,7 +109,11 @@ impl ClientInner { } let info = self.get_info().await?.node_info; let mut client_network_info = self.network_info.write().await; - client_network_info.protocol_parameters = info.protocol.clone(); + client_network_info.protocol_parameters = info + .protocol_parameters_by_version(PROTOCOL_VERSION) + .expect("missing v3 protocol parameters") + .parameters + .clone(); *LAST_SYNC.lock().unwrap() = Some(current_time + CACHE_NETWORK_INFO_TIMEOUT_IN_SECONDS); } @@ -124,7 +128,7 @@ impl ClientInner { /// Gets the protocol version of the node we're connecting to. pub async fn get_protocol_version(&self) -> Result { - Ok(self.get_network_info().await?.protocol_parameters.protocol_version()) + Ok(self.get_network_info().await?.protocol_parameters.version()) } /// Gets the network name of the node we're connecting to. @@ -139,17 +143,12 @@ impl ClientInner { /// Gets the bech32 HRP of the node we're connecting to. pub async fn get_bech32_hrp(&self) -> Result { - Ok(*self.get_network_info().await?.protocol_parameters.bech32_hrp()) - } - - /// Gets the below maximum depth of the node we're connecting to. - pub async fn get_below_max_depth(&self) -> Result { - Ok(self.get_network_info().await?.protocol_parameters.below_max_depth()) + Ok(self.get_network_info().await?.protocol_parameters.bech32_hrp()) } /// 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()) + Ok(self.get_network_info().await?.protocol_parameters.rent_structure()) } /// Gets the token supply of the node we're connecting to. diff --git a/sdk/src/client/error.rs b/sdk/src/client/error.rs index 843bd029b7..dad148f96e 100644 --- a/sdk/src/client/error.rs +++ b/sdk/src/client/error.rs @@ -128,15 +128,13 @@ pub enum Error { /// Tokio task join error #[error("{0}")] TaskJoin(#[from] tokio::task::JoinError), - /// Local time doesn't match the time of the latest milestone timestamp - #[error( - "local time {current_time} doesn't match the time of the latest milestone timestamp: {milestone_timestamp}" - )] + /// Local time doesn't match the network time + #[error("local time {current_time} doesn't match the tangle time: {tangle_time}")] TimeNotSynced { /// The local time. current_time: u32, - /// The timestamp of the latest milestone. - milestone_timestamp: u32, + /// The tangle time. + tangle_time: u32, }, /// The semantic validation of a transaction failed. #[error("the semantic validation of a transaction failed with conflict reason: {} - {0:?}", *.0 as u8)] diff --git a/sdk/src/client/node_manager/syncing.rs b/sdk/src/client/node_manager/syncing.rs index 2399526b7b..24fa9caaeb 100644 --- a/sdk/src/client/node_manager/syncing.rs +++ b/sdk/src/client/node_manager/syncing.rs @@ -4,6 +4,7 @@ #[cfg(not(target_family = "wasm"))] use { crate::types::api::core::response::InfoResponse, + crate::types::block::PROTOCOL_VERSION, std::{collections::HashSet, time::Duration}, tokio::time::sleep, }; @@ -73,13 +74,18 @@ impl ClientInner { match crate::client::Client::get_node_info(node.url.as_ref(), node.auth.clone()).await { Ok(info) => { if info.status.is_healthy || ignore_node_health { - match network_nodes.get_mut(info.protocol.network_name()) { + // Unwrap: We should always have parameters for this version. If we don't we can't recover. + let network_name = info + .protocol_parameters_by_version(PROTOCOL_VERSION) + .expect("missing v3 protocol parameters") + .parameters + .network_name(); + match network_nodes.get_mut(network_name) { Some(network_node_entry) => { network_node_entry.push((info, node.clone())); } None => { - network_nodes - .insert(info.protocol.network_name().to_owned(), vec![(info, node.clone())]); + network_nodes.insert(network_name.to_owned(), vec![(info, node.clone())]); } } } else { @@ -105,9 +111,13 @@ impl ClientInner { if let Some((info, _node_url)) = nodes.first() { let mut network_info = self.network_info.write().await; - // TODO change to one of the new timestamps, which ones ? - // network_info.latest_milestone_timestamp = info.status.latest_milestone.timestamp; - network_info.protocol_parameters = info.protocol.clone(); + network_info.tangle_time = info.status.relative_accepted_tangle_time; + // Unwrap: We should always have parameters for this version. If we don't we can't recover. + network_info.protocol_parameters = info + .protocol_parameters_by_version(PROTOCOL_VERSION) + .expect("missing v3 protocol parameters") + .parameters + .clone(); } for (info, node_url) in nodes { diff --git a/sdk/src/client/secret/ledger_nano.rs b/sdk/src/client/secret/ledger_nano.rs index 6c6eb4c29b..2bb4a8ab71 100644 --- a/sdk/src/client/secret/ledger_nano.rs +++ b/sdk/src/client/secret/ledger_nano.rs @@ -552,7 +552,7 @@ fn merge_unlocks( // address already at this point, because the reference index needs to be lower // than the current block index if !input_address.is_ed25519() { - return Err(Error::MissingInputWithEd25519Address)?; + return Err(Error::MissingInputWithEd25519Address); } let unlock = unlocks.next().ok_or(Error::MissingInputWithEd25519Address)?; @@ -561,7 +561,7 @@ fn merge_unlocks( let Signature::Ed25519(ed25519_signature) = signature_unlock.signature(); let ed25519_address = match input_address { Address::Ed25519(ed25519_address) => ed25519_address, - _ => return Err(Error::MissingInputWithEd25519Address)?, + _ => return Err(Error::MissingInputWithEd25519Address), }; ed25519_signature.is_valid(&hashed_essence, &ed25519_address)?; } diff --git a/sdk/src/types/api/core/response.rs b/sdk/src/types/api/core/response.rs index 1a6fcf96b1..481e66189c 100644 --- a/sdk/src/types/api/core/response.rs +++ b/sdk/src/types/api/core/response.rs @@ -1,14 +1,14 @@ // Copyright 2020-2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use alloc::{string::String, vec::Vec}; +use alloc::{boxed::Box, collections::BTreeMap, string::String, vec::Vec}; use crate::types::block::{ output::{dto::OutputDto, OutputId, OutputMetadata, OutputWithMetadata}, parent::{ShallowLikeParents, StrongParents, WeakParents}, protocol::ProtocolParameters, semantic::TransactionFailureReason, - slot::{SlotCommitment, SlotIndex}, + slot::{EpochIndex, SlotCommitment, SlotCommitmentId, SlotIndex}, BlockId, }; @@ -25,10 +25,23 @@ pub struct InfoResponse { pub version: String, pub status: StatusResponse, pub metrics: MetricsResponse, - pub supported_protocol_versions: Vec, - pub protocol: ProtocolParameters, + pub protocol_parameters: ProtocolParametersMap, pub base_token: BaseTokenResponse, - pub features: Vec, + pub features: Box<[String]>, +} + +impl InfoResponse { + pub fn latest_protocol_parameters(&self) -> &ProtocolParametersResponse { + self.protocol_parameters.latest() + } + + pub fn protocol_parameters_by_version(&self, protocol_version: u8) -> Option<&ProtocolParametersResponse> { + self.protocol_parameters.by_version(protocol_version) + } + + pub fn protocol_parameters_by_epoch(&self, epoch_index: EpochIndex) -> Option<&ProtocolParametersResponse> { + self.protocol_parameters.by_epoch(epoch_index) + } } #[cfg(feature = "serde")] @@ -48,19 +61,19 @@ impl core::fmt::Display for InfoResponse { )] pub struct StatusResponse { pub is_healthy: bool, - #[serde(with = "crate::utils::serde::string")] - pub accepted_tangle_time: u64, - #[serde(with = "crate::utils::serde::string")] - pub relative_accepted_tangle_time: u64, - #[serde(with = "crate::utils::serde::string")] - pub confirmed_tangle_time: u64, - #[serde(with = "crate::utils::serde::string")] - pub relative_confirmed_tangle_time: u64, - pub latest_committed_slot: SlotIndex, + #[serde(with = "crate::utils::serde::option_string")] + pub accepted_tangle_time: Option, + #[serde(with = "crate::utils::serde::option_string")] + pub relative_accepted_tangle_time: Option, + #[serde(with = "crate::utils::serde::option_string")] + pub confirmed_tangle_time: Option, + #[serde(with = "crate::utils::serde::option_string")] + pub relative_confirmed_tangle_time: Option, + pub latest_commitment_id: SlotCommitmentId, pub latest_finalized_slot: SlotIndex, + pub latest_accepted_block_slot: Option, + pub latest_confirmed_block_slot: Option, pub pruning_slot: SlotIndex, - pub latest_accepted_block_id: BlockId, - pub latest_confirmed_block_id: BlockId, } /// Returned in [`InfoResponse`]. @@ -72,9 +85,86 @@ pub struct StatusResponse { serde(rename_all = "camelCase") )] pub struct MetricsResponse { + #[serde(with = "crate::utils::serde::string")] pub blocks_per_second: f64, + #[serde(with = "crate::utils::serde::string")] pub confirmed_blocks_per_second: f64, - pub confirmed_rate: f64, + #[serde(with = "crate::utils::serde::string")] + pub confirmation_rate: f64, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +pub struct ProtocolParametersResponse { + pub parameters: ProtocolParameters, + pub start_epoch: EpochIndex, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ProtocolParametersMap { + parameters: Box<[ProtocolParametersResponse]>, + version_map: BTreeMap, + epoch_map: BTreeMap, +} + +impl ProtocolParametersMap { + pub fn iter(&self) -> impl Iterator { + self.parameters.iter() + } + + pub fn latest(&self) -> &ProtocolParametersResponse { + &self.parameters[*self.version_map.last_key_value().unwrap().1] + } + + pub fn by_version(&self, protocol_version: u8) -> Option<&ProtocolParametersResponse> { + self.version_map.get(&protocol_version).map(|&i| &self.parameters[i]) + } + + pub fn by_epoch(&self, epoch_index: EpochIndex) -> Option<&ProtocolParametersResponse> { + self.epoch_map + .range(..=epoch_index) + .map(|(_, &i)| &self.parameters[i]) + .last() + } +} + +#[cfg(feature = "serde")] +mod serde_protocol_params_response { + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + use super::*; + + impl Serialize for ProtocolParametersMap { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.collect_seq(self.iter()) + } + } + + impl<'de> Deserialize<'de> for ProtocolParametersMap { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let parameters = Box::<[ProtocolParametersResponse]>::deserialize(deserializer)?; + let (mut version_map, mut epoch_map) = (BTreeMap::default(), BTreeMap::default()); + for (i, res) in parameters.iter().enumerate() { + version_map.insert(res.parameters.version(), i); + epoch_map.insert(res.start_epoch, i); + } + Ok(Self { + parameters, + version_map, + epoch_map, + }) + } + } } /// Returned in [`InfoResponse`]. @@ -89,7 +179,7 @@ pub struct BaseTokenResponse { pub name: String, pub ticker_symbol: String, pub unit: String, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, skip_serializing_if = "Option::is_none")] pub subunit: Option, pub decimals: u32, pub use_metric_prefix: bool, @@ -200,13 +290,13 @@ pub enum BlockFailureReason { pub struct BlockMetadataResponse { pub block_id: BlockId, // TODO: verify if really optional: https://github.com/iotaledger/tips-draft/pull/24/files#r1293426314 - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, skip_serializing_if = "Option::is_none")] pub block_state: Option, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, skip_serializing_if = "Option::is_none")] pub tx_state: Option, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, skip_serializing_if = "Option::is_none")] pub block_failure_reason: Option, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, skip_serializing_if = "Option::is_none")] pub tx_failure_reason: Option, } @@ -308,11 +398,11 @@ pub enum Relation { pub struct PeerResponse { pub id: String, pub multi_addresses: Vec, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, skip_serializing_if = "Option::is_none")] pub alias: Option, pub relation: Relation, pub connected: bool, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, skip_serializing_if = "Option::is_none")] pub gossip: Option, } diff --git a/sdk/src/types/block/basic.rs b/sdk/src/types/block/basic.rs index 9da34ffe48..092a4577aa 100644 --- a/sdk/src/types/block/basic.rs +++ b/sdk/src/types/block/basic.rs @@ -188,9 +188,9 @@ impl Packable for BasicBlock { let protocol_version = u8::unpack::<_, VERIFY>(unpacker, &()).coerce()?; - if VERIFY && protocol_version != protocol_params.protocol_version() { + if VERIFY && protocol_version != protocol_params.version() { return Err(UnpackError::Packable(Error::ProtocolVersionMismatch { - expected: protocol_params.protocol_version(), + expected: protocol_params.version(), actual: protocol_version, })); } diff --git a/sdk/src/types/block/core.rs b/sdk/src/types/block/core.rs index b97422a03e..7e31a06461 100644 --- a/sdk/src/types/block/core.rs +++ b/sdk/src/types/block/core.rs @@ -123,9 +123,9 @@ impl Packable for Block { let protocol_version = u8::unpack::<_, VERIFY>(unpacker, &()).coerce()?; - if VERIFY && protocol_version != protocol_params.protocol_version() { + if VERIFY && protocol_version != protocol_params.version() { return Err(UnpackError::Packable(Error::ProtocolVersionMismatch { - expected: protocol_params.protocol_version(), + expected: protocol_params.version(), actual: protocol_version, })); } @@ -220,7 +220,7 @@ impl BlockWrapper { /// Returns the protocol version of a [`Block`]. #[inline(always)] pub fn protocol_version(&self) -> u8 { - self.protocol_params.protocol_version + self.protocol_params.version() } /// Returns the protocol parameters of a [`Block`]. @@ -321,6 +321,7 @@ impl Block { /// Creates a new [`BlockBuilder`] to construct an instance of a [`ValidationBlock`]. #[inline(always)] + #[allow(clippy::too_many_arguments)] pub fn build_validation( protocol_params: ProtocolParameters, issuing_time: u64, @@ -694,9 +695,9 @@ pub(crate) mod dto { impl Block { pub fn try_from_dto(dto: BlockDto, protocol_params: ProtocolParameters) -> Result { - if dto.protocol_version != protocol_params.protocol_version() { + if dto.protocol_version != protocol_params.version() { return Err(Error::ProtocolVersionMismatch { - expected: protocol_params.protocol_version(), + expected: protocol_params.version(), actual: dto.protocol_version, }); } diff --git a/sdk/src/types/block/error.rs b/sdk/src/types/block/error.rs index 4fc9f05e1d..1a58681fc8 100644 --- a/sdk/src/types/block/error.rs +++ b/sdk/src/types/block/error.rs @@ -5,10 +5,10 @@ use alloc::string::{FromUtf8Error, String}; use core::{convert::Infallible, fmt}; use crypto::Error as CryptoError; -// use packable::bounded::BoundedU8; use prefix_hex::Error as HexError; use primitive_types::U256; +use super::slot::EpochIndex; use crate::types::block::{ context_input::RewardContextInputIndex, input::UtxoInput, @@ -85,6 +85,7 @@ pub enum Error { InvalidMetadataFeatureLength(>::Error), InvalidNativeTokenCount(>::Error), InvalidNetworkName(FromUtf8Error), + InvalidManaDecayFactors, InvalidNftIndex(>::Error), InvalidOutputAmount(u64), InvalidOutputCount(>::Error), @@ -108,6 +109,7 @@ pub enum Error { InvalidSignature, InvalidSignatureKind(u8), InvalidPublicKeyKind(u8), + InvalidStartEpoch(EpochIndex), InvalidStringPrefix(>::Error), InvalidTaggedDataLength(>::Error), InvalidTagFeatureLength(>::Error), @@ -250,6 +252,7 @@ impl fmt::Display for Error { } Self::InvalidNativeTokenCount(count) => write!(f, "invalid native token count: {count}"), Self::InvalidNetworkName(err) => write!(f, "invalid network name: {err}"), + Self::InvalidManaDecayFactors => write!(f, "invalid mana decay factors"), Self::InvalidNftIndex(index) => write!(f, "invalid nft index: {index}"), Self::InvalidOutputAmount(amount) => write!(f, "invalid output amount: {amount}"), Self::InvalidOutputCount(count) => write!(f, "invalid output count: {count}"), @@ -273,6 +276,7 @@ impl fmt::Display for Error { Self::InvalidSignature => write!(f, "invalid signature provided"), Self::InvalidSignatureKind(k) => write!(f, "invalid signature kind: {k}"), Self::InvalidPublicKeyKind(k) => write!(f, "invalid public key kind: {k}"), + Self::InvalidStartEpoch(index) => write!(f, "invalid start epoch: {index}"), Self::InvalidStringPrefix(p) => write!(f, "invalid string prefix: {p}"), Self::InvalidTaggedDataLength(length) => { write!(f, "invalid tagged data length {length}") diff --git a/sdk/src/types/block/output/account.rs b/sdk/src/types/block/output/account.rs index 62c03b434f..d16b30b103 100644 --- a/sdk/src/types/block/output/account.rs +++ b/sdk/src/types/block/output/account.rs @@ -295,7 +295,7 @@ impl AccountOutputBuilder { output.amount = match self.amount { OutputBuilderAmount::Amount(amount) => amount, OutputBuilderAmount::MinimumStorageDeposit(rent_structure) => { - Output::Account(output.clone()).rent_cost(&rent_structure) + Output::Account(output.clone()).rent_cost(rent_structure) } }; @@ -954,7 +954,7 @@ mod tests { let metadata = rand_metadata_feature(); let output = builder - .with_minimum_storage_deposit(*protocol_parameters.rent_structure()) + .with_minimum_storage_deposit(protocol_parameters.rent_structure()) .add_unlock_condition(rand_state_controller_address_unlock_condition_different_from( &account_id, )) @@ -1041,7 +1041,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_structure(), 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 8a30f337ae..cf8feb66e1 100644 --- a/sdk/src/types/block/output/basic.rs +++ b/sdk/src/types/block/output/basic.rs @@ -173,7 +173,7 @@ impl BasicOutputBuilder { output.amount = match self.amount { OutputBuilderAmount::Amount(amount) => amount, OutputBuilderAmount::MinimumStorageDeposit(rent_structure) => { - Output::Basic(output.clone()).rent_cost(&rent_structure) + Output::Basic(output.clone()).rent_cost(rent_structure) } }; @@ -500,7 +500,7 @@ mod tests { let metadata = rand_metadata_feature(); let output = builder - .with_minimum_storage_deposit(*protocol_parameters.rent_structure()) + .with_minimum_storage_deposit(protocol_parameters.rent_structure()) .add_unlock_condition(rand_address_unlock_condition()) .with_features([Feature::from(metadata.clone()), sender_1.into()]) .finish_with_params(ValidationParams::default().with_protocol_parameters(protocol_parameters.clone())) @@ -569,7 +569,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_structure()) .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 1f079b3df8..f3a1ff8520 100644 --- a/sdk/src/types/block/output/delegation.rs +++ b/sdk/src/types/block/output/delegation.rs @@ -24,6 +24,7 @@ use crate::types::{ }, protocol::ProtocolParameters, semantic::{TransactionFailureReason, ValidationContext}, + slot::EpochIndex, unlock::Unlock, Error, }, @@ -55,8 +56,8 @@ pub struct DelegationOutputBuilder { delegated_amount: u64, delegation_id: DelegationId, validator_id: AccountId, - start_epoch: u64, - end_epoch: u64, + start_epoch: EpochIndex, + end_epoch: EpochIndex, unlock_conditions: BTreeSet, } @@ -103,8 +104,8 @@ impl DelegationOutputBuilder { delegated_amount, delegation_id, validator_id, - start_epoch: 0, - end_epoch: 0, + start_epoch: 0.into(), + end_epoch: 0.into(), unlock_conditions: BTreeSet::new(), } } @@ -134,14 +135,14 @@ impl DelegationOutputBuilder { } /// Sets the start epoch to the provided value. - pub fn with_start_epoch(mut self, start_epoch: u64) -> Self { - self.start_epoch = start_epoch; + pub fn with_start_epoch(mut self, start_epoch: impl Into) -> Self { + self.start_epoch = start_epoch.into(); self } /// Sets the end epoch to the provided value. - pub fn with_end_epoch(mut self, end_epoch: u64) -> Self { - self.end_epoch = end_epoch; + pub fn with_end_epoch(mut self, end_epoch: impl Into) -> Self { + self.end_epoch = end_epoch.into(); self } @@ -191,7 +192,7 @@ impl DelegationOutputBuilder { output.amount = match self.amount { OutputBuilderAmount::Amount(amount) => amount, OutputBuilderAmount::MinimumStorageDeposit(rent_structure) => { - Output::Delegation(output.clone()).rent_cost(&rent_structure) + Output::Delegation(output.clone()).rent_cost(rent_structure) } }; @@ -244,9 +245,9 @@ pub struct DelegationOutput { /// The Account ID of the validator to which this output is delegating. validator_id: AccountId, /// The index of the first epoch for which this output delegates. - start_epoch: u64, + start_epoch: EpochIndex, /// The index of the last epoch for which this output delegates. - end_epoch: u64, + end_epoch: EpochIndex, unlock_conditions: UnlockConditions, } @@ -310,12 +311,12 @@ impl DelegationOutput { } /// Returns the start epoch of the [`DelegationOutput`]. - pub fn start_epoch(&self) -> u64 { + pub fn start_epoch(&self) -> EpochIndex { self.start_epoch } /// Returns the end epoch of the [`DelegationOutput`]. - pub fn end_epoch(&self) -> u64 { + pub fn end_epoch(&self) -> EpochIndex { self.end_epoch } @@ -382,8 +383,8 @@ impl Packable for DelegationOutput { let delegated_amount = u64::unpack::<_, VERIFY>(unpacker, &()).coerce()?; let delegation_id = DelegationId::unpack::<_, VERIFY>(unpacker, &()).coerce()?; let validator_id = AccountId::unpack::<_, VERIFY>(unpacker, &()).coerce()?; - let start_epoch = u64::unpack::<_, VERIFY>(unpacker, &()).coerce()?; - let end_epoch = u64::unpack::<_, VERIFY>(unpacker, &()).coerce()?; + let start_epoch = EpochIndex::unpack::<_, VERIFY>(unpacker, &()).coerce()?; + let end_epoch = EpochIndex::unpack::<_, VERIFY>(unpacker, &()).coerce()?; let unlock_conditions = UnlockConditions::unpack::<_, VERIFY>(unpacker, visitor)?; verify_unlock_conditions::(&unlock_conditions).map_err(UnpackError::Packable)?; @@ -438,10 +439,8 @@ pub(crate) mod dto { pub delegated_amount: u64, pub delegation_id: DelegationId, pub validator_id: AccountId, - #[serde(with = "string")] - start_epoch: u64, - #[serde(with = "string")] - end_epoch: u64, + start_epoch: EpochIndex, + end_epoch: EpochIndex, pub unlock_conditions: Vec, } @@ -492,8 +491,8 @@ pub(crate) mod dto { delegated_amount: u64, delegation_id: &DelegationId, validator_id: &AccountId, - start_epoch: u64, - end_epoch: u64, + start_epoch: impl Into, + end_epoch: impl Into, unlock_conditions: Vec, params: impl Into> + Send, ) -> Result { diff --git a/sdk/src/types/block/output/feature/staking.rs b/sdk/src/types/block/output/feature/staking.rs index 97f52f7e6b..11742f69df 100644 --- a/sdk/src/types/block/output/feature/staking.rs +++ b/sdk/src/types/block/output/feature/staking.rs @@ -1,6 +1,8 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use crate::types::block::slot::EpochIndex; + /// Stakes coins to become eligible for committee selection, validate the network and receive Mana rewards. #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, packable::Packable)] pub struct StakingFeature { @@ -9,9 +11,9 @@ pub struct StakingFeature { /// The fixed cost of the validator, which it receives as part of its Mana rewards. fixed_cost: u64, /// The epoch index in which the staking started. - start_epoch: u64, + start_epoch: EpochIndex, /// The epoch index in which the staking ends. - end_epoch: u64, + end_epoch: EpochIndex, } impl StakingFeature { @@ -19,12 +21,17 @@ impl StakingFeature { pub const KIND: u8 = 5; /// Creates a new [`StakingFeature`]. - pub fn new(staked_amount: u64, fixed_cost: u64, start_epoch: u64, end_epoch: u64) -> Self { + pub fn new( + staked_amount: u64, + fixed_cost: u64, + start_epoch: impl Into, + end_epoch: impl Into, + ) -> Self { Self { staked_amount, fixed_cost, - start_epoch, - end_epoch, + start_epoch: start_epoch.into(), + end_epoch: end_epoch.into(), } } @@ -39,12 +46,12 @@ impl StakingFeature { } /// Returns the start epoch of the [`StakingFeature`]. - pub fn start_epoch(&self) -> u64 { + pub fn start_epoch(&self) -> EpochIndex { self.start_epoch } /// Returns the end epoch of the [`StakingFeature`]. - pub fn end_epoch(&self) -> u64 { + pub fn end_epoch(&self) -> EpochIndex { self.end_epoch } } @@ -64,10 +71,8 @@ mod dto { staked_amount: u64, #[serde(with = "string")] fixed_cost: u64, - #[serde(with = "string")] - start_epoch: u64, - #[serde(with = "string")] - end_epoch: u64, + start_epoch: EpochIndex, + end_epoch: EpochIndex, } impl From<&StakingFeature> for StakingFeatureDto { diff --git a/sdk/src/types/block/output/foundry.rs b/sdk/src/types/block/output/foundry.rs index c9b3c2f7e0..df0fe7e03c 100644 --- a/sdk/src/types/block/output/foundry.rs +++ b/sdk/src/types/block/output/foundry.rs @@ -237,7 +237,7 @@ impl FoundryOutputBuilder { output.amount = match self.amount { OutputBuilderAmount::Amount(amount) => amount, OutputBuilderAmount::MinimumStorageDeposit(rent_structure) => { - Output::Foundry(output.clone()).rent_cost(&rent_structure) + Output::Foundry(output.clone()).rent_cost(rent_structure) } }; @@ -793,7 +793,7 @@ mod tests { assert!(output.immutable_features().is_empty()); let output = builder - .with_minimum_storage_deposit(*protocol_parameters.rent_structure()) + .with_minimum_storage_deposit(protocol_parameters.rent_structure()) .add_unlock_condition(ImmutableAccountAddressUnlockCondition::new(rand_account_address())) .finish_with_params(&protocol_parameters) .unwrap(); @@ -851,7 +851,7 @@ mod tests { test_split_dto(builder); let builder = FoundryOutput::build_with_minimum_storage_deposit( - *protocol_parameters.rent_structure(), + protocol_parameters.rent_structure(), 123, rand_token_scheme(), ) diff --git a/sdk/src/types/block/output/mod.rs b/sdk/src/types/block/output/mod.rs index 6fa5c0c972..2773f1d932 100644 --- a/sdk/src/types/block/output/mod.rs +++ b/sdk/src/types/block/output/mod.rs @@ -385,7 +385,7 @@ impl Output { /// 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.rent_cost(&rent_structure); + let required_output_amount = self.rent_cost(rent_structure); if self.amount() < required_output_amount { return Err(Error::InsufficientStorageDepositAmount { @@ -470,7 +470,7 @@ impl Packable for Output { } impl Rent for Output { - fn weighted_bytes(&self, rent_structure: &RentStructure) -> u64 { + fn weighted_bytes(&self, rent_structure: RentStructure) -> u64 { self.packed_len() as u64 * rent_structure.byte_factor_data() as u64 } } diff --git a/sdk/src/types/block/output/nft.rs b/sdk/src/types/block/output/nft.rs index c76975ea57..12a176c24c 100644 --- a/sdk/src/types/block/output/nft.rs +++ b/sdk/src/types/block/output/nft.rs @@ -220,7 +220,7 @@ impl NftOutputBuilder { output.amount = match self.amount { OutputBuilderAmount::Amount(amount) => amount, OutputBuilderAmount::MinimumStorageDeposit(rent_structure) => { - Output::Nft(output.clone()).rent_cost(&rent_structure) + Output::Nft(output.clone()).rent_cost(rent_structure) } }; @@ -673,7 +673,7 @@ mod tests { assert!(output.immutable_features().is_empty()); let output = builder - .with_minimum_storage_deposit(*protocol_parameters.rent_structure()) + .with_minimum_storage_deposit(protocol_parameters.rent_structure()) .add_unlock_condition(rand_address_unlock_condition()) .finish_with_params(protocol_parameters.token_supply()) .unwrap(); @@ -741,7 +741,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_structure(), 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/rent.rs b/sdk/src/types/block/output/rent.rs index 938200e34a..eb11f69527 100644 --- a/sdk/src/types/block/output/rent.rs +++ b/sdk/src/types/block/output/rent.rs @@ -125,7 +125,7 @@ impl Packable for RentStructure { /// A trait to facilitate the computation of the byte cost of block outputs, which is central to dust protection. pub trait Rent { /// Computes the byte offset given a [`RentStructure`]. - fn byte_offset(&self, rent_structure: &RentStructure) -> u32 { + fn byte_offset(&self, rent_structure: RentStructure) -> u32 { // The ID of the output. size_of::() as u32 * rent_structure.v_byte_factor_key as u32 // The ID of the block in which the transaction payload that created this output was included. @@ -137,17 +137,17 @@ pub trait Rent { } /// Different fields in a type lead to different storage requirements for the ledger state. - fn weighted_bytes(&self, config: &RentStructure) -> u64; + fn weighted_bytes(&self, config: RentStructure) -> u64; /// Computes the rent cost given a [`RentStructure`]. - fn rent_cost(&self, rent_structure: &RentStructure) -> u64 { + fn rent_cost(&self, rent_structure: RentStructure) -> u64 { rent_structure.v_byte_cost as u64 * (self.weighted_bytes(rent_structure) + self.byte_offset(rent_structure) as u64) } } impl Rent for [T; N] { - fn weighted_bytes(&self, config: &RentStructure) -> u64 { + fn weighted_bytes(&self, config: RentStructure) -> u64 { self.iter().map(|elem| elem.weighted_bytes(config)).sum() } } @@ -196,6 +196,6 @@ impl MinimumStorageDepositBasicOutput { } pub fn finish(self) -> Result { - Ok(self.builder.finish_output(self.token_supply)?.rent_cost(&self.config)) + Ok(self.builder.finish_output(self.token_supply)?.rent_cost(self.config)) } } diff --git a/sdk/src/types/block/payload/transaction/essence/regular.rs b/sdk/src/types/block/payload/transaction/essence/regular.rs index bf19f228a2..8fb7b5900e 100644 --- a/sdk/src/types/block/payload/transaction/essence/regular.rs +++ b/sdk/src/types/block/payload/transaction/essence/regular.rs @@ -391,7 +391,7 @@ fn verify_outputs(outputs: &[Output], visitor: &ProtocolPara } } - output.verify_storage_deposit(*visitor.rent_structure(), visitor.token_supply())?; + output.verify_storage_deposit(visitor.rent_structure(), visitor.token_supply())?; } } diff --git a/sdk/src/types/block/protocol.rs b/sdk/src/types/block/protocol.rs index 3de670aa41..a8133b6037 100644 --- a/sdk/src/types/block/protocol.rs +++ b/sdk/src/types/block/protocol.rs @@ -5,42 +5,80 @@ use alloc::string::String; use core::borrow::Borrow; use crypto::hashes::{blake2b::Blake2b256, Digest}; -use packable::{prefix::StringPrefix, Packable, PackableExt}; +use getset::{CopyGetters, Getters}; +use packable::{ + prefix::{BoxedSlicePrefix, StringPrefix}, + Packable, PackableExt, +}; -use super::{address::Hrp, slot::SlotIndex}; +use super::{ + address::Hrp, + slot::{EpochIndex, SlotIndex}, +}; use crate::types::block::{helper::network_name_to_id, output::RentStructure, ConvertTo, Error, PROTOCOL_VERSION}; -/// Defines the parameters of the protocol. -#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Packable)] +/// Defines the parameters of the protocol at a particular version. +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Packable, Getters, CopyGetters)] #[packable(unpack_error = Error)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(rename_all = "camelCase") )] +#[getset(get_copy = "pub")] pub struct ProtocolParameters { - // The version of the protocol running. - #[serde(rename = "version")] - pub(crate) protocol_version: u8, - // The human friendly name of the network. + /// The version of the protocol running. + pub(crate) version: u8, + /// The human friendly name of the network. #[packable(unpack_error_with = |err| Error::InvalidNetworkName(err.into_item_err()))] #[serde(with = "crate::utils::serde::string_prefix")] - network_name: StringPrefix, - // The HRP prefix used for Bech32 addresses in the network. - bech32_hrp: Hrp, - // The below max depth parameter of the network. - below_max_depth: u8, - // The rent structure used by given node/network. - rent_structure: RentStructure, - // TokenSupply defines the current token supply on the network. + #[getset(skip)] + pub(crate) network_name: StringPrefix, + /// 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, + /// The work score structure used by the node/network. + pub(crate) work_score_structure: WorkScoreStructure, + /// TokenSupply defines the current token supply on the network. #[serde(with = "crate::utils::serde::string")] - token_supply: u64, - // Genesis timestamp at which the slots start to count. - #[serde(alias = "genesisUnixTimestamp")] - genesis_unix_timestamp: u32, - // Duration of each slot in seconds. - #[serde(alias = "slotDurationInSeconds")] - slot_duration_in_seconds: u8, + pub(crate) token_supply: u64, + /// Genesis timestamp at which the slots start to count. + #[serde(with = "crate::utils::serde::string")] + pub(crate) genesis_unix_timestamp: u32, + /// Duration of each slot in seconds. + pub(crate) slot_duration_in_seconds: u8, + /// The number of slots in an epoch expressed as an exponent of 2. + pub(crate) slots_per_epoch_exponent: u32, + /// The amount of potential Mana generated by 1 IOTA in 1 slot. + pub(crate) mana_generation_rate: u32, + /// The scaling of `mana_generation_rate` expressed as an exponent of 2. + pub(crate) mana_generation_rate_exponent: u32, + #[packable(unpack_error_with = |_| Error::InvalidManaDecayFactors)] + #[getset(skip)] + /// A lookup table of epoch index diff to mana decay factor. + pub(crate) mana_decay_factors: BoxedSlicePrefix, + /// The scaling of `mana_decay_factors` expressed as an exponent of 2. + pub(crate) mana_decay_factors_exponent: u32, + /// An integer approximation of the sum of decay over epochs. + pub(crate) mana_decay_factor_epochs_sum: u32, + /// The scaling of `mana_decay_factor_epochs_sum` expressed as an exponent of 2. + pub(crate) mana_decay_factor_epochs_sum_exponent: u32, + /// The unbonding period in epochs before an account can stop staking. + pub(crate) staking_unbonding_period: EpochIndex, + /// TODO + pub(crate) liveness_threshold: SlotIndex, + /// Minimum age relative to the accepted tangle time slot index that a slot can be committed. + pub(crate) min_committable_age: SlotIndex, + /// Maximum age for a slot commitment to be included in a block relative to the slot index of the block issuing + /// time. + pub(crate) max_committable_age: SlotIndex, + /// TODO + pub(crate) epoch_nearing_threshold: SlotIndex, + /// Parameters used to calculate the Reference Mana Cost (RMC). + pub(crate) congestion_control_parameters: CongestionControlParameters, + /// TODO + pub(crate) version_signaling: VersionSignalingParameters, } // This implementation is required to make [`ProtocolParameters`] a [`Packable`] visitor. @@ -52,18 +90,31 @@ impl Borrow<()> for ProtocolParameters { impl Default for ProtocolParameters { fn default() -> Self { - // PANIC: These values are known to be correct. - Self::new( - PROTOCOL_VERSION, - String::from("iota-core-testnet"), - "smr", - 15, - RentStructure::default(), - 1_813_620_509_061_365, - 1582328545, - 10, - ) - .unwrap() + Self { + version: PROTOCOL_VERSION, + // 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(), + work_score_structure: Default::default(), + token_supply: 1_813_620_509_061_365, + genesis_unix_timestamp: 1582328545, + slot_duration_in_seconds: 10, + epoch_nearing_threshold: 20.into(), + slots_per_epoch_exponent: Default::default(), + mana_generation_rate: Default::default(), + mana_generation_rate_exponent: Default::default(), + mana_decay_factors: Default::default(), + mana_decay_factors_exponent: Default::default(), + mana_decay_factor_epochs_sum: Default::default(), + mana_decay_factor_epochs_sum_exponent: Default::default(), + staking_unbonding_period: 10.into(), + liveness_threshold: 5.into(), + min_committable_age: 10.into(), + max_committable_age: 20.into(), + congestion_control_parameters: Default::default(), + version_signaling: Default::default(), + } } } @@ -71,32 +122,28 @@ impl ProtocolParameters { /// Creates a new [`ProtocolParameters`]. #[allow(clippy::too_many_arguments)] pub fn new( - protocol_version: u8, - network_name: String, + version: u8, + network_name: impl Into, bech32_hrp: impl ConvertTo, - below_max_depth: u8, rent_structure: RentStructure, token_supply: u64, genesis_unix_timestamp: u32, slot_duration_in_seconds: u8, + epoch_nearing_threshold: impl Into, ) -> Result { Ok(Self { - protocol_version, - network_name: >::try_from(network_name).map_err(Error::InvalidStringPrefix)?, + version, + network_name: >::try_from(network_name.into()).map_err(Error::InvalidStringPrefix)?, bech32_hrp: bech32_hrp.convert()?, - below_max_depth, rent_structure, token_supply, genesis_unix_timestamp, slot_duration_in_seconds, + epoch_nearing_threshold: epoch_nearing_threshold.into(), + ..Default::default() }) } - /// Returns the protocol version of the [`ProtocolParameters`]. - pub fn protocol_version(&self) -> u8 { - self.protocol_version - } - /// Returns the network name of the [`ProtocolParameters`]. pub fn network_name(&self) -> &str { &self.network_name @@ -107,49 +154,160 @@ impl ProtocolParameters { network_name_to_id(&self.network_name) } - /// Returns the bech32 HRP of the [`ProtocolParameters`]. - pub fn bech32_hrp(&self) -> &Hrp { - &self.bech32_hrp - } - - /// Returns the below max depth of the [`ProtocolParameters`]. - pub fn below_max_depth(&self) -> u8 { - self.below_max_depth + /// Returns the mana decay factors slice of the [`ProtocolParameters`]. + pub fn mana_decay_factors(&self) -> &[u32] { + &self.mana_decay_factors } - /// Returns the rent structure of the [`ProtocolParameters`]. - pub fn rent_structure(&self) -> &RentStructure { - &self.rent_structure + /// Returns the mana decay factor for the given epoch index. + pub fn mana_decay_factor_at(&self, epoch_index: EpochIndex) -> Option { + self.mana_decay_factors.get(*epoch_index as usize).copied() } - /// Returns the token supply of the [`ProtocolParameters`]. - pub fn token_supply(&self) -> u64 { - self.token_supply - } - - pub fn genesis_unix_timestamp(&self) -> u32 { - self.genesis_unix_timestamp - } - - pub fn slot_duration_in_seconds(&self) -> u8 { - self.slot_duration_in_seconds + /// Returns the slots per epoch of the [`ProtocolParameters`]. + pub fn slots_per_epoch(&self) -> u64 { + 2_u64.pow(self.slots_per_epoch_exponent()) } + /// Gets a [`SlotIndex`] from a unix timestamp. pub fn slot_index(&self, timestamp: u64) -> SlotIndex { - calc_slot_index( + SlotIndex::from_timestamp( timestamp, self.genesis_unix_timestamp(), self.slot_duration_in_seconds(), ) } + /// Returns the hash of the [`ProtocolParameters`]. pub fn hash(&self) -> ProtocolParametersHash { ProtocolParametersHash::new(Blake2b256::digest(self.pack_to_vec()).into()) } } -pub fn calc_slot_index(timestamp: u64, genesis_unix_timestamp: u32, slot_duration_in_seconds: u8) -> SlotIndex { - (1 + (timestamp - genesis_unix_timestamp as u64) / slot_duration_in_seconds as u64).into() +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Packable, CopyGetters)] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +#[packable(unpack_error = Error)] +#[getset(get_copy = "pub")] +pub struct WorkScoreStructure { + /// Modifier for network traffic per byte. + data_kilobyte: u32, + /// Modifier for work done to process a block. + block: u32, + /// Modifier for slashing when there are insufficient strong tips. + missing_parent: u32, + /// Modifier for loading UTXOs and performing mana calculations. + input: u32, + /// Modifier for loading and checking the context input. + context_input: u32, + /// Modifier for storing UTXOs. + output: u32, + /// Modifier for calculations using native tokens. + native_token: u32, + /// Modifier for storing staking features. + staking: u32, + /// Modifier for storing block issuer features. + block_issuer: u32, + /// Modifier for accessing the account-based ledger to transform mana to Block Issuance Credits. + allotment: u32, + /// Modifier for the block signature check. + signature_ed25519: u32, + /// The minimum count of strong parents in a basic block. + min_strong_parents_threshold: u32, +} + +impl Default for WorkScoreStructure { + fn default() -> Self { + Self { + data_kilobyte: 0, + block: 100, + missing_parent: 500, + input: 20, + context_input: 20, + output: 20, + native_token: 20, + staking: 100, + block_issuer: 100, + allotment: 100, + signature_ed25519: 200, + min_strong_parents_threshold: 4, + } + } +} + +/// Defines the parameters used to calculate the Reference Mana Cost (RMC). +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Packable, CopyGetters)] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +#[packable(unpack_error = Error)] +#[getset(get_copy = "pub")] +pub struct CongestionControlParameters { + /// Minimum value of the RMC. + #[serde(with = "crate::utils::serde::string")] + rmc_min: u64, + /// Increase step size of the RMC. + #[serde(with = "crate::utils::serde::string")] + increase: u64, + /// Decrease step size of the RMC. + #[serde(with = "crate::utils::serde::string")] + decrease: u64, + /// Threshold for increasing the RMC. + increase_threshold: u32, + /// Threshold for decreasing the RMC. + decrease_threshold: u32, + /// Rate at which the scheduler runs (in workscore units per second). + scheduler_rate: u32, + /// Minimum amount of Mana that an account must have to schedule a block. + #[serde(with = "crate::utils::serde::string")] + min_mana: u64, + /// Maximum size of the buffer. TODO what buffer? + max_buffer_size: u32, +} + +impl Default for CongestionControlParameters { + fn default() -> Self { + Self { + rmc_min: 500, + increase: 500, + decrease: 500, + increase_threshold: 800000, + decrease_threshold: 500000, + scheduler_rate: 100000, + min_mana: 1, + max_buffer_size: 3276800, + } + } +} + +// TODO docs +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Packable, CopyGetters)] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +#[packable(unpack_error = Error)] +#[getset(get_copy = "pub")] +pub struct VersionSignalingParameters { + window_size: u32, + window_target_ratio: u32, + activation_offset: u32, +} + +impl Default for VersionSignalingParameters { + fn default() -> Self { + Self { + window_size: 7, + window_target_ratio: 5, + activation_offset: 7, + } + } } /// Returns a [`ProtocolParameters`] for testing purposes. @@ -157,13 +315,13 @@ pub fn calc_slot_index(timestamp: u64, genesis_unix_timestamp: u32, slot_duratio pub fn protocol_parameters() -> ProtocolParameters { ProtocolParameters::new( 2, - String::from("testnet"), + "testnet", "rms", - 15, crate::types::block::output::RentStructure::new(500, 10, 1), 1_813_620_509_061_365, 1582328545, 10, + 20, ) .unwrap() } diff --git a/sdk/src/types/block/rand/output/feature.rs b/sdk/src/types/block/rand/output/feature.rs index 8b964a2cec..fcf1c4fafd 100644 --- a/sdk/src/types/block/rand/output/feature.rs +++ b/sdk/src/types/block/rand/output/feature.rs @@ -52,7 +52,7 @@ pub fn rand_block_issuer_feature() -> BlockIssuerFeature { /// Generates a random [`StakingFeature`]. pub fn rand_staking_feature() -> StakingFeature { - StakingFeature::new(rand_number(), rand_number(), rand_number(), rand_number()) + StakingFeature::new(rand_number(), rand_number(), rand_number::(), rand_number::()) } fn rand_feature_from_flag(flag: &FeatureFlags) -> Feature { diff --git a/sdk/src/types/block/slot/epoch.rs b/sdk/src/types/block/slot/epoch.rs new file mode 100644 index 0000000000..47857c394f --- /dev/null +++ b/sdk/src/types/block/slot/epoch.rs @@ -0,0 +1,175 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use derive_more::{Deref, Display, From, FromStr}; + +use super::SlotIndex; +use crate::types::block::Error; + +/// The tangle timeline is divided into epochs, and each epoch has a corresponding epoch index. Epochs are further +/// subdivided into slots, each with a [`SlotIndex`]. +/// To calculate the epoch index of a timestamp, `slotsPerEpochExponent` and `slotDurationInSeconds` are needed. +/// An epoch consists of `2^slotsPerEpochExponent` slots. +/// +/// # Examples +/// +/// Given `slotDurationInSeconds == 10` and `slotsPerEpochExponent == 3` +/// +/// ## Slots +/// +/// | slot
index | start timestamp
(inclusive) | end timestamp
(exclusive) | +/// | :- | :------------ | :------------ | +/// | 0 | -infinity | genesis | +/// | 1 | genesis | genesis + 10s | +/// | 2 | genesis + 10s | genesis + 20s | +/// +/// ## Epochs +/// +/// | epoch
index | start slot
(inclusive) | end slot
(exclusive) | +/// | :- | :-- | :-- | +/// | 0 | 0 | 8 | +/// | 1 | 8 | 16 | +/// | 2 | 16 | 24 | +// ... +#[derive( + Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, Deref, Display, FromStr, packable::Packable, +)] +#[repr(transparent)] +pub struct EpochIndex(u64); + +impl EpochIndex { + /// Creates a new [`EpochIndex`]. + pub fn new(index: u64) -> Self { + Self::from(index) + } + + /// Gets the epoch index given a [`SlotIndex`]. + pub fn from_slot_index( + slot_index: SlotIndex, + slots_per_epoch_exponent_iter: impl Iterator, + ) -> Result { + let mut slot_index = *slot_index; + let mut res = 0; + let mut last = None; + for (start_epoch, exponent) in slots_per_epoch_exponent_iter { + if let Some((last_start_epoch, last_exponent)) = last { + if *start_epoch <= last_start_epoch { + return Err(Error::InvalidStartEpoch(start_epoch)); + } + // Get the number of slots this range of epochs represents + let slots_in_range = (*start_epoch - last_start_epoch) << last_exponent; + // Check whether the slot index is contained in this range + if slot_index > slots_in_range { + // Update the slot index so it is in the context of the next epoch + slot_index -= slots_in_range; + } else { + break; + } + } + if *start_epoch > res { + // We can't calculate the epoch if we don't have the exponent for the containing range + if slot_index > 0 { + return Err(Error::InvalidStartEpoch(start_epoch)); + } else { + break; + } + } + res = *start_epoch + (slot_index >> exponent); + last = Some((*start_epoch, exponent)); + } + Ok(Self(res)) + } +} + +impl From for u64 { + fn from(epoch_index: EpochIndex) -> Self { + *epoch_index + } +} + +#[cfg(feature = "serde")] +string_serde_impl!(EpochIndex); + +#[cfg(test)] +mod test { + use super::*; + use crate::types::block::protocol::ProtocolParameters; + + #[test] + fn epoch_index_from_slot() { + let v3_params = ProtocolParameters { + version: 3, + slots_per_epoch_exponent: 10, + ..Default::default() + }; + let v4_params = ProtocolParameters { + version: 4, + slots_per_epoch_exponent: 11, + ..Default::default() + }; + let params = [(EpochIndex(0), v3_params.clone()), (EpochIndex(10), v4_params)]; + let slots_per_epoch_exponent_iter = params + .iter() + .map(|(start_index, params)| (*start_index, params.slots_per_epoch_exponent())); + + let slot_index = SlotIndex::new(3000); + let epoch_index = EpochIndex::from_slot_index(slot_index, slots_per_epoch_exponent_iter.clone()); + assert_eq!(epoch_index, Ok(EpochIndex(2))); + + let slot_index = SlotIndex::new(10 * v3_params.slots_per_epoch() + 3000); + let epoch_index = EpochIndex::from_slot_index(slot_index, slots_per_epoch_exponent_iter.clone()); + assert_eq!(epoch_index, Ok(EpochIndex(11))); + } + + #[test] + fn invalid_params() { + let v3_params = ProtocolParameters { + version: 3, + slots_per_epoch_exponent: 10, + ..Default::default() + }; + let v4_params = ProtocolParameters { + version: 4, + slots_per_epoch_exponent: 11, + ..Default::default() + }; + let v5_params = ProtocolParameters { + version: 5, + slots_per_epoch_exponent: 12, + ..Default::default() + }; + let slot_index = SlotIndex::new(100000); + + // Params must cover the entire history starting at epoch 0 + let params = [(EpochIndex(10), v4_params.clone()), (EpochIndex(20), v5_params.clone())]; + let slots_per_epoch_exponent_iter = params + .iter() + .map(|(start_index, params)| (*start_index, params.slots_per_epoch_exponent())); + let epoch_index = EpochIndex::from_slot_index(slot_index, slots_per_epoch_exponent_iter); + assert_eq!(epoch_index, Err(Error::InvalidStartEpoch(EpochIndex(10)))); + + // Params must not contain duplicate start epochs + let params = [ + (EpochIndex(0), v3_params.clone()), + (EpochIndex(10), v4_params.clone()), + (EpochIndex(10), v5_params.clone()), + ]; + let slots_per_epoch_exponent_iter = params + .iter() + .map(|(start_index, params)| (*start_index, params.slots_per_epoch_exponent())); + let epoch_index = EpochIndex::from_slot_index(slot_index, slots_per_epoch_exponent_iter); + assert_eq!(epoch_index, Err(Error::InvalidStartEpoch(EpochIndex(10)))); + + // Params must be properly ordered + let params = [ + (EpochIndex(10), v4_params), + (EpochIndex(0), v3_params), + (EpochIndex(20), v5_params), + ]; + let slots_per_epoch_exponent_iter = params + .iter() + .map(|(start_index, params)| (*start_index, params.slots_per_epoch_exponent())); + let epoch_index = EpochIndex::from_slot_index(slot_index, slots_per_epoch_exponent_iter); + assert_eq!(epoch_index, Err(Error::InvalidStartEpoch(EpochIndex(10)))); + } +} diff --git a/sdk/src/types/block/slot/index.rs b/sdk/src/types/block/slot/index.rs index 84625885de..7eba8d7d65 100644 --- a/sdk/src/types/block/slot/index.rs +++ b/sdk/src/types/block/slot/index.rs @@ -3,9 +3,25 @@ use derive_more::{Deref, Display, From, FromStr}; -/// Timeline is divided into slots, and each slot has a corresponding slot index. -/// To calculate the slot index of a timestamp, `genesisTimestamp` and the duration of a slot are needed. +use super::EpochIndex; +use crate::types::block::Error; + +/// The tangle timeline is divided into epochs, and each epoch has a corresponding [`EpochIndex`]. Epochs are further +/// subdivided into slots, each with a slot index. +/// To calculate the slot index of a timestamp, `genesisUnixTimestamp` and the `slotDurationInSeconds` are needed. /// The slot index of timestamp `ts` is `(ts - genesisTimestamp)/duration + 1`. +/// +/// # Examples +/// +/// Given `slotDurationInSeconds == 10` +/// +/// ## Slots +/// +/// | slot
index | start timestamp
(inclusive) | end timestamp
(exclusive) | +/// | :- | :------------ | :------------ | +/// | 0 | -infinity | genesis | +/// | 1 | genesis | genesis + 10s | +/// | 2 | genesis + 10s | genesis + 20s | #[derive( Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, Deref, Display, FromStr, packable::Packable, )] @@ -17,6 +33,24 @@ impl SlotIndex { pub fn new(index: u64) -> Self { Self::from(index) } + + /// Gets the [`EpochIndex`] of this slot. + pub fn to_epoch_index( + self, + slots_per_epoch_exponent_iter: impl Iterator, + ) -> Result { + EpochIndex::from_slot_index(self, slots_per_epoch_exponent_iter) + } + + /// Gets the slot index of a unix timestamp. + pub fn from_timestamp(timestamp: u64, genesis_unix_timestamp: u32, slot_duration_in_seconds: u8) -> SlotIndex { + (1 + (timestamp - genesis_unix_timestamp as u64) / slot_duration_in_seconds as u64).into() + } + + /// Converts the slot index into the corresponding unix timestamp. + pub fn to_timestamp(self, genesis_unix_timestamp: u32, slot_duration_in_seconds: u8) -> u64 { + ((self.0 - 1) * slot_duration_in_seconds as u64) + genesis_unix_timestamp as u64 + } } impl From for u64 { diff --git a/sdk/src/types/block/slot/mod.rs b/sdk/src/types/block/slot/mod.rs index d3a21fc517..ef8e74277b 100644 --- a/sdk/src/types/block/slot/mod.rs +++ b/sdk/src/types/block/slot/mod.rs @@ -3,7 +3,10 @@ mod commitment; mod commitment_id; +mod epoch; mod index; mod roots_id; -pub use self::{commitment::SlotCommitment, commitment_id::SlotCommitmentId, index::SlotIndex, roots_id::RootsId}; +pub use self::{ + commitment::SlotCommitment, commitment_id::SlotCommitmentId, epoch::EpochIndex, index::SlotIndex, roots_id::RootsId, +}; diff --git a/sdk/src/types/block/validation.rs b/sdk/src/types/block/validation.rs index 2428c1d065..92d4370220 100644 --- a/sdk/src/types/block/validation.rs +++ b/sdk/src/types/block/validation.rs @@ -22,6 +22,7 @@ pub type ValidationBlock = BlockWrapper; impl BlockBuilder { /// Creates a new [`BlockBuilder`] for a [`ValidationBlock`]. #[inline(always)] + #[allow(clippy::too_many_arguments)] pub fn new( protocol_params: ProtocolParameters, issuing_time: u64, @@ -182,9 +183,9 @@ impl Packable for ValidationBlock { let protocol_version = u8::unpack::<_, VERIFY>(unpacker, &()).coerce()?; - if VERIFY && protocol_version != protocol_params.protocol_version() { + if VERIFY && protocol_version != protocol_params.version() { return Err(UnpackError::Packable(Error::ProtocolVersionMismatch { - expected: protocol_params.protocol_version(), + expected: protocol_params.version(), actual: protocol_version, })); } diff --git a/sdk/src/wallet/account/mod.rs b/sdk/src/wallet/account/mod.rs index 092f29c8a2..70a7d3a4b7 100644 --- a/sdk/src/wallet/account/mod.rs +++ b/sdk/src/wallet/account/mod.rs @@ -611,13 +611,13 @@ fn serialize() { let protocol_parameters = ProtocolParameters::new( 2, - String::from("testnet"), + "testnet", "rms", - 15, crate::types::block::output::RentStructure::new(500, 10, 1), 1_813_620_509_061_365, 1582328545, 10, + 20, ) .unwrap(); diff --git a/sdk/src/wallet/account/operations/balance.rs b/sdk/src/wallet/account/operations/balance.rs index 09df04f29a..e45485efda 100644 --- a/sdk/src/wallet/account/operations/balance.rs +++ b/sdk/src/wallet/account/operations/balance.rs @@ -88,7 +88,7 @@ where } let output = &data.output; - let rent = output.rent_cost(&rent_structure); + let rent = output.rent_cost(rent_structure); // 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/transaction/mod.rs b/sdk/src/wallet/account/operations/transaction/mod.rs index 756f6522cb..4f3f44fe35 100644 --- a/sdk/src/wallet/account/operations/transaction/mod.rs +++ b/sdk/src/wallet/account/operations/transaction/mod.rs @@ -72,10 +72,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_structure(), - protocol_parameters.token_supply(), - )?; + output.verify_storage_deposit(protocol_parameters.rent_structure(), 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 b622ea54e7..4464a84f78 100644 --- a/sdk/src/wallet/account/operations/transaction/prepare_output.rs +++ b/sdk/src/wallet/account/operations/transaction/prepare_output.rs @@ -171,7 +171,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_structure); + available_base_coin += existing_nft_output_data.output.rent_cost(rent_structure); } if final_amount > available_base_coin { diff --git a/sdk/tests/client/client_builder.rs b/sdk/tests/client/client_builder.rs index 4f0fcd4470..22b07a23f1 100644 --- a/sdk/tests/client/client_builder.rs +++ b/sdk/tests/client/client_builder.rs @@ -1,7 +1,10 @@ // Copyright 2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use iota_sdk::client::{Client, ClientBuilder}; +use iota_sdk::{ + client::{Client, ClientBuilder}, + types::block::protocol::ProtocolParameters, +}; #[tokio::test] async fn invalid_url() { @@ -17,41 +20,28 @@ async fn valid_url() { #[tokio::test] async fn client_builder() { - let client_builder_json = r#"{ - "nodes":[ + let client_builder_json = serde_json::json!({ + "nodes": [ { "url":"http://localhost:14265/", - "disabled":false + "disabled": false } ], - "ignoreNodeHealth":true, - "nodeSyncInterval":{ - "secs":60, - "nanos":0 + "ignoreNodeHealth": true, + "nodeSyncInterval": { + "secs": 60, + "nanos": 0 }, - "quorum":false, - "minQuorumSize":3, - "quorumThreshold":66, - "userAgent":"iota-client/2.0.1-rc.3", - "protocolParameters":{ - "version":2, - "networkName":"shimmer", - "bech32Hrp":"smr", - "belowMaxDepth":15, - "rentStructure":{ - "vByteCost":100, - "vByteFactorKey":10, - "vByteFactorData":1 - }, - "tokenSupply":"1813620509061365", - "genesisUnixTimestamp":1582328545, - "slotDurationInSeconds":10 - }, - "apiTimeout":{ - "secs":15, - "nanos":0 + "quorum": false, + "minQuorumSize": 3, + "quorumThreshold": 66, + "userAgent": "iota-client/2.0.1-rc.3", + "protocolParameters": ProtocolParameters::default(), + "apiTimeout": { + "secs": 15, + "nanos": 0 } - }"#; + }); - let _client_builder = serde_json::from_str::(client_builder_json).unwrap(); + let _client_builder = serde_json::from_value::(client_builder_json).unwrap(); } diff --git a/sdk/tests/client/error.rs b/sdk/tests/client/error.rs index ace934cc9f..e89240d421 100644 --- a/sdk/tests/client/error.rs +++ b/sdk/tests/client/error.rs @@ -16,11 +16,11 @@ fn stringified_error() { let error = Error::TimeNotSynced { current_time: 0, - milestone_timestamp: 10000, + tangle_time: 10000, }; assert_eq!( &serde_json::to_string(&error).unwrap(), - "{\"type\":\"timeNotSynced\",\"error\":\"local time 0 doesn't match the time of the latest milestone timestamp: 10000\"}" + "{\"type\":\"timeNotSynced\",\"error\":\"local time 0 doesn't match the tangle time: 10000\"}" ); let error = Error::PlaceholderSecretManager; diff --git a/sdk/tests/client/input_selection/account_outputs.rs b/sdk/tests/client/input_selection/account_outputs.rs index 7577fa1571..dc06483db3 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_structure(), 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_structure()) .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_structure(), 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_structure()) .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 594b96318e..9da0b86a1b 100644 --- a/sdk/tests/client/input_selection/nft_outputs.rs +++ b/sdk/tests/client/input_selection/nft_outputs.rs @@ -1187,14 +1187,13 @@ fn changed_immutable_metadata() { let protocol_parameters = protocol_parameters(); let nft_id_1 = NftId::from_str(NFT_ID_1).unwrap(); - let nft_output = - NftOutputBuilder::new_with_minimum_storage_deposit(*protocol_parameters.rent_structure(), nft_id_1) - .with_immutable_features(MetadataFeature::new([1, 2, 3])) - .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_structure(), nft_id_1) + .with_immutable_features(MetadataFeature::new([1, 2, 3])) + .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(), @@ -1204,7 +1203,7 @@ fn changed_immutable_metadata() { // New nft output with changed immutable metadata feature let updated_account_output = NftOutputBuilder::from(nft_output.as_nft()) - .with_minimum_storage_deposit(*protocol_parameters.rent_structure()) + .with_minimum_storage_deposit(protocol_parameters.rent_structure()) .with_immutable_features(MetadataFeature::new([4, 5, 6])) .finish_output(protocol_parameters.token_supply()) .unwrap(); diff --git a/sdk/tests/types/block.rs b/sdk/tests/types/block.rs index 9400698076..201c31648a 100644 --- a/sdk/tests/types/block.rs +++ b/sdk/tests/types/block.rs @@ -110,7 +110,7 @@ fn getters() { .finish() .unwrap(); - assert_eq!(block.protocol_version(), protocol_parameters.protocol_version()); + assert_eq!(block.protocol_version(), protocol_parameters.version()); assert_eq!(*block.strong_parents(), parents); assert_eq!(*block.payload().as_ref().unwrap(), &payload); } @@ -160,7 +160,7 @@ fn dto_mismatch_version() { assert_eq!( block_res, Err(iota_sdk::types::block::Error::ProtocolVersionMismatch { - expected: protocol_parameters.protocol_version(), + expected: protocol_parameters.version(), actual: protocol_version }) ); diff --git a/sdk/tests/types/block_id.rs b/sdk/tests/types/block_id.rs index a29cd309eb..f78fa76d76 100644 --- a/sdk/tests/types/block_id.rs +++ b/sdk/tests/types/block_id.rs @@ -4,7 +4,7 @@ use core::str::FromStr; use iota_sdk::types::block::{ - protocol::ProtocolParameters, rand::bytes::rand_bytes_array, Block, BlockDto, BlockHash, BlockId, + protocol::ProtocolParameters, rand::bytes::rand_bytes_array, slot::SlotIndex, Block, BlockDto, BlockHash, BlockId, }; use packable::PackableExt; @@ -67,10 +67,13 @@ fn memory_layout() { fn compute() { let protocol_parameters = ProtocolParameters::default(); let protocol_parameters_hash = protocol_parameters.hash(); - let slot_index = 11_u64; - let issuing_time = protocol_parameters.genesis_unix_timestamp() as u64 - + (slot_index - 1) * protocol_parameters.slot_duration_in_seconds() as u64; + let slot_index = SlotIndex::new(11_u64); + let issuing_time = slot_index.to_timestamp( + protocol_parameters.genesis_unix_timestamp(), + protocol_parameters.slot_duration_in_seconds(), + ); let network_id = protocol_parameters.network_id(); + let block_dto_json = serde_json::json!({ "protocolVersion": 3, "networkId": network_id.to_string(), @@ -99,11 +102,11 @@ fn compute() { // TODO: Independently verify this value assert_eq!( block_id.to_string(), - "0x89664f06b89ec91ef53b69a6c5da763aad498da7fc344978cf09fcdcbbd6464a0b00000000000000" + "0x0cf6544579470043791cadc15284fd231b577d63bb780f9f615e54761c2eb8b30b00000000000000" ); assert_eq!( block_id.hash().to_string(), - "0x89664f06b89ec91ef53b69a6c5da763aad498da7fc344978cf09fcdcbbd6464a" + "0x0cf6544579470043791cadc15284fd231b577d63bb780f9f615e54761c2eb8b3" ); - assert_eq!(*block_id.slot_index(), slot_index); + assert_eq!(block_id.slot_index(), slot_index); } diff --git a/sdk/tests/types/rent.rs b/sdk/tests/types/rent.rs index 7467503011..ae4faec7fb 100644 --- a/sdk/tests/types/rent.rs +++ b/sdk/tests/types/rent.rs @@ -16,7 +16,7 @@ fn config() -> RentStructure { } fn output_in_range(output: Output, range: std::ops::RangeInclusive) { - let cost = output.rent_cost(&config()); + let cost = output.rent_cost(config()); assert!(range.contains(&cost), "{output:#?} has a required byte cost of {cost}"); }