@@ -364,6 +364,10 @@ impl<D> IntoIterator for CheckPoint<D> {
364364mod tests {
365365 use super :: * ;
366366
367+ use alloc:: vec:: Vec ;
368+ use bitcoin:: consensus:: encode:: deserialize_hex;
369+ use bitcoin:: hashes:: Hash ;
370+
367371 /// Make sure that dropping checkpoints does not result in recursion and stack overflow.
368372 #[ test]
369373 fn checkpoint_drop_is_not_recursive ( ) {
@@ -422,4 +426,100 @@ mod tests {
422426 "the checkpoint node should be freed when all strong references are dropped" ,
423427 ) ;
424428 }
429+
430+ #[ test]
431+ fn insert_displaces_conflicting_block ( ) {
432+ let headers: Vec < Header > = [
433+ "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff7f2002000000" ,
434+ "0000002006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f1c96cc459dbb0c7bbc722af14f913da868779290ad48ff87ee314ebb8ae08f384b166069ffff7f2001000000" ,
435+ "0000002078ce518c7dcfd99ad5859c35bd2be15794c0e5dc8e60c1fea3b0461c45da181ca80f828504f88c645ea46cfcf93156269807e3bd409e1317271a1546d238e9b24c166069ffff7f2001000000" ,
436+ "000000200dfd6c5af6ea3cb341a08db344f93743d94b630d122867faff855f6310e58864e6e0c859fda703d66d051b86f16f09b0cead182f3cbe13767cd91b6371ec252c4c166069ffff7f2000000000" ,
437+ ]
438+ . into_iter ( )
439+ . map ( |s| deserialize_hex :: < Header > ( s) . expect ( "failed to deserialize header" ) )
440+ . collect ( ) ;
441+
442+ let header_0 = headers[ 0 ] ;
443+ let header_1 = headers[ 1 ] ;
444+ let header_2 = headers[ 2 ] ;
445+ let header_3 = headers[ 3 ] ;
446+
447+ // Test: Insert 2' that conflicts at heights 1, 2.
448+ // tip [0, 1, 2, 3]
449+ // insert [1', 2']
450+ // new [0, 2'] (1 is displaced, 3 is evicted)
451+ let mut cp = CheckPoint :: new ( 0 , header_0) ;
452+ cp = cp. push ( 1 , header_1) . unwrap ( ) ;
453+ cp = cp. push ( 2 , header_2) . unwrap ( ) ;
454+ cp = cp. push ( 3 , header_3) . unwrap ( ) ;
455+
456+ let mut header_2_alt = header_2;
457+ header_2_alt. prev_blockhash = BlockHash :: all_zeros ( ) ;
458+ cp = cp. insert ( 2 , header_2_alt) ;
459+
460+ assert_eq ! ( cp. iter( ) . count( ) , 2 ) ;
461+ assert_eq ! ( cp. height( ) , 2 ) ;
462+ assert_eq ! ( cp. hash( ) , header_2_alt. block_hash( ) ) ;
463+
464+ // Test: Insert 3' that conflicts at height 2
465+ // tip [0, 1, 2]
466+ // insert [2', 3']
467+ // new [0, 1, 3'] (2 is displaced)
468+ let mut cp = CheckPoint :: new ( 0 , header_0) ;
469+ cp = cp. push ( 1 , header_1) . unwrap ( ) ;
470+ cp = cp. push ( 2 , header_2) . unwrap ( ) ;
471+
472+ let mut header_3_alt = header_3;
473+ header_3_alt. prev_blockhash = BlockHash :: all_zeros ( ) ;
474+ cp = cp. insert ( 3 , header_3_alt) ;
475+
476+ assert_eq ! ( cp. iter( ) . count( ) , 3 ) ;
477+ assert_eq ! ( cp. height( ) , 3 ) ;
478+ assert_eq ! ( cp. hash( ) , header_3_alt. block_hash( ) ) ;
479+
480+ // Test: Insert 1' that conflicts at height 1
481+ // tip [0, 1, 2, 3]
482+ // insert [1']
483+ // new [0, 1'] (2 and 3 are evicted)
484+ let mut cp = CheckPoint :: new ( 0 , header_0) ;
485+ cp = cp. push ( 1 , header_1) . unwrap ( ) ;
486+ cp = cp. push ( 2 , header_2) . unwrap ( ) ;
487+ cp = cp. push ( 3 , header_3) . unwrap ( ) ;
488+
489+ // Create a conflicting block at height 1 with different hash
490+ // header_2 expects header_1.block_hash() as its prev_blockhash, but we'll insert a
491+ // different block
492+ let mut header_1_alt = header_1;
493+ header_1_alt. nonce = 13 ;
494+ cp = cp. insert ( 1 , header_1_alt) ;
495+
496+ // Verify only genesis and the new block at height 1 remain
497+ assert_eq ! ( cp. iter( ) . count( ) , 2 ) ;
498+ assert_eq ! ( cp. height( ) , 1 ) ;
499+ assert_eq ! ( cp. hash( ) , header_1_alt. block_hash( ) ) ;
500+ }
501+
502+ #[ should_panic( expected = "cannot replace the genesis block" ) ]
503+ #[ test]
504+ fn insert_should_not_displace_genesis ( ) {
505+ let headers: Vec < Header > = [
506+ "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff7f2002000000" ,
507+ "0000002006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f1c96cc459dbb0c7bbc722af14f913da868779290ad48ff87ee314ebb8ae08f384b166069ffff7f2001000000" ,
508+ "0000002078ce518c7dcfd99ad5859c35bd2be15794c0e5dc8e60c1fea3b0461c45da181ca80f828504f88c645ea46cfcf93156269807e3bd409e1317271a1546d238e9b24c166069ffff7f2001000000" ,
509+ ]
510+ . into_iter ( )
511+ . map ( |s| deserialize_hex :: < Header > ( s) . expect ( "failed to deserialize header" ) )
512+ . collect ( ) ;
513+
514+ let header_0 = headers[ 0 ] ;
515+ let header_1 = headers[ 1 ] ;
516+ let header_2 = headers[ 2 ] ;
517+
518+ // Test: Insert 1' that conflicts at heights 0, 1.
519+ // tip [0, 1]
520+ // insert [0', 1']
521+ let mut cp = CheckPoint :: new ( 0 , header_0) ;
522+ cp = cp. push ( 1 , header_1) . unwrap ( ) ;
523+ let _cp = cp. insert ( 1 , header_2) ;
524+ }
425525}
0 commit comments