From: 志宇 Date: Thu, 10 Jul 2025 02:13:09 +0000 (+0000) Subject: feat(bitcoind_rpc)!: Use `getrawmempool` without verbose X-Git-Tag: bitcoind_rpc-0.21.0~5^2~2 X-Git-Url: http://internal-gitweb-vhost/script/%22https:/database/scripts/static/struct.LocalChain.html?a=commitdiff_plain;h=7e894f464ffc0fc21a05e485eae574292e8a63d5;p=bdk feat(bitcoind_rpc)!: Use `getrawmempool` without verbose This is the more performant method. The downside is that mempool txids are not returned to the seen-in-mempool timestamps so the caller either needs to provide this, or we need to rely on std to get the current timestamp. * `Emitter::mempool` method now requires the `std` feature. A non-std version of this is added: `Emitter::mempool_at` which takes in a `sync_time` parameter. Additional changes: * `NO_EXPECTED_MEMPOOL_TXIDS` is renamed to `NO_EXPECTED_MEMPOOL_TXS`. * Updated documentation. * Fix imports so that `bdk_bitcoind_rpc` compiles without `std`. --- diff --git a/crates/bitcoind_rpc/src/lib.rs b/crates/bitcoind_rpc/src/lib.rs index d72c22c7..ac0d190c 100644 --- a/crates/bitcoind_rpc/src/lib.rs +++ b/crates/bitcoind_rpc/src/lib.rs @@ -9,14 +9,16 @@ //! separate method, [`Emitter::mempool`] can be used to emit the whole mempool. #![warn(missing_docs)] +#[allow(unused_imports)] +#[macro_use] +extern crate alloc; + +use alloc::sync::Arc; +use bdk_core::collections::{HashMap, HashSet}; use bdk_core::{BlockId, CheckPoint}; use bitcoin::{Block, BlockHash, Transaction, Txid}; use bitcoincore_rpc::{bitcoincore_rpc_json, RpcApi}; -use std::{ - collections::{HashMap, HashSet}, - ops::Deref, - sync::Arc, -}; +use core::ops::Deref; pub mod bip158; @@ -53,11 +55,11 @@ pub struct Emitter { mempool_snapshot: HashMap>, } -/// Indicates that there are no initially expected mempool transactions. +/// Indicates that there are no initially-expected mempool transactions. /// -/// Pass this to the `expected_mempool_txids` field of [`Emitter::new`] when the wallet is known +/// Use this as the `expected_mempool_txs` field of [`Emitter::new`] when the wallet is known /// to start empty (i.e. with no unconfirmed transactions). -pub const NO_EXPECTED_MEMPOOL_TXIDS: core::iter::Empty> = core::iter::empty(); +pub const NO_EXPECTED_MEMPOOL_TXS: core::iter::Empty> = core::iter::empty(); impl Emitter where @@ -74,7 +76,7 @@ where /// /// `expected_mempool_txs` is the initial set of unconfirmed transactions provided by the /// wallet. This allows the [`Emitter`] to inform the wallet about relevant mempool evictions. - /// If it is known that the wallet is empty, [`NO_EXPECTED_MEMPOOL_TXIDS`] can be used. + /// If it is known that the wallet is empty, [`NO_EXPECTED_MEMPOOL_TXS`] can be used. pub fn new( client: C, last_cp: CheckPoint, @@ -99,21 +101,26 @@ where /// Emit mempool transactions and any evicted [`Txid`]s. /// /// This method returns a [`MempoolEvent`] containing the full transactions (with their - /// first-seen unix timestamps) that were emitted, and [`MempoolEvent::evicted_txids`] which are + /// first-seen unix timestamps) that were emitted, and [`MempoolEvent::evicted`] which are /// any [`Txid`]s which were previously seen in the mempool and are now missing. Evicted txids /// are only reported once the emitter’s checkpoint matches the RPC’s best block in both height /// and hash. Until `next_block()` advances the checkpoint to tip, `mempool()` will always - /// return an empty `evicted_txids` set. + /// return an empty `evicted` set. + #[cfg(feature = "std")] + pub fn mempool(&mut self) -> Result { + let sync_time = std::time::UNIX_EPOCH + .elapsed() + .expect("must get current time") + .as_secs(); + self.mempool_at(sync_time) + } + + /// Emit mempool transactions and any evicted [`Txid`]s at the given `sync_time`. /// - /// This method emits each transaction only once, unless we cannot guarantee the transaction's - /// ancestors are already emitted. + /// `sync_time` is in unix seconds. /// - /// To understand why, consider a receiver which filters transactions based on whether it - /// alters the UTXO set of tracked script pubkeys. If an emitted mempool transaction spends a - /// tracked UTXO which is confirmed at height `h`, but the receiver has only seen up to block - /// of height `h-1`, we want to re-emit this transaction until the receiver has seen the block - /// at height `h`. - pub fn mempool(&mut self) -> Result { + /// This is the no-std version of [`mempool`](Self::mempool). + pub fn mempool_at(&mut self, sync_time: u64) -> Result { let client = &*self.client; let mut rpc_tip_height; @@ -125,8 +132,8 @@ where loop { rpc_tip_height = client.get_block_count()?; rpc_tip_hash = client.get_block_hash(rpc_tip_height)?; - rpc_mempool = client.get_raw_mempool_verbose()?; - rpc_mempool_txids = rpc_mempool.keys().copied().collect::>(); + rpc_mempool = client.get_raw_mempool()?; + rpc_mempool_txids = rpc_mempool.iter().copied().collect::>(); let is_still_at_tip = rpc_tip_hash == client.get_block_hash(rpc_tip_height)? && rpc_tip_height == client.get_block_count()?; if is_still_at_tip { @@ -135,13 +142,11 @@ where } let mut mempool_event = MempoolEvent::default(); - let update_time = &mut 0_u64; mempool_event.update = rpc_mempool .into_iter() .filter_map({ - |(txid, tx_entry)| -> Option> { - *update_time = u64::max(*update_time, tx_entry.time); + |txid| -> Option> { let tx = match self.mempool_snapshot.get(&txid) { Some(tx) => tx.clone(), None => match client.get_raw_transaction(&txid, None) { @@ -154,7 +159,7 @@ where Err(err) => return Some(Err(err)), }, }; - Some(Ok((tx, tx_entry.time))) + Some(Ok((tx, sync_time))) } }) .collect::, _>>()?; @@ -171,7 +176,7 @@ where .mempool_snapshot .keys() .filter(|&txid| !rpc_mempool_txids.contains(txid)) - .map(|&txid| (txid, *update_time)) + .map(|&txid| (txid, sync_time)) .collect(); self.mempool_snapshot = mempool_event .update @@ -396,7 +401,7 @@ impl BitcoindRpcErrorExt for bitcoincore_rpc::Error { #[cfg(test)] mod test { - use crate::{bitcoincore_rpc::RpcApi, Emitter, NO_EXPECTED_MEMPOOL_TXIDS}; + use crate::{bitcoincore_rpc::RpcApi, Emitter, NO_EXPECTED_MEMPOOL_TXS}; use bdk_chain::local_chain::LocalChain; use bdk_testenv::{anyhow, TestEnv}; use bitcoin::{hashes::Hash, Address, Amount, ScriptBuf, Txid, WScriptHash}; @@ -411,7 +416,7 @@ mod test { env.rpc_client(), chain_tip.clone(), 1, - NO_EXPECTED_MEMPOOL_TXIDS, + NO_EXPECTED_MEMPOOL_TXS, ); env.mine_blocks(100, None)?; diff --git a/crates/bitcoind_rpc/tests/test_emitter.rs b/crates/bitcoind_rpc/tests/test_emitter.rs index 0ccb7fe6..9df3b404 100644 --- a/crates/bitcoind_rpc/tests/test_emitter.rs +++ b/crates/bitcoind_rpc/tests/test_emitter.rs @@ -1,6 +1,6 @@ use std::{collections::BTreeSet, ops::Deref}; -use bdk_bitcoind_rpc::{Emitter, NO_EXPECTED_MEMPOOL_TXIDS}; +use bdk_bitcoind_rpc::{Emitter, NO_EXPECTED_MEMPOOL_TXS}; use bdk_chain::{ bitcoin::{Address, Amount, Txid}, local_chain::{CheckPoint, LocalChain}, @@ -26,7 +26,7 @@ pub fn test_sync_local_chain() -> anyhow::Result<()> { env.rpc_client(), local_chain.tip(), 0, - NO_EXPECTED_MEMPOOL_TXIDS, + NO_EXPECTED_MEMPOOL_TXS, ); // Mine some blocks and return the actual block hashes. @@ -161,7 +161,7 @@ fn test_into_tx_graph() -> anyhow::Result<()> { index }); - let emitter = &mut Emitter::new(env.rpc_client(), chain.tip(), 0, NO_EXPECTED_MEMPOOL_TXIDS); + let emitter = &mut Emitter::new(env.rpc_client(), chain.tip(), 0, NO_EXPECTED_MEMPOOL_TXS); while let Some(emission) = emitter.next_block()? { let height = emission.block_height(); @@ -257,7 +257,7 @@ fn ensure_block_emitted_after_reorg_is_at_reorg_height() -> anyhow::Result<()> { hash: env.rpc_client().get_block_hash(0)?, }), EMITTER_START_HEIGHT as _, - NO_EXPECTED_MEMPOOL_TXIDS, + NO_EXPECTED_MEMPOOL_TXS, ); env.mine_blocks(CHAIN_TIP_HEIGHT, None)?; @@ -339,7 +339,7 @@ fn tx_can_become_unconfirmed_after_reorg() -> anyhow::Result<()> { hash: env.rpc_client().get_block_hash(0)?, }), 0, - NO_EXPECTED_MEMPOOL_TXIDS, + NO_EXPECTED_MEMPOOL_TXS, ); // setup addresses @@ -430,7 +430,7 @@ fn mempool_avoids_re_emission() -> anyhow::Result<()> { hash: env.rpc_client().get_block_hash(0)?, }), 0, - NO_EXPECTED_MEMPOOL_TXIDS, + NO_EXPECTED_MEMPOOL_TXS, ); // mine blocks and sync up emitter @@ -503,7 +503,7 @@ fn no_agreement_point() -> anyhow::Result<()> { hash: env.rpc_client().get_block_hash(0)?, }), (PREMINE_COUNT - 2) as u32, - NO_EXPECTED_MEMPOOL_TXIDS, + NO_EXPECTED_MEMPOOL_TXS, ); // mine 101 blocks @@ -675,7 +675,7 @@ fn detect_new_mempool_txs() -> anyhow::Result<()> { hash: env.rpc_client().get_block_hash(0)?, }), 0, - NO_EXPECTED_MEMPOOL_TXIDS, + NO_EXPECTED_MEMPOOL_TXS, ); while emitter.next_block()?.is_some() {}