@@ -112,7 +112,7 @@ where
112112 prometheus:: set_balance ( account_info. data . free as f64 ) ;
113113
114114 let current_block_round = state. round ;
115- let mut rounds_to_remove = Vec :: new ( ) ;
115+ let rounds_to_remove = Vec :: new ( ) ;
116116 let mut rounds_to_potentially_clear = Vec :: new ( ) ;
117117
118118 // Scope to release lock quickly
@@ -128,111 +128,18 @@ where
128128 } // Lock released
129129
130130 // This checking logic runs on EVERY block
131- if !rounds_to_potentially_clear. is_empty ( ) {
132- log:: debug!( target: LOG_TARGET , "Checking status of PAST rounds (< {}): {:?}" , current_block_round, rounds_to_potentially_clear. iter( ) . map( |( r, _, _) | r) . collect:: <Vec <_>>( ) ) ;
133-
134- // Use finalized state for checking past rounds
135- let get_finalized_storage = async {
136- let finalized_hash = client. rpc ( ) . chain_get_finalized_head ( ) . await ?;
137- utils:: storage_at ( Some ( finalized_hash) , client. chain_api ( ) ) . await
138- } ;
139- let storage_finalized = match get_finalized_storage. await {
140- Ok ( s) => s,
141- Err :: < _ , Error > ( e) => {
142- log:: warn!( target: LOG_TARGET , "Failed to get finalized storage state for checking old rounds: {:?}. Skipping check." , e) ;
143- // Skip checking old rounds this iteration if we can't get finalized state
144- rounds_to_potentially_clear. clear ( ) ; // Clear the list to avoid processing below
145- continue ; // Skip the rest of the checks for this block
146- }
147- } ;
148-
149- for ( round_to_check, n_pages, submitted_score) in rounds_to_potentially_clear {
150- // Check if our submission metadata still exists for this PAST round
151- let maybe_submission_metadata = storage_finalized
152- . fetch (
153- & runtime:: storage ( )
154- . multi_block_signed ( )
155- . submission_metadata_storage ( round_to_check, signer. account_id ( ) ) ,
156- )
157- . await ?;
158-
159- if maybe_submission_metadata. is_none ( ) {
160- log:: debug!( target: LOG_TARGET , "Submission metadata for past round {} gone. Removing from tracking." , round_to_check) ;
161- rounds_to_remove. push ( round_to_check) ;
162- continue ; // Move to the next round to check
163- }
164-
165- // Metadata exists for this PAST round. Check phase and potential winning solution.
166- let maybe_round_phase = storage_finalized
167- . fetch ( & runtime:: storage ( ) . multi_block ( ) . current_phase ( ) )
168- . await ?;
169-
170- let mut should_clear = false ;
171- let mut clear_reason = "" ;
172-
173- match maybe_round_phase {
174- Some ( Phase :: SignedValidation ( _) ) => {
175- // Check if a better solution was confirmed during its validation phase
176- let sorted_scores = storage_finalized
177- . fetch_or_default (
178- & runtime:: storage ( )
179- . multi_block_signed ( )
180- . sorted_scores ( round_to_check) ,
181- )
182- . await ?;
183- if let Some ( ( _, winning_score) ) = sorted_scores. 0 . first ( ) {
184- if score_passes_strategy (
185- winning_score. 0 , // Access the inner ElectionScore from Static<ElectionScore>
186- submitted_score,
187- SubmissionStrategy :: IfLeading ,
188- ) && winning_score. 0 != submitted_score
189- {
190- should_clear = true ;
191- clear_reason =
192- "Better solution validated during its SignedValidation phase" ;
193- } else {
194- log:: trace!( target: LOG_TARGET , "Past round {} was in SignedValidation, but no better score confirmed." , round_to_check) ;
195- }
196- } else {
197- log:: trace!( target: LOG_TARGET , "Past round {} was in SignedValidation, but SortedScores is empty." , round_to_check) ;
198- }
199- }
200- Some ( Phase :: Signed ( _) ) => {
201- // Still in Signed phase, should not happen if round_to_check < current_block_round and finalized state is used.
202- log:: trace!( target: LOG_TARGET , "Past round {} unexpectedly found in Signed phase in finalized state." , round_to_check) ;
203- }
204- Some ( _) | None => {
205- should_clear = true ;
206- clear_reason = "Phase is past Signed/SignedValidation or round gone, and metadata still exists" ;
207- }
208- }
209-
210- // Attempt clearing if needed
211- if should_clear {
212- log:: info!( target: LOG_TARGET , "Submission for past round {} detected as DISCARDED (Reason: {}). Attempting to clear." , round_to_check, clear_reason) ;
213- match clear_submission (
214- config. listen ,
215- & client,
216- signer. clone ( ) ,
217- round_to_check,
218- n_pages,
219- )
220- . await
221- {
222- Ok ( _) => {
223- log:: info!( target: LOG_TARGET , "Successfully cleared submission for past round {}" , round_to_check) ;
224- rounds_to_remove. push ( round_to_check) ; // Mark for removal
225- }
226- Err ( e) => {
227- log:: error!( target: LOG_TARGET , "Failed to clear submission for past round {}: {:?}. Will retry." , round_to_check, e) ;
228- // Don't remove, retry next block
229- }
230- }
231- } else {
232- log:: trace!( target: LOG_TARGET , "Conditions not met to clear past round {}." , round_to_check) ;
233- }
234- }
235- }
131+ let rounds_to_potentially_clear = {
132+ let submitted_data = submitted_rounds. lock ( ) . await ;
133+ collect_past_rounds_to_clear ( & submitted_data, current_block_round)
134+ } ;
135+ check_and_clear_discarded_submissions (
136+ & client,
137+ & config,
138+ & signer,
139+ rounds_to_potentially_clear,
140+ & submitted_rounds,
141+ )
142+ . await ?;
236143
237144 // Remove successfully cleared or confirmed non-existent rounds from tracking
238145 if !rounds_to_remove. is_empty ( ) {
@@ -758,3 +665,135 @@ async fn clear_submission(
758665
759666 Ok ( ( ) )
760667}
668+
669+ async fn check_and_clear_discarded_submissions (
670+ client : & Client ,
671+ config : & ExperimentalMultiBlockMonitorConfig ,
672+ signer : & Signer ,
673+ rounds_to_potentially_clear : Vec < ( u32 , u32 , ElectionScore ) > ,
674+ submitted_rounds : & Arc < Mutex < HashMap < u32 , ( u32 , ElectionScore ) > > > ,
675+ ) -> Result < ( ) , Error > {
676+ if rounds_to_potentially_clear. is_empty ( ) {
677+ return Ok ( ( ) ) ;
678+ }
679+
680+ log:: debug!(
681+ target: LOG_TARGET ,
682+ "Checking status of PAST rounds: {:?}" ,
683+ rounds_to_potentially_clear. iter( ) . map( |( r, _, _) | r) . collect:: <Vec <_>>( )
684+ ) ;
685+
686+ let get_finalized_storage = async {
687+ let finalized_hash = client. rpc ( ) . chain_get_finalized_head ( ) . await ?;
688+ utils:: storage_at ( Some ( finalized_hash) , client. chain_api ( ) ) . await
689+ } ;
690+ let storage_finalized = match get_finalized_storage. await {
691+ Ok ( s) => s,
692+ Err ( e) => {
693+ log:: warn!( target: LOG_TARGET , "Failed to get finalized storage state for checking old rounds: {:?}. Skipping check." , e) ;
694+ return Ok ( ( ) ) ;
695+ }
696+ } ;
697+
698+ let mut rounds_to_remove = Vec :: new ( ) ;
699+
700+ for ( round_to_check, n_pages, submitted_score) in rounds_to_potentially_clear {
701+ let maybe_submission_metadata = storage_finalized
702+ . fetch (
703+ & runtime:: storage ( )
704+ . multi_block_signed ( )
705+ . submission_metadata_storage ( round_to_check, signer. account_id ( ) ) ,
706+ )
707+ . await ?;
708+
709+ if maybe_submission_metadata. is_none ( ) {
710+ log:: debug!( target: LOG_TARGET , "Submission metadata for past round {} gone. Removing from tracking." , round_to_check) ;
711+ rounds_to_remove. push ( round_to_check) ;
712+ continue ;
713+ }
714+
715+ let maybe_round_phase = storage_finalized
716+ . fetch ( & runtime:: storage ( ) . multi_block ( ) . current_phase ( ) )
717+ . await ?;
718+
719+ let mut should_clear = false ;
720+ let mut clear_reason = "" ;
721+
722+ match maybe_round_phase {
723+ Some ( Phase :: SignedValidation ( _) ) => {
724+ let sorted_scores = storage_finalized
725+ . fetch_or_default (
726+ & runtime:: storage ( )
727+ . multi_block_signed ( )
728+ . sorted_scores ( round_to_check) ,
729+ )
730+ . await ?;
731+ if let Some ( ( _, winning_score) ) = sorted_scores. 0 . first ( ) {
732+ if score_passes_strategy (
733+ winning_score. 0 ,
734+ submitted_score,
735+ SubmissionStrategy :: IfLeading ,
736+ ) && winning_score. 0 != submitted_score
737+ {
738+ should_clear = true ;
739+ clear_reason =
740+ "Better solution validated during its SignedValidation phase" ;
741+ }
742+ }
743+ }
744+ Some ( Phase :: Signed ( _) ) => { }
745+ Some ( _) | None => {
746+ should_clear = true ;
747+ clear_reason = "Phase is past Signed/SignedValidation or round gone, and metadata still exists" ;
748+ }
749+ }
750+
751+ if should_clear {
752+ log:: info!( target: LOG_TARGET , "Submission for past round {} detected as DISCARDED (Reason: {}). Attempting to clear." , round_to_check, clear_reason) ;
753+ match clear_submission (
754+ config. listen ,
755+ client,
756+ signer. clone ( ) ,
757+ round_to_check,
758+ n_pages,
759+ )
760+ . await
761+ {
762+ Ok ( _) => {
763+ log:: info!( target: LOG_TARGET , "Successfully cleared submission for past round {}" , round_to_check) ;
764+ rounds_to_remove. push ( round_to_check) ;
765+ }
766+ Err ( e) => {
767+ log:: error!( target: LOG_TARGET , "Failed to clear submission for past round {}: {:?}. Will retry." , round_to_check, e) ;
768+ }
769+ }
770+ }
771+ }
772+
773+ if !rounds_to_remove. is_empty ( ) {
774+ let mut submitted_data = submitted_rounds. lock ( ) . await ;
775+ for round_index in rounds_to_remove {
776+ if submitted_data. remove ( & round_index) . is_some ( ) {
777+ log:: info!( target: LOG_TARGET , "Removed past round {} from local tracking." , round_index) ;
778+ }
779+ }
780+ }
781+
782+ Ok ( ( ) )
783+ }
784+
785+ fn collect_past_rounds_to_clear (
786+ submitted_rounds : & HashMap < u32 , ( u32 , ElectionScore ) > ,
787+ current_block_round : u32 ,
788+ ) -> Vec < ( u32 , u32 , ElectionScore ) > {
789+ submitted_rounds
790+ . iter ( )
791+ . filter_map ( |( & round_to_check, & ( n_pages, submitted_score) ) | {
792+ if round_to_check < current_block_round {
793+ Some ( ( round_to_check, n_pages, submitted_score) )
794+ } else {
795+ None
796+ }
797+ } )
798+ . collect ( )
799+ }
0 commit comments