]> Untitled Git - bdk/commitdiff
[db] Add the `last_sync_time` database entry
authorAlekos Filini <alekos.filini@gmail.com>
Sat, 23 Oct 2021 12:51:42 +0000 (14:51 +0200)
committerAlekos Filini <alekos.filini@gmail.com>
Wed, 10 Nov 2021 11:29:47 +0000 (12:29 +0100)
This will be used to store the height and timestamp after every sync.

src/database/any.rs
src/database/keyvalue.rs
src/database/memory.rs
src/database/mod.rs
src/database/sqlite.rs

index 5186452cf253ebdfb4c28d2cf3bbe81ff856e42d..ba06e79ae5ecec6c22286ad2c2265499416c7b8f 100644 (file)
@@ -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_last_sync_time(&mut self, last_sync_time: ConfirmationTime) -> Result<(), Error> {
+        impl_inner_method!(AnyDatabase, self, set_last_sync_time, last_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<Option<u32>, Error> {
         impl_inner_method!(AnyDatabase, self, del_last_index, keychain)
     }
+    fn del_last_sync_time(&mut self) -> Result<Option<ConfirmationTime>, Error> {
+        impl_inner_method!(AnyDatabase, self, del_last_sync_time)
+    }
 }
 
 impl Database for AnyDatabase {
@@ -241,6 +247,9 @@ impl Database for AnyDatabase {
     fn get_last_index(&self, keychain: KeychainKind) -> Result<Option<u32>, Error> {
         impl_inner_method!(AnyDatabase, self, get_last_index, keychain)
     }
+    fn get_last_sync_time(&self) -> Result<Option<ConfirmationTime>, Error> {
+        impl_inner_method!(AnyDatabase, self, get_last_sync_time)
+    }
 
     fn increment_last_index(&mut self, keychain: KeychainKind) -> Result<u32, Error> {
         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_last_sync_time(&mut self, last_sync_time: ConfirmationTime) -> Result<(), Error> {
+        impl_inner_method!(AnyBatch, self, set_last_sync_time, last_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<Option<u32>, Error> {
         impl_inner_method!(AnyBatch, self, del_last_index, keychain)
     }
+    fn del_last_sync_time(&mut self) -> Result<Option<ConfirmationTime>, Error> {
+        impl_inner_method!(AnyBatch, self, del_last_sync_time)
+    }
 }
 
 impl BatchDatabase for AnyDatabase {
index 2da92f2285262a0e02f427bda72ced26a521fd3a..b856b5c5e31cb2b58595fe30fe2e6c7e099e2201 100644 (file)
@@ -82,6 +82,13 @@ macro_rules! impl_batch_operations {
             Ok(())
         }
 
+        fn set_last_sync_time(&mut self, ct: ConfirmationTime) -> Result<(), Error> {
+            let key = MapKey::LastSyncTime.as_map_key();
+            self.insert(key, serde_json::to_vec(&ct)?)$($after_insert)*;
+
+            Ok(())
+        }
+
         fn del_script_pubkey_from_path(&mut self, keychain: KeychainKind, path: u32) -> Result<Option<Script>, 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_last_sync_time(&mut self) -> Result<Option<ConfirmationTime>, Error> {
+            let key = MapKey::LastSyncTime.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_last_sync_time(&self) -> Result<Option<ConfirmationTime>, Error> {
+        let key = MapKey::LastSyncTime.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<u32, Error> {
         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_last_sync_time() {
+        crate::database::test::test_last_sync_time(get_tree());
+    }
 }
index 78cc031d405e12f0ee3d0bd6f7db87a37e7f4d6f..21f00ed942bc01d19cac8a1424d61bf3898369d4 100644 (file)
@@ -33,6 +33,7 @@ use crate::types::*;
 // transactions         t<txid> -> tx details
 // deriv indexes        c{i,e} -> u32
 // descriptor checksum  d{i,e} -> vec<u8>
+// last sync time       l -> { height, timestamp }
 
 pub(crate) enum MapKey<'a> {
     Path((Option<KeychainKind>, Option<u32>)),
@@ -41,6 +42,7 @@ pub(crate) enum MapKey<'a> {
     RawTx(Option<&'a Txid>),
     Transaction(Option<&'a Txid>),
     LastIndex(KeychainKind),
+    LastSyncTime,
     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::LastSyncTime => b"l".to_vec(),
             MapKey::DescriptorChecksum(st) => [b"d", st.as_ref()].concat(),
         }
     }
@@ -180,6 +183,12 @@ impl BatchOperations for MemoryDatabase {
 
         Ok(())
     }
+    fn set_last_sync_time(&mut self, ct: ConfirmationTime) -> Result<(), Error> {
+        let key = MapKey::LastSyncTime.as_map_key();
+        self.map.insert(key, Box::new(ct));
+
+        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_last_sync_time(&mut self) -> Result<Option<ConfirmationTime>, Error> {
+        let key = MapKey::LastSyncTime.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_last_sync_time(&self) -> Result<Option<ConfirmationTime>, Error> {
+        let key = MapKey::LastSyncTime.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<u32, Error> {
         let key = MapKey::LastIndex(keychain).as_map_key();
@@ -590,4 +614,9 @@ mod test {
     fn test_last_index() {
         crate::database::test::test_last_index(get_tree());
     }
+
+    #[test]
+    fn test_last_sync_time() {
+        crate::database::test::test_last_sync_time(get_tree());
+    }
 }
index 4a3936f55d98c83ae5f87f88dfc78ecbba924438..310dd1929380d94185b37df5c93eef609faee01c 100644 (file)
@@ -64,6 +64,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: ConfirmationTime) -> Result<(), Error>;
 
     /// Delete a script_pubkey given the keychain and its child number.
     fn del_script_pubkey_from_path(
@@ -89,6 +91,10 @@ pub trait BatchOperations {
     ) -> Result<Option<TransactionDetails>, Error>;
     /// Delete the last derivation index for a keychain.
     fn del_last_index(&mut self, keychain: KeychainKind) -> Result<Option<u32>, Error>;
+    /// Reset the last sync time to `None`
+    ///
+    /// Returns the removed value
+    fn del_last_sync_time(&mut self) -> Result<Option<ConfirmationTime>, Error>;
 }
 
 /// Trait for reading data from a database
@@ -134,6 +140,8 @@ pub trait Database: BatchOperations {
     fn get_tx(&self, txid: &Txid, include_raw: bool) -> Result<Option<TransactionDetails>, Error>;
     /// Return the last defivation index for a keychain.
     fn get_last_index(&self, keychain: KeychainKind) -> Result<Option<u32>, Error>;
+    /// Return the last sync time, if present
+    fn get_last_sync_time(&self) -> Result<Option<ConfirmationTime>, Error>;
 
     /// Increment the last derivation index for a keychain and return it
     ///
@@ -377,5 +385,23 @@ pub mod test {
         );
     }
 
+    pub fn test_last_sync_time<D: Database>(mut tree: D) {
+        assert!(tree.get_last_sync_time().unwrap().is_none());
+
+        tree.set_last_sync_time(ConfirmationTime {
+            height: 100,
+            timestamp: 1000,
+        })
+        .unwrap();
+
+        let extracted = tree.get_last_sync_time().unwrap();
+        assert!(extracted.is_some());
+        assert_eq!(extracted.as_ref().unwrap().height, 100);
+        assert_eq!(extracted.as_ref().unwrap().timestamp, 1000);
+
+        tree.del_last_sync_time().unwrap();
+        assert!(tree.get_last_sync_time().unwrap().is_none());
+    }
+
     // TODO: more tests...
 }
index 0dbaed44ed6a38b55c634d5b8b335c7847e0ed6d..813a8f9c8f5a226b05f3254109a43edd48668853 100644 (file)
@@ -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 last_sync_time (id INTEGER PRIMARY KEY, height INTEGER, timestamp INTEGER);"
 ];
 
 /// Sqlite database stored on filesystem
@@ -205,6 +206,19 @@ impl SqliteDatabase {
         Ok(())
     }
 
+    fn update_last_sync_time(&self, ct: ConfirmationTime) -> Result<i64, Error> {
+        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",
+        )?;
+
+        statement.execute(named_params! {
+            ":height": ct.height,
+            ":timestamp": ct.timestamp,
+        })?;
+
+        Ok(self.connection.last_insert_rowid())
+    }
+
     fn select_script_pubkeys(&self) -> Result<Vec<Script>, Error> {
         let mut statement = self
             .connection
@@ -487,6 +501,20 @@ impl SqliteDatabase {
         }
     }
 
+    fn select_last_sync_time(&self) -> Result<Option<ConfirmationTime>, 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(ConfirmationTime {
+                height: row.get(0)?,
+                timestamp: row.get(1)?,
+            })
+        })?;
+
+        Ok(rows.next().transpose()?)
+    }
+
     fn select_checksum_by_keychain(&self, keychain: String) -> Result<Option<Vec<u8>>, Error> {
         let mut statement = self
             .connection
@@ -563,6 +591,14 @@ impl SqliteDatabase {
 
         Ok(())
     }
+
+    fn delete_last_sync_time(&self) -> Result<(), Error> {
+        let mut statement = self
+            .connection
+            .prepare_cached("DELETE FROM last_sync_time WHERE id = 0")?;
+        statement.execute([])?;
+        Ok(())
+    }
 }
 
 impl BatchOperations for SqliteDatabase {
@@ -622,6 +658,11 @@ impl BatchOperations for SqliteDatabase {
         Ok(())
     }
 
+    fn set_last_sync_time(&mut self, ct: ConfirmationTime) -> Result<(), Error> {
+        self.update_last_sync_time(ct)?;
+        Ok(())
+    }
+
     fn del_script_pubkey_from_path(
         &mut self,
         keychain: KeychainKind,
@@ -707,6 +748,17 @@ impl BatchOperations for SqliteDatabase {
             None => Ok(None),
         }
     }
+
+    fn del_last_sync_time(&mut self) -> Result<Option<ConfirmationTime>, Error> {
+        match self.select_last_sync_time()? {
+            Some(value) => {
+                self.delete_last_sync_time()?;
+
+                Ok(Some(value))
+            }
+            None => Ok(None),
+        }
+    }
 }
 
 impl Database for SqliteDatabase {
@@ -818,6 +870,10 @@ impl Database for SqliteDatabase {
         Ok(value)
     }
 
+    fn get_last_sync_time(&self) -> Result<Option<ConfirmationTime>, Error> {
+        self.select_last_sync_time()
+    }
+
     fn increment_last_index(&mut self, keychain: KeychainKind) -> Result<u32, Error> {
         let keychain_string = serde_json::to_string(&keychain)?;
         match self.get_last_index(keychain)? {
@@ -965,4 +1021,9 @@ pub mod test {
     fn test_last_index() {
         crate::database::test::test_last_index(get_database());
     }
+
+    #[test]
+    fn test_last_sync_time() {
+        crate::database::test::test_last_sync_time(get_database());
+    }
 }