Skip to content

Commit d99b3ef

Browse files
committed
Merge #1489: feat(electrum)!: Update bdk_electrum to use merkle proofs
1a62488 feat(chain)!: Implement `ConfirmationBlockTime` (Wei Chen) e761adf test(electrum): Imported `bdk_esplora` tests into `bdk_electrum` (Wei Chen) d7f4ab7 feat(electrum)!: Update `bdk_electrum` to use merkle proofs (Wei Chen) Pull request description: <!-- You can erase any parts of this template not applicable to your Pull Request. --> Fixes #980. ### Description This PR is the first step in reworking `bdk_electrum` to use merkle proofs. When we fetch a transaction, we now also obtain the merkle proof and block header for verification. We then insert an anchor only after validation that the transaction exists in a confirmed block. The loop logic that previously existed in `full_scan` to account for re-orgs has also been removed as part of this rework. This is a breaking change because `graph_update`s now provide the full `ConfirmationTimeHeightAnchor` type. This removes the need for the `ElectrumFullScanResult` and `ElectrumSyncResult` structs that existed only to provide the option for converting the anchor type from `ConfirmationHeightAnchor` into `ConfirmationTimeHeightAnchor`. ### Notes to the reviewers <!-- In this section you can include notes directed to the reviewers, like explaining why some parts of the PR were done in a specific way --> ### Changelog notice <!-- Notice the release manager should include in the release tag message changelog --> <!-- See https://keepachangelog.com/en/1.0.0/ for examples --> * `ConfirmationTimeHeightAnchor` and `ConfirmationHeightAnchor` have been removed. * `ConfirmationBlockTime` has been introduced as a new anchor type. * `bdk_electrum`'s `full_scan` and `sync` now return `graph_update`s with `ConfirmationBlockTime`. ### Checklists #### All Submissions: * [x] I've signed all my commits * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) * [x] I ran `cargo fmt` and `cargo clippy` before committing #### New Features: * [ ] I've added tests for the new feature * [x] I've added docs for the new feature ACKs for top commit: ValuedMammal: ACK 1a62488 notmandatory: ACK 1a62488 Tree-SHA512: 77af05bffcb9668ecb99b41abacc6b6aa503dc559226fa88c4cab6863e3af431b937706696ec765bb802c9c152333cd430c284d17a6cd190520e10b13d89e02f
2 parents 1a39821 + 1a62488 commit d99b3ef

File tree

18 files changed

+556
-516
lines changed

18 files changed

+556
-516
lines changed

crates/chain/src/chain_data.rs

Lines changed: 21 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,11 @@ impl ConfirmationTime {
7474
}
7575
}
7676

