diff --git a/CHANGELOG.md b/CHANGELOG.md index 757baaa34..48b0bca3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ 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. - 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 +- Rename `ConfirmationTime` to `BlockTime` ## [v0.13.0] - [v0.12.0] @@ -391,4 +393,4 @@ final transaction is created by calling `finish` on the builder. [v0.10.0]: https://github.com/bitcoindevkit/bdk/compare/v0.9.0...v0.10.0 [v0.11.0]: https://github.com/bitcoindevkit/bdk/compare/v0.10.0...v0.11.0 [v0.12.0]: https://github.com/bitcoindevkit/bdk/compare/v0.11.0...v0.12.0 -[v0.13.0]: https://github.com/bitcoindevkit/bdk/compare/v0.12.0...v0.13.0 \ No newline at end of file +[v0.13.0]: https://github.com/bitcoindevkit/bdk/compare/v0.12.0...v0.13.0 diff --git a/src/blockchain/compact_filters/mod.rs b/src/blockchain/compact_filters/mod.rs index b513d378f..c2e2b8eac 100644 --- a/src/blockchain/compact_filters/mod.rs +++ b/src/blockchain/compact_filters/mod.rs @@ -71,7 +71,7 @@ use super::{Blockchain, Capability, ConfigurableBlockchain, Progress}; use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils}; use crate::error::Error; use crate::types::{KeychainKind, LocalUtxo, TransactionDetails}; -use crate::{ConfirmationTime, FeeRate}; +use crate::{BlockTime, FeeRate}; use peer::*; use store::*; @@ -206,7 +206,7 @@ impl CompactFiltersBlockchain { transaction: Some(tx.clone()), received: incoming, sent: outgoing, - confirmation_time: ConfirmationTime::new(height, timestamp), + confirmation_time: BlockTime::new(height, timestamp), verified: height.is_some(), fee: Some(inputs_sum.saturating_sub(outputs_sum)), }; diff --git a/src/blockchain/rpc.rs b/src/blockchain/rpc.rs index d4a5beca1..4cd22943d 100644 --- a/src/blockchain/rpc.rs +++ b/src/blockchain/rpc.rs @@ -37,7 +37,7 @@ use crate::blockchain::{Blockchain, Capability, ConfigurableBlockchain, Progress use crate::database::{BatchDatabase, DatabaseUtils}; use crate::descriptor::{get_checksum, IntoWalletDescriptor}; 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::{ GetAddressInfoResultLabel, ImportMultiOptions, ImportMultiRequest, ImportMultiRequestScriptPubkey, ImportMultiRescanSince, @@ -230,7 +230,7 @@ impl Blockchain for RpcBlockchain { list_txs_ids.insert(txid); if let Some(mut known_tx) = known_txs.get_mut(&txid) { 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 { // reorg may change tx height debug!( @@ -266,7 +266,7 @@ impl Blockchain for RpcBlockchain { let td = TransactionDetails { transaction: Some(tx), txid: tx_result.info.txid, - confirmation_time: ConfirmationTime::new( + confirmation_time: BlockTime::new( tx_result.info.blockheight, tx_result.info.blocktime, ), diff --git a/src/blockchain/utils.rs b/src/blockchain/utils.rs index 7385b1db8..760cfe6f9 100644 --- a/src/blockchain/utils.rs +++ b/src/blockchain/utils.rs @@ -21,7 +21,7 @@ use bitcoin::{BlockHeader, OutPoint, Script, Transaction, Txid}; use super::*; use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils}; 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::utils::ChunksIterator; @@ -151,7 +151,7 @@ pub trait ElectrumLikeSync { // 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 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(); new_tx_details.confirmation_time = confirmation_time; batch.set_tx(&new_tx_details)?; @@ -359,7 +359,7 @@ fn save_transaction_details_and_utxos( transaction: Some(tx), received: incoming, 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 */ verified: height.is_some(), }; diff --git a/src/database/any.rs b/src/database/any.rs index 5186452cf..8b626e4b7 100644 --- a/src/database/any.rs +++ b/src/database/any.rs @@ -144,6 +144,9 @@ impl BatchOperations for AnyDatabase { fn set_last_index(&mut self, keychain: KeychainKind, value: u32) -> Result<(), Error> { impl_inner_method!(AnyDatabase, self, set_last_index, keychain, value) } + fn set_sync_time(&mut self, sync_time: SyncTime) -> Result<(), Error> { + impl_inner_method!(AnyDatabase, self, set_sync_time, sync_time) + } fn del_script_pubkey_from_path( &mut self, @@ -180,6 +183,9 @@ impl BatchOperations for AnyDatabase { fn del_last_index(&mut self, keychain: KeychainKind) -> Result, Error> { impl_inner_method!(AnyDatabase, self, del_last_index, keychain) } + fn del_sync_time(&mut self) -> Result, Error> { + impl_inner_method!(AnyDatabase, self, del_sync_time) + } } impl Database for AnyDatabase { @@ -241,6 +247,9 @@ impl Database for AnyDatabase { fn get_last_index(&self, keychain: KeychainKind) -> Result, Error> { impl_inner_method!(AnyDatabase, self, get_last_index, keychain) } + fn get_sync_time(&self) -> Result, Error> { + impl_inner_method!(AnyDatabase, self, get_sync_time) + } fn increment_last_index(&mut self, keychain: KeychainKind) -> Result { impl_inner_method!(AnyDatabase, self, increment_last_index, keychain) @@ -272,6 +281,9 @@ impl BatchOperations for AnyBatch { fn set_last_index(&mut self, keychain: KeychainKind, value: u32) -> Result<(), Error> { impl_inner_method!(AnyBatch, self, set_last_index, keychain, value) } + fn set_sync_time(&mut self, sync_time: SyncTime) -> Result<(), Error> { + impl_inner_method!(AnyBatch, self, set_sync_time, sync_time) + } fn del_script_pubkey_from_path( &mut self, @@ -302,6 +314,9 @@ impl BatchOperations for AnyBatch { fn del_last_index(&mut self, keychain: KeychainKind) -> Result, Error> { impl_inner_method!(AnyBatch, self, del_last_index, keychain) } + fn del_sync_time(&mut self) -> Result, Error> { + impl_inner_method!(AnyBatch, self, del_sync_time) + } } impl BatchDatabase for AnyDatabase { diff --git a/src/database/keyvalue.rs b/src/database/keyvalue.rs index 2da92f228..07499e9f1 100644 --- a/src/database/keyvalue.rs +++ b/src/database/keyvalue.rs @@ -18,7 +18,7 @@ use bitcoin::hash_types::Txid; use bitcoin::{OutPoint, Script, Transaction}; use crate::database::memory::MapKey; -use crate::database::{BatchDatabase, BatchOperations, Database}; +use crate::database::{BatchDatabase, BatchOperations, Database, SyncTime}; use crate::error::Error; use crate::types::*; @@ -82,6 +82,13 @@ macro_rules! impl_batch_operations { Ok(()) } + fn set_sync_time(&mut self, data: SyncTime) -> Result<(), Error> { + let key = MapKey::SyncTime.as_map_key(); + self.insert(key, serde_json::to_vec(&data)?)$($after_insert)*; + + Ok(()) + } + fn del_script_pubkey_from_path(&mut self, keychain: KeychainKind, path: u32) -> Result, Error> { let key = MapKey::Path((Some(keychain), Some(path))).as_map_key(); let res = self.remove(key); @@ -168,6 +175,14 @@ macro_rules! impl_batch_operations { } } } + + fn del_sync_time(&mut self) -> Result, Error> { + let key = MapKey::SyncTime.as_map_key(); + let res = self.remove(key); + let res = $process_delete!(res); + + Ok(res.map(|b| serde_json::from_slice(&b)).transpose()?) + } } } @@ -342,6 +357,14 @@ impl Database for Tree { .transpose() } + fn get_sync_time(&self) -> Result, Error> { + let key = MapKey::SyncTime.as_map_key(); + Ok(self + .get(key)? + .map(|b| serde_json::from_slice(&b)) + .transpose()?) + } + // inserts 0 if not present fn increment_last_index(&mut self, keychain: KeychainKind) -> Result { let key = MapKey::LastIndex(keychain).as_map_key(); @@ -470,4 +493,9 @@ mod test { fn test_last_index() { crate::database::test::test_last_index(get_tree()); } + + #[test] + fn test_sync_time() { + crate::database::test::test_sync_time(get_tree()); + } } diff --git a/src/database/memory.rs b/src/database/memory.rs index 78cc031d4..e828dc9df 100644 --- a/src/database/memory.rs +++ b/src/database/memory.rs @@ -22,7 +22,7 @@ use bitcoin::consensus::encode::{deserialize, serialize}; use bitcoin::hash_types::Txid; use bitcoin::{OutPoint, Script, Transaction}; -use crate::database::{BatchDatabase, BatchOperations, ConfigurableDatabase, Database}; +use crate::database::{BatchDatabase, BatchOperations, ConfigurableDatabase, Database, SyncTime}; use crate::error::Error; use crate::types::*; @@ -33,6 +33,7 @@ use crate::types::*; // transactions t -> tx details // deriv indexes c{i,e} -> u32 // descriptor checksum d{i,e} -> vec +// last sync time l -> { height, timestamp } pub(crate) enum MapKey<'a> { Path((Option, Option)), @@ -41,6 +42,7 @@ pub(crate) enum MapKey<'a> { RawTx(Option<&'a Txid>), Transaction(Option<&'a Txid>), LastIndex(KeychainKind), + SyncTime, DescriptorChecksum(KeychainKind), } @@ -59,6 +61,7 @@ impl MapKey<'_> { MapKey::RawTx(_) => b"r".to_vec(), MapKey::Transaction(_) => b"t".to_vec(), MapKey::LastIndex(st) => [b"c", st.as_ref()].concat(), + MapKey::SyncTime => b"l".to_vec(), MapKey::DescriptorChecksum(st) => [b"d", st.as_ref()].concat(), } } @@ -180,6 +183,12 @@ impl BatchOperations for MemoryDatabase { Ok(()) } + fn set_sync_time(&mut self, data: SyncTime) -> Result<(), Error> { + let key = MapKey::SyncTime.as_map_key(); + self.map.insert(key, Box::new(data)); + + Ok(()) + } fn del_script_pubkey_from_path( &mut self, @@ -270,6 +279,13 @@ impl BatchOperations for MemoryDatabase { Some(b) => Ok(Some(*b.downcast_ref().unwrap())), } } + fn del_sync_time(&mut self) -> Result, Error> { + let key = MapKey::SyncTime.as_map_key(); + let res = self.map.remove(&key); + self.deleted_keys.push(key); + + Ok(res.map(|b| b.downcast_ref().cloned().unwrap())) + } } impl Database for MemoryDatabase { @@ -407,6 +423,14 @@ impl Database for MemoryDatabase { Ok(self.map.get(&key).map(|b| *b.downcast_ref().unwrap())) } + fn get_sync_time(&self) -> Result, Error> { + let key = MapKey::SyncTime.as_map_key(); + Ok(self + .map + .get(&key) + .map(|b| b.downcast_ref().cloned().unwrap())) + } + // inserts 0 if not present fn increment_last_index(&mut self, keychain: KeychainKind) -> Result { let key = MapKey::LastIndex(keychain).as_map_key(); @@ -479,12 +503,10 @@ macro_rules! populate_test_db { }; let txid = tx.txid(); - let confirmation_time = tx_meta - .min_confirmations - .map(|conf| $crate::ConfirmationTime { - height: current_height.unwrap().checked_sub(conf as u32).unwrap(), - timestamp: 0, - }); + let confirmation_time = tx_meta.min_confirmations.map(|conf| $crate::BlockTime { + height: current_height.unwrap().checked_sub(conf as u32).unwrap(), + timestamp: 0, + }); let tx_details = $crate::TransactionDetails { transaction: Some(tx.clone()), @@ -590,4 +612,9 @@ mod test { fn test_last_index() { crate::database::test::test_last_index(get_tree()); } + + #[test] + fn test_sync_time() { + crate::database::test::test_sync_time(get_tree()); + } } diff --git a/src/database/mod.rs b/src/database/mod.rs index 4a3936f55..e160b7422 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -24,6 +24,8 @@ //! //! [`Wallet`]: crate::wallet::Wallet +use serde::{Deserialize, Serialize}; + use bitcoin::hash_types::Txid; use bitcoin::{OutPoint, Script, Transaction, TxOut}; @@ -44,6 +46,15 @@ pub use sqlite::SqliteDatabase; pub mod memory; pub use memory::MemoryDatabase; +/// Blockchain state at the time of syncing +/// +/// Contains only the block time and height at the moment +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct SyncTime { + /// Block timestamp and height at the time of sync + pub block_time: BlockTime, +} + /// Trait for operations that can be batched /// /// This trait defines the list of operations that must be implemented on the [`Database`] type and @@ -64,6 +75,8 @@ pub trait BatchOperations { fn set_tx(&mut self, transaction: &TransactionDetails) -> Result<(), Error>; /// Store the last derivation index for a given keychain. fn set_last_index(&mut self, keychain: KeychainKind, value: u32) -> Result<(), Error>; + /// Store the sync time + fn set_sync_time(&mut self, sync_time: SyncTime) -> Result<(), Error>; /// Delete a script_pubkey given the keychain and its child number. fn del_script_pubkey_from_path( @@ -89,6 +102,10 @@ pub trait BatchOperations { ) -> Result, Error>; /// Delete the last derivation index for a keychain. fn del_last_index(&mut self, keychain: KeychainKind) -> Result, Error>; + /// Reset the sync time to `None` + /// + /// Returns the removed value + fn del_sync_time(&mut self) -> Result, Error>; } /// Trait for reading data from a database @@ -134,6 +151,8 @@ pub trait Database: BatchOperations { fn get_tx(&self, txid: &Txid, include_raw: bool) -> Result, Error>; /// Return the last defivation index for a keychain. fn get_last_index(&self, keychain: KeychainKind) -> Result, Error>; + /// Return the sync time, if present + fn get_sync_time(&self) -> Result, Error>; /// Increment the last derivation index for a keychain and return it /// @@ -325,7 +344,7 @@ pub mod test { received: 1337, sent: 420420, fee: Some(140), - confirmation_time: Some(ConfirmationTime { + confirmation_time: Some(BlockTime { timestamp: 123456, height: 1000, }), @@ -377,5 +396,25 @@ pub mod test { ); } + pub fn test_sync_time(mut tree: D) { + assert!(tree.get_sync_time().unwrap().is_none()); + + tree.set_sync_time(SyncTime { + block_time: BlockTime { + height: 100, + timestamp: 1000, + }, + }) + .unwrap(); + + let extracted = tree.get_sync_time().unwrap(); + assert!(extracted.is_some()); + assert_eq!(extracted.as_ref().unwrap().block_time.height, 100); + assert_eq!(extracted.as_ref().unwrap().block_time.timestamp, 1000); + + tree.del_sync_time().unwrap(); + assert!(tree.get_sync_time().unwrap().is_none()); + } + // TODO: more tests... } diff --git a/src/database/sqlite.rs b/src/database/sqlite.rs index 0dbaed44e..597ef6548 100644 --- a/src/database/sqlite.rs +++ b/src/database/sqlite.rs @@ -13,7 +13,7 @@ use bitcoin::consensus::encode::{deserialize, serialize}; use bitcoin::hash_types::Txid; use bitcoin::{OutPoint, Script, Transaction, TxOut}; -use crate::database::{BatchDatabase, BatchOperations, Database}; +use crate::database::{BatchDatabase, BatchOperations, Database, SyncTime}; use crate::error::Error; use crate::types::*; @@ -35,6 +35,7 @@ static MIGRATIONS: &[&str] = &[ "CREATE UNIQUE INDEX idx_indices_keychain ON last_derivation_indices(keychain);", "CREATE TABLE checksums (keychain TEXT, checksum BLOB);", "CREATE INDEX idx_checksums_keychain ON checksums(keychain);", + "CREATE TABLE sync_time (id INTEGER PRIMARY KEY, height INTEGER, timestamp INTEGER);" ]; /// Sqlite database stored on filesystem @@ -205,6 +206,19 @@ impl SqliteDatabase { Ok(()) } + fn update_sync_time(&self, data: SyncTime) -> Result { + let mut statement = self.connection.prepare_cached( + "INSERT INTO sync_time (id, height, timestamp) VALUES (0, :height, :timestamp) ON CONFLICT(id) DO UPDATE SET height=:height, timestamp=:timestamp WHERE id = 0", + )?; + + statement.execute(named_params! { + ":height": data.block_time.height, + ":timestamp": data.block_time.timestamp, + })?; + + Ok(self.connection.last_insert_rowid()) + } + fn select_script_pubkeys(&self) -> Result, Error> { let mut statement = self .connection @@ -375,7 +389,7 @@ impl SqliteDatabase { }; let confirmation_time = match (height, timestamp) { - (Some(height), Some(timestamp)) => Some(ConfirmationTime { height, timestamp }), + (Some(height), Some(timestamp)) => Some(BlockTime { height, timestamp }), _ => None, }; @@ -409,7 +423,7 @@ impl SqliteDatabase { let verified: bool = row.get(6)?; let confirmation_time = match (height, timestamp) { - (Some(height), Some(timestamp)) => Some(ConfirmationTime { height, timestamp }), + (Some(height), Some(timestamp)) => Some(BlockTime { height, timestamp }), _ => None, }; @@ -452,7 +466,7 @@ impl SqliteDatabase { }; let confirmation_time = match (height, timestamp) { - (Some(height), Some(timestamp)) => Some(ConfirmationTime { height, timestamp }), + (Some(height), Some(timestamp)) => Some(BlockTime { height, timestamp }), _ => None, }; @@ -487,6 +501,24 @@ impl SqliteDatabase { } } + fn select_sync_time(&self) -> Result, Error> { + let mut statement = self + .connection + .prepare_cached("SELECT height, timestamp FROM sync_time WHERE id = 0")?; + let mut rows = statement.query([])?; + + if let Some(row) = rows.next()? { + Ok(Some(SyncTime { + block_time: BlockTime { + height: row.get(0)?, + timestamp: row.get(1)?, + }, + })) + } else { + Ok(None) + } + } + fn select_checksum_by_keychain(&self, keychain: String) -> Result>, Error> { let mut statement = self .connection @@ -563,6 +595,14 @@ impl SqliteDatabase { Ok(()) } + + fn delete_sync_time(&self) -> Result<(), Error> { + let mut statement = self + .connection + .prepare_cached("DELETE FROM sync_time WHERE id = 0")?; + statement.execute([])?; + Ok(()) + } } impl BatchOperations for SqliteDatabase { @@ -622,6 +662,11 @@ impl BatchOperations for SqliteDatabase { Ok(()) } + fn set_sync_time(&mut self, ct: SyncTime) -> Result<(), Error> { + self.update_sync_time(ct)?; + Ok(()) + } + fn del_script_pubkey_from_path( &mut self, keychain: KeychainKind, @@ -707,6 +752,17 @@ impl BatchOperations for SqliteDatabase { None => Ok(None), } } + + fn del_sync_time(&mut self) -> Result, Error> { + match self.select_sync_time()? { + Some(value) => { + self.delete_sync_time()?; + + Ok(Some(value)) + } + None => Ok(None), + } + } } impl Database for SqliteDatabase { @@ -818,6 +874,10 @@ impl Database for SqliteDatabase { Ok(value) } + fn get_sync_time(&self) -> Result, Error> { + self.select_sync_time() + } + fn increment_last_index(&mut self, keychain: KeychainKind) -> Result { let keychain_string = serde_json::to_string(&keychain)?; match self.get_last_index(keychain)? { @@ -965,4 +1025,9 @@ pub mod test { fn test_last_index() { crate::database::test::test_last_index(get_database()); } + + #[test] + fn test_sync_time() { + crate::database::test::test_sync_time(get_database()); + } } diff --git a/src/testutils/blockchain_tests.rs b/src/testutils/blockchain_tests.rs index b44a97182..7174bf58c 100644 --- a/src/testutils/blockchain_tests.rs +++ b/src/testutils/blockchain_tests.rs @@ -394,6 +394,9 @@ macro_rules! bdk_blockchain_tests { #[test] fn test_sync_simple() { + use std::ops::Deref; + use crate::database::Database; + let (wallet, descriptors, mut test_client) = init_single_sig(); let tx = testutils! { @@ -402,7 +405,13 @@ macro_rules! bdk_blockchain_tests { println!("{:?}", tx); let txid = test_client.receive(tx); + // the RPC blockchain needs to call `sync()` during initialization to import the + // addresses (see `init_single_sig()`), so we skip this assertion + #[cfg(not(feature = "test-rpc"))] + assert!(wallet.database().deref().get_sync_time().unwrap().is_none(), "initial sync_time not none"); + wallet.sync(noop_progress(), None).unwrap(); + assert!(wallet.database().deref().get_sync_time().unwrap().is_some(), "sync_time hasn't been updated"); assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance"); assert_eq!(wallet.list_unspent().unwrap()[0].keychain, KeychainKind::External, "incorrect keychain kind"); diff --git a/src/types.rs b/src/types.rs index 3e4d8edf4..ac4a2228f 100644 --- a/src/types.rs +++ b/src/types.rs @@ -210,7 +210,7 @@ pub struct TransactionDetails { pub fee: Option, /// If the transaction is confirmed, contains height and timestamp of the block containing the /// transaction, unconfirmed transaction contains `None`. - pub confirmation_time: Option, + pub confirmation_time: Option, /// Whether the tx has been verified against the consensus rules /// /// Confirmed txs are considered "verified" by default, while unconfirmed txs are checked to @@ -222,20 +222,26 @@ pub struct TransactionDetails { 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)] -pub struct ConfirmationTime { +pub struct BlockTime { /// confirmation block height pub height: u32, /// confirmation block timestamp pub timestamp: u64, } -impl ConfirmationTime { - /// Returns `Some` `ConfirmationTime` if both `height` and `timestamp` are `Some` +/// **DEPRECATED**: Confirmation time of a transaction +/// +/// 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, timestamp: Option) -> Option { match (height, timestamp) { - (Some(height), Some(timestamp)) => Some(ConfirmationTime { height, timestamp }), + (Some(height), Some(timestamp)) => Some(BlockTime { height, timestamp }), _ => None, } } diff --git a/src/wallet/export.rs b/src/wallet/export.rs index 047e93a1b..e39d178ea 100644 --- a/src/wallet/export.rs +++ b/src/wallet/export.rs @@ -212,7 +212,7 @@ mod test { use crate::database::{memory::MemoryDatabase, BatchOperations}; use crate::types::TransactionDetails; use crate::wallet::Wallet; - use crate::ConfirmationTime; + use crate::BlockTime; fn get_test_db() -> MemoryDatabase { let mut db = MemoryDatabase::new(); @@ -226,7 +226,7 @@ mod test { received: 100_000, sent: 0, fee: Some(500), - confirmation_time: Some(ConfirmationTime { + confirmation_time: Some(BlockTime { timestamp: 12345678, height: 5000, }), diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index b8d1196d9..d5a6cb30e 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -57,7 +57,7 @@ use utils::{check_nlocktime, check_nsequence_rbf, After, Older, SecpCtx, DUST_LI use crate::blockchain::{Blockchain, Progress}; use crate::database::memory::MemoryDatabase; -use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils}; +use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils, SyncTime}; use crate::descriptor::derived::AsDerived; use crate::descriptor::policy::BuildSatisfaction; use crate::descriptor::{ @@ -1447,6 +1447,11 @@ where Ok(()) } + + /// Return an immutable reference to the internal database + pub fn database(&self) -> impl std::ops::Deref + '_ { + self.database.borrow() + } } impl Wallet @@ -1549,6 +1554,15 @@ where } } + let sync_time = SyncTime { + block_time: BlockTime { + height: maybe_await!(self.client.get_height())?, + timestamp: time::get_timestamp(), + }, + }; + debug!("Saving `sync_time` = {:?}", sync_time); + self.database.borrow_mut().set_sync_time(sync_time)?; + Ok(()) } @@ -2778,7 +2792,7 @@ pub(crate) mod test { let txid = tx.txid(); // skip saving the utxos, we know they can't be used anyways details.transaction = Some(tx); - details.confirmation_time = Some(ConfirmationTime { + details.confirmation_time = Some(BlockTime { timestamp: 12345678, height: 42, });