bitcoin: add mempool_spenders to Bitcoin interface

`is_in_mempool` has been updated to use `mempool_entry`.
This commit is contained in:
jp1ac4 2023-11-10 09:36:10 +00:00
parent 68b2503b12
commit 714fd5e142
No known key found for this signature in database
GPG Key ID: A7ACD32423568D7B
3 changed files with 96 additions and 4 deletions

View File

@ -1120,20 +1120,49 @@ impl BitcoinD {
/// Whether this transaction is in the mempool.
pub fn is_in_mempool(&self, txid: &bitcoin::Txid) -> bool {
self.mempool_entry(txid).is_some()
}
/// Get mempool entry of the given transaction.
/// Returns `None` if it is not in the mempool.
pub fn mempool_entry(&self, txid: &bitcoin::Txid) -> Option<MempoolEntry> {
match self
.make_fallible_node_request("getmempoolentry", &params!(Json::String(txid.to_string())))
{
Ok(_) => true,
Ok(json) => Some(MempoolEntry::from(json)),
Err(BitcoindError::Server(jsonrpc::Error::Rpc(jsonrpc::error::RpcError {
code: -5,
..
}))) => false,
}))) => None,
Err(e) => {
panic!("Unexpected error returned by bitcoind {}", e);
}
}
}
/// Get the list of txids spending those outpoints in mempool.
pub fn mempool_txs_spending_prevouts(
&self,
outpoints: &[bitcoin::OutPoint],
) -> Vec<bitcoin::Txid> {
let prevouts: Json = outpoints
.iter()
.map(|op| serde_json::json!({"txid": op.txid.to_string(), "vout": op.vout}))
.collect();
self.make_node_request("gettxspendingprevout", &params!(prevouts))
.as_array()
.expect("Always returns an array")
.iter()
.filter_map(|e| {
e.get("spendingtxid").map(|e| {
e.as_str()
.and_then(|s| bitcoin::Txid::from_str(s).ok())
.expect("Must be a valid txid if present")
})
})
.collect()
}
/// Stop bitcoind.
pub fn stop(&self) {
self.make_node_request("stop", &[]);
@ -1394,3 +1423,48 @@ impl<'a> CachedTxGetter<'a> {
}
}
}
#[derive(Debug, Clone)]
pub struct MempoolEntry {
pub vsize: u64,
pub fees: MempoolEntryFees,
}
impl From<Json> for MempoolEntry {
fn from(json: Json) -> MempoolEntry {
let vsize = json
.get("vsize")
.and_then(Json::as_u64)
.expect("Must be present in bitcoind response");
let fees = json
.get("fees")
.as_ref()
.expect("Must be present in bitcoind response")
.into();
MempoolEntry { vsize, fees }
}
}
#[derive(Debug, Clone)]
pub struct MempoolEntryFees {
pub base: bitcoin::Amount,
pub descendant: bitcoin::Amount,
}
impl From<&&Json> for MempoolEntryFees {
fn from(json: &&Json) -> MempoolEntryFees {
let json = json.as_object().expect("fees must be an object");
let base = json
.get("base")
.and_then(Json::as_f64)
.and_then(|a| bitcoin::Amount::from_btc(a).ok())
.expect("Must be present and a valid amount");
let descendant = json
.get("descendant")
.and_then(Json::as_f64)
.and_then(|a| bitcoin::Amount::from_btc(a).ok())
.expect("Must be present and a valid amount");
MempoolEntryFees { base, descendant }
}
}

View File

@ -9,7 +9,7 @@ use crate::{
bitcoin::d::{BitcoindError, CachedTxGetter, LSBlockEntry},
descriptors,
};
pub use d::SyncProgress;
pub use d::{MempoolEntry, MempoolEntryFees, SyncProgress};
use std::{fmt, sync};
@ -113,6 +113,9 @@ pub trait BitcoinInterface: Send {
&self,
txid: &bitcoin::Txid,
) -> Option<(bitcoin::Transaction, Option<Block>)>;
/// Get the details of unconfirmed transactions spending these outpoints, if any.
fn mempool_spenders(&self, outpoints: &[bitcoin::OutPoint]) -> Vec<MempoolEntry>;
}
impl BitcoinInterface for d::BitcoinD {
@ -356,6 +359,13 @@ impl BitcoinInterface for d::BitcoinD {
) -> Option<(bitcoin::Transaction, Option<Block>)> {
self.get_transaction(txid).map(|res| (res.tx, res.block))
}
fn mempool_spenders(&self, outpoints: &[bitcoin::OutPoint]) -> Vec<MempoolEntry> {
self.mempool_txs_spending_prevouts(outpoints)
.into_iter()
.filter_map(|txid| self.mempool_entry(&txid))
.collect()
}
}
// FIXME: do we need to repeat the entire trait implemenation? Isn't there a nicer way?
@ -442,6 +452,10 @@ impl BitcoinInterface for sync::Arc<sync::Mutex<dyn BitcoinInterface + 'static>>
) -> Option<(bitcoin::Transaction, Option<Block>)> {
self.lock().unwrap().wallet_transaction(txid)
}
fn mempool_spenders(&self, outpoints: &[bitcoin::OutPoint]) -> Vec<MempoolEntry> {
self.lock().unwrap().mempool_spenders(outpoints)
}
}
// FIXME: We could avoid this type (and all the conversions entailing allocations) if bitcoind

View File

@ -1,5 +1,5 @@
use crate::{
bitcoin::{BitcoinInterface, Block, BlockChainTip, SyncProgress, UTxO},
bitcoin::{BitcoinInterface, Block, BlockChainTip, MempoolEntry, SyncProgress, UTxO},
config::{BitcoinConfig, Config},
database::{BlockInfo, Coin, CoinStatus, DatabaseConnection, DatabaseInterface, LabelItem},
descriptors, DaemonHandle,
@ -119,6 +119,10 @@ impl BitcoinInterface for DummyBitcoind {
) -> Option<(bitcoin::Transaction, Option<Block>)> {
self.txs.get(txid).cloned()
}
fn mempool_spenders(&self, _: &[bitcoin::OutPoint]) -> Vec<MempoolEntry> {
Vec::new()
}
}
struct DummyDbState {