@@ -290,6 +290,77 @@ impl ProtoArrayForkChoice {
290
290
. map_err ( |e| format ! ( "find_head failed: {:?}" , e) )
291
291
}
292
292
293
+ /// For all nodes, regardless of their relationship to the finalized block, set their execution
294
+ /// status to be optimistic.
295
+ ///
296
+ /// In practice this means forgetting any `VALID` or `INVALID` statuses.
297
+ pub fn set_all_blocks_to_optimistic ( & mut self ) -> Result < ( ) , String > {
298
+ // Iterate backwards through all nodes in the `proto_array`. Whilst it's not strictly
299
+ // required to do this process in reverse, it seems natural when we consider how LMD votes
300
+ // are counted.
301
+ //
302
+ // This function will touch all blocks, even those that do not descend from the finalized
303
+ // block. Since this function is expected to run at start-up during very rare
304
+ // circumstances we prefer simplicity over efficiency.
305
+ for node_index in ( 0 ..self . proto_array . nodes . len ( ) ) . rev ( ) {
306
+ let node = self
307
+ . proto_array
308
+ . nodes
309
+ . get_mut ( node_index)
310
+ . ok_or ( "unreachable index out of bounds in proto_array nodes" ) ?;
311
+
312
+ match node. execution_status {
313
+ ExecutionStatus :: Invalid ( block_hash) => {
314
+ node. execution_status = ExecutionStatus :: Optimistic ( block_hash) ;
315
+
316
+ // Restore the weight of the node, it would have been set to `0` in
317
+ // `apply_score_changes` when it was invalidated.
318
+ let restored_weight = self
319
+ . votes
320
+ . 0
321
+ . iter ( )
322
+ . enumerate ( )
323
+ . filter_map ( |( validator_index, vote) | {
324
+ if vote. current_root == node. root {
325
+ Some ( validator_index)
326
+ } else {
327
+ None
328
+ }
329
+ } )
330
+ // This `filter_map` asserts that any voting validator that does not have a
331
+ // balance should be ignored. This is consistent with `compute_deltas`.
332
+ . filter_map ( |validator_index| self . balances . get ( validator_index) )
333
+ . sum ( ) ;
334
+
335
+ // Add the restored weight to the node and all ancestors.
336
+ if restored_weight > 0 {
337
+ let mut node_or_ancestor = node;
338
+ while let Some ( parent_index) = node_or_ancestor. parent {
339
+ node_or_ancestor
340
+ . weight
341
+ . checked_add ( restored_weight)
342
+ . ok_or ( "Overflow when adding weight to ancestor" ) ?;
343
+ node_or_ancestor = self
344
+ . proto_array
345
+ . nodes
346
+ . get_mut ( parent_index)
347
+ . ok_or ( format ! ( "Missing parent index: {}" , parent_index) ) ?;
348
+ }
349
+ }
350
+ }
351
+ // There are no balance changes required if the node was either valid or
352
+ // optimistic.
353
+ ExecutionStatus :: Valid ( block_hash) | ExecutionStatus :: Optimistic ( block_hash) => {
354
+ node. execution_status = ExecutionStatus :: Optimistic ( block_hash)
355
+ }
356
+ // An irrelevant node cannot become optimistic, this is a no-op.
357
+ ExecutionStatus :: Irrelevant ( _) => ( ) ,
358
+ }
359
+ }
360
+
361
+ Ok ( ( ) )
362
+ }
363
+
293
364
pub fn maybe_prune ( & mut self , finalized_root : Hash256 ) -> Result < ( ) , String > {
294
365
self . proto_array
295
366
. maybe_prune ( finalized_root)
0 commit comments