This will be used to store the height and timestamp after every sync.
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,
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 {
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)
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,
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 {
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);
}
}
}
+
+ 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()?)
+ }
}
}
.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();
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());
+ }
}
// 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>)),
RawTx(Option<&'a Txid>),
Transaction(Option<&'a Txid>),
LastIndex(KeychainKind),
+ LastSyncTime,
DescriptorChecksum(KeychainKind),
}
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(),
}
}
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,
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 {
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();
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());
+ }
}
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(
) -> 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
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
///
);
}
+ 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...
}
"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
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
}
}
+ 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
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 {
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,
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 {
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)? {
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());
+ }
}