Rename ConfirmationTime to BlockTime

This commit is contained in:
Alekos Filini 2021-11-03 16:05:30 +00:00
parent 3e5bb077ac
commit 2c77329333
No known key found for this signature in database
GPG Key ID: 431401E4A4530061
12 changed files with 51 additions and 44 deletions

View File

@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- BIP39 implementation dependency, in `keys::bip39` changed from tiny-bip39 to rust-bip39. - BIP39 implementation dependency, in `keys::bip39` changed from tiny-bip39 to rust-bip39.
- Add new method on the `TxBuilder` to embed data in the transaction via `OP_RETURN`. To allow that a fix to check the dust only on spendable output has been introduced. - Add new method on the `TxBuilder` to embed data in the transaction via `OP_RETURN`. To allow that a fix to check the dust only on spendable output has been introduced.
- Update the `Database` trait to store the last sync timestamp and block height - Update the `Database` trait to store the last sync timestamp and block height
- Rename `ConfirmationTime` to `BlockTime`
## [v0.13.0] - [v0.12.0] ## [v0.13.0] - [v0.12.0]

View File

@ -71,7 +71,7 @@ use super::{Blockchain, Capability, ConfigurableBlockchain, Progress};
use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils}; use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils};
use crate::error::Error; use crate::error::Error;
use crate::types::{KeychainKind, LocalUtxo, TransactionDetails}; use crate::types::{KeychainKind, LocalUtxo, TransactionDetails};
use crate::{ConfirmationTime, FeeRate}; use crate::{BlockTime, FeeRate};
use peer::*; use peer::*;
use store::*; use store::*;
@ -206,7 +206,7 @@ impl CompactFiltersBlockchain {
transaction: Some(tx.clone()), transaction: Some(tx.clone()),
received: incoming, received: incoming,
sent: outgoing, sent: outgoing,
confirmation_time: ConfirmationTime::new(height, timestamp), confirmation_time: BlockTime::new(height, timestamp),
verified: height.is_some(), verified: height.is_some(),
fee: Some(inputs_sum.saturating_sub(outputs_sum)), fee: Some(inputs_sum.saturating_sub(outputs_sum)),
}; };

View File

