@@ -9,24 +9,17 @@ use alloc::vec::Vec;
9
9
use bdk_core:: BlockId ;
10
10
use bitcoin:: { Transaction , Txid } ;
11
11
12
- /// A request for chain data needed during canonicalization .
12
+ /// A request to check which anchors are confirmed in the chain .
13
13
#[ derive( Debug , Clone , PartialEq , Eq ) ]
14
- pub enum CanonicalizationRequest {
15
- /// Request to check if a block is in the chain.
16
- IsBlockInChain {
17
- /// The block to check.
18
- block : BlockId ,
19
- /// The chain tip to check against.
20
- chain_tip : BlockId ,
21
- } ,
14
+ pub struct CanonicalizationRequest < A > {
15
+ /// The anchors to check.
16
+ pub anchors : Vec < A > ,
17
+ /// The chain tip to check against.
18
+ pub chain_tip : BlockId ,
22
19
}
23
20
24
- /// A response to a canonicalization request.
25
- #[ derive( Debug , Clone , PartialEq , Eq ) ]
26
- pub enum CanonicalizationResponse {
27
- /// Response to IsBlockInChain request.
28
- IsBlockInChain ( Option < bool > ) ,
29
- }
21
+ /// Response containing the best confirmed anchor, if any.
22
+ pub type CanonicalizationResponse < A > = Option < A > ;
30
23
31
24
/// Parameters that modify the canonicalization algorithm.
32
25
pub use crate :: canonical_iter:: CanonicalizationParams ;
@@ -48,7 +41,7 @@ pub struct CanonicalizationTask<'g, A> {
48
41
canonical : CanonicalMap < A > ,
49
42
not_canonical : NotCanonicalSet ,
50
43
51
- pending_anchor_checks : VecDeque < ( Txid , Arc < Transaction > , Vec < A > , usize ) > ,
44
+ pending_anchor_checks : VecDeque < ( Txid , Arc < Transaction > , Vec < A > ) > ,
52
45
53
46
// Store canonical transactions in order
54
47
canonical_order : Vec < Txid > ,
@@ -65,7 +58,7 @@ impl<'g, A: Anchor> CanonicalizationTask<'g, A> {
65
58
tx_graph : & ' g TxGraph < A > ,
66
59
chain_tip : BlockId ,
67
60
params : CanonicalizationParams ,
68
- ) -> ( Self , Option < CanonicalizationRequest > ) {
61
+ ) -> ( Self , Option < CanonicalizationRequest < A > > ) {
69
62
let anchors = tx_graph. all_anchors ( ) ;
70
63
let unprocessed_assumed_txs = Box :: new (
71
64
params
@@ -88,13 +81,17 @@ impl<'g, A: Anchor> CanonicalizationTask<'g, A> {
88
81
let mut task = Self {
89
82
tx_graph,
90
83
chain_tip,
84
+
91
85
unprocessed_assumed_txs,
92
86
unprocessed_anchored_txs,
93
87
unprocessed_seen_txs,
94
88
unprocessed_leftover_txs : VecDeque :: new ( ) ,
89
+
95
90
canonical : HashMap :: new ( ) ,
96
91
not_canonical : HashSet :: new ( ) ,
92
+
97
93
pending_anchor_checks : VecDeque :: new ( ) ,
94
+
98
95
canonical_order : Vec :: new ( ) ,
99
96
confirmed_anchors : HashMap :: new ( ) ,
100
97
} ;
@@ -103,79 +100,48 @@ impl<'g, A: Anchor> CanonicalizationTask<'g, A> {
103
100
task. process_assumed_txs ( ) ;
104
101
105
102
// Process anchored transactions and get the first request if needed
106
- let initial_request = task. process_next_anchored_batch ( ) ;
103
+ let initial_request = task. process_anchored_txs ( ) ;
107
104
108
105
( task, initial_request)
109
106
}
110
107
111
108
/// Returns the next query needed, if any.
112
- pub fn next_query ( & mut self ) -> Option < CanonicalizationRequest > {
109
+ pub fn next_query ( & mut self ) -> Option < CanonicalizationRequest < A > > {
113
110
// Check if we have pending anchor checks
114
- if let Some ( ( _, _, anchors, idx) ) = self . pending_anchor_checks . front ( ) {
115
- if * idx < anchors. len ( ) {
116
- let anchor = & anchors[ * idx] ;
117
- return Some ( CanonicalizationRequest :: IsBlockInChain {
118
- block : anchor. anchor_block ( ) ,
119
- chain_tip : self . chain_tip ,
120
- } ) ;
121
- }
111
+ if let Some ( ( _, _, anchors) ) = self . pending_anchor_checks . front ( ) {
112
+ return Some ( CanonicalizationRequest {
113
+ anchors : anchors. clone ( ) ,
114
+ chain_tip : self . chain_tip ,
115
+ } ) ;
122
116
}
123
117
124
118
// Process more anchored transactions if available
125
- self . process_next_anchored_batch ( )
119
+ self . process_anchored_txs ( )
126
120
}
127
121
128
122
/// Resolves a query with the given response.
129
- pub fn resolve_query ( & mut self , response : CanonicalizationResponse ) {
130
- match response {
131
- CanonicalizationResponse :: IsBlockInChain ( result) => {
132
- if let Some ( ( txid, tx, anchors, idx) ) = self . pending_anchor_checks . front_mut ( ) {
133
- if result == Some ( true ) && * idx < anchors. len ( ) {
134
- // This anchor is in the chain, mark transaction as canonical
135
- let anchor = anchors[ * idx] . clone ( ) ;
136
- let txid = * txid;
137
- let tx = tx. clone ( ) ;
138
-
139
- // Remove from pending checks
140
- self . pending_anchor_checks . pop_front ( ) ;
141
-
142
- // Track this confirmed anchor
143
- self . confirmed_anchors . insert ( txid, anchor. clone ( ) ) ;
144
-
145
- // Check if this transaction was already marked canonical transitively
146
- if let Some ( ( _, reason) ) = self . canonical . get ( & txid) {
147
- if matches ! (
148
- reason,
149
- CanonicalReason :: Anchor {
150
- descendant: Some ( _) ,
151
- ..
152
- }
153
- ) {
154
- // Update to direct anchor
155
- if let Some ( ( _, reason) ) = self . canonical . get_mut ( & txid) {
156
- * reason = CanonicalReason :: from_anchor ( anchor) ;
157
- }
158
- }
159
- } else {
160
- // Mark as canonical
161
- self . mark_canonical ( txid, tx, CanonicalReason :: from_anchor ( anchor) ) ;
162
- }
163
- } else {
164
- // Move to next anchor
165
- * idx += 1 ;
166
-
167
- // If we've checked all anchors, move to leftover
168
- if * idx >= anchors. len ( ) {
169
- let ( txid, tx, anchors, _) =
170
- self . pending_anchor_checks . pop_front ( ) . unwrap ( ) ;
171
- let height = anchors
172
- . last ( )
173
- . map ( |a| a. confirmation_height_upper_bound ( ) )
174
- . unwrap_or ( 0 ) ;
175
- self . unprocessed_leftover_txs . push_back ( ( txid, tx, height) ) ;
176
- }
123
+ pub fn resolve_query ( & mut self , response : CanonicalizationResponse < A > ) {
124
+ if let Some ( ( txid, tx, anchors) ) = self . pending_anchor_checks . pop_front ( ) {
125
+ match response {
126
+ Some ( best_anchor) => {
127
+ self . confirmed_anchors . insert ( txid, best_anchor. clone ( ) ) ;
128
+ if !self . is_canonicalized ( txid) {
129
+ self . mark_canonical ( txid, tx, CanonicalReason :: from_anchor ( best_anchor) ) ;
177
130
}
178
131
}
132
+ None => {
133
+ self . unprocessed_leftover_txs . push_back ( (
134
+ txid,
135
+ tx,
136
+ anchors
137
+ . iter ( )
138
+ . last ( )
139
+ . expect (
140
+ "tx taken from `unprocessed_txs_with_anchors` so it must at least have an anchor" ,
141
+ )
142
+ . confirmation_height_upper_bound ( ) ,
143
+ ) )
144
+ }
179
145
}
180
146
}
181
147
}
@@ -208,7 +174,6 @@ impl<'g, A: Anchor> CanonicalizationTask<'g, A> {
208
174
}
209
175
210
176
// Get transaction node for first_seen/last_seen info
211
- // let tx_node = self.tx_graph.get_tx_node(*txid);
212
177
let tx_node = match self . tx_graph . get_tx_node ( * txid) {
213
178
Some ( tx_node) => tx_node,
214
179
None => {
@@ -270,23 +235,6 @@ impl<'g, A: Anchor> CanonicalizationTask<'g, A> {
270
235
CanonicalView :: from_parts ( self . chain_tip , view_order, view_txs, view_spends)
271
236
}
272
237
273
- fn process_next_anchored_batch ( & mut self ) -> Option < CanonicalizationRequest > {
274
- while let Some ( ( txid, tx, anchors) ) = self . unprocessed_anchored_txs . next ( ) {
275
- if !self . is_canonicalized ( txid) {
276
- // Check if we already have a confirmed anchor for this transaction
277
- if let Some ( anchor) = self . confirmed_anchors . get ( & txid) . cloned ( ) {
278
- self . mark_canonical ( txid, tx, CanonicalReason :: from_anchor ( anchor) ) ;
279
- } else if !anchors. is_empty ( ) {
280
- let anchors_vec: Vec < A > = anchors. iter ( ) . cloned ( ) . collect ( ) ;
281
- self . pending_anchor_checks
282
- . push_back ( ( txid, tx, anchors_vec, 0 ) ) ;
283
- return self . next_query ( ) ;
284
- }
285
- }
286
- }
287
- None
288
- }
289
-
290
238
fn is_canonicalized ( & self , txid : Txid ) -> bool {
291
239
self . canonical . contains_key ( & txid) || self . not_canonical . contains ( & txid)
292
240
}
@@ -299,6 +247,17 @@ impl<'g, A: Anchor> CanonicalizationTask<'g, A> {
299
247
}
300
248
}
301
249
250
+ fn process_anchored_txs ( & mut self ) -> Option < CanonicalizationRequest < A > > {
251
+ while let Some ( ( txid, tx, anchors) ) = self . unprocessed_anchored_txs . next ( ) {
252
+ if !self . is_canonicalized ( txid) {
253
+ self . pending_anchor_checks
254
+ . push_back ( ( txid, tx, anchors. iter ( ) . cloned ( ) . collect ( ) ) ) ;
255
+ return self . next_query ( ) ;
256
+ }
257
+ }
258
+ None
259
+ }
260
+
302
261
fn process_seen_txs ( & mut self ) {
303
262
while let Some ( ( txid, tx, last_seen) ) = self . unprocessed_seen_txs . next ( ) {
304
263
debug_assert ! (
@@ -395,7 +354,7 @@ impl<'g, A: Anchor> CanonicalizationTask<'g, A> {
395
354
}
396
355
} else {
397
356
// Add to canonical order
398
- for ( txid, _ , reason) in & staged_canonical {
357
+ for ( txid, tx , reason) in & staged_canonical {
399
358
self . canonical_order . push ( * txid) ;
400
359
401
360
// If this was marked transitively, check if it has anchors to verify
@@ -412,12 +371,13 @@ impl<'g, A: Anchor> CanonicalizationTask<'g, A> {
412
371
413
372
if is_transitive {
414
373
if let Some ( anchors) = self . tx_graph . all_anchors ( ) . get ( txid) {
415
- // Only check anchors we haven't already confirmed
416
- if !self . confirmed_anchors . contains_key ( txid) && !anchors. is_empty ( ) {
417
- let tx = self . tx_graph . get_tx ( * txid) . expect ( "tx must exist" ) ;
418
- let anchors_vec: Vec < A > = anchors. iter ( ) . cloned ( ) . collect ( ) ;
419
- self . pending_anchor_checks
420
- . push_back ( ( * txid, tx, anchors_vec, 0 ) ) ;
374
+ // only check anchors we haven't already confirmed
375
+ if !self . confirmed_anchors . contains_key ( txid) {
376
+ self . pending_anchor_checks . push_back ( (
377
+ * txid,
378
+ tx. clone ( ) ,
379
+ anchors. iter ( ) . cloned ( ) . collect ( ) ,
380
+ ) ) ;
421
381
}
422
382
}
423
383
}
0 commit comments