Skip to content

Commit e04d221

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 e04d221

File tree

25 files changed

+625
-776
lines changed

25 files changed

+625
-776
lines changed

crates/bitcoind_rpc/tests/test_emitter.rs

+12-10
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::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::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

+40-92
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)]
97
pub enum ChainPosition<A> {
10-
/// The chain data is seen as confirmed, and in anchored by `A`.
11-
Confirmed(A),
8+
/// The chain data is seen as confirmed, and in anchored by `Anchor`.
9+
Confirmed(Anchor, A),
1210
/// The chain data is not confirmed and last seen in the mempool at this timestamp.
1311
Unconfirmed(u64),
1412
}
1513

1614
impl<A> ChainPosition<A> {
1715
/// Returns whether [`ChainPosition`] is confirmed or not.
1816
pub fn is_confirmed(&self) -> bool {
19-
matches!(self, Self::Confirmed(_))
17+
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.
21+
impl<A: Clone> ChainPosition<A> {
22+
/// Maps a [`ChainPosition`] into a [`ChainPosition`] by cloning the contents.
2523
pub fn cloned(self) -> ChainPosition<A> {
2624
match self {
27-
ChainPosition::Confirmed(a) => ChainPosition::Confirmed(a.clone()),
25+
ChainPosition::Confirmed(anchor, a) => ChainPosition::Confirmed(anchor, 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,41 +121,6 @@ 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)]
185126
pub struct FullTxOut<A> {
@@ -195,7 +136,7 @@ pub struct FullTxOut<A> {
195136
pub is_on_coinbase: bool,
196137
}
197138

198-
impl<A: Anchor> FullTxOut<A> {
139+
impl<A> FullTxOut<A> {
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

+4-1
Original file line numberDiff line numberDiff line change
@@ -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, A> crate::Merge for CombinedChangeSet<K, A>
38+
where
39+
A: 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);

0 commit comments

Comments
 (0)