]> Untitled Git - bdk/commitdiff
feat(bitcoind_rpc)!: Use `getrawmempool` without verbose
author志宇 <hello@evanlinjin.me>
Thu, 10 Jul 2025 02:13:09 +0000 (02:13 +0000)
committer志宇 <hello@evanlinjin.me>
Thu, 10 Jul 2025 02:13:09 +0000 (02:13 +0000)
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`.

crates/bitcoind_rpc/src/lib.rs
crates/bitcoind_rpc/tests/test_emitter.rs

index d72c22c7a60ffd24310fea435232f0f55f3f72a8..ac0d190ca28ba853d3d5d385b1fa9d9fd8cd9bec 100644 (file)
@@ -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<C> {
     mempool_snapshot: HashMap<Txid, Arc<Transaction>>,
 }
 
-/// 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<Arc<Transaction>> = core::iter::empty();
+pub const NO_EXPECTED_MEMPOOL_TXS: core::iter::Empty<Arc<Transaction>> = core::iter::empty();
 
 impl<C> Emitter<C>
 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<MempoolEvent, bitcoincore_rpc::Error> {
+        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<MempoolEvent, bitcoincore_rpc::Error> {
+    /// This is the no-std version of [`mempool`](Self::mempool).
+    pub fn mempool_at(&mut self, sync_time: u64) -> Result<MempoolEvent, bitcoincore_rpc::Error> {
         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::<HashSet<Txid>>();
+            rpc_mempool = client.get_raw_mempool()?;
+            rpc_mempool_txids = rpc_mempool.iter().copied().collect::<HashSet<Txid>>();
             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<Result<_, bitcoincore_rpc::Error>> {
-                    *update_time = u64::max(*update_time, tx_entry.time);
+                |txid| -> Option<Result<_, bitcoincore_rpc::Error>> {
                     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::<Result<Vec<_>, _>>()?;
@@ -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)?;
index 0ccb7fe61b849f676b5cce27639dba2ecc31110a..9df3b404c02f704195205906f4bcfe579688dd71 100644 (file)
@@ -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() {}