77-
impl From<ChainPosition<ConfirmationTimeHeightAnchor>> for ConfirmationTime {
78-
fn from(observed_as: ChainPosition<ConfirmationTimeHeightAnchor>) -> Self {
77+
impl From<ChainPosition<ConfirmationBlockTime>> for ConfirmationTime {
78+
fn from(observed_as: ChainPosition<ConfirmationBlockTime>) -> Self {
7979
match observed_as {
8080
ChainPosition::Confirmed(a) => Self::Confirmed {
81-
height: a.confirmation_height,
81+
height: a.block_id.height,
8282
time: a.confirmation_time,
8383
},
8484
ChainPosition::Unconfirmed(last_seen) => Self::Unconfirmed { last_seen },
@@ -145,9 +145,7 @@ impl From<(&u32, &BlockHash)> for BlockId {
145145
}
146146
}
147147

148-
/// An [`Anchor`] implementation that also records the exact confirmation height of the transaction.
149-
///
150-
/// Note that the confirmation block and the anchor block can be different here.
148+
/// An [`Anchor`] implementation that also records the exact confirmation time of the transaction.
151149
///
152150
/// Refer to [`Anchor`] for more details.
153151
#[derive(Debug, Default, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
@@ -156,70 +154,27 @@ impl From<(&u32, &BlockHash)> for BlockId {
156154
derive(serde::Deserialize, serde::Serialize),
157155
serde(crate = "serde_crate")
158156
)]
159-
pub struct ConfirmationHeightAnchor {
160-
/// The exact confirmation height of the transaction.
161-
///
162-
/// It is assumed that this value is never larger than the height of the anchor block.
163-
pub confirmation_height: u32,
157+
pub struct ConfirmationBlockTime {
164158
/// The anchor block.
165-
pub anchor_block: BlockId,
166-
}
167-
168-
impl Anchor for ConfirmationHeightAnchor {
169-
fn anchor_block(&self) -> BlockId {
170-
self.anchor_block
171-
}
172-
173-
fn confirmation_height_upper_bound(&self) -> u32 {
174-
self.confirmation_height
175-
}
176-
}
177-
178-
impl AnchorFromBlockPosition for ConfirmationHeightAnchor {
179-
fn from_block_position(_block: &bitcoin::Block, block_id: BlockId, _tx_pos: usize) -> Self {
180-
Self {
181-
anchor_block: block_id,
182-
confirmation_height: block_id.height,
183-
}
184-
}
185-
}
186-
187-
/// An [`Anchor`] implementation that also records the exact confirmation time and height of the
188-
/// transaction.
189-
///
190-
/// Note that the confirmation block and the anchor block can be different here.
191-
///
192-
/// Refer to [`Anchor`] for more details.
193-
#[derive(Debug, Default, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
194-
#[cfg_attr(
195-
feature = "serde",
196-
derive(serde::Deserialize, serde::Serialize),
197-
serde(crate = "serde_crate")
198-
)]
199-
pub struct ConfirmationTimeHeightAnchor {
200-
/// The confirmation height of the transaction being anchored.
201-
pub confirmation_height: u32,
159+
pub block_id: BlockId,
202160
/// The confirmation time of the transaction being anchored.
203161
pub confirmation_time: u64,
204-
/// The anchor block.
205-
pub anchor_block: BlockId,
206162
}
207163

208-
impl Anchor for ConfirmationTimeHeightAnchor {
164+
impl Anchor for ConfirmationBlockTime {
209165
fn anchor_block(&self) -> BlockId {
210-
self.anchor_block
166+
self.block_id
211167
}
212168

213169
fn confirmation_height_upper_bound(&self) -> u32 {
214-
self.confirmation_height
170+
self.block_id.height
215171
}
216172
}
217173

218-
impl AnchorFromBlockPosition for ConfirmationTimeHeightAnchor {
174+
impl AnchorFromBlockPosition for ConfirmationBlockTime {
219175
fn from_block_position(block: &bitcoin::Block, block_id: BlockId, _tx_pos: usize) -> Self {
220176
Self {
221-
anchor_block: block_id,
222-
confirmation_height: block_id.height,
177+
block_id,
223178
confirmation_time: block.header.time as _,
224179
}
225180
}
@@ -305,19 +260,19 @@ mod test {
305260

306261
#[test]
307262
fn chain_position_ord() {
308-
let unconf1 = ChainPosition::<ConfirmationHeightAnchor>::Unconfirmed(10);
309-
let unconf2 = ChainPosition::<ConfirmationHeightAnchor>::Unconfirmed(20);
310-
let conf1 = ChainPosition::Confirmed(ConfirmationHeightAnchor {
311-
confirmation_height: 9,
312-
anchor_block: BlockId {
313-
height: 20,
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,
314269
..Default::default()
315270
},
316271
});
317-
let conf2 = ChainPosition::Confirmed(ConfirmationHeightAnchor {
318-
confirmation_height: 12,
319-
anchor_block: BlockId {
320-
height: 15,
272+
let conf2 = ChainPosition::Confirmed(ConfirmationBlockTime {
273+
confirmation_time: 15,
274+
block_id: BlockId {
275+
height: 12,
321276
..Default::default()
322277
},
323278
});

crates/chain/src/spk_client.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Helper types for spk-based blockchain clients.
22
33
use crate::{
4-
collections::BTreeMap, local_chain::CheckPoint, ConfirmationTimeHeightAnchor, Indexed, TxGraph,
4+
collections::BTreeMap, local_chain::CheckPoint, ConfirmationBlockTime, Indexed, TxGraph,
55
};
66
use alloc::boxed::Box;
77
use bitcoin::{OutPoint, Script, ScriptBuf, Txid};
@@ -176,7 +176,7 @@ impl SyncRequest {
176176
/// Data returned from a spk-based blockchain client sync.
177177
///
178178
/// See also [`SyncRequest`].
179-
pub struct SyncResult<A = ConfirmationTimeHeightAnchor> {
179+
pub struct SyncResult<A = ConfirmationBlockTime> {
180180
/// The update to apply to the receiving [`TxGraph`].
181181
pub graph_update: TxGraph<A>,
182182
/// The update to apply to the receiving [`LocalChain`](crate::local_chain::LocalChain).
@@ -317,7 +317,7 @@ impl<K: Ord + Clone> FullScanRequest<K> {
317317
/// Data returned from a spk-based blockchain client full scan.
318318
///
319319
/// See also [`FullScanRequest`].
320-
pub struct FullScanResult<K, A = ConfirmationTimeHeightAnchor> {
320+
pub struct FullScanResult<K, A = ConfirmationBlockTime> {
321321
/// The update to apply to the receiving [`LocalChain`](crate::local_chain::LocalChain).
322322
pub graph_update: TxGraph<A>,
323323
/// The update to apply to the receiving [`TxGraph`].

crates/chain/src/tx_data_traits.rs

Lines changed: 8 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ use alloc::vec::Vec;
2020
/// # use bdk_chain::local_chain::LocalChain;
2121
/// # use bdk_chain::tx_graph::TxGraph;
2222
/// # use bdk_chain::BlockId;
23-
/// # use bdk_chain::ConfirmationHeightAnchor;
24-
/// # use bdk_chain::ConfirmationTimeHeightAnchor;
23+
/// # use bdk_chain::ConfirmationBlockTime;
2524
/// # use bdk_chain::example_utils::*;
2625
/// # use bitcoin::hashes::Hash;
2726
/// // Initialize the local chain with two blocks.
@@ -50,39 +49,19 @@ use alloc::vec::Vec;
5049
/// },
5150
/// );
5251
///
53-
/// // Insert `tx` into a `TxGraph` that uses `ConfirmationHeightAnchor` as the anchor type.
54-
/// // This anchor records the anchor block and the confirmation height of the transaction.
55-
/// // When a transaction is anchored with `ConfirmationHeightAnchor`, the anchor block and
56-
/// // confirmation block can be different. However, the confirmation block cannot be higher than
57-
/// // the anchor block and both blocks must be in the same chain for the anchor to be valid.
58-
/// let mut graph_b = TxGraph::<ConfirmationHeightAnchor>::default();
59-
/// let _ = graph_b.insert_tx(tx.clone());
60-
/// graph_b.insert_anchor(
61-
/// tx.compute_txid(),
62-
/// ConfirmationHeightAnchor {
63-
/// anchor_block: BlockId {
64-
/// height: 2,
65-
/// hash: Hash::hash("second".as_bytes()),
66-
/// },
67-
/// confirmation_height: 1,
68-
/// },
69-
/// );
70-
///
71-
/// // Insert `tx` into a `TxGraph` that uses `ConfirmationTimeHeightAnchor` as the anchor type.
72-
/// // This anchor records the anchor block, the confirmation height and time of the transaction.
73-
/// // When a transaction is anchored with `ConfirmationTimeHeightAnchor`, the anchor block and
74-
/// // confirmation block can be different. However, the confirmation block cannot be higher than
75-
/// // the anchor block and both blocks must be in the same chain for the anchor to be valid.
76-
/// let mut graph_c = TxGraph::<ConfirmationTimeHeightAnchor>::default();
52+
/// // Insert `tx` into a `TxGraph` that uses `ConfirmationBlockTime` as the anchor type.
53+
/// // This anchor records the anchor block and the confirmation time of the transaction. When a
54+
/// // transaction is anchored with `ConfirmationBlockTime`, the anchor block and confirmation block
55+
/// // of the transaction is the same block.
56+
/// let mut graph_c = TxGraph::<ConfirmationBlockTime>::default();
7757
/// let _ = graph_c.insert_tx(tx.clone());
7858
/// graph_c.insert_anchor(
7959
/// tx.compute_txid(),
80-
/// ConfirmationTimeHeightAnchor {
81-
/// anchor_block: BlockId {
60+
/// ConfirmationBlockTime {
61+
/// block_id: BlockId {
8262
/// height: 2,
8363
/// hash: Hash::hash("third".as_bytes()),
8464
/// },
85-
/// confirmation_height: 1,
8665
/// confirmation_time: 123,
8766
/// },
8867
/// );

crates/chain/tests/test_indexed_tx_graph.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use bdk_chain::{
1010
indexed_tx_graph::{self, IndexedTxGraph},
1111
indexer::keychain_txout::KeychainTxOutIndex,
1212
local_chain::LocalChain,
13-
tx_graph, Balance, ChainPosition, ConfirmationHeightAnchor, DescriptorExt, Merge,
13+
tx_graph, Balance, ChainPosition, ConfirmationBlockTime, DescriptorExt, Merge,
1414
};
1515
use bitcoin::{
1616
secp256k1::Secp256k1, Amount, OutPoint, Script, ScriptBuf, Transaction, TxIn, TxOut,
@@ -32,7 +32,7 @@ fn insert_relevant_txs() {
3232
let spk_0 = descriptor.at_derivation_index(0).unwrap().script_pubkey();
3333
let spk_1 = descriptor.at_derivation_index(9).unwrap().script_pubkey();
3434

35-
let mut graph = IndexedTxGraph::<ConfirmationHeightAnchor, KeychainTxOutIndex<()>>::new(
35+
let mut graph = IndexedTxGraph::<ConfirmationBlockTime, KeychainTxOutIndex<()>>::new(
3636
KeychainTxOutIndex::new(10),
3737
);
3838
let _ = graph
@@ -140,7 +140,7 @@ fn test_list_owned_txouts() {
140140
let (desc_2, _) =
141141
Descriptor::parse_descriptor(&Secp256k1::signing_only(), common::DESCRIPTORS[3]).unwrap();
142142

143-
let mut graph = IndexedTxGraph::<ConfirmationHeightAnchor, KeychainTxOutIndex<String>>::new(
143+
let mut graph = IndexedTxGraph::<ConfirmationBlockTime, KeychainTxOutIndex<String>>::new(
144144
KeychainTxOutIndex::new(10),
145145
);
146146

@@ -250,9 +250,9 @@ fn test_list_owned_txouts() {
250250
local_chain
251251
.get(height)
252252
.map(|cp| cp.block_id())
253-
.map(|anchor_block| ConfirmationHeightAnchor {
254-
anchor_block,
255-
confirmation_height: anchor_block.height,
253+
.map(|block_id| ConfirmationBlockTime {
254+
block_id,
255+
confirmation_time: 100,
256256
}),
257257
)
258258
}));
@@ -261,8 +261,7 @@ fn test_list_owned_txouts() {
261261

262262
// A helper lambda to extract and filter data from the graph.
263263
let fetch =
264-
|height: u32,
265-
graph: &IndexedTxGraph<ConfirmationHeightAnchor, KeychainTxOutIndex<String>>| {
264+
|height: u32, graph: &IndexedTxGraph<ConfirmationBlockTime, KeychainTxOutIndex<String>>| {
266265
let chain_tip = local_chain
267266
.get(height)
268267
.map(|cp| cp.block_id())

crates/chain/tests/test_tx_graph.rs

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use bdk_chain::{
77
collections::*,
88
local_chain::LocalChain,
99
tx_graph::{ChangeSet, TxGraph},
10-
Anchor, BlockId, ChainOracle, ChainPosition, ConfirmationHeightAnchor, Merge,
10+
Anchor, BlockId, ChainOracle, ChainPosition, ConfirmationBlockTime, Merge,
1111
};
1212
use bitcoin::{
1313
absolute, hashes::Hash, transaction, Amount, BlockHash, OutPoint, ScriptBuf, SignedAmount,
@@ -935,7 +935,7 @@ fn test_chain_spends() {
935935
..common::new_tx(0)
936936
};
937937

938-
let mut graph = TxGraph::<ConfirmationHeightAnchor>::default();
938+
let mut graph = TxGraph::<ConfirmationBlockTime>::default();
939939

940940
let _ = graph.insert_tx(tx_0.clone());
941941
let _ = graph.insert_tx(tx_1.clone());
@@ -944,9 +944,9 @@ fn test_chain_spends() {
944944
for (ht, tx) in [(95, &tx_0), (98, &tx_1)] {
945945
let _ = graph.insert_anchor(
946946
tx.compute_txid(),
947-
ConfirmationHeightAnchor {
948-
anchor_block: tip.block_id(),
949-
confirmation_height: ht,
947+
ConfirmationBlockTime {
948+
block_id: tip.get(ht).unwrap().block_id(),
949+
confirmation_time: 100,
950950
},
951951
);
952952
}
@@ -959,9 +959,12 @@ fn test_chain_spends() {
959959
OutPoint::new(tx_0.compute_txid(), 0)
960960
),
961961
Some((
962-
ChainPosition::Confirmed(&ConfirmationHeightAnchor {
963-
anchor_block: tip.block_id(),
964-
confirmation_height: 98
962+
ChainPosition::Confirmed(&ConfirmationBlockTime {
963+
block_id: BlockId {
964+
hash: tip.get(98).unwrap().hash(),
965+
height: 98,
966+
},
967+
confirmation_time: 100
965968
}),
966969
tx_1.compute_txid(),
967970
)),
@@ -971,9 +974,12 @@ fn test_chain_spends() {
971974
assert_eq!(
972975
graph.get_chain_position(&local_chain, tip.block_id(), tx_0.compute_txid()),
973976
// Some(ObservedAs::Confirmed(&local_chain.get_block(95).expect("block expected"))),
974-
Some(ChainPosition::Confirmed(&ConfirmationHeightAnchor {
975-
anchor_block: tip.block_id(),
976-
confirmation_height: 95
977+
Some(ChainPosition::Confirmed(&ConfirmationBlockTime {
978+
block_id: BlockId {
979+
hash: tip.get(95).unwrap().hash(),
980+
height: 95,
981+
},
982+
confirmation_time: 100
977983
}))
978984
);
979985

0 commit comments

Comments
 (0)