@@ -315,104 +315,234 @@ impl<P: Platform> CheckpointExt<P> for Checkpoint<P> {
315315#[ cfg( test) ]
316316mod tests {
317317 use {
318- crate :: alloy:: primitives:: Address ,
319- alloy_origin:: primitives:: { TxHash , U256 } ,
320- rblib:: { prelude:: * , test_utils:: BlockContextMocked } ,
318+ crate :: {
319+ alloy:: primitives:: Address ,
320+ payload:: { Checkpoint , CheckpointExt } ,
321+ prelude:: { BlockContext , Ethereum } ,
322+ test_utils:: { BlockContextMocked , FundedAccounts , transfer_tx} ,
323+ } ,
324+ alloy_origin:: primitives:: U256 ,
321325 std:: {
322326 thread,
323327 time:: { Duration , Instant } ,
324328 } ,
325329 } ;
326330
327331 #[ test]
328- fn test_is_empty_and_root ( ) {
332+ fn test_new_at_block ( ) {
329333 let ( block, _) = BlockContext :: < Ethereum > :: mocked ( ) ;
330- let root = block. start ( ) ;
331- let mid = root. barrier ( ) ;
332- let leaf = mid. barrier ( ) ;
334+ let cp = Checkpoint :: new_at_block ( block) ;
333335
334- assert ! ( root. is_empty( ) ) ;
335- assert_eq ! ( leaf. root( ) , root) ;
336- }
336+ let cp2 = cp. barrier ( ) ;
337+ let cp3 = cp2. barrier ( ) ;
338+
339+ assert ! ( cp. is_empty( ) ) ;
340+ assert_eq ! ( cp2. root( ) , cp) ;
341+ assert_eq ! ( cp3. root( ) , cp) ;
337342
338- #[ test]
339- fn test_gas_used_and_cumulative_gas_used ( ) {
340- let ( block, _) = BlockContext :: < Ethereum > :: mocked ( ) ;
341- let cp = block. start ( ) ;
342343 assert_eq ! ( cp. gas_used( ) , 0 ) ;
343344 assert_eq ! ( cp. cumulative_gas_used( ) , 0 ) ;
344- }
345345
346- #[ test]
347- fn test_effective_tip_and_blob_gas ( ) {
348- let ( block, _) = BlockContext :: < Ethereum > :: mocked ( ) ;
349- let cp = block. start ( ) ;
350346 assert_eq ! ( cp. effective_tip_per_gas( ) , 0 ) ;
351347 assert ! ( !cp. has_blobs( ) ) ;
352348 assert_eq ! ( cp. blob_gas_used( ) , Some ( 0 ) ) ;
353349 assert_eq ! ( cp. cumulative_blob_gas_used( ) , 0 ) ;
354- }
355-
356- #[ test]
357- fn test_to_between_linear_history ( ) {
358- let ( block, _) = BlockContext :: < Ethereum > :: mocked ( ) ;
359- let a = block. start ( ) ;
360- let b = a. barrier ( ) ;
361- let c = b. barrier ( ) ;
362350
363- let span1 = c . to ( & a ) . unwrap ( ) ;
364- let span2 = a . to ( & c ) . unwrap ( ) ;
351+ let span1 = cp3 . to ( & cp ) . unwrap ( ) ;
352+ let span2 = cp . to ( & cp3 ) . unwrap ( ) ;
365353
354+ assert_eq ! ( span1. len( ) , span2. len( ) ) ;
355+ for i in 0 ..span2. len ( ) {
356+ assert_eq ! ( span1. at( i) , span2. at( i) ) ;
357+ }
366358 assert_eq ! ( span1. len( ) , 3 ) ;
367359 assert_eq ! ( span2. len( ) , 3 ) ;
368- }
369-
370- #[ test]
371- fn test_balance_and_nonce_defaults ( ) {
372- let ( block, _) = BlockContext :: < Ethereum > :: mocked ( ) ;
373- let cp = block. start ( ) ;
374360
375361 let addr = Address :: ZERO ;
376362 assert_eq ! ( cp. balance_of( addr) . unwrap( ) , U256 :: ZERO ) ;
377363 assert_eq ! ( cp. nonce_of( addr) . unwrap( ) , 0 ) ;
378- }
379-
380- #[ test]
381- fn test_signers_and_nonces_are_empty ( ) {
382- let ( block, _) = BlockContext :: < Ethereum > :: mocked ( ) ;
383- let cp = block. start ( ) ;
384364
385365 assert ! ( cp. signers( ) . is_empty( ) ) ;
386366 assert ! ( cp. nonces( ) . is_empty( ) ) ;
387- }
388-
389- #[ test]
390- fn test_hash_and_is_bundle_and_has_failures_defaults ( ) {
391- let ( block, _) = BlockContext :: < Ethereum > :: mocked ( ) ;
392- let cp = block. start ( ) ;
393367
394368 assert_eq ! ( cp. hash( ) , None ) ;
395369 assert ! ( !cp. is_bundle( ) ) ;
396370 assert ! ( !cp. has_failures( ) ) ;
397371 assert_eq ! ( cp. failed_txs( ) . count( ) , 0 ) ;
372+
373+ let random_addr = Address :: random ( ) ;
374+ assert_eq ! (
375+ cp. balance_of( random_addr) . unwrap( ) ,
376+ U256 :: ZERO ,
377+ "Nonexistent account should have zero balance"
378+ ) ;
379+
380+ assert_eq ! (
381+ cp. nonce_of( random_addr) . unwrap( ) ,
382+ 0 ,
383+ "Nonexistent account should have zero nonce"
384+ ) ;
398385 }
399386
400387 #[ test]
401388 fn test_contains_is_false_without_txs ( ) {
402389 let ( block, _) = BlockContext :: < Ethereum > :: mocked ( ) ;
403- let cp = block. start ( ) ;
390+ let cp1 = Checkpoint :: new_at_block ( block) ;
391+
392+ let tx1 = transfer_tx ( & FundedAccounts :: signer ( 0 ) , 0 , U256 :: from ( 50_000u64 ) ) ;
393+ let tx1_hash = tx1. hash ( ) . clone ( ) ;
394+ assert ! ( !cp1. contains( tx1_hash) ) ;
395+ let cp2 = cp1. apply ( tx1) . unwrap ( ) ;
404396
405- let fake_hash = TxHash :: repeat_byte ( 0x42 ) ;
406- assert ! ( !cp. contains( fake_hash) ) ;
397+ assert ! ( cp2. contains( tx1_hash) ) ;
407398 }
408399
409400 #[ test]
410401 fn test_history_timestamps ( ) {
411402 let ( block, _) = BlockContext :: < Ethereum > :: mocked ( ) ;
412- let cp1 = block. start ( ) ;
403+ let cp1 = Checkpoint :: new_at_block ( block) ;
404+
413405 thread:: sleep ( Duration :: from_millis ( 5 ) ) ;
406+
414407 let cp2 = cp1. barrier ( ) ;
408+
415409 assert ! ( cp2. building_since( ) <= Instant :: now( ) ) ;
416410 assert ! ( cp2. building_since( ) >= cp1. created_at( ) ) ;
417411 }
412+
413+ #[ test]
414+ fn test_to_self ( ) {
415+ let ( block, _) = BlockContext :: < Ethereum > :: mocked ( ) ;
416+ let cp = Checkpoint :: new_at_block ( block) ;
417+
418+ // to(self, self) should produce a span of length 1 containing the
419+ // checkpoint itself
420+ let span = cp. to ( & cp) . expect ( "to(self,self) must succeed" ) ;
421+ assert_eq ! ( span. len( ) , 1 ) ;
422+ assert_eq ! ( * span. at( 0 ) . unwrap( ) , cp) ;
423+ }
424+
425+ #[ test]
426+ fn test_to_non_linear_error ( ) {
427+ let ( block_a, _) = BlockContext :: < Ethereum > :: mocked ( ) ;
428+ let ( block_b, _) = BlockContext :: < Ethereum > :: mocked ( ) ;
429+
430+ let cp_a = Checkpoint :: new_at_block ( block_a) ;
431+ let cp_b = Checkpoint :: new_at_block ( block_b) ;
432+
433+ // They are not on the same linear history, so to should return an Err.
434+ assert ! ( cp_a. to( & cp_b) . is_err( ) ) ;
435+ assert ! ( cp_b. to( & cp_a) . is_err( ) ) ;
436+ }
437+
438+ #[ test]
439+ fn test_to_includes_all_intermediates_and_is_linear ( ) {
440+ let ( block, _) = BlockContext :: < Ethereum > :: mocked ( ) ;
441+ let base = Checkpoint :: new_at_block ( block) ;
442+
443+ // base -> x -> y
444+ let tx_x = transfer_tx ( & FundedAccounts :: signer ( 0 ) , 0 , U256 :: from ( 10u64 ) ) ;
445+ let x = base. apply ( tx_x) . unwrap ( ) ;
446+
447+ let tx_y = transfer_tx ( & FundedAccounts :: signer ( 1 ) , 0 , U256 :: from ( 20u64 ) ) ;
448+ let y = x. apply ( tx_y) . unwrap ( ) ;
449+
450+ let x_barrier = x. barrier ( ) ;
451+ let y_barrier = y. barrier ( ) ;
452+
453+ // `to` between base and y_barrier should include `base``, `x` (or
454+ // x_barrier), `y` (or y_barrier)
455+ let span_by = y_barrier
456+ . to ( & base)
457+ . expect ( "to should succeed for linear history" ) ;
458+ let collected: Vec < Checkpoint < Ethereum > > = ( 0 ..span_by. len ( ) )
459+ . map ( |i| span_by. at ( i) . unwrap ( ) . clone ( ) )
460+ . collect ( ) ;
461+
462+ assert ! ( collected. contains( & base) ) ;
463+ assert ! ( collected. contains( & y_barrier) ) ;
464+ assert ! ( collected. iter( ) . any( |cp| * cp == x || * cp == x_barrier) ) ;
465+ }
466+
467+ #[ test]
468+ fn test_to_different_roots_error ( ) {
469+ let ( block1, _) = BlockContext :: < Ethereum > :: mocked ( ) ;
470+ let ( block2, _) = BlockContext :: < Ethereum > :: mocked ( ) ;
471+
472+ let root1 = Checkpoint :: new_at_block ( block1) ;
473+ let root2 = Checkpoint :: new_at_block ( block2) ;
474+
475+ let tx = transfer_tx ( & FundedAccounts :: signer ( 0 ) , 0 , U256 :: from ( 5u64 ) ) ;
476+ let root1_child = root1. apply ( tx) . unwrap ( ) ;
477+
478+ assert ! ( root1_child. to( & root2) . is_err( ) ) ;
479+ assert ! ( root2. to( & root1_child) . is_err( ) ) ;
480+ }
481+
482+ #[ test]
483+ fn test_effective_tip_checkpoint ( ) {
484+ let ( block, _) = BlockContext :: < Ethereum > :: mocked ( ) ;
485+ let cp = Checkpoint :: new_at_block ( block) ;
486+ assert_eq ! (
487+ cp. effective_tip_per_gas( ) ,
488+ 0 ,
489+ "Empty checkpoint should have zero tip"
490+ ) ;
491+
492+ let tx = transfer_tx ( & FundedAccounts :: signer ( 0 ) , 0 , U256 :: from ( 100u64 ) ) ;
493+ let cp2 = cp. apply ( tx. clone ( ) ) . unwrap ( ) ;
494+
495+ let tip = cp2. effective_tip_per_gas ( ) ;
496+ assert ! ( tip > 0 , "Transaction should have positive effective tip" ) ;
497+ }
498+
499+ #[ test]
500+ fn test_history_staging_no_barrier ( ) {
501+ let ( block, _) = BlockContext :: < Ethereum > :: mocked ( ) ;
502+ let base = Checkpoint :: new_at_block ( block) ;
503+
504+ let tx1 = transfer_tx ( & FundedAccounts :: signer ( 0 ) , 0 , U256 :: from ( 50u64 ) ) ;
505+ let cp1 = base. apply ( tx1) . unwrap ( ) ;
506+
507+ let tx2 = transfer_tx ( & FundedAccounts :: signer ( 1 ) , 0 , U256 :: from ( 75u64 ) ) ;
508+ let cp2 = cp1. apply ( tx2) . unwrap ( ) ;
509+
510+ let staging = cp2. history_staging ( ) ;
511+ let full = cp2. history ( ) ;
512+
513+ assert_eq ! (
514+ staging. len( ) ,
515+ full. len( ) ,
516+ "Without barriers, staging should equal full history"
517+ ) ;
518+ }
519+
520+ #[ test]
521+ fn test_history_staging_with_barrier ( ) {
522+ let ( block, _) = BlockContext :: < Ethereum > :: mocked ( ) ;
523+ let base = Checkpoint :: new_at_block ( block) ;
524+
525+ let tx1 = transfer_tx ( & FundedAccounts :: signer ( 0 ) , 0 , U256 :: from ( 50u64 ) ) ;
526+ let cp1 = base. apply ( tx1) . unwrap ( ) ;
527+
528+ let barrier = cp1. barrier ( ) ;
529+
530+ let tx2 = transfer_tx ( & FundedAccounts :: signer ( 1 ) , 0 , U256 :: from ( 75u64 ) ) ;
531+ let cp2 = barrier. apply ( tx2) . unwrap ( ) ;
532+
533+ let staging = cp2. history_staging ( ) ;
534+
535+ // Staging should only include checkpoints after the barrier
536+ assert ! (
537+ staging. len( ) < cp2. history( ) . len( ) ,
538+ "Staging should be shorter than full history"
539+ ) ;
540+ let sealed = cp2. history_sealed ( ) ;
541+
542+ // Sealed should include everything up to and including the barrier
543+ assert ! (
544+ sealed. len( ) > 0 ,
545+ "Sealed should include checkpoints up to barrier"
546+ ) ;
547+ }
418548}
0 commit comments