@@ -9,24 +9,17 @@ use alloc::vec::Vec;
99use bdk_core:: BlockId ;
1010use bitcoin:: { Transaction , Txid } ;
1111
12- /// A request for chain data needed during canonicalization .
12+ /// A request to check which anchors are confirmed in the chain .
1313#[ 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 ,
2219}
2320
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 > ;
3023
3124/// Parameters that modify the canonicalization algorithm.
3225pub use crate :: canonical_iter:: CanonicalizationParams ;
@@ -48,7 +41,7 @@ pub struct CanonicalizationTask<'g, A> {
4841 canonical : CanonicalMap < A > ,
4942 not_canonical : NotCanonicalSet ,
5043
51- pending_anchor_checks : VecDeque < ( Txid , Arc < Transaction > , Vec < A > , usize ) > ,
44+ pending_anchor_checks : VecDeque < ( Txid , Arc < Transaction > , Vec < A > ) > ,
5245
5346 // Store canonical transactions in order
5447 canonical_order : Vec < Txid > ,
@@ -65,7 +58,7 @@ impl<'g, A: Anchor> CanonicalizationTask<'g, A> {
6558 tx_graph : & ' g TxGraph < A > ,
6659 chain_tip : BlockId ,
6760 params : CanonicalizationParams ,
68- ) -> ( Self , Option < CanonicalizationRequest > ) {
61+ ) -> ( Self , Option < CanonicalizationRequest < A > > ) {
6962 let anchors = tx_graph. all_anchors ( ) ;
7063 let unprocessed_assumed_txs = Box :: new (
7164 params
@@ -88,13 +81,17 @@ impl<'g, A: Anchor> CanonicalizationTask<'g, A> {
8881 let mut task = Self {
8982 tx_graph,
9083 chain_tip,
84+
9185 unprocessed_assumed_txs,
9286 unprocessed_anchored_txs,
9387 unprocessed_seen_txs,
9488 unprocessed_leftover_txs : VecDeque :: new ( ) ,
89+
9590 canonical : HashMap :: new ( ) ,
9691 not_canonical : HashSet :: new ( ) ,
92+
9793 pending_anchor_checks : VecDeque :: new ( ) ,
94+
9895 canonical_order : Vec :: new ( ) ,
9996 confirmed_anchors : HashMap :: new ( ) ,
10097 } ;
@@ -103,79 +100,48 @@ impl<'g, A: Anchor> CanonicalizationTask<'g, A> {
103100 task. process_assumed_txs ( ) ;
104101
105102 // 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 ( ) ;
107104
108105 ( task, initial_request)
109106 }
110107
111108 /// 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 > > {
113110 // 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+ } ) ;
122116 }
123117
124118 // Process more anchored transactions if available
125- self . process_next_anchored_batch ( )
119+ self . process_anchored_txs ( )
126120 }
127121
128122 /// 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) ) ;
177130 }
178131 }
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+ }
179145 }
180146 }
181147 }
@@ -208,7 +174,6 @@ impl<'g, A: Anchor> CanonicalizationTask<'g, A> {
208174 }
209175
210176 // Get transaction node for first_seen/last_seen info
211- // let tx_node = self.tx_graph.get_tx_node(*txid);
212177 let tx_node = match self . tx_graph . get_tx_node ( * txid) {
213178 Some ( tx_node) => tx_node,
214179 None => {
@@ -270,23 +235,6 @@ impl<'g, A: Anchor> CanonicalizationTask<'g, A> {
270235 CanonicalView :: from_parts ( self . chain_tip , view_order, view_txs, view_spends)
271236 }
272237
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-
290238 fn is_canonicalized ( & self , txid : Txid ) -> bool {
291239 self . canonical . contains_key ( & txid) || self . not_canonical . contains ( & txid)
292240 }
@@ -299,6 +247,17 @@ impl<'g, A: Anchor> CanonicalizationTask<'g, A> {
299247 }
300248 }
301249
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+
302261 fn process_seen_txs ( & mut self ) {
303262 while let Some ( ( txid, tx, last_seen) ) = self . unprocessed_seen_txs . next ( ) {
304263 debug_assert ! (
@@ -395,7 +354,7 @@ impl<'g, A: Anchor> CanonicalizationTask<'g, A> {
395354 }
396355 } else {
397356 // Add to canonical order
398- for ( txid, _ , reason) in & staged_canonical {
357+ for ( txid, tx , reason) in & staged_canonical {
399358 self . canonical_order . push ( * txid) ;
400359
401360 // If this was marked transitively, check if it has anchors to verify
@@ -412,12 +371,13 @@ impl<'g, A: Anchor> CanonicalizationTask<'g, A> {
412371
413372 if is_transitive {
414373 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+ ) ) ;
421381 }
422382 }
423383 }
0 commit comments