diff --git a/bindings/nodejs/lib/types/wallet/transaction.ts b/bindings/nodejs/lib/types/wallet/transaction.ts index 60eb35692e..5aa0c75fc7 100644 --- a/bindings/nodejs/lib/types/wallet/transaction.ts +++ b/bindings/nodejs/lib/types/wallet/transaction.ts @@ -31,8 +31,6 @@ export class TransactionWithMetadata { blockId?: BlockId; /** The inclusion state of the transaction */ inclusionState!: InclusionState; - /** The creation time */ - timestamp!: string; /** The transaction id */ transactionId!: TransactionId; /** The network id in which the transaction was sent */ diff --git a/bindings/nodejs/lib/types/wallet/wallet.ts b/bindings/nodejs/lib/types/wallet/wallet.ts index 88e53d3b6e..5edc9d066d 100644 --- a/bindings/nodejs/lib/types/wallet/wallet.ts +++ b/bindings/nodejs/lib/types/wallet/wallet.ts @@ -11,7 +11,7 @@ import { import { DecayedMana, HexEncodedString, u256, u64 } from '../utils'; import { ClientOptions } from '../client'; import { Bip44, SecretManagerType } from '../secret_manager/secret-manager'; -import { Bech32Address } from '../block'; +import { Bech32Address, SlotIndex } from '../block'; /** Options for the Wallet builder. */ export interface WalletOptions { @@ -99,9 +99,9 @@ export interface NativeTokenBalance { /** Sync options for a wallet */ export interface SyncOptions { /** - * Usually syncing is skipped if it's called in between 200ms, because there can only be new changes every - * milestone and calling it twice "at the same time" will not return new data - * When this to true, we will sync anyways, even if it's called 0ms after the las sync finished. Default: false. + * Syncing is usually skipped if it's called repeatedly in a short amount of time as there can only be new changes every + * slot and calling it twice "at the same time" will not return new data. + * When this to true, we sync anyways, even if it's called 0ms after the last sync finished. Default: false. */ forceSyncing?: boolean; /// Try to sync transactions from incoming outputs with their inputs. Some data may not be obtained if it has been @@ -163,10 +163,10 @@ export interface NftSyncOptions { /** Options to filter outputs */ export interface FilterOptions { - /** Filter all outputs where the booked milestone index is below the specified timestamp */ - lowerBoundBookedTimestamp?: number; - /** Filter all outputs where the booked milestone index is above the specified timestamp */ - upperBoundBookedTimestamp?: number; + /** Include all outputs where the included slot is below the specified slot */ + includedBelowSlot?: SlotIndex; + /** Include all outputs where the included slot is above the specified slot */ + includedAboveSlot?: SlotIndex; /** Filter all outputs for the provided types (Basic = 3, Account = 4, Foundry = 5, NFT = 6) */ outputTypes?: number[]; /** Return all account outputs matching these IDs. */ diff --git a/bindings/python/iota_sdk/types/filter_options.py b/bindings/python/iota_sdk/types/filter_options.py index ae6ec2cf48..42f726cc1b 100644 --- a/bindings/python/iota_sdk/types/filter_options.py +++ b/bindings/python/iota_sdk/types/filter_options.py @@ -4,7 +4,7 @@ from __future__ import annotations from typing import List, Optional from dataclasses import dataclass -from iota_sdk.types.common import json +from iota_sdk.types.common import json, SlotIndex @json @@ -13,8 +13,8 @@ class FilterOptions: """Options to filter outputs. """ - lowerBoundBookedTimestamp: Optional[int] = None - upperBoundBookedTimestamp: Optional[int] = None + includedBelowSlot: Optional[SlotIndex] = None + includedAboveSlot: Optional[SlotIndex] = None outputTypes: Optional[List[int]] = None accountIds: Optional[List[str]] = None foundryIds: Optional[List[str]] = None diff --git a/bindings/python/iota_sdk/types/transaction_with_metadata.py b/bindings/python/iota_sdk/types/transaction_with_metadata.py index 68df84391d..66c8640ec8 100644 --- a/bindings/python/iota_sdk/types/transaction_with_metadata.py +++ b/bindings/python/iota_sdk/types/transaction_with_metadata.py @@ -39,7 +39,6 @@ class TransactionWithMetadata: Attributes: payload: The transaction payload. inclusion_state: The inclusion state of the transaction. - timestamp: The timestamp of the transaction. transaction_id: The ID of the corresponding transaction. network_id: The ID of the network this transaction was issued in. incoming: Indicates whether the transaction was created by the wallet or whether it was sent by someone else and is incoming. @@ -49,7 +48,6 @@ class TransactionWithMetadata: """ payload: SignedTransactionPayload inclusion_state: InclusionState - timestamp: int transaction_id: TransactionId network_id: int incoming: bool diff --git a/bindings/python/iota_sdk/wallet/sync_options.py b/bindings/python/iota_sdk/wallet/sync_options.py index 7a2b702c1b..f32f5acf9a 100644 --- a/bindings/python/iota_sdk/wallet/sync_options.py +++ b/bindings/python/iota_sdk/wallet/sync_options.py @@ -69,10 +69,9 @@ class SyncOptions: **Attributes** force_syncing : - Usually syncing is skipped if it's called in between 200ms, because there can only be new - changes every milestone and calling it twice "at the same time" will not return new data. - When this is set to true, we will sync anyways, even if it's called 0ms after the last sync - finished. + Syncing is usually skipped if it's called repeatedly in a short amount of time as there can only be new changes every + slot and calling it twice "at the same time" will not return new data. + When this to true, we sync anyways, even if it's called 0ms after the last sync finished. sync_incoming_transactions : Try to sync transactions from incoming outputs with their inputs. Some data may not be obtained if it has been pruned. diff --git a/cli/src/wallet_cli/mod.rs b/cli/src/wallet_cli/mod.rs index 9cff41dc0f..5b81f1363b 100644 --- a/cli/src/wallet_cli/mod.rs +++ b/cli/src/wallet_cli/mod.rs @@ -1159,7 +1159,12 @@ pub async fn transaction_command(wallet: &Wallet, selector: TransactionSelector) TransactionSelector::Id(id) => wallet_ledger.get_transaction(&id), TransactionSelector::Index(index) => { let mut transactions = wallet_ledger.transactions().values().collect::>(); - transactions.sort_unstable_by(|a, b| b.timestamp.cmp(&a.timestamp)); + transactions.sort_unstable_by(|a, b| { + b.payload + .transaction() + .creation_slot() + .cmp(&a.payload.transaction().creation_slot()) + }); transactions.into_iter().nth(index) } }; @@ -1177,7 +1182,12 @@ pub async fn transaction_command(wallet: &Wallet, selector: TransactionSelector) pub async fn transactions_command(wallet: &Wallet, show_details: bool) -> Result<(), Error> { let wallet_ledger = wallet.ledger().await; let mut transactions = wallet_ledger.transactions().values().collect::>(); - transactions.sort_unstable_by(|a, b| b.timestamp.cmp(&a.timestamp)); + transactions.sort_unstable_by(|a, b| { + b.payload + .transaction() + .creation_slot() + .cmp(&a.payload.transaction().creation_slot()) + }); if transactions.is_empty() { println_log_info!("No transactions found"); @@ -1186,10 +1196,16 @@ pub async fn transactions_command(wallet: &Wallet, show_details: bool) -> Result if show_details { println_log_info!("{:#?}", tx); } else { - let transaction_time = to_utc_date_time(tx.timestamp)?; - let formatted_time = transaction_time.format("%Y-%m-%d %H:%M:%S UTC").to_string(); - - println_log_info!("{:<5}{}\t{}", i, tx.transaction_id, formatted_time); + let protocol_parameters = wallet.client().get_protocol_parameters().await?; + let creation_slot = tx.payload.transaction().creation_slot(); + let creation_time = to_utc_date_time(creation_slot.to_timestamp( + protocol_parameters.genesis_unix_timestamp(), + protocol_parameters.slot_duration_in_seconds(), + ) as u128)? + .format("%Y-%m-%d %H:%M:%S UTC") + .to_string(); + + println_log_info!("{:<5}{}\t{}\t{}", i, tx.transaction_id, creation_slot, creation_time); } } } diff --git a/sdk/examples/client/block/01_block_confirmation_time.rs b/sdk/examples/client/block/01_block_confirmation_time.rs index e11bb9f84b..4748953ffb 100644 --- a/sdk/examples/client/block/01_block_confirmation_time.rs +++ b/sdk/examples/client/block/01_block_confirmation_time.rs @@ -8,6 +8,8 @@ //! cargo run --release --example block_confirmation_time //! ``` +use std::time::Instant; + use crypto::keys::bip44::Bip44; use iota_sdk::{ client::{ @@ -45,34 +47,32 @@ async fn main() -> Result<(), Box> { println!("{block:#?}"); + let mut block_state = BlockState::Pending; + let start = Instant::now(); + // Wait for the block to get included for _ in 0..30 { tokio::time::sleep(std::time::Duration::from_secs(1)).await; let metadata = client.get_block_metadata(&block_id).await?; - if let BlockState::Confirmed | BlockState::Finalized = metadata.block_state { + block_state = metadata.block_state; + + if let BlockState::Confirmed | BlockState::Finalized = block_state { break; } } - println!( - "Block with no payload included: {}/block/{}", - std::env::var("EXPLORER_URL").unwrap(), - block_id - ); - - // TODO uncomment when we have a new confirmation logic - // Get the block metadata. - // let metadata = client.get_block_metadata(&block_id).await?; - - // if let Some(ms_index) = metadata.referenced_by_milestone_index { - // let ms = client.get_milestone_by_index(ms_index).await?; - // println!( - // "Block {block_id} got confirmed by milestone {ms_index} at timestamp {}.", - // ms.essence().timestamp() - // ); - // } else { - // println!("Block {block_id} is not confirmed.") - // } + if let BlockState::Confirmed | BlockState::Finalized = block_state { + let duration = start.elapsed(); + + println!( + "Block with no payload included after {} seconds: {}/block/{}", + duration.as_secs(), + std::env::var("EXPLORER_URL").unwrap(), + block_id + ); + } else { + println!("Block with no payload was not included"); + } Ok(()) } diff --git a/sdk/src/client/constants.rs b/sdk/src/client/constants.rs index b4d9b0252f..ba82f47aa8 100644 --- a/sdk/src/client/constants.rs +++ b/sdk/src/client/constants.rs @@ -16,7 +16,7 @@ pub(crate) const DEFAULT_QUORUM_THRESHOLD: usize = 66; pub(crate) const DEFAULT_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")); #[cfg(not(target_family = "wasm"))] pub(crate) const MAX_PARALLEL_API_REQUESTS: usize = 100; -/// Max allowed difference between the local time and latest milestone time, 5 minutes in seconds +/// Max allowed difference between local time and tangle time, 5 minutes in seconds pub(crate) const FIVE_MINUTES_IN_NANOSECONDS: u64 = 300_000_000_000; /// Delay for caching a node info response in WASM runtime #[cfg(target_family = "wasm")] diff --git a/sdk/src/types/block/output/unlock_condition/error.rs b/sdk/src/types/block/output/unlock_condition/error.rs index 48f0bd8861..d6c0f8ee8f 100644 --- a/sdk/src/types/block/output/unlock_condition/error.rs +++ b/sdk/src/types/block/output/unlock_condition/error.rs @@ -12,9 +12,9 @@ pub enum UnlockConditionError { Kind(u8), #[display(fmt = "invalid unlock condition count: {_0}")] Count(>::Error), - #[display(fmt = "expiration unlock condition with milestone index and timestamp set to 0")] + #[display(fmt = "expiration unlock condition with slot index set to 0")] ExpirationZero, - #[display(fmt = "timelock unlock condition with milestone index and timestamp set to 0")] + #[display(fmt = "timelock unlock condition with slot index set to 0")] TimelockZero, #[display(fmt = "unlock conditions are not unique and/or sorted")] NotUniqueSorted, diff --git a/sdk/src/wallet/core/mod.rs b/sdk/src/wallet/core/mod.rs index 30fa56f3e7..e8290e1718 100644 --- a/sdk/src/wallet/core/mod.rs +++ b/sdk/src/wallet/core/mod.rs @@ -188,17 +188,17 @@ impl WalletLedger { _ => {} } - // TODO filter based on slot index - // if let Some(lower_bound_booked_timestamp) = filter.lower_bound_booked_timestamp { - // if output.metadata.milestone_timestamp_booked() < lower_bound_booked_timestamp { - // continue; - // } - // } - // if let Some(upper_bound_booked_timestamp) = filter.upper_bound_booked_timestamp { - // if output.metadata.milestone_timestamp_booked() > upper_bound_booked_timestamp { - // continue; - // } - // } + if let Some(included_below_slot) = filter.included_below_slot { + if output.metadata.included().slot() > included_below_slot { + return false; + } + } + + if let Some(included_above_slot) = filter.included_above_slot { + if output.metadata.included().slot() < included_above_slot { + return false; + } + } if let Some(output_types) = &filter.output_types { if !output_types.contains(&output.output.kind()) { @@ -655,7 +655,6 @@ mod test { payload: tx_payload, block_id: None, network_id: 0, - timestamp: 0, inclusion_state: InclusionState::Pending, incoming: false, note: None, diff --git a/sdk/src/wallet/mod.rs b/sdk/src/wallet/mod.rs index 590c1f24ee..8b704305ab 100644 --- a/sdk/src/wallet/mod.rs +++ b/sdk/src/wallet/mod.rs @@ -78,6 +78,7 @@ use crate::{ block::{ output::{AccountId, AnchorId, DelegationId, FoundryId, NftId, OutputWithMetadata}, payload::signed_transaction::{SignedTransactionPayload, TransactionId}, + slot::SlotIndex, }, }, wallet::types::InclusionState, @@ -87,10 +88,10 @@ use crate::{ #[derive(Debug, Default, Clone, Serialize, Deserialize, Eq, PartialEq)] #[serde(rename_all = "camelCase")] pub struct FilterOptions { - /// Filter all outputs where the booked milestone index is below the specified timestamp - pub lower_bound_booked_timestamp: Option, - /// Filter all outputs where the booked milestone index is above the specified timestamp - pub upper_bound_booked_timestamp: Option, + /// Include all outputs where the included slot is below the specified slot. + pub included_below_slot: Option, + /// Include all outputs where the included slot is above the specified slot. + pub included_above_slot: Option, /// Filter all outputs for the provided types (Basic = 3, Account = 4, Foundry = 5, NFT = 6). pub output_types: Option>, /// Return all account outputs matching these IDs. @@ -114,12 +115,6 @@ pub(crate) fn build_transaction_from_payload_and_inputs( payload: tx_payload.clone(), block_id: inputs.first().map(|i| *i.metadata.block_id()), inclusion_state: InclusionState::Confirmed, - timestamp: 0, - // TODO use slot index since milestone_timestamp_spent is gone - // inputs - // .first() - // .and_then(|i| i.metadata.milestone_timestamp_spent.map(|t| t as u128 * 1000)) - // .unwrap_or_else(|| crate::utils::unix_timestamp_now().as_millis()), transaction_id: tx_id, network_id: tx_payload.transaction().network_id(), incoming: true, diff --git a/sdk/src/wallet/operations/syncing/options.rs b/sdk/src/wallet/operations/syncing/options.rs index f5a1d09816..f2626acf09 100644 --- a/sdk/src/wallet/operations/syncing/options.rs +++ b/sdk/src/wallet/operations/syncing/options.rs @@ -14,9 +14,9 @@ const DEFAULT_SYNC_IMPLICIT_ACCOUNTS: bool = false; #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SyncOptions { - /// Usually syncing is skipped if it's called in between 200ms, because there can only be new changes every - /// milestone and calling it twice "at the same time" will not return new data - /// When this to true, we will sync anyways, even if it's called 0ms after the las sync finished. + /// Syncing is usually skipped if it's called repeatedly in a short amount of time as there can only be new changes + /// every slot and calling it twice "at the same time" will not return new data. + /// When this to true, we sync anyways, even if it's called 0ms after the last sync finished. #[serde(default)] pub force_syncing: bool, /// Try to sync transactions from incoming outputs with their inputs. Some data may not be obtained if it has been diff --git a/sdk/src/wallet/operations/transaction/mod.rs b/sdk/src/wallet/operations/transaction/mod.rs index 063180d48d..a4e7027c83 100644 --- a/sdk/src/wallet/operations/transaction/mod.rs +++ b/sdk/src/wallet/operations/transaction/mod.rs @@ -119,7 +119,6 @@ where payload: signed_transaction_data.payload, block_id, network_id, - timestamp: crate::client::unix_timestamp_now().as_millis(), inclusion_state: InclusionState::Pending, incoming: false, note: options.and_then(|o| o.note), diff --git a/sdk/src/wallet/types/mod.rs b/sdk/src/wallet/types/mod.rs index 543b49055c..e2dac2034d 100644 --- a/sdk/src/wallet/types/mod.rs +++ b/sdk/src/wallet/types/mod.rs @@ -97,8 +97,6 @@ pub struct TransactionWithMetadata { pub payload: SignedTransactionPayload, pub block_id: Option, pub inclusion_state: InclusionState, - // Transaction creation time - pub timestamp: u128, pub transaction_id: TransactionId, // network id to ignore outputs when set_client_options is used to switch to another network pub network_id: u64, @@ -122,8 +120,6 @@ pub struct TransactionWithMetadataDto { pub block_id: Option, /// Inclusion state of the transaction pub inclusion_state: InclusionState, - /// Timestamp - pub timestamp: String, pub transaction_id: TransactionId, /// Network id to ignore outputs when set_client_options is used to switch to another network pub network_id: String, @@ -140,7 +136,6 @@ impl From<&TransactionWithMetadata> for TransactionWithMetadataDto { payload: SignedTransactionPayloadDto::from(&value.payload), block_id: value.block_id, inclusion_state: value.inclusion_state, - timestamp: value.timestamp.to_string(), transaction_id: value.transaction_id, network_id: value.network_id.to_string(), incoming: value.incoming, @@ -161,10 +156,6 @@ impl TryFromDto for TransactionWithMetadata { payload: SignedTransactionPayload::try_from_dto_with_params_inner(dto.payload, params)?, block_id: dto.block_id, inclusion_state: dto.inclusion_state, - timestamp: dto - .timestamp - .parse::() - .map_err(|e| PayloadError::Timestamp(e.to_string()))?, transaction_id: dto.transaction_id, network_id: dto .network_id