Skip to content

Commit

Permalink
commands: use database for TxGetter
Browse files Browse the repository at this point in the history
  • Loading branch information
jp1ac4 committed Jul 9, 2024
1 parent d22477a commit d4ab115
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 31 deletions.
53 changes: 22 additions & 31 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<sync::Mutex<dyn BitcoinInterface>>,
/// calls when the txid isn't known by our database backend.
struct DbTxGetter<'a> {
db: &'a sync::Arc<sync::Mutex<dyn DatabaseInterface>>,
cache: HashMap<bitcoin::Txid, Option<bitcoin::Transaction>>,
}

impl<'a> BitcoindTxGetter<'a> {
pub fn new(bitcoind: &'a sync::Arc<sync::Mutex<dyn BitcoinInterface>>) -> Self {
impl<'a> DbTxGetter<'a> {
pub fn new(db: &'a sync::Arc<sync::Mutex<dyn DatabaseInterface>>) -> 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<bitcoin::Transaction> {
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()
}
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -732,7 +732,7 @@ impl DaemonControl {
feerate_vb: Option<u64>,
) -> Result<CreateSpendResult, CommandError> {
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));
Expand Down Expand Up @@ -1027,7 +1027,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)?);

Expand Down Expand Up @@ -1379,25 +1379,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 =
Expand Down Expand Up @@ -1428,7 +1420,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,
Expand Down
7 changes: 7 additions & 0 deletions src/database/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ pub trait DatabaseConnection {
&mut self,
txids: &[bitcoin::Txid],
) -> Vec<(bitcoin::Transaction, Option<i32>, Option<u32>)>;

/// Retrieve a transaction if it exists.
fn get_tx(&mut self, txid: &bitcoin::Txid) -> Option<bitcoin::Transaction>;
}

impl DatabaseConnection for SqliteConn {
Expand Down Expand Up @@ -346,6 +349,10 @@ impl DatabaseConnection for SqliteConn {
})
.collect()
}

fn get_tx(&mut self, txid: &bitcoin::Txid) -> Option<bitcoin::Transaction> {
self.get_tx(txid)
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
Expand Down
15 changes: 15 additions & 0 deletions src/database/sqlite/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,21 @@ impl SqliteConn {
.expect("Db must not fail")
}

pub fn get_tx(&mut self, txid: &bitcoin::Txid) -> Option<bitcoin::Transaction> {
db_query(
&mut self.conn,
"SELECT tx FROM transactions WHERE txid = ?1",
rusqlite::params![txid[..].to_vec()],
|row| {
let tx: Vec<u8> = 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(
Expand Down
4 changes: 4 additions & 0 deletions src/testutils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,10 @@ impl DatabaseConnection for DummyDatabase {
}
wallet_txs
}

fn get_tx(&mut self, txid: &bitcoin::Txid) -> Option<bitcoin::Transaction> {
self.db.read().unwrap().txs.get(txid).cloned()
}
}

pub struct DummyLiana {
Expand Down

0 comments on commit d4ab115

Please sign in to comment.