@@ -155,16 +155,15 @@ fn check_builds_on(child_header: &BlockHeaderData, previous_header: &BlockHeader
155
155
Ok ( ( ) )
156
156
}
157
157
158
- async fn look_up_prev_header < ' a , ' b > ( block_source : & ' a mut dyn BlockSource , header : & BlockHeaderData , cache : & mut & ' b [ BlockHeaderData ] , mainnet : bool ) -> BlockSourceResult < BlockHeaderData > {
159
- if !cache. is_empty ( ) {
160
- let prev_header = * cache. last ( ) . unwrap ( ) ;
161
- * cache = & cache[ ..cache. len ( ) - 1 ] ;
162
- return Ok ( prev_header) ;
158
+ async fn look_up_prev_header ( block_source : & mut dyn BlockSource , header : & BlockHeaderData , cache : & HeaderCache , mainnet : bool ) -> BlockSourceResult < BlockHeaderData > {
159
+ match cache. get ( & header. header . prev_blockhash ) {
160
+ Some ( prev_header) => Ok ( * prev_header) ,
161
+ None => {
162
+ let prev_header = block_source. get_header ( & header. header . prev_blockhash , Some ( header. height - 1 ) ) . await ?;
163
+ check_builds_on ( & header, & prev_header, mainnet) ?;
164
+ Ok ( prev_header)
165
+ } ,
163
166
}
164
-
165
- let prev_header = block_source. get_header ( & header. header . prev_blockhash , Some ( header. height - 1 ) ) . await ?;
166
- check_builds_on ( & header, & prev_header, mainnet) ?;
167
- Ok ( prev_header)
168
167
}
169
168
170
169
enum ForkStep {
@@ -173,10 +172,9 @@ enum ForkStep {
173
172
ConnectBlock ( BlockHeaderData ) ,
174
173
}
175
174
176
- async fn find_fork_step < ' a > ( steps_tx : & ' a mut Vec < ForkStep > , current_header : BlockHeaderData , prev_header : & ' a BlockHeaderData , block_source : & ' a mut dyn BlockSource , head_blocks : & ' a [ BlockHeaderData ] , mainnet : bool ) -> BlockSourceResult < ( ) > {
175
+ async fn find_fork_step ( steps_tx : & mut Vec < ForkStep > , current_header : BlockHeaderData , prev_header : & BlockHeaderData , block_source : & mut dyn BlockSource , cache : & HeaderCache , mainnet : bool ) -> BlockSourceResult < ( ) > {
177
176
let mut current = current_header;
178
177
let mut previous = * prev_header;
179
- let mut cache = & head_blocks[ ..] ;
180
178
loop {
181
179
// Found a different genesis block.
182
180
if current. height == 0 {
@@ -192,7 +190,7 @@ async fn find_fork_step<'a>(steps_tx: &'a mut Vec<ForkStep>, current_header: Blo
192
190
193
191
// Found a chain fork.
194
192
if current. header . prev_blockhash == previous. header . prev_blockhash {
195
- let fork_point = look_up_prev_header ( block_source, & previous, & mut cache, mainnet) . await ?;
193
+ let fork_point = look_up_prev_header ( block_source, & previous, cache, mainnet) . await ?;
196
194
steps_tx. push ( ForkStep :: DisconnectBlock ( previous) ) ;
197
195
steps_tx. push ( ForkStep :: ConnectBlock ( current) ) ;
198
196
steps_tx. push ( ForkStep :: ForkPoint ( fork_point) ) ;
@@ -205,29 +203,23 @@ async fn find_fork_step<'a>(steps_tx: &'a mut Vec<ForkStep>, current_header: Blo
205
203
let previous_height = previous. height ;
206
204
if current_height <= previous_height {
207
205
steps_tx. push ( ForkStep :: DisconnectBlock ( previous) ) ;
208
- previous = look_up_prev_header ( block_source, & previous, & mut cache, mainnet) . await ?;
206
+ previous = look_up_prev_header ( block_source, & previous, cache, mainnet) . await ?;
209
207
}
210
208
if current_height >= previous_height {
211
209
steps_tx. push ( ForkStep :: ConnectBlock ( current) ) ;
212
- current = look_up_prev_header ( block_source, & current, & mut & [ ] [ .. ] , mainnet) . await ?;
210
+ current = look_up_prev_header ( block_source, & current, cache , mainnet) . await ?;
213
211
}
214
212
}
215
213
}
216
214
217
215
/// Walks backwards from current_header and prev_header finding the fork and sending ForkStep events
218
216
/// into the steps_tx Sender. There is no ordering guarantee between different ForkStep types, but
219
217
/// DisconnectBlock and ConnectBlock events are each in reverse, height-descending order.
220
- async fn find_fork < ' a > ( current_header : BlockHeaderData , prev_header : & ' a BlockHeaderData , block_source : & ' a mut dyn BlockSource , mut head_blocks : & ' a [ BlockHeaderData ] , mainnet : bool ) -> BlockSourceResult < Vec < ForkStep > > {
218
+ async fn find_fork ( current_header : BlockHeaderData , prev_header : & BlockHeaderData , block_source : & mut dyn BlockSource , cache : & HeaderCache , mainnet : bool ) -> BlockSourceResult < Vec < ForkStep > > {
221
219
let mut steps_tx = Vec :: new ( ) ;
222
220
if current_header. header == prev_header. header { return Ok ( steps_tx) ; }
223
221
224
- // If we have cached headers, they have to end with where we used to be
225
- head_blocks = if !head_blocks. is_empty ( ) {
226
- assert_eq ! ( head_blocks. last( ) . unwrap( ) , prev_header) ;
227
- & head_blocks[ ..head_blocks. len ( ) - 1 ]
228
- } else { head_blocks } ;
229
-
230
- find_fork_step ( & mut steps_tx, current_header, & prev_header, block_source, head_blocks, mainnet) . await ?;
222
+ find_fork_step ( & mut steps_tx, current_header, & prev_header, block_source, cache, mainnet) . await ?;
231
223
Ok ( steps_tx)
232
224
}
233
225
@@ -273,17 +265,18 @@ impl<CS, B, F, L> ChainListener for (&mut ChannelMonitor<CS>, &B, &F, &L)
273
265
/// disconnected to the fork point. Thus, we may return an Err() that includes where our tip ended
274
266
/// up which may not be new_header. Note that iff the returned Err has a BlockHeaderData, the
275
267
/// header transition from old_header to new_header is valid.
276
- async fn sync_chain_monitor < CL : ChainListener + Sized > ( new_header : BlockHeaderData , old_header : & BlockHeaderData , block_source : & mut dyn BlockSource , chain_notifier : & mut CL , head_blocks : & mut Vec < BlockHeaderData > , mainnet : bool )
268
+ async fn sync_chain_monitor < CL : ChainListener + Sized > ( new_header : BlockHeaderData , old_header : & BlockHeaderData , block_source : & mut dyn BlockSource , chain_notifier : & mut CL , header_cache : & mut HeaderCache , mainnet : bool )
277
269
-> Result < ( ) , ( BlockSourceError , Option < BlockHeaderData > ) > {
278
- let mut events = find_fork ( new_header, old_header, block_source, & * head_blocks , mainnet) . await . map_err ( |e| ( e, None ) ) ?;
270
+ let mut events = find_fork ( new_header, old_header, block_source, header_cache , mainnet) . await . map_err ( |e| ( e, None ) ) ?;
279
271
280
272
let mut last_disconnect_tip = None ;
281
273
let mut new_tip = None ;
282
274
for event in events. iter ( ) {
283
275
match & event {
284
276
& ForkStep :: DisconnectBlock ( ref header) => {
285
- println ! ( "Disconnecting block {}" , header. header. block_hash( ) ) ;
286
- if let Some ( cached_head) = head_blocks. pop ( ) {
277
+ let block_hash = header. header . block_hash ( ) ;
278
+ println ! ( "Disconnecting block {}" , block_hash) ;
279
+ if let Some ( cached_head) = header_cache. remove ( & block_hash) {
287
280
assert_eq ! ( cached_head, * header) ;
288
281
}
289
282
chain_notifier. block_disconnected ( & header. header , header. height ) ;
@@ -296,14 +289,10 @@ async fn sync_chain_monitor<CL: ChainListener + Sized>(new_header: BlockHeaderDa
296
289
}
297
290
}
298
291
299
- // If we disconnected any blocks, we should have new tip data available, which should match our
300
- // cached header data if it is available. If we didn't disconnect any blocks we shouldn't have
301
- // set a ForkPoint as there is no fork.
292
+ // If we disconnected any blocks, we should have new tip data available. If we didn't disconnect
293
+ // any blocks we shouldn't have set a ForkPoint as there is no fork.
302
294
assert_eq ! ( last_disconnect_tip. is_some( ) , new_tip. is_some( ) ) ;
303
295
if let & Some ( ref tip_header) = & new_tip {
304
- if let Some ( cached_head) = head_blocks. last ( ) {
305
- assert_eq ! ( cached_head, tip_header) ;
306
- }
307
296
debug_assert_eq ! ( tip_header. header. block_hash( ) , * last_disconnect_tip. as_ref( ) . unwrap( ) ) ;
308
297
} else {
309
298
// Set new_tip to indicate that we got a valid header chain we wanted to connect to, but
@@ -313,16 +302,17 @@ async fn sync_chain_monitor<CL: ChainListener + Sized>(new_header: BlockHeaderDa
313
302
314
303
for event in events. drain ( ..) . rev ( ) {
315
304
if let ForkStep :: ConnectBlock ( header_data) = event {
316
- let block = match block_source. get_block ( & header_data. header . block_hash ( ) ) . await {
305
+ let block_hash = header_data. header . block_hash ( ) ;
306
+ let block = match block_source. get_block ( & block_hash) . await {
317
307
Err ( e) => return Err ( ( e, new_tip) ) ,
318
308
Ok ( b) => b,
319
309
} ;
320
310
if block. header != header_data. header || !block. check_merkle_root ( ) || !block. check_witness_commitment ( ) {
321
311
return Err ( ( BlockSourceError :: Persistent , new_tip) ) ;
322
312
}
323
- println ! ( "Connecting block {}" , header_data . header . block_hash( ) . to_hex( ) ) ;
313
+ println ! ( "Connecting block {}" , block_hash. to_hex( ) ) ;
324
314
chain_notifier. block_connected ( & block, header_data. height ) ;
325
- head_blocks . push ( header_data. clone ( ) ) ;
315
+ header_cache . insert ( block_hash , header_data. clone ( ) ) ;
326
316
new_tip = Some ( header_data) ;
327
317
}
328
318
}
@@ -343,9 +333,12 @@ pub async fn init_sync_chain_monitor<CL: ChainListener + Sized, B: BlockSource>(
343
333
let old_header = block_source. get_header ( & old_block, None ) . await . unwrap ( ) ;
344
334
assert_eq ! ( old_header. header. block_hash( ) , old_block) ;
345
335
stateless_check_header ( & old_header. header ) . unwrap ( ) ;
346
- sync_chain_monitor ( new_header, & old_header, block_source, chain_notifier, & mut Vec :: new ( ) , false ) . await . unwrap ( ) ;
336
+ sync_chain_monitor ( new_header, & old_header, block_source, chain_notifier, & mut HeaderCache :: new ( ) , false ) . await . unwrap ( ) ;
347
337
}
348
338
339
+ /// Unbounded cache of header data keyed by block hash.
340
+ pub ( crate ) type HeaderCache = std:: collections:: HashMap < BlockHash , BlockHeaderData > ;
341
+
349
342
/// Keep the chain that a chain listener knows about up-to-date with the best chain from any of the
350
343
/// given block_sources.
351
344
///
@@ -368,7 +361,7 @@ where P: Poll<'a, B>,
368
361
chain_tip : ( BlockHash , BlockHeaderData ) ,
369
362
chain_poller : P ,
370
363
backup_block_sources : Vec < B > ,
371
- blocks_past_common_tip : Vec < BlockHeaderData > ,
364
+ header_cache : HeaderCache ,
372
365
chain_notifier : CL ,
373
366
mainnet : bool
374
367
}
@@ -391,10 +384,10 @@ where P: Poll<'a, B>,
391
384
/// which only provides headers. In this case, we can use such source(s) to learn of a censorship
392
385
/// attack without giving up privacy by querying a privacy-losing block sources.
393
386
pub fn init ( chain_tip : BlockHeaderData , chain_poller : P , backup_block_sources : Vec < B > , chain_notifier : CL , mainnet : bool ) -> Self {
394
- let blocks_past_common_tip = Vec :: new ( ) ;
387
+ let header_cache = HeaderCache :: new ( ) ;
395
388
Self {
396
389
chain_tip : ( chain_tip. header . block_hash ( ) , chain_tip) ,
397
- chain_poller, backup_block_sources, blocks_past_common_tip , chain_notifier, mainnet
390
+ chain_poller, backup_block_sources, header_cache , chain_notifier, mainnet
398
391
}
399
392
}
400
393
@@ -404,7 +397,7 @@ where P: Poll<'a, B>,
404
397
macro_rules! sync_chain_monitor {
405
398
( $new_hash: expr, $new_header: expr, $source: expr) => { {
406
399
let mut blocks_connected = false ;
407
- match sync_chain_monitor( $new_header, & self . chain_tip. 1 , $source, & mut self . chain_notifier, & mut self . blocks_past_common_tip , self . mainnet) . await {
400
+ match sync_chain_monitor( $new_header, & self . chain_tip. 1 , $source, & mut self . chain_notifier, & mut self . header_cache , self . mainnet) . await {
408
401
Err ( ( _, latest_tip) ) => {
409
402
if let Some ( latest_tip) = latest_tip {
410
403
let latest_tip_hash = latest_tip. header. block_hash( ) ;
@@ -428,7 +421,7 @@ where P: Poll<'a, B>,
428
421
Err ( BlockSourceError :: Transient ) => false ,
429
422
Ok ( ( ChainTip :: Common ( all_common) , _) ) => {
430
423
if all_common {
431
- self . blocks_past_common_tip . clear ( ) ;
424
+ self . header_cache . clear ( ) ;
432
425
}
433
426
false
434
427
} ,
@@ -470,7 +463,7 @@ mod sync_tests {
470
463
let mut listener = MockChainListener :: new ( )
471
464
. expect_block_connected ( chain. at_height ( 2 ) )
472
465
. expect_block_connected ( new_tip) ;
473
- let mut cache = ( 0 ..=1 ) . map ( |i| chain . at_height ( i ) ) . collect ( ) ;
466
+ let mut cache = chain . header_cache ( 0 ..=1 ) ;
474
467
match sync_chain_monitor ( new_tip, & old_tip, & mut chain, & mut listener, & mut cache, false ) . await {
475
468
Err ( ( e, _) ) => panic ! ( "Unexpected error: {:?}" , e) ,
476
469
Ok ( _) => { } ,
@@ -485,7 +478,7 @@ mod sync_tests {
485
478
let new_tip = test_chain. tip ( ) ;
486
479
let old_tip = main_chain. tip ( ) ;
487
480
let mut listener = MockChainListener :: new ( ) ;
488
- let mut cache = ( 0 ..=1 ) . map ( |i| main_chain . at_height ( i ) ) . collect ( ) ;
481
+ let mut cache = main_chain . header_cache ( 0 ..=1 ) ;
489
482
match sync_chain_monitor ( new_tip, & old_tip, & mut test_chain, & mut listener, & mut cache, false ) . await {
490
483
Err ( ( e, _) ) => assert_eq ! ( e, BlockSourceError :: Persistent ) ,
491
484
Ok ( _) => panic ! ( "Expected error" ) ,
@@ -502,7 +495,7 @@ mod sync_tests {
502
495
let mut listener = MockChainListener :: new ( )
503
496
. expect_block_disconnected ( old_tip)
504
497
. expect_block_connected ( new_tip) ;
505
- let mut cache = ( 0 ..=2 ) . map ( |i| main_chain . at_height ( i ) ) . collect ( ) ;
498
+ let mut cache = main_chain . header_cache ( 0 ..=2 ) ;
506
499
match sync_chain_monitor ( new_tip, & old_tip, & mut fork_chain, & mut listener, & mut cache, false ) . await {
507
500
Err ( ( e, _) ) => panic ! ( "Unexpected error: {:?}" , e) ,
508
501
Ok ( _) => { } ,
@@ -521,7 +514,7 @@ mod sync_tests {
521
514
. expect_block_disconnected ( old_tip)
522
515
. expect_block_disconnected ( main_chain. at_height ( 2 ) )
523
516
. expect_block_connected ( new_tip) ;
524
- let mut cache = ( 0 ..=3 ) . map ( |i| main_chain . at_height ( i ) ) . collect ( ) ;
517
+ let mut cache = main_chain . header_cache ( 0 ..=3 ) ;
525
518
match sync_chain_monitor ( new_tip, & old_tip, & mut fork_chain, & mut listener, & mut cache, false ) . await {
526
519
Err ( ( e, _) ) => panic ! ( "Unexpected error: {:?}" , e) ,
527
520
Ok ( _) => { } ,
@@ -540,7 +533,7 @@ mod sync_tests {
540
533
. expect_block_disconnected ( old_tip)
541
534
. expect_block_connected ( fork_chain. at_height ( 2 ) )
542
535
. expect_block_connected ( new_tip) ;
543
- let mut cache = ( 0 ..=2 ) . map ( |i| main_chain . at_height ( i ) ) . collect ( ) ;
536
+ let mut cache = main_chain . header_cache ( 0 ..=2 ) ;
544
537
match sync_chain_monitor ( new_tip, & old_tip, & mut fork_chain, & mut listener, & mut cache, false ) . await {
545
538
Err ( ( e, _) ) => panic ! ( "Unexpected error: {:?}" , e) ,
546
539
Ok ( _) => { } ,
@@ -809,9 +802,9 @@ mod tests {
809
802
assert ! ( client. poll_best_tip( ) . await ) ;
810
803
assert_eq ! ( & chain_notifier. blocks_disconnected. lock( ) . unwrap( ) [ ..] , & [ ( block_1a_hash, 1 ) ] [ ..] ) ;
811
804
assert_eq ! ( & chain_notifier. blocks_connected. lock( ) . unwrap( ) [ ..] , & [ ( block_1b_hash, 1 ) , ( block_2b_hash, 2 ) ] [ ..] ) ;
812
- assert_eq ! ( client. blocks_past_common_tip . len( ) , 2 ) ;
813
- assert_eq ! ( client. blocks_past_common_tip [ 0 ] . header . block_hash ( ) , block_1b_hash) ;
814
- assert_eq ! ( client. blocks_past_common_tip [ 1 ] . header . block_hash ( ) , block_2b_hash) ;
805
+ assert_eq ! ( client. header_cache . len( ) , 2 ) ;
806
+ assert ! ( client. header_cache . contains_key ( & block_1b_hash) ) ;
807
+ assert ! ( client. header_cache . contains_key ( & block_2b_hash) ) ;
815
808
816
809
// Test that even if chain_one (which we just got blocks from) stops responding to block or
817
810
// header requests we can still reorg back because we never wiped our block cache as
@@ -837,10 +830,10 @@ mod tests {
837
830
838
831
// Note that blocks_past_common_tip is not wiped as chain_one still returns 2a as its tip
839
832
// (though a smarter MicroSPVClient may wipe 1a and 2a from the set eventually.
840
- assert_eq ! ( client. blocks_past_common_tip . len( ) , 3 ) ;
841
- assert_eq ! ( client. blocks_past_common_tip [ 0 ] . header . block_hash ( ) , block_1a_hash) ;
842
- assert_eq ! ( client. blocks_past_common_tip [ 1 ] . header . block_hash ( ) , block_2a_hash) ;
843
- assert_eq ! ( client. blocks_past_common_tip [ 2 ] . header . block_hash ( ) , block_3a_hash) ;
833
+ assert_eq ! ( client. header_cache . len( ) , 3 ) ;
834
+ assert ! ( client. header_cache . contains_key ( & block_1a_hash) ) ;
835
+ assert ! ( client. header_cache . contains_key ( & block_2a_hash) ) ;
836
+ assert ! ( client. header_cache . contains_key ( & block_3a_hash) ) ;
844
837
845
838
chain_notifier. blocks_connected . lock ( ) . unwrap ( ) . clear ( ) ;
846
839
chain_notifier. blocks_disconnected . lock ( ) . unwrap ( ) . clear ( ) ;
@@ -853,7 +846,7 @@ mod tests {
853
846
assert ! ( chain_notifier. blocks_disconnected. lock( ) . unwrap( ) . is_empty( ) ) ;
854
847
assert ! ( chain_notifier. blocks_connected. lock( ) . unwrap( ) . is_empty( ) ) ;
855
848
856
- assert ! ( client. blocks_past_common_tip . is_empty( ) ) ;
849
+ assert ! ( client. header_cache . is_empty( ) ) ;
857
850
858
851
// Test that setting the header chain to 4a does...almost nothing (though backup_chain
859
852
// should now be queried) since we can't get the blocks from anywhere.
@@ -866,7 +859,7 @@ mod tests {
866
859
assert ! ( !client. poll_best_tip( ) . await ) ;
867
860
assert ! ( chain_notifier. blocks_disconnected. lock( ) . unwrap( ) . is_empty( ) ) ;
868
861
assert ! ( chain_notifier. blocks_connected. lock( ) . unwrap( ) . is_empty( ) ) ;
869
- assert ! ( client. blocks_past_common_tip . is_empty( ) ) ;
862
+ assert ! ( client. header_cache . is_empty( ) ) ;
870
863
871
864
// But if backup_chain *also* has 4a, we'll fetch it from there:
872
865
backup_chain. blocks . lock ( ) . unwrap ( ) . insert ( block_4a_hash, block_4a) ;
@@ -875,8 +868,8 @@ mod tests {
875
868
assert ! ( client. poll_best_tip( ) . await ) ;
876
869
assert ! ( chain_notifier. blocks_disconnected. lock( ) . unwrap( ) . is_empty( ) ) ;
877
870
assert_eq ! ( & chain_notifier. blocks_connected. lock( ) . unwrap( ) [ ..] , & [ ( block_4a_hash, 4 ) ] [ ..] ) ;
878
- assert_eq ! ( client. blocks_past_common_tip . len( ) , 1 ) ;
879
- assert_eq ! ( client. blocks_past_common_tip [ 0 ] . header . block_hash ( ) , block_4a_hash) ;
871
+ assert_eq ! ( client. header_cache . len( ) , 1 ) ;
872
+ assert ! ( client. header_cache . contains_key ( & block_4a_hash) ) ;
880
873
881
874
chain_notifier. blocks_connected . lock ( ) . unwrap ( ) . clear ( ) ;
882
875
chain_notifier. blocks_disconnected . lock ( ) . unwrap ( ) . clear ( ) ;
0 commit comments