]> Untitled Git - bdk/commitdiff
doc: Improve TxGraph & co docs
authorDaniela Brozzoni <danielabrozzoni@protonmail.com>
Tue, 17 Oct 2023 09:00:05 +0000 (11:00 +0200)
committerDaniela Brozzoni <danielabrozzoni@protonmail.com>
Wed, 13 Dec 2023 09:12:12 +0000 (10:12 +0100)
crates/bdk/src/wallet/mod.rs
crates/bdk/src/wallet/signer.rs
crates/bitcoind_rpc/src/lib.rs
crates/chain/src/chain_data.rs
crates/chain/src/chain_oracle.rs
crates/chain/src/indexed_tx_graph.rs
crates/chain/src/lib.rs
crates/chain/src/local_chain.rs
crates/chain/src/tx_data_traits.rs
crates/chain/src/tx_graph.rs
crates/chain/tests/test_tx_graph_conflicts.rs

index 4f06876900730b6581f787665fa5a177c7630410..e945484dd86a5c373baafb65644a58077e1a70cf 100644 (file)
@@ -11,7 +11,7 @@
 
 //! Wallet
 //!
-//! This module defines the [`Wallet`] structure.
+//! This module defines the [`Wallet`].
 use crate::collections::{BTreeMap, HashMap, HashSet};
 use alloc::{
     boxed::Box,
@@ -77,7 +77,7 @@ const COINBASE_MATURITY: u32 = 100;
 
 /// A Bitcoin wallet
 ///
-/// The `Wallet` struct acts as a way of coherently interfacing with output descriptors and related transactions.
+/// The `Wallet` acts as a way of coherently interfacing with output descriptors and related transactions.
 /// Its main components are:
 ///
 /// 1. output *descriptors* from which it can derive addresses.
index e1e003c6172db31bc34dcd68342d7ab589e60467..da4940bf93dce627f3734933921fdb46033e4967 100644 (file)
@@ -221,7 +221,7 @@ pub enum SignerContext {
     },
 }
 
