bdk_chain/tx_data_traits.rs
1use crate::{BlockId, ConfirmationBlockTime};
2
3/// Trait that "anchors" blockchain data to a specific block of height and hash.
4///
5/// If transaction A is anchored in block B, and block B is in the best chain, we can
6/// assume that transaction A is also confirmed in the best chain. This does not necessarily mean
7/// that transaction A is confirmed in block B. It could also mean transaction A is confirmed in a
8/// parent block of B.
9///
10/// Every [`Anchor`] implementation must contain a [`BlockId`] parameter, and must implement
11/// [`Ord`]. When implementing [`Ord`], the anchors' [`BlockId`]s should take precedence
12/// over other elements inside the [`Anchor`]s for comparison purposes, i.e., you should first
13/// compare the anchors' [`BlockId`]s and then care about the rest.
14///
15/// The example shows different types of anchors:
16/// ```
17/// # use bdk_chain::local_chain::LocalChain;
18/// # use bdk_chain::tx_graph::TxGraph;
19/// # use bdk_chain::BlockId;
20/// # use bdk_chain::ConfirmationBlockTime;
21/// # use bdk_chain::example_utils::*;
22/// # use bitcoin::hashes::Hash;
23/// // Initialize the local chain with two blocks.
24/// let chain = LocalChain::from_blocks(
25/// [
26/// (1, Hash::hash("first".as_bytes())),
27/// (2, Hash::hash("second".as_bytes())),
28/// ]
29/// .into_iter()
30/// .collect(),
31/// );
32///
33/// // Transaction to be inserted into `TxGraph`s with different anchor types.
34/// let tx = tx_from_hex(RAW_TX_1);
35///
36/// // Insert `tx` into a `TxGraph` that uses `BlockId` as the anchor type.
37/// // When a transaction is anchored with `BlockId`, the anchor block and the confirmation block of
38/// // the transaction is the same block.
39/// let mut graph_a = TxGraph::<BlockId>::default();
40/// let _ = graph_a.insert_tx(tx.clone());
41/// graph_a.insert_anchor(
42/// tx.compute_txid(),
43/// BlockId {
44/// height: 1,
45/// hash: Hash::hash("first".as_bytes()),
46/// },
47/// );
48///
49/// // Insert `tx` into a `TxGraph` that uses `ConfirmationBlockTime` as the anchor type.
50/// // This anchor records the anchor block and the confirmation time of the transaction. When a
51/// // transaction is anchored with `ConfirmationBlockTime`, the anchor block and confirmation block
52/// // of the transaction is the same block.
53/// let mut graph_c = TxGraph::<ConfirmationBlockTime>::default();
54/// let _ = graph_c.insert_tx(tx.clone());
55/// graph_c.insert_anchor(
56/// tx.compute_txid(),
57/// ConfirmationBlockTime {
58/// block_id: BlockId {
59/// height: 2,
60/// hash: Hash::hash("third".as_bytes()),
61/// },
62/// confirmation_time: 123,
63/// },
64/// );
65/// ```
66pub trait Anchor: core::fmt::Debug + Clone + Eq + PartialOrd + Ord + core::hash::Hash {
67 /// Returns the [`BlockId`] that the associated blockchain data is "anchored" in.
68 fn anchor_block(&self) -> BlockId;
69
70 /// Get the upper bound of the chain data's confirmation height.
71 ///
72 /// The default definition gives a pessimistic answer. This can be overridden by the `Anchor`
73 /// implementation for a more accurate value.
74 fn confirmation_height_upper_bound(&self) -> u32 {
75 self.anchor_block().height
76 }
77}
78
79impl<A: Anchor> Anchor for &A {
80 fn anchor_block(&self) -> BlockId {
81 <A as Anchor>::anchor_block(self)
82 }
83}
84
85impl Anchor for BlockId {
86 fn anchor_block(&self) -> Self {
87 *self
88 }
89}
90
91impl Anchor for ConfirmationBlockTime {
92 fn anchor_block(&self) -> BlockId {
93 self.block_id
94 }
95
96 fn confirmation_height_upper_bound(&self) -> u32 {
97 self.block_id.height
98 }
99}
100
101/// Set of parameters sufficient to construct an [`Anchor`].
102///
103/// Typically used as an additional constraint on anchor:
104/// `for<'b> A: Anchor + From<TxPosInBlock<'b>>`.
105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
106pub struct TxPosInBlock<'b> {
107 /// Block in which the transaction appeared.
108 pub block: &'b bitcoin::Block,
109 /// Block's [`BlockId`].
110 pub block_id: BlockId,
111 /// Position in the block on which the transaction appeared.
112 pub tx_pos: usize,
113}
114
115impl From<TxPosInBlock<'_>> for BlockId {
116 fn from(pos: TxPosInBlock) -> Self {
117 pos.block_id
118 }
119}
120
121impl From<TxPosInBlock<'_>> for ConfirmationBlockTime {
122 fn from(pos: TxPosInBlock) -> Self {
123 Self {
124 block_id: pos.block_id,
125 confirmation_time: pos.block.header.time as _,
126 }
127 }
128}