@ -37,7 +37,7 @@ use crate::blockchain::{Blockchain, Capability, ConfigurableBlockchain, Progress
use crate::database::{BatchDatabase, DatabaseUtils}; use crate::database::{BatchDatabase, DatabaseUtils};
use crate::descriptor::{get_checksum, IntoWalletDescriptor}; use crate::descriptor::{get_checksum, IntoWalletDescriptor};
use crate::wallet::utils::SecpCtx; use crate::wallet::utils::SecpCtx;
use crate::{ConfirmationTime, Error, FeeRate, KeychainKind, LocalUtxo, TransactionDetails}; use crate::{BlockTime, Error, FeeRate, KeychainKind, LocalUtxo, TransactionDetails};
use bitcoincore_rpc::json::{ use bitcoincore_rpc::json::{
GetAddressInfoResultLabel, ImportMultiOptions, ImportMultiRequest, GetAddressInfoResultLabel, ImportMultiOptions, ImportMultiRequest,
ImportMultiRequestScriptPubkey, ImportMultiRescanSince, ImportMultiRequestScriptPubkey, ImportMultiRescanSince,
@ -230,7 +230,7 @@ impl Blockchain for RpcBlockchain {
list_txs_ids.insert(txid); list_txs_ids.insert(txid);
if let Some(mut known_tx) = known_txs.get_mut(&txid) { if let Some(mut known_tx) = known_txs.get_mut(&txid) {
let confirmation_time = let confirmation_time =
ConfirmationTime::new(tx_result.info.blockheight, tx_result.info.blocktime); BlockTime::new(tx_result.info.blockheight, tx_result.info.blocktime);
if confirmation_time != known_tx.confirmation_time { if confirmation_time != known_tx.confirmation_time {
// reorg may change tx height // reorg may change tx height
debug!( debug!(
@ -266,7 +266,7 @@ impl Blockchain for RpcBlockchain {
let td = TransactionDetails { let td = TransactionDetails {
transaction: Some(tx), transaction: Some(tx),
txid: tx_result.info.txid, txid: tx_result.info.txid,
confirmation_time: ConfirmationTime::new( confirmation_time: BlockTime::new(
tx_result.info.blockheight, tx_result.info.blockheight,
tx_result.info.blocktime, tx_result.info.blocktime,
), ),

View File

@ -21,7 +21,7 @@ use bitcoin::{BlockHeader, OutPoint, Script, Transaction, Txid};
use super::*; use super::*;
use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils}; use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils};
use crate::error::Error; use crate::error::Error;
use crate::types::{ConfirmationTime, KeychainKind, LocalUtxo, TransactionDetails}; use crate::types::{BlockTime, KeychainKind, LocalUtxo, TransactionDetails};
use crate::wallet::time::Instant; use crate::wallet::time::Instant;
use crate::wallet::utils::ChunksIterator; use crate::wallet::utils::ChunksIterator;
@ -151,7 +151,7 @@ pub trait ElectrumLikeSync {
// check if tx height matches, otherwise updates it. timestamp is not in the if clause // check if tx height matches, otherwise updates it. timestamp is not in the if clause
// because we are not asking headers for confirmed tx we know about // because we are not asking headers for confirmed tx we know about
if tx_details.confirmation_time.as_ref().map(|c| c.height) != height { if tx_details.confirmation_time.as_ref().map(|c| c.height) != height {
let confirmation_time = ConfirmationTime::new(height, timestamp); let confirmation_time = BlockTime::new(height, timestamp);
let mut new_tx_details = tx_details.clone(); let mut new_tx_details = tx_details.clone();
new_tx_details.confirmation_time = confirmation_time; new_tx_details.confirmation_time = confirmation_time;
batch.set_tx(&new_tx_details)?; batch.set_tx(&new_tx_details)?;
@ -359,7 +359,7 @@ fn save_transaction_details_and_utxos<D: BatchDatabase>(
transaction: Some(tx), transaction: Some(tx),
received: incoming, received: incoming,
sent: outgoing, sent: outgoing,
confirmation_time: ConfirmationTime::new(height, timestamp), confirmation_time: BlockTime::new(height, timestamp),
fee: Some(inputs_sum.saturating_sub(outputs_sum)), /* if the tx is a coinbase, fees would be negative */ fee: Some(inputs_sum.saturating_sub(outputs_sum)), /* if the tx is a coinbase, fees would be negative */
verified: height.is_some(), verified: height.is_some(),
}; };

View File

@ -144,7 +144,7 @@ impl BatchOperations for AnyDatabase {
fn set_last_index(&mut self, keychain: KeychainKind, value: u32) -> Result<(), Error> { fn set_last_index(&mut self, keychain: KeychainKind, value: u32) -> Result<(), Error> {
impl_inner_method!(AnyDatabase, self, set_last_index, keychain, value) impl_inner_method!(AnyDatabase, self, set_last_index, keychain, value)
} }
fn set_last_sync_time(&mut self, last_sync_time: ConfirmationTime) -> Result<(), Error> { fn set_last_sync_time(&mut self, last_sync_time: BlockTime) -> Result<(), Error> {
impl_inner_method!(AnyDatabase, self, set_last_sync_time, last_sync_time) impl_inner_method!(AnyDatabase, self, set_last_sync_time, last_sync_time)
} }
@ -183,7 +183,7 @@ impl BatchOperations for AnyDatabase {
fn del_last_index(&mut self, keychain: KeychainKind) -> Result<Option<u32>, Error> { fn del_last_index(&mut self, keychain: KeychainKind) -> Result<Option<u32>, Error> {
impl_inner_method!(AnyDatabase, self, del_last_index, keychain) impl_inner_method!(AnyDatabase, self, del_last_index, keychain)
} }
fn del_last_sync_time(&mut self) -> Result<Option<ConfirmationTime>, Error> { fn del_last_sync_time(&mut self) -> Result<Option<BlockTime>, Error> {
impl_inner_method!(AnyDatabase, self, del_last_sync_time) impl_inner_method!(AnyDatabase, self, del_last_sync_time)
} }
} }
@ -247,7 +247,7 @@ impl Database for AnyDatabase {
fn get_last_index(&self, keychain: KeychainKind) -> Result<Option<u32>, Error> { fn get_last_index(&self, keychain: KeychainKind) -> Result<Option<u32>, Error> {
impl_inner_method!(AnyDatabase, self, get_last_index, keychain) impl_inner_method!(AnyDatabase, self, get_last_index, keychain)
} }
fn get_last_sync_time(&self) -> Result<Option<ConfirmationTime>, Error> { fn get_last_sync_time(&self) -> Result<Option<BlockTime>, Error> {
impl_inner_method!(AnyDatabase, self, get_last_sync_time) impl_inner_method!(AnyDatabase, self, get_last_sync_time)
} }
@ -281,7 +281,7 @@ impl BatchOperations for AnyBatch {
fn set_last_index(&mut self, keychain: KeychainKind, value: u32) -> Result<(), Error> { fn set_last_index(&mut self, keychain: KeychainKind, value: u32) -> Result<(), Error> {
impl_inner_method!(AnyBatch, self, set_last_index, keychain, value) impl_inner_method!(AnyBatch, self, set_last_index, keychain, value)
} }
fn set_last_sync_time(&mut self, last_sync_time: ConfirmationTime) -> Result<(), Error> { fn set_last_sync_time(&mut self, last_sync_time: BlockTime) -> Result<(), Error> {
impl_inner_method!(AnyBatch, self, set_last_sync_time, last_sync_time) impl_inner_method!(AnyBatch, self, set_last_sync_time, last_sync_time)
} }
@ -314,7 +314,7 @@ impl BatchOperations for AnyBatch {
fn del_last_index(&mut self, keychain: KeychainKind) -> Result<Option<u32>, Error> { fn del_last_index(&mut self, keychain: KeychainKind) -> Result<Option<u32>, Error> {
impl_inner_method!(AnyBatch, self, del_last_index, keychain) impl_inner_method!(AnyBatch, self, del_last_index, keychain)
} }
fn del_last_sync_time(&mut self) -> Result<Option<ConfirmationTime>, Error> { fn del_last_sync_time(&mut self) -> Result<Option<BlockTime>, Error> {
impl_inner_method!(AnyBatch, self, del_last_sync_time) impl_inner_method!(AnyBatch, self, del_last_sync_time)
} }
} }

View File

@ -82,7 +82,7 @@ macro_rules! impl_batch_operations {
Ok(()) Ok(())
} }
fn set_last_sync_time(&mut self, ct: ConfirmationTime) -> Result<(), Error> { fn set_last_sync_time(&mut self, ct: BlockTime) -> Result<(), Error> {
let key = MapKey::LastSyncTime.as_map_key(); let key = MapKey::LastSyncTime.as_map_key();
self.insert(key, serde_json::to_vec(&ct)?)$($after_insert)*; self.insert(key, serde_json::to_vec(&ct)?)$($after_insert)*;
@ -176,7 +176,7 @@ macro_rules! impl_batch_operations {
} }
} }
fn del_last_sync_time(&mut self) -> Result<Option<ConfirmationTime>, Error> { fn del_last_sync_time(&mut self) -> Result<Option<BlockTime>, Error> {
let key = MapKey::LastSyncTime.as_map_key(); let key = MapKey::LastSyncTime.as_map_key();
let res = self.remove(key); let res = self.remove(key);
let res = $process_delete!(res); let res = $process_delete!(res);
@ -357,7 +357,7 @@ impl Database for Tree {
.transpose() .transpose()
} }
fn get_last_sync_time(&self) -> Result<Option<ConfirmationTime>, Error> { fn get_last_sync_time(&self) -> Result<Option<BlockTime>, Error> {
let key = MapKey::LastSyncTime.as_map_key(); let key = MapKey::LastSyncTime.as_map_key();
Ok(self Ok(self
.get(key)? .get(key)?

View File

@ -183,7 +183,7 @@ impl BatchOperations for MemoryDatabase {
Ok(()) Ok(())
} }
fn set_last_sync_time(&mut self, ct: ConfirmationTime) -> Result<(), Error> { fn set_last_sync_time(&mut self, ct: BlockTime) -> Result<(), Error> {
let key = MapKey::LastSyncTime.as_map_key(); let key = MapKey::LastSyncTime.as_map_key();
self.map.insert(key, Box::new(ct)); self.map.insert(key, Box::new(ct));
@ -279,7 +279,7 @@ impl BatchOperations for MemoryDatabase {
Some(b) => Ok(Some(*b.downcast_ref().unwrap())), Some(b) => Ok(Some(*b.downcast_ref().unwrap())),
} }
} }
fn del_last_sync_time(&mut self) -> Result<Option<ConfirmationTime>, Error> { fn del_last_sync_time(&mut self) -> Result<Option<BlockTime>, Error> {
let key = MapKey::LastSyncTime.as_map_key(); let key = MapKey::LastSyncTime.as_map_key();
let res = self.map.remove(&key); let res = self.map.remove(&key);
self.deleted_keys.push(key); self.deleted_keys.push(key);
@ -423,7 +423,7 @@ impl Database for MemoryDatabase {
Ok(self.map.get(&key).map(|b| *b.downcast_ref().unwrap())) Ok(self.map.get(&key).map(|b| *b.downcast_ref().unwrap()))
} }
fn get_last_sync_time(&self) -> Result<Option<ConfirmationTime>, Error> { fn get_last_sync_time(&self) -> Result<Option<BlockTime>, Error> {
let key = MapKey::LastSyncTime.as_map_key(); let key = MapKey::LastSyncTime.as_map_key();
Ok(self Ok(self
.map .map
@ -505,7 +505,7 @@ macro_rules! populate_test_db {
let txid = tx.txid(); let txid = tx.txid();
let confirmation_time = tx_meta let confirmation_time = tx_meta
.min_confirmations .min_confirmations
.map(|conf| $crate::ConfirmationTime { .map(|conf| $crate::BlockTime {
height: current_height.unwrap().checked_sub(conf as u32).unwrap(), height: current_height.unwrap().checked_sub(conf as u32).unwrap(),
timestamp: 0, timestamp: 0,
}); });

View File

@ -65,7 +65,7 @@ pub trait BatchOperations {
/// Store the last derivation index for a given keychain. /// Store the last derivation index for a given keychain.
fn set_last_index(&mut self, keychain: KeychainKind, value: u32) -> Result<(), Error>; fn set_last_index(&mut self, keychain: KeychainKind, value: u32) -> Result<(), Error>;
/// Store the sync time in terms of block height and timestamp /// Store the sync time in terms of block height and timestamp
fn set_last_sync_time(&mut self, last_sync_time: ConfirmationTime) -> Result<(), Error>; fn set_last_sync_time(&mut self, last_sync_time: BlockTime) -> Result<(), Error>;
/// Delete a script_pubkey given the keychain and its child number. /// Delete a script_pubkey given the keychain and its child number.
fn del_script_pubkey_from_path( fn del_script_pubkey_from_path(
@ -94,7 +94,7 @@ pub trait BatchOperations {
/// Reset the last sync time to `None` /// Reset the last sync time to `None`
/// ///
/// Returns the removed value /// Returns the removed value
fn del_last_sync_time(&mut self) -> Result<Option<ConfirmationTime>, Error>; fn del_last_sync_time(&mut self) -> Result<Option<BlockTime>, Error>;
} }
/// Trait for reading data from a database /// Trait for reading data from a database
@ -141,7 +141,7 @@ pub trait Database: BatchOperations {
/// Return the last defivation index for a keychain. /// Return the last defivation index for a keychain.
fn get_last_index(&self, keychain: KeychainKind) -> Result<Option<u32>, Error>; fn get_last_index(&self, keychain: KeychainKind) -> Result<Option<u32>, Error>;
/// Return the last sync time, if present /// Return the last sync time, if present
fn get_last_sync_time(&self) -> Result<Option<ConfirmationTime>, Error>; fn get_last_sync_time(&self) -> Result<Option<BlockTime>, Error>;
/// Increment the last derivation index for a keychain and return it /// Increment the last derivation index for a keychain and return it
/// ///
@ -333,7 +333,7 @@ pub mod test {
received: 1337, received: 1337,
sent: 420420, sent: 420420,
fee: Some(140), fee: Some(140),
confirmation_time: Some(ConfirmationTime { confirmation_time: Some(BlockTime {
timestamp: 123456, timestamp: 123456,
height: 1000, height: 1000,
}), }),
@ -388,7 +388,7 @@ pub mod test {
pub fn test_last_sync_time<D: Database>(mut tree: D) { pub fn test_last_sync_time<D: Database>(mut tree: D) {
assert!(tree.get_last_sync_time().unwrap().is_none()); assert!(tree.get_last_sync_time().unwrap().is_none());
tree.set_last_sync_time(ConfirmationTime { tree.set_last_sync_time(BlockTime {
height: 100, height: 100,
timestamp: 1000, timestamp: 1000,
}) })

View File

@ -206,7 +206,7 @@ impl SqliteDatabase {
Ok(()) Ok(())
} }
fn update_last_sync_time(&self, ct: ConfirmationTime) -> Result<i64, Error> { fn update_last_sync_time(&self, ct: BlockTime) -> Result<i64, Error> {
let mut statement = self.connection.prepare_cached( let mut statement = self.connection.prepare_cached(
"INSERT INTO last_sync_time (id, height, timestamp) VALUES (0, :height, :timestamp) ON CONFLICT(id) DO UPDATE SET height=:height, timestamp=:timestamp WHERE id = 0", "INSERT INTO last_sync_time (id, height, timestamp) VALUES (0, :height, :timestamp) ON CONFLICT(id) DO UPDATE SET height=:height, timestamp=:timestamp WHERE id = 0",
)?; )?;
@ -389,7 +389,7 @@ impl SqliteDatabase {
}; };
let confirmation_time = match (height, timestamp) { let confirmation_time = match (height, timestamp) {
(Some(height), Some(timestamp)) => Some(ConfirmationTime { height, timestamp }), (Some(height), Some(timestamp)) => Some(BlockTime { height, timestamp }),
_ => None, _ => None,
}; };
@ -423,7 +423,7 @@ impl SqliteDatabase {
let verified: bool = row.get(6)?; let verified: bool = row.get(6)?;
let confirmation_time = match (height, timestamp) { let confirmation_time = match (height, timestamp) {
(Some(height), Some(timestamp)) => Some(ConfirmationTime { height, timestamp }), (Some(height), Some(timestamp)) => Some(BlockTime { height, timestamp }),
_ => None, _ => None,
}; };
@ -466,7 +466,7 @@ impl SqliteDatabase {
}; };
let confirmation_time = match (height, timestamp) { let confirmation_time = match (height, timestamp) {
(Some(height), Some(timestamp)) => Some(ConfirmationTime { height, timestamp }), (Some(height), Some(timestamp)) => Some(BlockTime { height, timestamp }),
_ => None, _ => None,
}; };
@ -501,12 +501,12 @@ impl SqliteDatabase {
} }
} }
fn select_last_sync_time(&self) -> Result<Option<ConfirmationTime>, Error> { fn select_last_sync_time(&self) -> Result<Option<BlockTime>, Error> {
let mut statement = self let mut statement = self
.connection .connection
.prepare_cached("SELECT height, timestamp FROM last_sync_time WHERE id = 0")?; .prepare_cached("SELECT height, timestamp FROM last_sync_time WHERE id = 0")?;
let mut rows = statement.query_map([], |row| { let mut rows = statement.query_map([], |row| {
Ok(ConfirmationTime { Ok(BlockTime {
height: row.get(0)?, height: row.get(0)?,
timestamp: row.get(1)?, timestamp: row.get(1)?,
}) })
@ -658,7 +658,7 @@ impl BatchOperations for SqliteDatabase {
Ok(()) Ok(())
} }
fn set_last_sync_time(&mut self, ct: ConfirmationTime) -> Result<(), Error> { fn set_last_sync_time(&mut self, ct: BlockTime) -> Result<(), Error> {
self.update_last_sync_time(ct)?; self.update_last_sync_time(ct)?;
Ok(()) Ok(())
} }
@ -749,7 +749,7 @@ impl BatchOperations for SqliteDatabase {
} }
} }
fn del_last_sync_time(&mut self) -> Result<Option<ConfirmationTime>, Error> { fn del_last_sync_time(&mut self) -> Result<Option<BlockTime>, Error> {
match self.select_last_sync_time()? { match self.select_last_sync_time()? {
Some(value) => { Some(value) => {
self.delete_last_sync_time()?; self.delete_last_sync_time()?;
@ -870,7 +870,7 @@ impl Database for SqliteDatabase {
Ok(value) Ok(value)
} }
fn get_last_sync_time(&self) -> Result<Option<ConfirmationTime>, Error> { fn get_last_sync_time(&self) -> Result<Option<BlockTime>, Error> {
self.select_last_sync_time() self.select_last_sync_time()
} }

View File

@ -210,7 +210,7 @@ pub struct TransactionDetails {
pub fee: Option<u64>, pub fee: Option<u64>,
/// If the transaction is confirmed, contains height and timestamp of the block containing the /// If the transaction is confirmed, contains height and timestamp of the block containing the
/// transaction, unconfirmed transaction contains `None`. /// transaction, unconfirmed transaction contains `None`.
pub confirmation_time: Option<ConfirmationTime>, pub confirmation_time: Option<BlockTime>,
/// Whether the tx has been verified against the consensus rules /// Whether the tx has been verified against the consensus rules
/// ///
/// Confirmed txs are considered "verified" by default, while unconfirmed txs are checked to /// Confirmed txs are considered "verified" by default, while unconfirmed txs are checked to
@ -222,20 +222,26 @@ pub struct TransactionDetails {
pub verified: bool, pub verified: bool,
} }
/// Block height and timestamp of the block containing the confirmed transaction /// Block height and timestamp of a block
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
pub struct ConfirmationTime { pub struct BlockTime {
/// confirmation block height /// confirmation block height
pub height: u32, pub height: u32,
/// confirmation block timestamp /// confirmation block timestamp
pub timestamp: u64, pub timestamp: u64,
} }
impl ConfirmationTime { /// **DEPRECATED**: Confirmation time of a transaction
/// Returns `Some` `ConfirmationTime` if both `height` and `timestamp` are `Some` ///
/// The structure has been renamed to `BlockTime`
#[deprecated(note = "This structure has been renamed to `BlockTime`")]
pub type ConfirmationTime = BlockTime;
impl BlockTime {
/// Returns `Some` `BlockTime` if both `height` and `timestamp` are `Some`
pub fn new(height: Option<u32>, timestamp: Option<u64>) -> Option<Self> { pub fn new(height: Option<u32>, timestamp: Option<u64>) -> Option<Self> {
match (height, timestamp) { match (height, timestamp) {
(Some(height), Some(timestamp)) => Some(ConfirmationTime { height, timestamp }), (Some(height), Some(timestamp)) => Some(BlockTime { height, timestamp }),
_ => None, _ => None,
} }
} }

View File

@ -212,7 +212,7 @@ mod test {
use crate::database::{memory::MemoryDatabase, BatchOperations}; use crate::database::{memory::MemoryDatabase, BatchOperations};
use crate::types::TransactionDetails; use crate::types::TransactionDetails;
use crate::wallet::Wallet; use crate::wallet::Wallet;
use crate::ConfirmationTime; use crate::BlockTime;
fn get_test_db() -> MemoryDatabase { fn get_test_db() -> MemoryDatabase {
let mut db = MemoryDatabase::new(); let mut db = MemoryDatabase::new();
@ -226,7 +226,7 @@ mod test {
received: 100_000, received: 100_000,
sent: 0, sent: 0,
fee: Some(500), fee: Some(500),
confirmation_time: Some(ConfirmationTime { confirmation_time: Some(BlockTime {
timestamp: 12345678, timestamp: 12345678,
height: 5000, height: 5000,
}), }),

View File

@ -1554,7 +1554,7 @@ where
} }
} }
let last_sync_time = ConfirmationTime { let last_sync_time = BlockTime {
height: maybe_await!(self.client.get_height())?, height: maybe_await!(self.client.get_height())?,
timestamp: time::get_timestamp(), timestamp: time::get_timestamp(),
}; };
@ -2792,7 +2792,7 @@ pub(crate) mod test {
let txid = tx.txid(); let txid = tx.txid();
// skip saving the utxos, we know they can't be used anyways // skip saving the utxos, we know they can't be used anyways
details.transaction = Some(tx); details.transaction = Some(tx);
details.confirmation_time = Some(ConfirmationTime { details.confirmation_time = Some(BlockTime {
timestamp: 12345678, timestamp: 12345678,
height: 42, height: 42,
}); });