From 5830226216218f2b3ce89206c58fb24c88ef8c9a Mon Sep 17 00:00:00 2001 From: Alekos Filini Date: Thu, 4 Nov 2021 15:38:38 +0000 Subject: [PATCH] [database] Wrap `BlockTime` in another struct to allow adding more fields in the future --- src/database/any.rs | 20 +++++++------- src/database/keyvalue.rs | 20 +++++++------- src/database/memory.rs | 34 ++++++++++++------------ src/database/mod.rs | 45 ++++++++++++++++++++------------ src/database/sqlite.rs | 56 +++++++++++++++++++++------------------- src/wallet/mod.rs | 16 ++++++------ 6 files changed, 103 insertions(+), 88 deletions(-) diff --git a/src/database/any.rs b/src/database/any.rs index a608daca..8b626e4b 100644 --- a/src/database/any.rs +++ b/src/database/any.rs @@ -144,8 +144,8 @@ 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_last_sync_time(&mut self, last_sync_time: BlockTime) -> Result<(), Error> { - impl_inner_method!(AnyDatabase, self, set_last_sync_time, last_sync_time) + 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( @@ -183,8 +183,8 @@ 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_last_sync_time(&mut self) -> Result, Error> { - impl_inner_method!(AnyDatabase, self, del_last_sync_time) + fn del_sync_time(&mut self) -> Result, Error> { + impl_inner_method!(AnyDatabase, self, del_sync_time) } } @@ -247,8 +247,8 @@ impl Database for AnyDatabase { fn get_last_index(&self, keychain: KeychainKind) -> Result, Error> { impl_inner_method!(AnyDatabase, self, get_last_index, keychain) } - fn get_last_sync_time(&self) -> Result, Error> { - impl_inner_method!(AnyDatabase, self, get_last_sync_time) + fn get_sync_time(&self) -> Result, Error> { + impl_inner_method!(AnyDatabase, self, get_sync_time) } fn increment_last_index(&mut self, keychain: KeychainKind) -> Result { @@ -281,8 +281,8 @@ 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_last_sync_time(&mut self, last_sync_time: BlockTime) -> Result<(), Error> { - impl_inner_method!(AnyBatch, self, set_last_sync_time, last_sync_time) + 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( @@ -314,8 +314,8 @@ 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_last_sync_time(&mut self) -> Result, Error> { - impl_inner_method!(AnyBatch, self, del_last_sync_time) + fn del_sync_time(&mut self) -> Result, Error> { + impl_inner_method!(AnyBatch, self, del_sync_time) } } diff --git a/src/database/keyvalue.rs b/src/database/keyvalue.rs index 976c3856..07499e9f 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,9 +82,9 @@ macro_rules! impl_batch_operations { Ok(()) } - fn set_last_sync_time(&mut self, ct: BlockTime) -> Result<(), Error> { - let key = MapKey::LastSyncTime.as_map_key(); - self.insert(key, serde_json::to_vec(&ct)?)$($after_insert)*; + 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(()) } @@ -176,8 +176,8 @@ macro_rules! impl_batch_operations { } } - fn del_last_sync_time(&mut self) -> Result, Error> { - let key = MapKey::LastSyncTime.as_map_key(); + 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); @@ -357,8 +357,8 @@ impl Database for Tree { .transpose() } - fn get_last_sync_time(&self) -> Result, Error> { - let key = MapKey::LastSyncTime.as_map_key(); + 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)) @@ -495,7 +495,7 @@ mod test { } #[test] - fn test_last_sync_time() { - crate::database::test::test_last_sync_time(get_tree()); + 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 35b3283a..e828dc9d 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::*; @@ -42,7 +42,7 @@ pub(crate) enum MapKey<'a> { RawTx(Option<&'a Txid>), Transaction(Option<&'a Txid>), LastIndex(KeychainKind), - LastSyncTime, + SyncTime, DescriptorChecksum(KeychainKind), } @@ -61,7 +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::LastSyncTime => b"l".to_vec(), + MapKey::SyncTime => b"l".to_vec(), MapKey::DescriptorChecksum(st) => [b"d", st.as_ref()].concat(), } } @@ -183,9 +183,9 @@ impl BatchOperations for MemoryDatabase { Ok(()) } - fn set_last_sync_time(&mut self, ct: BlockTime) -> Result<(), Error> { - let key = MapKey::LastSyncTime.as_map_key(); - self.map.insert(key, Box::new(ct)); + 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(()) } @@ -279,8 +279,8 @@ impl BatchOperations for MemoryDatabase { Some(b) => Ok(Some(*b.downcast_ref().unwrap())), } } - fn del_last_sync_time(&mut self) -> Result, Error> { - let key = MapKey::LastSyncTime.as_map_key(); + 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); @@ -423,8 +423,8 @@ impl Database for MemoryDatabase { Ok(self.map.get(&key).map(|b| *b.downcast_ref().unwrap())) } - fn get_last_sync_time(&self) -> Result, Error> { - let key = MapKey::LastSyncTime.as_map_key(); + fn get_sync_time(&self) -> Result, Error> { + let key = MapKey::SyncTime.as_map_key(); Ok(self .map .get(&key) @@ -503,12 +503,10 @@ macro_rules! populate_test_db { }; let txid = tx.txid(); - let confirmation_time = tx_meta - .min_confirmations - .map(|conf| $crate::BlockTime { - 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()), @@ -616,7 +614,7 @@ mod test { } #[test] - fn test_last_sync_time() { - crate::database::test::test_last_sync_time(get_tree()); + 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 a73d45b9..e160b742 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,8 +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 in terms of block height and timestamp - fn set_last_sync_time(&mut self, last_sync_time: BlockTime) -> 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( @@ -91,10 +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 last sync time to `None` + /// Reset the sync time to `None` /// /// Returns the removed value - fn del_last_sync_time(&mut self) -> Result, Error>; + fn del_sync_time(&mut self) -> Result, Error>; } /// Trait for reading data from a database @@ -140,8 +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 last sync time, if present - fn get_last_sync_time(&self) -> 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 /// @@ -385,22 +396,24 @@ pub mod test { ); } - pub fn test_last_sync_time(mut tree: D) { - assert!(tree.get_last_sync_time().unwrap().is_none()); + pub fn test_sync_time(mut tree: D) { + assert!(tree.get_sync_time().unwrap().is_none()); - tree.set_last_sync_time(BlockTime { - height: 100, - timestamp: 1000, + tree.set_sync_time(SyncTime { + block_time: BlockTime { + height: 100, + timestamp: 1000, + }, }) .unwrap(); - let extracted = tree.get_last_sync_time().unwrap(); + let extracted = tree.get_sync_time().unwrap(); assert!(extracted.is_some()); - assert_eq!(extracted.as_ref().unwrap().height, 100); - assert_eq!(extracted.as_ref().unwrap().timestamp, 1000); + assert_eq!(extracted.as_ref().unwrap().block_time.height, 100); + assert_eq!(extracted.as_ref().unwrap().block_time.timestamp, 1000); - tree.del_last_sync_time().unwrap(); - assert!(tree.get_last_sync_time().unwrap().is_none()); + 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 6394f744..597ef654 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,7 +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 last_sync_time (id INTEGER PRIMARY KEY, height INTEGER, timestamp INTEGER);" + "CREATE TABLE sync_time (id INTEGER PRIMARY KEY, height INTEGER, timestamp INTEGER);" ]; /// Sqlite database stored on filesystem @@ -206,14 +206,14 @@ impl SqliteDatabase { Ok(()) } - fn update_last_sync_time(&self, ct: BlockTime) -> Result { + fn update_sync_time(&self, data: SyncTime) -> Result { 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 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": ct.height, - ":timestamp": ct.timestamp, + ":height": data.block_time.height, + ":timestamp": data.block_time.timestamp, })?; Ok(self.connection.last_insert_rowid()) @@ -501,18 +501,22 @@ impl SqliteDatabase { } } - fn select_last_sync_time(&self) -> Result, Error> { + fn select_sync_time(&self) -> Result, Error> { let mut statement = self .connection - .prepare_cached("SELECT height, timestamp FROM last_sync_time WHERE id = 0")?; - let mut rows = statement.query_map([], |row| { - Ok(BlockTime { - height: row.get(0)?, - timestamp: row.get(1)?, - }) - })?; + .prepare_cached("SELECT height, timestamp FROM sync_time WHERE id = 0")?; + let mut rows = statement.query([])?; - Ok(rows.next().transpose()?) + 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> { @@ -592,10 +596,10 @@ impl SqliteDatabase { Ok(()) } - fn delete_last_sync_time(&self) -> Result<(), Error> { + fn delete_sync_time(&self) -> Result<(), Error> { let mut statement = self .connection - .prepare_cached("DELETE FROM last_sync_time WHERE id = 0")?; + .prepare_cached("DELETE FROM sync_time WHERE id = 0")?; statement.execute([])?; Ok(()) } @@ -658,8 +662,8 @@ impl BatchOperations for SqliteDatabase { Ok(()) } - fn set_last_sync_time(&mut self, ct: BlockTime) -> Result<(), Error> { - self.update_last_sync_time(ct)?; + fn set_sync_time(&mut self, ct: SyncTime) -> Result<(), Error> { + self.update_sync_time(ct)?; Ok(()) } @@ -749,10 +753,10 @@ impl BatchOperations for SqliteDatabase { } } - fn del_last_sync_time(&mut self) -> Result, Error> { - match self.select_last_sync_time()? { + fn del_sync_time(&mut self) -> Result, Error> { + match self.select_sync_time()? { Some(value) => { - self.delete_last_sync_time()?; + self.delete_sync_time()?; Ok(Some(value)) } @@ -870,8 +874,8 @@ impl Database for SqliteDatabase { Ok(value) } - fn get_last_sync_time(&self) -> Result, Error> { - self.select_last_sync_time() + fn get_sync_time(&self) -> Result, Error> { + self.select_sync_time() } fn increment_last_index(&mut self, keychain: KeychainKind) -> Result { @@ -1023,7 +1027,7 @@ pub mod test { } #[test] - fn test_last_sync_time() { - crate::database::test::test_last_sync_time(get_database()); + fn test_sync_time() { + crate::database::test::test_sync_time(get_database()); } } diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index 5ae46e99..d5a6cb30 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::{ @@ -1554,14 +1554,14 @@ where } } - let last_sync_time = BlockTime { - height: maybe_await!(self.client.get_height())?, - timestamp: time::get_timestamp(), + let sync_time = SyncTime { + block_time: BlockTime { + height: maybe_await!(self.client.get_height())?, + timestamp: time::get_timestamp(), + }, }; - debug!("Saving `last_sync_time` = {:?}", last_sync_time); - self.database - .borrow_mut() - .set_last_sync_time(last_sync_time)?; + debug!("Saving `sync_time` = {:?}", sync_time); + self.database.borrow_mut().set_sync_time(sync_time)?; Ok(()) }