Skip to content

Commit 6afb3a2

Browse files
committed
refactor(chain)!: Remove Anchor trait
The `Anchor` trait has been removed and anchors are now unique to (`Txid`, `BlockId`).
1 parent d99b3ef commit 6afb3a2

File tree

25 files changed

+768
-904
lines changed

25 files changed

+768
-904
lines changed

crates/bitcoind_rpc/tests/test_emitter.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use bdk_bitcoind_rpc::Emitter;
44
use bdk_chain::{
55
bitcoin::{Address, Amount, Txid},
66
local_chain::{CheckPoint, LocalChain},
7-
Balance, BlockId, IndexedTxGraph, Merge, SpkTxOutIndex,
7+
Balance, BlockId, BlockTime, IndexedTxGraph, Merge, SpkTxOutIndex,
88
};
99
use bdk_testenv::{anyhow, TestEnv};
1010
use bitcoin::{hashes::Hash, Block, OutPoint, ScriptBuf, WScriptHash};
@@ -149,7 +149,7 @@ fn test_into_tx_graph() -> anyhow::Result<()> {
149149
println!("mined blocks!");
150150

151151
let (mut chain, _) = LocalChain::from_genesis_hash(env.rpc_client().get_block_hash(0)?);
152-
let mut indexed_tx_graph = IndexedTxGraph::<BlockId, _>::new({
152+
let mut indexed_tx_graph = IndexedTxGraph::<BlockTime, _>::new({
153153
let mut index = SpkTxOutIndex::<usize>::default();
154154
index.insert_spk(0, addr_0.script_pubkey());
155155
index.insert_spk(1, addr_1.script_pubkey());
@@ -206,17 +206,19 @@ fn test_into_tx_graph() -> anyhow::Result<()> {
206206

207207
// mine a block that confirms the 3 txs
208208
let exp_block_hash = env.mine_blocks(1, None)?[0];
209-
let exp_block_height = env.rpc_client().get_block_info(&exp_block_hash)?.height as u32;
209+
let exp_block = env.rpc_client().get_block_info(&exp_block_hash)?;
210+
let exp_block_height = exp_block.height as u32;
211+
let exp_block_time = exp_block.time as u32;
210212
let exp_anchors = exp_txids
211213
.iter()
212214
.map({
213-
let anchor = BlockId {
215+
let blockid = BlockId {
214216
height: exp_block_height,
215217
hash: exp_block_hash,
216218
};
217-
move |&txid| (anchor, txid)
219+
move |&txid| ((txid, blockid), BlockTime::new(exp_block_time))
218220
})
219-
.collect::<BTreeSet<_>>();
221+
.collect::<BTreeMap<_, _>>();
220222

221223
// must receive mined block which will confirm the transactions.
222224
{
@@ -277,7 +279,7 @@ fn ensure_block_emitted_after_reorg_is_at_reorg_height() -> anyhow::Result<()> {
277279

278280
fn process_block(
279281
recv_chain: &mut LocalChain,
280-
recv_graph: &mut IndexedTxGraph<BlockId, SpkTxOutIndex<()>>,
282+
recv_graph: &mut IndexedTxGraph<BlockTime, SpkTxOutIndex<()>>,
281283
block: Block,
282284
block_height: u32,
283285
) -> anyhow::Result<()> {
@@ -288,7 +290,7 @@ fn process_block(
288290

289291
fn sync_from_emitter<C>(
290292
recv_chain: &mut LocalChain,
291-
recv_graph: &mut IndexedTxGraph<BlockId, SpkTxOutIndex<()>>,
293+
recv_graph: &mut IndexedTxGraph<BlockTime, SpkTxOutIndex<()>>,
292294
emitter: &mut Emitter<C>,
293295
) -> anyhow::Result<()>
294296
where
@@ -303,7 +305,7 @@ where
303305

304306
fn get_balance(
305307
recv_chain: &LocalChain,
306-
recv_graph: &IndexedTxGraph<BlockId, SpkTxOutIndex<()>>,
308+
recv_graph: &IndexedTxGraph<BlockTime, SpkTxOutIndex<()>>,
307309
) -> anyhow::Result<Balance> {
308310
let chain_tip = recv_chain.tip().block_id();
309311
let outpoints = recv_graph.index.outpoints().clone();
@@ -341,7 +343,7 @@ fn tx_can_become_unconfirmed_after_reorg() -> anyhow::Result<()> {
341343

342344
// setup receiver
343345
let (mut recv_chain, _) = LocalChain::from_genesis_hash(env.rpc_client().get_block_hash(0)?);
344-
let mut recv_graph = IndexedTxGraph::<BlockId, _>::new({
346+
let mut recv_graph = IndexedTxGraph::<BlockTime, _>::new({
345347
let mut recv_index = SpkTxOutIndex::default();
346348
recv_index.insert_spk((), spk_to_track.clone());
347349
recv_index

crates/chain/src/chain_data.rs

Lines changed: 45 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,33 @@
11
use bitcoin::{hashes::Hash, BlockHash, OutPoint, TxOut, Txid};
22

3-
use crate::{Anchor, AnchorFromBlockPosition, COINBASE_MATURITY};
3+
use crate::{Anchor, BlockTime, COINBASE_MATURITY};
44

55
/// Represents the observed position of some chain data.
6-
///
7-
/// The generic `A` should be a [`Anchor`] implementation.
86
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, core::hash::Hash)]
9-
pub enum ChainPosition<A> {
10-
/// The chain data is seen as confirmed, and in anchored by `A`.
11-
Confirmed(A),
7+
pub enum ChainPosition<AM> {
8+
/// The chain data is seen as confirmed, and in anchored by `Anchor`.
9+
Confirmed((Anchor, AM)),
1210
/// The chain data is not confirmed and last seen in the mempool at this timestamp.
1311
Unconfirmed(u64),
1412
}
1513

16-
impl<A> ChainPosition<A> {
14+
impl<AM> ChainPosition<AM> {
1715
/// Returns whether [`ChainPosition`] is confirmed or not.
1816
pub fn is_confirmed(&self) -> bool {
1917
matches!(self, Self::Confirmed(_))
2018
}
2119
}
2220

23-
impl<A: Clone> ChainPosition<&A> {
24-
/// Maps a [`ChainPosition<&A>`] into a [`ChainPosition<A>`] by cloning the contents.
25-
pub fn cloned(self) -> ChainPosition<A> {
21+
impl<AM: Clone> ChainPosition<AM> {
22+
/// Maps a [`ChainPosition`] into a [`ChainPosition`] by cloning the contents.
23+
pub fn cloned(self) -> ChainPosition<AM> {
2624
match self {
27-
ChainPosition::Confirmed(a) => ChainPosition::Confirmed(a.clone()),
25+
ChainPosition::Confirmed(a) => ChainPosition::Confirmed(a),
2826
ChainPosition::Unconfirmed(last_seen) => ChainPosition::Unconfirmed(last_seen),
2927
}
3028
}
3129
}
3230

33-
impl<A: Anchor> ChainPosition<A> {
34-
/// Determines the upper bound of the confirmation height.
35-
pub fn confirmation_height_upper_bound(&self) -> Option<u32> {
36-
match self {
37-
ChainPosition::Confirmed(a) => Some(a.confirmation_height_upper_bound()),
38-
ChainPosition::Unconfirmed(_) => None,
39-
}
40-
}
41-
}
42-
4331
/// Block height and timestamp at which a transaction is confirmed.
4432
#[derive(Debug, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
4533
#[cfg_attr(
@@ -74,12 +62,12 @@ impl ConfirmationTime {
7462
}
7563
}
7664

77-
impl From<ChainPosition<ConfirmationBlockTime>> for ConfirmationTime {
78-
fn from(observed_as: ChainPosition<ConfirmationBlockTime>) -> Self {
65+
impl From<ChainPosition<BlockTime>> for ConfirmationTime {
66+
fn from(observed_as: ChainPosition<BlockTime>) -> Self {
7967
match observed_as {
80-
ChainPosition::Confirmed(a) => Self::Confirmed {
81-
height: a.block_id.height,
82-
time: a.confirmation_time,
68+
ChainPosition::Confirmed(((_txid, blockid), anchor_meta)) => Self::Confirmed {
69+
height: blockid.height,
70+
time: *anchor_meta.as_ref() as u64,
8371
},
8472
ChainPosition::Unconfirmed(last_seen) => Self::Unconfirmed { last_seen },
8573
}
@@ -103,18 +91,6 @@ pub struct BlockId {
10391
pub hash: BlockHash,
10492
}
10593

106-
impl Anchor for BlockId {
107-
fn anchor_block(&self) -> Self {
108-
*self
109-
}
110-
}
111-
112-
impl AnchorFromBlockPosition for BlockId {
113-
fn from_block_position(_block: &bitcoin::Block, block_id: BlockId, _tx_pos: usize) -> Self {
114-
block_id
115-
}
116-
}
117-
11894
impl Default for BlockId {
11995
fn default() -> Self {
12096
Self {
@@ -145,57 +121,22 @@ impl From<(&u32, &BlockHash)> for BlockId {
145121
}
146122
}
147123

148-
/// An [`Anchor`] implementation that also records the exact confirmation time of the transaction.
149-
///
150-
/// Refer to [`Anchor`] for more details.
151-
#[derive(Debug, Default, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
152-
#[cfg_attr(
153-
feature = "serde",
154-
derive(serde::Deserialize, serde::Serialize),
155-
serde(crate = "serde_crate")
156-
)]
157-
pub struct ConfirmationBlockTime {
158-
/// The anchor block.
159-
pub block_id: BlockId,
160-
/// The confirmation time of the transaction being anchored.
161-
pub confirmation_time: u64,
162-
}
163-
164-
impl Anchor for ConfirmationBlockTime {
165-
fn anchor_block(&self) -> BlockId {
166-
self.block_id
167-
}
168-
169-
fn confirmation_height_upper_bound(&self) -> u32 {
170-
self.block_id.height
171-
}
172-
}
173-
174-
impl AnchorFromBlockPosition for ConfirmationBlockTime {
175-
fn from_block_position(block: &bitcoin::Block, block_id: BlockId, _tx_pos: usize) -> Self {
176-
Self {
177-
block_id,
178-
confirmation_time: block.header.time as _,
179-
}
180-
}
181-
}
182-
183124
/// A `TxOut` with as much data as we can retrieve about it
184125
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
185-
pub struct FullTxOut<A> {
126+
pub struct FullTxOut<AM> {
186127
/// The position of the transaction in `outpoint` in the overall chain.
187-
pub chain_position: ChainPosition<A>,
128+
pub chain_position: ChainPosition<AM>,
188129
/// The location of the `TxOut`.
189130
pub outpoint: OutPoint,
190131
/// The `TxOut`.
191132
pub txout: TxOut,
192133
/// The txid and chain position of the transaction (if any) that has spent this output.
193-
pub spent_by: Option<(ChainPosition<A>, Txid)>,
134+
pub spent_by: Option<(ChainPosition<AM>, Txid)>,
194135
/// Whether this output is on a coinbase transaction.
195136
pub is_on_coinbase: bool,
196137
}
197138

198-
impl<A: Anchor> FullTxOut<A> {
139+
impl<AM> FullTxOut<AM> {
199140
/// Whether the `txout` is considered mature.
200141
///
201142
/// Depending on the implementation of [`confirmation_height_upper_bound`] in [`Anchor`], this
@@ -206,7 +147,7 @@ impl<A: Anchor> FullTxOut<A> {
206147
pub fn is_mature(&self, tip: u32) -> bool {
207148
if self.is_on_coinbase {
208149
let tx_height = match &self.chain_position {
209-
ChainPosition::Confirmed(anchor) => anchor.confirmation_height_upper_bound(),
150+
ChainPosition::Confirmed(((_, blockid), _)) => blockid.height,
210151
ChainPosition::Unconfirmed(_) => {
211152
debug_assert!(false, "coinbase tx can never be unconfirmed");
212153
return false;
@@ -236,16 +177,16 @@ impl<A: Anchor> FullTxOut<A> {
236177
}
237178

238179
let confirmation_height = match &self.chain_position {
239-
ChainPosition::Confirmed(anchor) => anchor.confirmation_height_upper_bound(),
180+
ChainPosition::Confirmed(((_, blockid), _)) => blockid.height,
240181
ChainPosition::Unconfirmed(_) => return false,
241182
};
242183
if confirmation_height > tip {
243184
return false;
244185
}
245186

246187
// if the spending tx is confirmed within tip height, the txout is no longer spendable
247-
if let Some((ChainPosition::Confirmed(spending_anchor), _)) = &self.spent_by {
248-
if spending_anchor.anchor_block().height <= tip {
188+
if let Some((ChainPosition::Confirmed(((_, spending_blockid), _)), _)) = &self.spent_by {
189+
if spending_blockid.height <= tip {
249190
return false;
250191
}
251192
}
@@ -257,25 +198,32 @@ impl<A: Anchor> FullTxOut<A> {
257198
#[cfg(test)]
258199
mod test {
259200
use super::*;
201+
use crate::BlockTime;
260202

261203
#[test]
262204
fn chain_position_ord() {
263-
let unconf1 = ChainPosition::<ConfirmationBlockTime>::Unconfirmed(10);
264-
let unconf2 = ChainPosition::<ConfirmationBlockTime>::Unconfirmed(20);
265-
let conf1 = ChainPosition::Confirmed(ConfirmationBlockTime {
266-
confirmation_time: 20,
267-
block_id: BlockId {
268-
height: 9,
269-
..Default::default()
270-
},
271-
});
272-
let conf2 = ChainPosition::Confirmed(ConfirmationBlockTime {
273-
confirmation_time: 15,
274-
block_id: BlockId {
275-
height: 12,
276-
..Default::default()
277-
},
278-
});
205+
let unconf1 = ChainPosition::Unconfirmed(10);
206+
let unconf2 = ChainPosition::Unconfirmed(20);
207+
let conf1 = ChainPosition::Confirmed((
208+
(
209+
Txid::all_zeros(),
210+
BlockId {
211+
height: 9,
212+
..Default::default()
213+
},
214+
),
215+
BlockTime::new(20),
216+
));
217+
let conf2 = ChainPosition::Confirmed((
218+
(
219+
Txid::all_zeros(),
220+
BlockId {
221+
height: 12,
222+
..Default::default()
223+
},
224+
),
225+
BlockTime::new(15),
226+
));
279227

280228
assert!(unconf2 > unconf1, "higher last_seen means higher ord");
281229
assert!(unconf1 > conf1, "unconfirmed is higher ord than confirmed");

crates/chain/src/changeset.rs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,23 @@
77
serde(
88
crate = "crate::serde",
99
bound(
10-
deserialize = "A: Ord + crate::serde::Deserialize<'de>, K: Ord + crate::serde::Deserialize<'de>",
11-
serialize = "A: Ord + crate::serde::Serialize, K: Ord + crate::serde::Serialize",
10+
deserialize = "AM: Ord + crate::serde::Deserialize<'de>, K: Ord + crate::serde::Deserialize<'de>",
11+
serialize = "AM: Ord + crate::serde::Serialize, K: Ord + crate::serde::Serialize",
1212
),
1313
)
1414
)]
15-
pub struct CombinedChangeSet<K, A> {
15+
pub struct CombinedChangeSet<K, AM> {
1616
/// Changes to the [`LocalChain`](crate::local_chain::LocalChain).
1717
pub chain: crate::local_chain::ChangeSet,
1818
/// Changes to [`IndexedTxGraph`](crate::indexed_tx_graph::IndexedTxGraph).
1919
pub indexed_tx_graph:
20-
crate::indexed_tx_graph::ChangeSet<A, crate::indexer::keychain_txout::ChangeSet<K>>,
20+
crate::indexed_tx_graph::ChangeSet<AM, crate::indexer::keychain_txout::ChangeSet<K>>,
2121
/// Stores the network type of the transaction data.
2222
pub network: Option<bitcoin::Network>,
2323
}
2424

2525
#[cfg(feature = "miniscript")]
26-
impl<K, A> core::default::Default for CombinedChangeSet<K, A> {
26+
impl<K, AM> core::default::Default for CombinedChangeSet<K, AM> {
2727
fn default() -> Self {
2828
Self {
2929
chain: core::default::Default::default(),
@@ -34,7 +34,10 @@ impl<K, A> core::default::Default for CombinedChangeSet<K, A> {
3434
}
3535

3636
#[cfg(feature = "miniscript")]
37-
impl<K: Ord, A: crate::Anchor> crate::Merge for CombinedChangeSet<K, A> {
37+
impl<K: Ord, AM> crate::Merge for CombinedChangeSet<K, AM>
38+
where
39+
AM: Ord + Clone,
40+
{
3841
fn merge(&mut self, other: Self) {
3942
crate::Merge::merge(&mut self.chain, other.chain);
4043
crate::Merge::merge(&mut self.indexed_tx_graph, other.indexed_tx_graph);
@@ -53,7 +56,7 @@ impl<K: Ord, A: crate::Anchor> crate::Merge for CombinedChangeSet<K, A> {
5356
}
5457

5558
#[cfg(feature = "miniscript")]
56-
impl<K, A> From<crate::local_chain::ChangeSet> for CombinedChangeSet<K, A> {
59+
impl<K, AM> From<crate::local_chain::ChangeSet> for CombinedChangeSet<K, AM> {
5760
fn from(chain: crate::local_chain::ChangeSet) -> Self {
5861
Self {
5962
chain,
@@ -63,12 +66,13 @@ impl<K, A> From<crate::local_chain::ChangeSet> for CombinedChangeSet<K, A> {
6366
}
6467

6568
#[cfg(feature = "miniscript")]
66-
impl<K, A> From<crate::indexed_tx_graph::ChangeSet<A, crate::indexer::keychain_txout::ChangeSet<K>>>
67-
for CombinedChangeSet<K, A>
69+
impl<K, AM>
70+
From<crate::indexed_tx_graph::ChangeSet<AM, crate::indexer::keychain_txout::ChangeSet<K>>>
71+
for CombinedChangeSet<K, AM>
6872
{
6973
fn from(
7074
indexed_tx_graph: crate::indexed_tx_graph::ChangeSet<
71-
A,
75+
AM,
7276
crate::indexer::keychain_txout::ChangeSet<K>,
7377
>,
7478
) -> Self {
@@ -80,7 +84,7 @@ impl<K, A> From<crate::indexed_tx_graph::ChangeSet<A, crate::indexer::keychain_t
8084
}
8185

8286
#[cfg(feature = "miniscript")]
83-
impl<K, A> From<crate::indexer::keychain_txout::ChangeSet<K>> for CombinedChangeSet<K, A> {
87+
impl<K, AM> From<crate::indexer::keychain_txout::ChangeSet<K>> for CombinedChangeSet<K, AM> {
8488
fn from(indexer: crate::indexer::keychain_txout::ChangeSet<K>) -> Self {
8589
Self {
8690
indexed_tx_graph: crate::indexed_tx_graph::ChangeSet {

0 commit comments

Comments
 (0)