@@ -339,9 +339,9 @@ impl CommitFinalizer {
339339 }
340340 }
341341 // Initialize the block state.
342- blocks_map
343- . entry ( block_ref )
344- . or_insert_with ( || RwLock :: new ( BlockState :: new ( block ) ) ) ;
342+ blocks_map. entry ( block_ref ) . or_insert_with ( || {
343+ RwLock :: new ( BlockState :: new ( block , commit_state . commit . commit_ref . index ) )
344+ } ) ;
345345 }
346346 }
347347
@@ -477,6 +477,19 @@ impl CommitFinalizer {
477477 . map ( |( k, v) | ( * k, v. clone ( ) ) )
478478 . collect ( ) ;
479479
480+ let gc_rounds = self
481+ . pending_commits
482+ . iter ( )
483+ . map ( |c| {
484+ (
485+ c. commit . commit_ref . index ,
486+ self . dag_state
487+ . read ( )
488+ . calculate_gc_round ( c. commit . leader . round ) ,
489+ )
490+ } )
491+ . collect :: < Vec < _ > > ( ) ;
492+
480493 // Number of blocks to process in each task.
481494 const BLOCKS_PER_INDIRECT_COMMIT_TASK : usize = 8 ;
482495
@@ -488,6 +501,7 @@ impl CommitFinalizer {
488501 for chunk in pending_blocks. chunks ( BLOCKS_PER_INDIRECT_COMMIT_TASK ) {
489502 let context = self . context . clone ( ) ;
490503 let blocks = self . blocks . clone ( ) ;
504+ let gc_rounds = gc_rounds. clone ( ) ;
491505 let chunk: Vec < ( BlockRef , BTreeSet < TransactionIndex > ) > = chunk. to_vec ( ) ;
492506
493507 join_set. spawn ( tokio:: task:: spawn_blocking ( move || {
@@ -497,6 +511,7 @@ impl CommitFinalizer {
497511 let finalized = Self :: try_indirect_finalize_pending_transactions_in_block (
498512 & context,
499513 & blocks,
514+ & gc_rounds,
500515 block_ref,
501516 pending_transactions,
502517 ) ;
@@ -578,6 +593,7 @@ impl CommitFinalizer {
578593 fn try_indirect_finalize_pending_transactions_in_block (
579594 context : & Arc < Context > ,
580595 blocks : & Arc < RwLock < BTreeMap < BlockRef , RwLock < BlockState > > > > ,
596+ gc_rounds : & [ ( CommitIndex , Round ) ] ,
581597 pending_block_ref : BlockRef ,
582598 pending_transactions : BTreeSet < TransactionIndex > ,
583599 ) -> Vec < TransactionIndex > {
@@ -591,13 +607,11 @@ impl CommitFinalizer {
591607 . collect ( ) ;
592608 let mut finalized_transactions = vec ! [ ] ;
593609 let blocks_map = blocks. read ( ) ;
594- // Use BTreeSet to ensure always visit blocks in the earliest round.
595- let mut to_visit_blocks = blocks_map
596- . get ( & pending_block_ref)
597- . unwrap ( )
598- . read ( )
599- . children
600- . clone ( ) ;
610+ // Use BTreeSet for to_visit_blocks, to visit blocks in the earliest round first.
611+ let ( pending_commit_index, mut to_visit_blocks) = {
612+ let block_state = blocks_map. get ( & pending_block_ref) . unwrap ( ) . read ( ) ;
613+ ( block_state. commit_index , block_state. children . clone ( ) )
614+ } ;
601615 // Blocks that have been visited.
602616 let mut visited = BTreeSet :: new ( ) ;
603617 // Blocks where votes and origin descendants should be ignored for processing.
@@ -608,15 +622,15 @@ impl CommitFinalizer {
608622 continue ;
609623 }
610624 let curr_block_state = blocks_map. get ( & curr_block_ref) . unwrap_or_else ( || panic ! ( "Block {curr_block_ref} is either incorrectly gc'ed or failed to be recovered after crash." ) ) . read ( ) ;
611- // The first ancestor of current block should have the same origin / author as the current block.
612- // If it is not found in the blocks map but have round higher than the pending block, it might have
613- // voted on the pending block but have been GC'ed .
614- // Because the GC'ed block might have voted on the pending block and rejected some of the pending transactions,
615- // we cannot assume current block is voting to accept transactions from the pending block.
616- let curr_origin_ancestor_ref = curr_block_state . block . ancestors ( ) . first ( ) . unwrap ( ) ;
617- let skip_votes = curr_block_ref . author == curr_origin_ancestor_ref . author
618- && pending_block_ref . round < curr_origin_ancestor_ref . round
619- && !blocks_map . contains_key ( curr_origin_ancestor_ref ) ;
625+ // When proposing the current block, if it is possible that the pending block has already been GC'ed,
626+ // then it is possible that the current block does not carry votes for the pending block.
627+ // In this case, skip counting votes from the current block .
628+ let skip_votes = Self :: check_gc_at_current_commit (
629+ gc_rounds ,
630+ pending_block_ref . round ,
631+ pending_commit_index ,
632+ curr_block_state . commit_index ,
633+ ) ;
620634 // Skip counting votes from the block if it has been marked to be ignored.
621635 if ignored. insert ( curr_block_ref) {
622636 // Skip collecting votes from origin descendants of current block.
@@ -683,6 +697,33 @@ impl CommitFinalizer {
683697 finalized_transactions
684698 }
685699
700+ // Returns true if the pending block round can be GC'ed when proposing blocks in current commit.
701+ fn check_gc_at_current_commit (
702+ gc_rounds : & [ ( CommitIndex , Round ) ] ,
703+ pending_block_round : Round ,
704+ pending_commit_index : CommitIndex ,
705+ current_commit_index : CommitIndex ,
706+ ) -> bool {
707+ assert ! (
708+ pending_commit_index <= current_commit_index,
709+ "Pending {pending_commit_index} should be <= current {current_commit_index}"
710+ ) ;
711+ if pending_commit_index == current_commit_index {
712+ return false ;
713+ }
714+ // Since GC round only advances after a commit, when proposing blocks for the current commit,
715+ // the GC round is determined by the commit previous to the current commit.
716+ let ( commit_index, gc_round) = * gc_rounds
717+ . get ( ( current_commit_index - 1 - pending_commit_index) as usize )
718+ . unwrap ( ) ;
719+ assert_eq ! (
720+ commit_index,
721+ current_commit_index - 1 ,
722+ "Commit index mismatch {commit_index} != {current_commit_index}"
723+ ) ;
724+ pending_block_round <= gc_round
725+ }
726+
686727 fn pop_finalized_commits ( & mut self ) -> Vec < CommittedSubDag > {
687728 let mut finalized_commits = vec ! [ ] ;
688729
@@ -800,10 +841,12 @@ struct BlockState {
800841 // Other committed blocks that are origin descendants of this block.
801842 // See the comment above append_origin_descendants_from_last_commit() for more details.
802843 origin_descendants : Vec < BlockRef > ,
844+ // Commit which contains this block.
845+ commit_index : CommitIndex ,
803846}
804847
805848impl BlockState {
806- fn new ( block : VerifiedBlock ) -> Self {
849+ fn new ( block : VerifiedBlock , commit_index : CommitIndex ) -> Self {
807850 let reject_votes: BTreeMap < _ , _ > = block
808851 . transaction_votes ( )
809852 . iter ( )
@@ -817,6 +860,7 @@ impl BlockState {
817860 children : BTreeSet :: new ( ) ,
818861 reject_votes,
819862 origin_descendants,
863+ commit_index,
820864 }
821865 }
822866}
0 commit comments