feat(esplora): include previous TxOuts for fee calculation

The previous `TxOut` for transactions received from an external
wallet are added as floating `TxOut`s to `TxGraph` to allow for
fee calculation.
This commit is contained in:
Wei Chen 2024-01-31 23:41:42 +08:00
parent f099b42005
commit 552f11cb5f
No known key found for this signature in database
4 changed files with 82 additions and 2 deletions

View File

@ -1,7 +1,7 @@
use async_trait::async_trait; use async_trait::async_trait;
use bdk_chain::collections::btree_map; use bdk_chain::collections::btree_map;
use bdk_chain::{ use bdk_chain::{
bitcoin::{BlockHash, OutPoint, ScriptBuf, Txid}, bitcoin::{BlockHash, OutPoint, ScriptBuf, TxOut, Txid},
collections::BTreeMap, collections::BTreeMap,
local_chain::{self, CheckPoint}, local_chain::{self, CheckPoint},
BlockId, ConfirmationTimeHeightAnchor, TxGraph, BlockId, ConfirmationTimeHeightAnchor, TxGraph,
@ -204,6 +204,24 @@ impl EsploraAsyncExt for esplora_client::AsyncClient {
if let Some(anchor) = anchor_from_status(&tx.status) { if let Some(anchor) = anchor_from_status(&tx.status) {
let _ = graph.insert_anchor(tx.txid, anchor); let _ = graph.insert_anchor(tx.txid, anchor);
} }
let previous_outputs = tx.vin.iter().filter_map(|vin| {
let prevout = vin.prevout.as_ref()?;
Some((
OutPoint {
txid: vin.txid,
vout: vin.vout,
},
TxOut {
script_pubkey: prevout.scriptpubkey.clone(),
value: prevout.value,
},
))
});
for (outpoint, txout) in previous_outputs {
let _ = graph.insert_txout(outpoint, txout);
}
} }
} }

View File

@ -3,7 +3,7 @@ use std::thread::JoinHandle;
use bdk_chain::collections::btree_map; use bdk_chain::collections::btree_map;
use bdk_chain::collections::BTreeMap; use bdk_chain::collections::BTreeMap;
use bdk_chain::{ use bdk_chain::{
bitcoin::{BlockHash, OutPoint, ScriptBuf, Txid}, bitcoin::{BlockHash, OutPoint, ScriptBuf, TxOut, Txid},
local_chain::{self, CheckPoint}, local_chain::{self, CheckPoint},
BlockId, ConfirmationTimeHeightAnchor, TxGraph, BlockId, ConfirmationTimeHeightAnchor, TxGraph,
}; };
@ -194,6 +194,24 @@ impl EsploraExt for esplora_client::BlockingClient {
if let Some(anchor) = anchor_from_status(&tx.status) { if let Some(anchor) = anchor_from_status(&tx.status) {
let _ = graph.insert_anchor(tx.txid, anchor); let _ = graph.insert_anchor(tx.txid, anchor);
} }
let previous_outputs = tx.vin.iter().filter_map(|vin| {
let prevout = vin.prevout.as_ref()?;
Some((
OutPoint {
txid: vin.txid,
vout: vin.vout,
},
TxOut {
script_pubkey: prevout.scriptpubkey.clone(),
value: prevout.value,
},
))
});
for (outpoint, txout) in previous_outputs {
let _ = graph.insert_txout(outpoint, txout);
}
} }
} }

View File

@ -109,6 +109,28 @@ pub async fn test_update_tx_graph_without_keychain() -> anyhow::Result<()> {
) )
.await?; .await?;
// Check to see if we have the floating txouts available from our two created transactions'
// previous outputs in order to calculate transaction fees.
for tx in graph_update.full_txs() {
// Retrieve the calculated fee from `TxGraph`, which will panic if we do not have the
// floating txouts available from the transactions' previous outputs.
let fee = graph_update.calculate_fee(tx.tx).expect("Fee must exist");
// Retrieve the fee in the transaction data from `bitcoind`.
let tx_fee = env
.bitcoind
.client
.get_transaction(&tx.txid, None)
.expect("Tx must exist")
.fee
.expect("Fee must exist")
.abs()
.to_sat() as u64;
// Check that the calculated fee matches the fee from the transaction data.
assert_eq!(fee, tx_fee);
}
let mut graph_update_txids: Vec<Txid> = graph_update.full_txs().map(|tx| tx.txid).collect(); let mut graph_update_txids: Vec<Txid> = graph_update.full_txs().map(|tx| tx.txid).collect();
graph_update_txids.sort(); graph_update_txids.sort();
let mut expected_txids = vec![txid1, txid2]; let mut expected_txids = vec![txid1, txid2];

View File

@ -136,6 +136,28 @@ pub fn test_update_tx_graph_without_keychain() -> anyhow::Result<()> {
1, 1,
)?; )?;
// Check to see if we have the floating txouts available from our two created transactions'
// previous outputs in order to calculate transaction fees.
for tx in graph_update.full_txs() {
// Retrieve the calculated fee from `TxGraph`, which will panic if we do not have the
// floating txouts available from the transactions' previous outputs.
let fee = graph_update.calculate_fee(tx.tx).expect("Fee must exist");
// Retrieve the fee in the transaction data from `bitcoind`.
let tx_fee = env
.bitcoind
.client
.get_transaction(&tx.txid, None)
.expect("Tx must exist")
.fee
.expect("Fee must exist")
.abs()
.to_sat() as u64;
// Check that the calculated fee matches the fee from the transaction data.
assert_eq!(fee, tx_fee);
}
let mut graph_update_txids: Vec<Txid> = graph_update.full_txs().map(|tx| tx.txid).collect(); let mut graph_update_txids: Vec<Txid> = graph_update.full_txs().map(|tx| tx.txid).collect();
graph_update_txids.sort(); graph_update_txids.sort();
let mut expected_txids = vec![txid1, txid2]; let mut expected_txids = vec![txid1, txid2];