From e13cf8a160b1e973c1c12a2e1c4d2e6b1307ca13 Mon Sep 17 00:00:00 2001 From: jp1ac4 <121959000+jp1ac4@users.noreply.github.com> Date: Mon, 8 Jul 2024 11:24:11 +0100 Subject: [PATCH] commands: use database for TxGetter --- src/commands/mod.rs | 53 ++++++++++++++++---------------------- src/database/mod.rs | 7 +++++ src/database/sqlite/mod.rs | 15 +++++++++++ src/testutils.rs | 4 +++ 4 files changed, 48 insertions(+), 31 deletions(-) diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 6588aa674..2ee4cfe67 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -150,27 +150,27 @@ impl fmt::Display for RbfErrorInfo { } } -/// A wallet transaction getter which fetches the transaction from our Bitcoin backend with a cache +/// A wallet transaction getter which fetches the transaction from our database backend with a cache /// to avoid needless redundant calls. Note the cache holds an Option<> so we also avoid redundant -/// calls when the txid isn't known by our Bitcoin backend. -struct BitcoindTxGetter<'a> { - bitcoind: &'a sync::Arc>, +/// calls when the txid isn't known by our database backend. +struct DbTxGetter<'a> { + db: &'a sync::Arc>, cache: HashMap>, } -impl<'a> BitcoindTxGetter<'a> { - pub fn new(bitcoind: &'a sync::Arc>) -> Self { +impl<'a> DbTxGetter<'a> { + pub fn new(db: &'a sync::Arc>) -> Self { Self { - bitcoind, + db, cache: HashMap::new(), } } } -impl<'a> TxGetter for BitcoindTxGetter<'a> { +impl<'a> TxGetter for DbTxGetter<'a> { fn get_tx(&mut self, txid: &bitcoin::Txid) -> Option { if let hash_map::Entry::Vacant(entry) = self.cache.entry(*txid) { - entry.insert(self.bitcoind.wallet_transaction(txid).map(|wtx| wtx.0)); + entry.insert(self.db.connection().get_tx(txid)); } self.cache.get(txid).cloned().flatten() } @@ -437,7 +437,7 @@ impl DaemonControl { return Err(CommandError::InvalidFeerate(feerate_vb)); } let mut db_conn = self.db.connection(); - let mut tx_getter = BitcoindTxGetter::new(&self.bitcoin); + let mut tx_getter = DbTxGetter::new(&self.db); // Prepare the destination addresses. let mut destinations_checked = Vec::with_capacity(destinations.len()); @@ -732,7 +732,7 @@ impl DaemonControl { feerate_vb: Option, ) -> Result { let mut db_conn = self.db.connection(); - let mut tx_getter = BitcoindTxGetter::new(&self.bitcoin); + let mut tx_getter = DbTxGetter::new(&self.db); if is_cancel && feerate_vb.is_some() { return Err(CommandError::RbfError(RbfErrorInfo::SuperfluousFeerate)); @@ -1023,7 +1023,7 @@ impl DaemonControl { if feerate_vb < 1 { return Err(CommandError::InvalidFeerate(feerate_vb)); } - let mut tx_getter = BitcoindTxGetter::new(&self.bitcoin); + let mut tx_getter = DbTxGetter::new(&self.db); let mut db_conn = self.db.connection(); let sweep_addr = self.spend_addr(&mut db_conn, self.validate_address(address)?); @@ -1375,25 +1375,17 @@ mod tests { #[test] fn create_spend() { - let dummy_op = bitcoin::OutPoint::from_str( - "3753a1d74c0af8dd0a0f3b763c14faf3bd9ed03cbdf33337a074fb0e9f6c7810:0", - ) - .unwrap(); - let mut dummy_bitcoind = DummyBitcoind::new(); - dummy_bitcoind.txs.insert( - dummy_op.txid, - ( - bitcoin::Transaction { - version: TxVersion::TWO, - lock_time: absolute::LockTime::Blocks(absolute::Height::ZERO), - input: vec![], - output: vec![], - }, - None, - ), - ); - let ms = DummyLiana::new(dummy_bitcoind, DummyDatabase::new()); + let dummy_tx = bitcoin::Transaction { + version: TxVersion::TWO, + lock_time: absolute::LockTime::Blocks(absolute::Height::ZERO), + input: vec![], + output: vec![], + }; + let dummy_op = bitcoin::OutPoint::new(dummy_tx.txid(), 0); + let ms = DummyLiana::new(DummyBitcoind::new(), DummyDatabase::new()); let control = &ms.control(); + let mut db_conn = control.db().lock().unwrap().connection(); + db_conn.new_txs(&[dummy_tx]); // Arguments sanity checking let dummy_addr = @@ -1424,7 +1416,6 @@ mod tests { control.create_spend(&destinations, &[dummy_op], 1, None), Err(CommandError::UnknownOutpoint(dummy_op)) ); - let mut db_conn = control.db().lock().unwrap().connection(); db_conn.new_unspent_coins(&[Coin { outpoint: dummy_op, is_immature: false, diff --git a/src/database/mod.rs b/src/database/mod.rs index 0fe9fb73a..631cfda0c 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -158,6 +158,9 @@ pub trait DatabaseConnection { &mut self, txids: &[bitcoin::Txid], ) -> Vec<(bitcoin::Transaction, Option, Option)>; + + /// Retrieve a transaction if it exists. + fn get_tx(&mut self, txid: &bitcoin::Txid) -> Option; } impl DatabaseConnection for SqliteConn { @@ -346,6 +349,10 @@ impl DatabaseConnection for SqliteConn { }) .collect() } + + fn get_tx(&mut self, txid: &bitcoin::Txid) -> Option { + self.get_tx(txid) + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/src/database/sqlite/mod.rs b/src/database/sqlite/mod.rs index e8fb8b55f..351b829dc 100644 --- a/src/database/sqlite/mod.rs +++ b/src/database/sqlite/mod.rs @@ -779,6 +779,21 @@ impl SqliteConn { .expect("Db must not fail") } + pub fn get_tx(&mut self, txid: &bitcoin::Txid) -> Option { + db_query( + &mut self.conn, + "SELECT tx FROM transactions WHERE txid = ?1", + rusqlite::params![txid[..].to_vec()], + |row| { + let tx: Vec = row.get(0)?; + let tx = bitcoin::consensus::deserialize(&tx).expect("We only store valid txs"); + Ok(tx) + }, + ) + .expect("Db must not fail") + .pop() + } + pub fn delete_spend(&mut self, txid: &bitcoin::Txid) { db_exec(&mut self.conn, |db_tx| { db_tx.execute( diff --git a/src/testutils.rs b/src/testutils.rs index c05c884ed..7f50e570e 100644 --- a/src/testutils.rs +++ b/src/testutils.rs @@ -491,6 +491,10 @@ impl DatabaseConnection for DummyDatabase { } wallet_txs } + + fn get_tx(&mut self, txid: &bitcoin::Txid) -> Option { + self.db.read().unwrap().txs.get(txid).cloned() + } } pub struct DummyLiana {