@@ -14,86 +14,63 @@ use std::{
14
14
/// We assume that a block of this depth and deeper cannot be reorged.
15
15
const ASSUME_FINAL_DEPTH : u32 = 8 ;
16
16
17
- /// Represents an update fetched from an Electrum server, but excludes full
18
- /// transactions.
17
+ /// Represents a [`TxGraph`] update fetched from an Electrum server, but excludes full transactions.
19
18
///
20
19
/// To provide a complete update to [`TxGraph`], you'll need to call [`Self::missing_full_txs`] to
21
- /// determine the full transactions missing from [`TxGraph`]. Then call [`Self::finalize`] to fetch
22
- /// the full transactions from Electrum and finalize the update.
23
- #[ derive( Debug , Clone ) ]
24
- pub struct ElectrumUpdate < K , A > {
25
- /// Map of [`Txid`]s to associated [`Anchor`]s.
26
- pub graph_update : HashMap < Txid , BTreeSet < A > > ,
27
- /// The latest chain tip, as seen by the Electrum server.
28
- pub new_tip : local_chain:: CheckPoint ,
29
- /// Last-used index update for [`KeychainTxOutIndex`](bdk_chain::keychain::KeychainTxOutIndex).
30
- pub keychain_update : BTreeMap < K , u32 > ,
31
- }
32
-
33
- impl < K , A : Anchor > ElectrumUpdate < K , A > {
34
- fn new ( new_tip : local_chain:: CheckPoint ) -> Self {
35
- Self {
36
- new_tip,
37
- graph_update : HashMap :: new ( ) ,
38
- keychain_update : BTreeMap :: new ( ) ,
39
- }
40
- }
20
+ /// determine the full transactions missing from [`TxGraph`]. Then call [`Self::finalize`] to
21
+ /// fetch the full transactions from Electrum and finalize the update.
22
+ #[ derive( Debug , Default , Clone ) ]
23
+ pub struct IncompleteTxGraph < A > ( HashMap < Txid , BTreeSet < A > > ) ;
41
24
25
+ impl < A : Anchor > IncompleteTxGraph < A > {
42
26
/// Determine the full transactions that are missing from `graph`.
43
27
///
44
- /// Refer to [`ElectrumUpdate`] .
28
+ /// Refer to [`IncompleteTxGraph`] for more .
45
29
pub fn missing_full_txs < A2 > ( & self , graph : & TxGraph < A2 > ) -> Vec < Txid > {
46
- self . graph_update
30
+ self . 0
47
31
. keys ( )
48
32
. filter ( move |& & txid| graph. as_ref ( ) . get_tx ( txid) . is_none ( ) )
49
33
. cloned ( )
50
34
. collect ( )
51
35
}
52
36
53
- /// Finalizes update with `missing` txids to fetch from `client`.
37
+ /// Finalizes the [`TxGraph`] update by fetching `missing` txids from the `client`.
54
38
///
55
- /// Refer to [`ElectrumUpdate`] .
39
+ /// Refer to [`IncompleteTxGraph`] for more .
56
40
pub fn finalize (
57
41
self ,
58
42
client : & Client ,
59
43
seen_at : Option < u64 > ,
60
44
missing : Vec < Txid > ,
61
- ) -> Result < ( TxGraph < A > , BTreeMap < K , u32 > , local_chain :: CheckPoint ) , Error > {
45
+ ) -> Result < TxGraph < A > , Error > {
62
46
let new_txs = client. batch_transaction_get ( & missing) ?;
63
- let mut graph_update = TxGraph :: < A > :: new ( new_txs) ;
64
- for ( txid, anchors) in self . graph_update {
47
+ let mut graph = TxGraph :: < A > :: new ( new_txs) ;
48
+ for ( txid, anchors) in self . 0 {
65
49
if let Some ( seen_at) = seen_at {
66
- let _ = graph_update . insert_seen_at ( txid, seen_at) ;
50
+ let _ = graph . insert_seen_at ( txid, seen_at) ;
67
51
}
68
52
for anchor in anchors {
69
- let _ = graph_update . insert_anchor ( txid, anchor) ;
53
+ let _ = graph . insert_anchor ( txid, anchor) ;
70
54
}
71
55
}
72
- Ok ( ( graph_update , self . keychain_update , self . new_tip ) )
56
+ Ok ( graph )
73
57
}
74
58
}
75
59
76
- impl < K > ElectrumUpdate < K , ConfirmationHeightAnchor > {
77
- /// Finalizes the [`ElectrumUpdate `] with `new_txs` and anchors of type
60
+ impl IncompleteTxGraph < ConfirmationHeightAnchor > {
61
+ /// Finalizes the [`IncompleteTxGraph `] with `new_txs` and anchors of type
78
62
/// [`ConfirmationTimeAnchor`].
79
63
///
80
64
/// **Note:** The confirmation time might not be precisely correct if there has been a reorg.
81
65
/// Electrum's API intends that we use the merkle proof API, we should change `bdk_electrum` to
82
66
/// use it.
83
- pub fn finalize_as_confirmation_time (
67
+ pub fn finalize_with_confirmation_time (
84
68
self ,
85
69
client : & Client ,
86
70
seen_at : Option < u64 > ,
87
71
missing : Vec < Txid > ,
88
- ) -> Result <
89
- (
90
- TxGraph < ConfirmationTimeAnchor > ,
91
- BTreeMap < K , u32 > ,
92
- local_chain:: CheckPoint ,
93
- ) ,
94
- Error ,
95
- > {
96
- let ( graph, keychain_update, update_tip) = self . finalize ( client, seen_at, missing) ?;
72
+ ) -> Result < TxGraph < ConfirmationTimeAnchor > , Error > {
73
+ let graph = self . finalize ( client, seen_at, missing) ?;
97
74
98
75
let relevant_heights = {
99
76
let mut visited_heights = HashSet :: new ( ) ;
@@ -117,7 +94,7 @@ impl<K> ElectrumUpdate<K, ConfirmationHeightAnchor> {
117
94
. collect :: < HashMap < u32 , u64 > > ( ) ;
118
95
119
96
let graph_changeset = {
120
- let old_changeset = TxGraph :: default ( ) . apply_update ( graph. clone ( ) ) ;
97
+ let old_changeset = TxGraph :: default ( ) . apply_update ( graph) ;
121
98
tx_graph:: ChangeSet {
122
99
txs : old_changeset. txs ,
123
100
txouts : old_changeset. txouts ,
@@ -139,16 +116,16 @@ impl<K> ElectrumUpdate<K, ConfirmationHeightAnchor> {
139
116
}
140
117
} ;
141
118
142
- let mut update = TxGraph :: default ( ) ;
143
- update. apply_changeset ( graph_changeset) ;
144
-
145
- Ok ( ( update, keychain_update, update_tip) )
119
+ let mut new_graph = TxGraph :: default ( ) ;
120
+ new_graph. apply_changeset ( graph_changeset) ;
121
+ Ok ( new_graph)
146
122
}
147
123
}
148
124
149
125
/// Trait to extend [`Client`] functionality.
150
126
pub trait ElectrumExt < A > {
151
- /// Scan the blockchain (via electrum) for the data specified and returns a [`ElectrumUpdate`].
127
+ /// Scan the blockchain (via electrum) for the data specified and returns updates for
128
+ /// [`bdk_chain`] data structures.
152
129
///
153
130
/// - `prev_tip`: the most recent blockchain tip present locally
154
131
/// - `keychain_spks`: keychains that we want to scan transactions for
@@ -159,6 +136,7 @@ pub trait ElectrumExt<A> {
159
136
/// The scan for each keychain stops after a gap of `stop_gap` script pubkeys with no associated
160
137
/// transactions. `batch_size` specifies the max number of script pubkeys to request for in a
161
138
/// single batch request.
139
+ #[ allow( clippy:: type_complexity) ]
162
140
fn scan < K : Ord + Clone > (
163
141
& self ,
164
142
prev_tip : Option < CheckPoint > ,
@@ -167,7 +145,7 @@ pub trait ElectrumExt<A> {
167
145
outpoints : impl IntoIterator < Item = OutPoint > ,
168
146
stop_gap : usize ,
169
147
batch_size : usize ,
170
- ) -> Result < ElectrumUpdate < K , A > , Error > ;
148
+ ) -> Result < ( local_chain :: Update , IncompleteTxGraph < A > , BTreeMap < K , u32 > ) , Error > ;
171
149
172
150
/// Convenience method to call [`scan`] without requiring a keychain.
173
151
///
@@ -179,20 +157,22 @@ pub trait ElectrumExt<A> {
179
157
txids : impl IntoIterator < Item = Txid > ,
180
158
outpoints : impl IntoIterator < Item = OutPoint > ,
181
159
batch_size : usize ,
182
- ) -> Result < ElectrumUpdate < ( ) , A > , Error > {
160
+ ) -> Result < ( local_chain :: Update , IncompleteTxGraph < A > ) , Error > {
183
161
let spk_iter = misc_spks
184
162
. into_iter ( )
185
163
. enumerate ( )
186
164
. map ( |( i, spk) | ( i as u32 , spk) ) ;
187
165
188
- self . scan (
166
+ let ( chain , graph , _ ) = self . scan (
189
167
prev_tip,
190
168
[ ( ( ) , spk_iter) ] . into ( ) ,
191
169
txids,
192
170
outpoints,
193
171
usize:: MAX ,
194
172
batch_size,
195
- )
173
+ ) ?;
174
+
175
+ Ok ( ( chain, graph) )
196
176
}
197
177
}
198
178
@@ -205,7 +185,14 @@ impl ElectrumExt<ConfirmationHeightAnchor> for Client {
205
185
outpoints : impl IntoIterator < Item = OutPoint > ,
206
186
stop_gap : usize ,
207
187
batch_size : usize ,
208
- ) -> Result < ElectrumUpdate < K , ConfirmationHeightAnchor > , Error > {
188
+ ) -> Result <
189
+ (
190
+ local_chain:: Update ,
191
+ IncompleteTxGraph < ConfirmationHeightAnchor > ,
192
+ BTreeMap < K , u32 > ,
193
+ ) ,
194
+ Error ,
195
+ > {
209
196
let mut request_spks = keychain_spks
210
197
. into_iter ( )
211
198
. map ( |( k, s) | ( k, s. into_iter ( ) ) )
@@ -217,9 +204,8 @@ impl ElectrumExt<ConfirmationHeightAnchor> for Client {
217
204
218
205
let update = loop {
219
206
let ( tip, _) = construct_update_tip ( self , prev_tip. clone ( ) ) ?;
220
- let mut update = ElectrumUpdate :: < K , ConfirmationHeightAnchor > :: new ( tip. clone ( ) ) ;
221
- let cps = update
222
- . new_tip
207
+ let mut graph_update = IncompleteTxGraph :: < ConfirmationHeightAnchor > :: default ( ) ;
208
+ let cps = tip
223
209
. iter ( )
224
210
. take ( 10 )
225
211
. map ( |cp| ( cp. height ( ) , cp) )
@@ -230,7 +216,7 @@ impl ElectrumExt<ConfirmationHeightAnchor> for Client {
230
216
scanned_spks. append ( & mut populate_with_spks (
231
217
self ,
232
218
& cps,
233
- & mut update ,
219
+ & mut graph_update ,
234
220
& mut scanned_spks
235
221
. iter ( )
236
222
. map ( |( i, ( spk, _) ) | ( i. clone ( ) , spk. clone ( ) ) ) ,
@@ -243,7 +229,7 @@ impl ElectrumExt<ConfirmationHeightAnchor> for Client {
243
229
populate_with_spks (
244
230
self ,
245
231
& cps,
246
- & mut update ,
232
+ & mut graph_update ,
247
233
keychain_spks,
248
234
stop_gap,
249
235
batch_size,
@@ -254,18 +240,27 @@ impl ElectrumExt<ConfirmationHeightAnchor> for Client {
254
240
}
255
241
}
256
242
257
- populate_with_txids ( self , & cps, & mut update , & mut txids. iter ( ) . cloned ( ) ) ?;
243
+ populate_with_txids ( self , & cps, & mut graph_update , & mut txids. iter ( ) . cloned ( ) ) ?;
258
244
259
- let _txs =
260
- populate_with_outpoints ( self , & cps, & mut update, & mut outpoints. iter ( ) . cloned ( ) ) ?;
245
+ let _txs = populate_with_outpoints (
246
+ self ,
247
+ & cps,
248
+ & mut graph_update,
249
+ & mut outpoints. iter ( ) . cloned ( ) ,
250
+ ) ?;
261
251
262
252
// check for reorgs during scan process
263
253
let server_blockhash = self . block_header ( tip. height ( ) as usize ) ?. block_hash ( ) ;
264
254
if tip. hash ( ) != server_blockhash {
265
255
continue ; // reorg
266
256
}
267
257
268
- update. keychain_update = request_spks
258
+ let chain_update = local_chain:: Update {
259
+ tip,
260
+ introduce_older_blocks : true ,
261
+ } ;
262
+
263
+ let keychain_update = request_spks
269
264
. into_keys ( )
270
265
. filter_map ( |k| {
271
266
scanned_spks
@@ -275,7 +270,8 @@ impl ElectrumExt<ConfirmationHeightAnchor> for Client {
275
270
. map ( |( ( _, i) , _) | ( k, * i) )
276
271
} )
277
272
. collect :: < BTreeMap < _ , _ > > ( ) ;
278
- break update;
273
+
274
+ break ( chain_update, graph_update, keychain_update) ;
279
275
} ;
280
276
281
277
Ok ( update)
@@ -399,10 +395,10 @@ fn determine_tx_anchor(
399
395
}
400
396
}
401
397
402
- fn populate_with_outpoints < K > (
398
+ fn populate_with_outpoints (
403
399
client : & Client ,
404
400
cps : & BTreeMap < u32 , CheckPoint > ,
405
- update : & mut ElectrumUpdate < K , ConfirmationHeightAnchor > ,
401
+ graph_update : & mut IncompleteTxGraph < ConfirmationHeightAnchor > ,
406
402
outpoints : & mut impl Iterator < Item = OutPoint > ,
407
403
) -> Result < HashMap < Txid , Transaction > , Error > {
408
404
let mut full_txs = HashMap :: new ( ) ;
@@ -451,7 +447,7 @@ fn populate_with_outpoints<K>(
451
447
} ;
452
448
453
449
let anchor = determine_tx_anchor ( cps, res. height , res. tx_hash ) ;
454
- let tx_entry = update . graph_update . entry ( res. tx_hash ) . or_default ( ) ;
450
+ let tx_entry = graph_update. 0 . entry ( res. tx_hash ) . or_default ( ) ;
455
451
if let Some ( anchor) = anchor {
456
452
tx_entry. insert ( anchor) ;
457
453
}
@@ -460,10 +456,10 @@ fn populate_with_outpoints<K>(
460
456
Ok ( full_txs)
461
457
}
462
458
463
- fn populate_with_txids < K > (
459
+ fn populate_with_txids (
464
460
client : & Client ,
465
461
cps : & BTreeMap < u32 , CheckPoint > ,
466
- update : & mut ElectrumUpdate < K , ConfirmationHeightAnchor > ,
462
+ graph_update : & mut IncompleteTxGraph < ConfirmationHeightAnchor > ,
467
463
txids : & mut impl Iterator < Item = Txid > ,
468
464
) -> Result < ( ) , Error > {
469
465
for txid in txids {
@@ -488,18 +484,18 @@ fn populate_with_txids<K>(
488
484
None => continue ,
489
485
} ;
490
486
491
- let tx_entry = update . graph_update . entry ( txid) . or_default ( ) ;
487
+ let tx_entry = graph_update. 0 . entry ( txid) . or_default ( ) ;
492
488
if let Some ( anchor) = anchor {
493
489
tx_entry. insert ( anchor) ;
494
490
}
495
491
}
496
492
Ok ( ( ) )
497
493
}
498
494
499
- fn populate_with_spks < K , I : Ord + Clone > (
495
+ fn populate_with_spks < I : Ord + Clone > (
500
496
client : & Client ,
501
497
cps : & BTreeMap < u32 , CheckPoint > ,
502
- update : & mut ElectrumUpdate < K , ConfirmationHeightAnchor > ,
498
+ graph_update : & mut IncompleteTxGraph < ConfirmationHeightAnchor > ,
503
499
spks : & mut impl Iterator < Item = ( I , ScriptBuf ) > ,
504
500
stop_gap : usize ,
505
501
batch_size : usize ,
@@ -532,7 +528,7 @@ fn populate_with_spks<K, I: Ord + Clone>(
532
528
}
533
529
534
530
for tx in spk_history {
535
- let tx_entry = update . graph_update . entry ( tx. tx_hash ) . or_default ( ) ;
531
+ let tx_entry = graph_update. 0 . entry ( tx. tx_hash ) . or_default ( ) ;
536
532
if let Some ( anchor) = determine_tx_anchor ( cps, tx. height , tx. tx_hash ) {
537
533
tx_entry. insert ( anchor) ;
538
534
}
0 commit comments