]> Untitled Git - bdk/commitdiff
docs(chain): add doctest for min confirmation balance filtering
authorWei Chen <wzc110@gmail.com>
Thu, 7 Aug 2025 05:50:27 +0000 (05:50 +0000)
committerWei Chen <wzc110@gmail.com>
Wed, 3 Sep 2025 09:15:43 +0000 (09:15 +0000)
crates/chain/src/tx_graph.rs
crates/core/src/checkpoint.rs

index 1e2b7cede05bef9f08c0826a55a988e8a55ed0c0..7bbdef63f0d01abe5668bf6aef53fb43d24403dd 100644 (file)
@@ -1315,6 +1315,69 @@ impl<A: Anchor> TxGraph<A> {
     ///
     /// This is the infallible version of [`try_balance`].
     ///
+    /// ### Minimum confirmations
+    ///
+    /// To filter for transactions with at least `N` confirmations, pass a `chain_tip` that is
+    /// `N - 1` blocks below the actual tip. This ensures that only transactions with at least `N`
+    /// confirmations are counted as confirmed in the returned [`Balance`].
+    ///
+    /// ```
+    /// # use bdk_chain::tx_graph::TxGraph;
+    /// # use bdk_chain::{local_chain::LocalChain, CanonicalizationParams, ConfirmationBlockTime};
+    /// # use bdk_testenv::{hash, utils::new_tx};
+    /// # use bitcoin::{Amount, OutPoint, ScriptBuf, Transaction, TxIn, TxOut};
+    ///
+    /// # let spk = ScriptBuf::from_hex("0014c692ecf13534982a9a2834565cbd37add8027140").unwrap();
+    /// # let chain =
+    /// #     LocalChain::from_blocks((0..=15).map(|i| (i as u32, hash!("h"))).collect()).unwrap();
+    /// # let mut graph: TxGraph = TxGraph::default();
+    /// # let coinbase_tx = Transaction {
+    /// #     input: vec![TxIn {
+    /// #         previous_output: OutPoint::null(),
+    /// #         ..Default::default()
+    /// #     }],
+    /// #     output: vec![TxOut {
+    /// #         value: Amount::from_sat(70000),
+    /// #         script_pubkey: spk.clone(),
+    /// #     }],
+    /// #     ..new_tx(0)
+    /// # };
+    /// # let tx = Transaction {
+    /// #     input: vec![TxIn {
+    /// #         previous_output: OutPoint::new(coinbase_tx.compute_txid(), 0),
+    /// #         ..Default::default()
+    /// #     }],
+    /// #     output: vec![TxOut {
+    /// #         value: Amount::from_sat(42_000),
+    /// #         script_pubkey: spk.clone(),
+    /// #     }],
+    /// #     ..new_tx(1)
+    /// # };
+    /// # let txid = tx.compute_txid();
+    /// # let _ = graph.insert_tx(tx.clone());
+    /// # let _ = graph.insert_anchor(
+    /// #     txid,
+    /// #     ConfirmationBlockTime {
+    /// #         block_id: chain.get(10).unwrap().block_id(),
+    /// #         confirmation_time: 123456,
+    /// #     },
+    /// # );
+    ///
+    /// let minimum_confirmations = 6;
+    /// let target_tip = chain
+    ///     .tip()
+    ///     .floor_below(minimum_confirmations - 1)
+    ///     .expect("checkpoint from local chain must have genesis");
+    /// let balance = graph.balance(
+    ///     &chain,
+    ///     target_tip.block_id(),
+    ///     CanonicalizationParams::default(),
+    ///     std::iter::once(((), OutPoint::new(txid, 0))),
+    ///     |_: &(), _| true,
+    /// );
+    /// assert_eq!(balance.confirmed, Amount::from_sat(42_000));
+    /// ```
+    ///
     /// [`try_balance`]: Self::try_balance
     pub fn balance<C: ChainOracle<Error = Infallible>, OI: Clone>(
         &self,
index bb6bb9fe48345cb218b68bd5d543f8b55e6a82b3..01b36e2587071138ff707bf145e94884dca71071 100644 (file)
@@ -192,6 +192,30 @@ impl CheckPoint {
             })
     }
 
+    /// Returns the checkpoint at `height` if one exists, otherwise the nearest checkpoint at a
+    /// lower height.
+    ///
+    /// This is equivalent to taking the "floor" of `height` over this checkpoint chain.
+    ///
+    /// Returns `None` if no checkpoint exists at or below the given height.
+    pub fn floor_at(&self, height: u32) -> Option<Self> {
+        self.range(..=height).next()
+    }
+
+    /// Returns the checkpoint located a number of heights below this one.
+    ///
+    /// This is a convenience wrapper for [`CheckPoint::floor_at`], subtracting `to_subtract` from
+    /// the current height.
+    ///
+    /// - If a checkpoint exists exactly `offset` heights below, it is returned.
+    /// - Otherwise, the nearest checkpoint *below that target height* is returned.
+    ///
+    /// Returns `None` if `to_subtract` is greater than the current height, or if there is no
+    /// checkpoint at or below the target height.
+    pub fn floor_below(&self, offset: u32) -> Option<Self> {
+        self.floor_at(self.height().checked_sub(offset)?)
+    }
+
     /// Inserts `block_id` at its height within the chain.
     ///
     /// The effect of `insert` depends on whether a height already exists. If it doesn't the