-/// Wrapper structure to pair a signer with its context
+/// Wrapper to pair a signer with its context
 #[derive(Debug, Clone)]
 pub struct SignerWrapper<S: Sized + fmt::Debug + Clone> {
     signer: S,
index 0fafbc7674971bdc2c0936ba730ce6c0cb45e35b..e790b8a8ed08c96c2be1676327b9e618054d9d72 100644 (file)
@@ -14,7 +14,7 @@ use bitcoin::{block::Header, Block, BlockHash, Transaction};
 pub use bitcoincore_rpc;
 use bitcoincore_rpc::bitcoincore_rpc_json;
 
-/// A structure that emits data sourced from [`bitcoincore_rpc::Client`].
+/// The [`Emitter`] is used to emit data sourced from [`bitcoincore_rpc::Client`].
 ///
 /// Refer to [module-level documentation] for more.
 ///
index 4da6449653cd6150f762c8ac3cdcca980f247bf2..c32d3e1c9a339ac021676ea3518c867013bca8bb 100644 (file)
@@ -147,6 +147,8 @@ impl From<(&u32, &BlockHash)> for BlockId {
 
 /// An [`Anchor`] implementation that also records the exact confirmation height of the transaction.
 ///
+/// Note that the confirmation block and the anchor block can be different here.
+///
 /// Refer to [`Anchor`] for more details.
 #[derive(Debug, Default, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
 #[cfg_attr(
@@ -186,6 +188,8 @@ impl AnchorFromBlockPosition for ConfirmationHeightAnchor {
 /// An [`Anchor`] implementation that also records the exact confirmation time and height of the
 /// transaction.
 ///
+/// Note that the confirmation block and the anchor block can be different here.
+///
 /// Refer to [`Anchor`] for more details.
 #[derive(Debug, Default, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
 #[cfg_attr(
index 038025619f00774006e57af34456c8472e5166e2..08e697ed4c0e2a3c23370c178ebce072f276e738 100644 (file)
@@ -3,7 +3,7 @@ use crate::BlockId;
 /// Represents a service that tracks the blockchain.
 ///
 /// The main method is [`is_block_in_chain`] which determines whether a given block of [`BlockId`]
-/// is an ancestor of another "static block".
+/// is an ancestor of the `chain_tip`.
 ///
 /// [`is_block_in_chain`]: Self::is_block_in_chain
 pub trait ChainOracle {
index 68e7846b6e263d86c5278a2bb4879a505de1120b..777b5d978b6b7acb798d73e290f3628a486ccb4b 100644 (file)
@@ -1,7 +1,5 @@
-//! Contains the [`IndexedTxGraph`] structure and associated types.
-//!
-//! This is essentially a [`TxGraph`] combined with an indexer.
-
+//! Contains the [`IndexedTxGraph`] and associated types. Refer to the
+//! [`IndexedTxGraph`] documentation for more.
 use alloc::vec::Vec;
 use bitcoin::{Block, OutPoint, Transaction, TxOut, Txid};
 
@@ -11,9 +9,9 @@ use crate::{
     Anchor, AnchorFromBlockPosition, Append, BlockId,
 };
 
-/// A struct that combines [`TxGraph`] and an [`Indexer`] implementation.
+/// The [`IndexedTxGraph`] combines a [`TxGraph`] and an [`Indexer`] implementation.
 ///
-/// This structure ensures that [`TxGraph`] and [`Indexer`] are updated atomically.
+/// It ensures that [`TxGraph`] and [`Indexer`] are updated atomically.
 #[derive(Debug)]
 pub struct IndexedTxGraph<A, I> {
     /// Transaction index.
@@ -266,7 +264,7 @@ where
     }
 }
 
-/// A structure that represents changes to an [`IndexedTxGraph`].
+/// Represents changes to an [`IndexedTxGraph`].
 #[derive(Clone, Debug, PartialEq)]
 #[cfg_attr(
     feature = "serde",
index ed167ebf6c114851d6c3720357d48a931728c538..04ca62c4c628562f56a8d4769126de668f27133f 100644 (file)
@@ -12,9 +12,8 @@
 //!    you do it synchronously or asynchronously. If you know a fact about the blockchain, you can just
 //!    tell `bdk_chain`'s APIs about it, and that information will be integrated, if it can be done
 //!    consistently.
-//! 2. Error-free APIs.
-//! 3. Data persistence agnostic -- `bdk_chain` does not care where you cache on-chain data, what you
-//!    cache or how you fetch it.
+//! 2. Data persistence agnostic -- `bdk_chain` does not care where you cache on-chain data, what you
+//!    cache or how you retrieve it from persistent storage.
 //!
 //! [Bitcoin Dev Kit]: https://bitcoindevkit.org/
 
index 1538cdfda55949f3bf1569e0754abc17b4bae8ce..bdd25d8e0c063c95258b1fe6f03791437803db13 100644 (file)
@@ -7,7 +7,7 @@ use crate::{BlockId, ChainOracle};
 use alloc::sync::Arc;
 use bitcoin::BlockHash;
 
-/// A structure that represents changes to [`LocalChain`].
+/// The [`ChangeSet`] represents changes to [`LocalChain`].
 ///
 /// The key represents the block height, and the value either represents added a new [`CheckPoint`]
 /// (if [`Some`]), or removing a [`CheckPoint`] (if [`None`]).
@@ -127,7 +127,7 @@ impl CheckPoint {
     }
 }
 
-/// A structure that iterates over checkpoints backwards.
+/// Iterates over checkpoints backwards.
 pub struct CheckPointIter {
     current: Option<Arc<CPInner>>,
 }
@@ -153,7 +153,7 @@ impl IntoIterator for CheckPoint {
     }
 }
 
-/// A struct to update [`LocalChain`].
+/// Used to update [`LocalChain`].
 ///
 /// This is used as input for [`LocalChain::apply_update`]. It contains the update's chain `tip` and
 /// a flag `introduce_older_blocks` which signals whether this update intends to introduce missing
index c957a3e57e07668382afdf19c96b69f395f33bf3..5c854a9569c151285fecf42b21078daa8fa1f51c 100644 (file)
@@ -5,21 +5,25 @@ use alloc::vec::Vec;
 
 /// Trait that "anchors" blockchain data to a specific block of height and hash.
 ///
-/// [`Anchor`] implementations must be [`Ord`] by the anchor block's [`BlockId`] first.
-///
-/// I.e. If transaction A is anchored in block B, then if block B is in the best chain, we can
+/// If transaction A is anchored in block B, and block B is in the best chain, we can
 /// assume that transaction A is also confirmed in the best chain. This does not necessarily mean
 /// that transaction A is confirmed in block B. It could also mean transaction A is confirmed in a
 /// parent block of B.
 ///
+/// Every [`Anchor`] implementation must contain a [`BlockId`] parameter, and must implement
+/// [`Ord`]. When implementing [`Ord`], the anchors' [`BlockId`]s should take precedence
+/// over other elements inside the [`Anchor`]s for comparison purposes, i.e., you should first
+/// compare the anchors' [`BlockId`]s and then care about the rest.
+///
+/// The example shows different types of anchors:
 /// ```
 /// # use bdk_chain::local_chain::LocalChain;
 /// # use bdk_chain::tx_graph::TxGraph;
 /// # use bdk_chain::BlockId;
 /// # use bdk_chain::ConfirmationHeightAnchor;
+/// # use bdk_chain::ConfirmationTimeHeightAnchor;
 /// # use bdk_chain::example_utils::*;
 /// # use bitcoin::hashes::Hash;
-///
 /// // Initialize the local chain with two blocks.
 /// let chain = LocalChain::from_blocks(
 ///     [
@@ -47,6 +51,7 @@ use alloc::vec::Vec;
 /// );
 ///
 /// // Insert `tx` into a `TxGraph` that uses `ConfirmationHeightAnchor` as the anchor type.
+/// // This anchor records the anchor block and the confirmation height of the transaction.
 /// // When a transaction is anchored with `ConfirmationHeightAnchor`, the anchor block and
 /// // confirmation block can be different. However, the confirmation block cannot be higher than
 /// // the anchor block and both blocks must be in the same chain for the anchor to be valid.
@@ -62,6 +67,25 @@ use alloc::vec::Vec;
 ///         confirmation_height: 1,
 ///     },
 /// );
+///
+/// // Insert `tx` into a `TxGraph` that uses `ConfirmationTimeHeightAnchor` as the anchor type.
+/// // This anchor records the anchor block, the confirmation height and time of the transaction.
+/// // When a transaction is anchored with `ConfirmationTimeHeightAnchor`, the anchor block and
+/// // confirmation block can be different. However, the confirmation block cannot be higher than
+/// // the anchor block and both blocks must be in the same chain for the anchor to be valid.
+/// let mut graph_c = TxGraph::<ConfirmationTimeHeightAnchor>::default();
+/// let _ = graph_c.insert_tx(tx.clone());
+/// graph_c.insert_anchor(
+///     tx.txid(),
+///     ConfirmationTimeHeightAnchor {
+///         anchor_block: BlockId {
+///             height: 2,
+///             hash: Hash::hash("third".as_bytes()),
+///         },
+///         confirmation_height: 1,
+///         confirmation_time: 123,
+///     },
+/// );
 /// ```
 pub trait Anchor: core::fmt::Debug + Clone + Eq + PartialOrd + Ord + core::hash::Hash {
     /// Returns the [`BlockId`] that the associated blockchain data is "anchored" in.
index f84c3a3dc1f3b5fc8254ef1321534ee5c4f8a9d7..10cf104466a23128f6ad819d36044567d1eb8a7e 100644 (file)
@@ -1,12 +1,32 @@
 //! Module for structures that store and traverse transactions.
 //!
-//! [`TxGraph`] is a monotone structure that inserts transactions and indexes the spends. The
-//! [`ChangeSet`] structure reports changes of [`TxGraph`] but can also be applied to a
-//! [`TxGraph`] as well. Lastly, [`TxDescendants`] is an [`Iterator`] that traverses descendants of
-//! a given transaction.
+//! [`TxGraph`] contains transactions and indexes them so you can easily traverse the graph of those transactions.
+//! `TxGraph` is *monotone* in that you can always insert a transaction -- it doesn't care whether that
+//! transaction is in the current best chain or whether it conflicts with any of the
+//! existing transactions or what order you insert the transactions. This means that you can always
+//! combine two [`TxGraph`]s together, without resulting in inconsistencies.
+//! Furthermore, there is currently no way to delete a transaction.
+//!
+//! Transactions can be either whole or partial (i.e., transactions for which we only
+//! know some outputs, which we usually call "floating outputs"; these are usually inserted
+//! using the [`insert_txout`] method.).
+//!
+//! The graph contains transactions in the form of [`TxNode`]s. Each node contains the
+//! txid, the transaction (whole or partial), the blocks it's anchored in (see the [`Anchor`]
+//! documentation for more details), and the timestamp of the last time we saw
+//! the transaction as unconfirmed.
 //!
 //! Conflicting transactions are allowed to coexist within a [`TxGraph`]. This is useful for
-//! identifying and traversing conflicts and descendants of a given transaction.
+//! identifying and traversing conflicts and descendants of a given transaction. Some [`TxGraph`]
+//! methods only consider "canonical" (i.e., in the best chain or in mempool) transactions,
+//! we decide which transactions are canonical based on anchors `last_seen_unconfirmed`;
+//! see the [`try_get_chain_position`] documentation for more details.
+//!
+//! The [`ChangeSet`] reports changes made to a [`TxGraph`]; it can be used to either save to
+//! persistent storage, or to be applied to another [`TxGraph`].
+//!
+//! Lastly, you can use [`TxAncestors`]/[`TxDescendants`] to traverse ancestors and descendants of
+//! a given transaction, respectively.
 //!
 //! # Applying changes
 //!
@@ -49,6 +69,8 @@
 //! let changeset = graph.apply_update(update);
 //! assert!(changeset.is_empty());
 //! ```
+//! [`try_get_chain_position`]: TxGraph::try_get_chain_position
+//! [`insert_txout`]: TxGraph::insert_txout
 
 use crate::{
     collections::*, keychain::Balance, local_chain::LocalChain, Anchor, Append, BlockId,
@@ -91,7 +113,7 @@ impl<A> Default for TxGraph<A> {
     }
 }
 
-/// An outward-facing view of a (transaction) node in the [`TxGraph`].
+/// A transaction node in the [`TxGraph`].
 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
 pub struct TxNode<'a, T, A> {
     /// Txid of the transaction.
@@ -128,7 +150,7 @@ impl Default for TxNodeInternal {
     }
 }
 
-/// An outwards-facing view of a transaction that is part of the *best chain*'s history.
+/// A transaction that is included in the chain, or is still in mempool.
 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
 pub struct CanonicalTx<'a, T, A> {
     /// How the transaction is observed as (confirmed or unconfirmed).
@@ -475,7 +497,7 @@ impl<A: Clone + Ord> TxGraph<A> {
     /// Batch insert unconfirmed transactions.
     ///
     /// Items of `txs` are tuples containing the transaction and a *last seen* timestamp. The
-    /// *last seen* communicates when the transaction is last seen in the mempool which is used for
+    /// *last seen* communicates when the transaction is last seen in mempool which is used for
     /// conflict-resolution (refer to [`TxGraph::insert_seen_at`] for details).
     pub fn batch_insert_unconfirmed(
         &mut self,
@@ -708,8 +730,20 @@ impl<A: Anchor> TxGraph<A> {
 
     /// Get the position of the transaction in `chain` with tip `chain_tip`.
     ///
-    /// If the given transaction of `txid` does not exist in the chain of `chain_tip`, `None` is
-    /// returned.
+    /// Chain data is fetched from `chain`, a [`ChainOracle`] implementation.
+    ///
+    /// This method returns `Ok(None)` if the transaction is not found in the chain, and no longer
+    /// belongs in the mempool. The following factors are used to approximate whether an
+    /// unconfirmed transaction exists in the mempool (not evicted):
+    ///
+    /// 1. Unconfirmed transactions that conflict with confirmed transactions are evicted.
+    /// 2. Unconfirmed transactions that spend from transactions that are evicted, are also
+    ///    evicted.
+    /// 3. Given two conflicting unconfirmed transactions, the transaction with the lower
+    ///    `last_seen_unconfirmed` parameter is evicted. A transaction's `last_seen_unconfirmed`
+    ///    parameter is the max of all it's descendants' `last_seen_unconfirmed` parameters. If the
+    ///    final `last_seen_unconfirmed`s are the same, the transaction with the lower `txid` (by
+    ///    lexicographical order) is evicted.
     ///
     /// # Error
     ///
@@ -735,7 +769,7 @@ impl<A: Anchor> TxGraph<A> {
             }
         }
 
-        // The tx is not anchored to a block which is in the best chain, which means that it
+        // The tx is not anchored to a block in the best chain, which means that it
         // might be in mempool, or it might have been dropped already.
         // Let's check conflicts to find out!
         let tx = match tx_node {
@@ -945,7 +979,8 @@ impl<A: Anchor> TxGraph<A> {
     /// (`OI`) for convenience. If `OI` is not necessary, the caller can use `()`, or
     /// [`Iterator::enumerate`] over a list of [`OutPoint`]s.
     ///
-    /// Floating outputs are ignored.
+    /// Floating outputs (i.e., outputs for which we don't have the full transaction in the graph)
+    /// are ignored.
     ///
     /// # Error
     ///
@@ -1136,9 +1171,9 @@ impl<A: Anchor> TxGraph<A> {
     }
 }
 
-/// A structure that represents changes to a [`TxGraph`].
+/// The [`ChangeSet`] represents changes to a [`TxGraph`].
 ///
-/// Since [`TxGraph`] is monotone "changeset" can only contain transactions to be added and
+/// Since [`TxGraph`] is monotone, the "changeset" can only contain transactions to be added and
 /// not removed.
 ///
 /// Refer to [module-level documentation] for more.
@@ -1272,7 +1307,7 @@ impl<A> AsRef<TxGraph<A>> for TxGraph<A> {
 ///
 /// The iterator excludes partial transactions.
 ///
-/// This `struct` is created by the [`walk_ancestors`] method of [`TxGraph`].
+/// Returned by the [`walk_ancestors`] method of [`TxGraph`].
 ///
 /// [`walk_ancestors`]: TxGraph::walk_ancestors
 pub struct TxAncestors<'g, A, F> {
@@ -1390,7 +1425,7 @@ where
 
 /// An iterator that traverses transaction descendants.
 ///
-/// This `struct` is created by the [`walk_descendants`] method of [`TxGraph`].
+/// Returned by the [`walk_descendants`] method of [`TxGraph`].
 ///
 /// [`walk_descendants`]: TxGraph::walk_descendants
 pub struct TxDescendants<'g, A, F> {
index f2a161b43c014b112bdc95c30cdaea653bbe9551..8ac440f3e432f1d58a0410e0c8c7a9960a5c400c 100644 (file)
@@ -110,6 +110,7 @@ fn test_tx_conflict_handling() {
                     ..Default::default()
                 },
             ],
+            // the txgraph is going to pick tx_conflict_2 because of higher lexicographical txid
             exp_chain_txs: HashSet::from(["tx1", "tx_conflict_2"]),
             exp_chain_txouts: HashSet::from([("tx1", 0), ("tx_conflict_2", 0)]),
             exp_unspents: HashSet::from([("tx_conflict_2", 0)]),