package forkchoice import ( "fmt" "github.com/ledgerwatch/erigon/cl/phase1/core/transition" "github.com/ledgerwatch/erigon/cl/phase1/forkchoice/fork_graph" "github.com/ledgerwatch/erigon/cl/cltypes" "github.com/ledgerwatch/log/v3" ) func (f *ForkChoiceStore) OnBlock(block *cltypes.SignedBeaconBlock, newPayload, fullValidation bool) error { f.mu.Lock() defer f.mu.Unlock() blockRoot, err := block.Block.HashSSZ() if err != nil { return err } if f.Slot() < block.Block.Slot { return fmt.Errorf("block is too early compared to current_slot") } // Check that block is later than the finalized epoch slot (optimization to reduce calls to get_ancestor) finalizedSlot := f.computeStartSlotAtEpoch(f.finalizedCheckpoint.Epoch()) if block.Block.Slot <= finalizedSlot { return nil } config := f.forkGraph.Config() lastProcessedState, status, err := f.forkGraph.AddChainSegment(block, fullValidation) if err != nil { return err } switch status { case fork_graph.PreValidated: return nil case fork_graph.Success: default: return fmt.Errorf("replay block, code: %+v", status) } if newPayload && f.engine != nil { if err := f.engine.NewPayload(block.Block.Body.ExecutionPayload); err != nil { log.Warn("newPayload failed", "err", err) return err } } if block.Block.Body.ExecutionPayload != nil { f.eth2Roots.Add(blockRoot, block.Block.Body.ExecutionPayload.BlockHash) } if block.Block.Slot > f.highestSeen { f.highestSeen = block.Block.Slot } // Add proposer score boost if the block is timely timeIntoSlot := (f.time - f.forkGraph.GenesisTime()) % lastProcessedState.BeaconConfig().SecondsPerSlot isBeforeAttestingInterval := timeIntoSlot < config.SecondsPerSlot/config.IntervalsPerSlot if f.Slot() == block.Block.Slot && isBeforeAttestingInterval { f.proposerBoostRoot = blockRoot } // Update checkpoints f.updateCheckpoints(lastProcessedState.CurrentJustifiedCheckpoint().Copy(), lastProcessedState.FinalizedCheckpoint().Copy()) // First thing save previous values of the checkpoints (avoid memory copy of all states and ensure easy revert) var ( previousJustifiedCheckpoint = lastProcessedState.PreviousJustifiedCheckpoint().Copy() currentJustifiedCheckpoint = lastProcessedState.CurrentJustifiedCheckpoint().Copy() finalizedCheckpoint = lastProcessedState.FinalizedCheckpoint().Copy() justificationBits = lastProcessedState.JustificationBits().Copy() ) // Eagerly compute unrealized justification and finality if err := transition.ProcessJustificationBitsAndFinality(lastProcessedState); err != nil { return err } f.updateUnrealizedCheckpoints(lastProcessedState.CurrentJustifiedCheckpoint().Copy(), lastProcessedState.FinalizedCheckpoint().Copy()) // Set the changed value pre-simulation lastProcessedState.SetPreviousJustifiedCheckpoint(previousJustifiedCheckpoint) lastProcessedState.SetCurrentJustifiedCheckpoint(currentJustifiedCheckpoint) lastProcessedState.SetFinalizedCheckpoint(finalizedCheckpoint) lastProcessedState.SetJustificationBits(justificationBits) // If the block is from a prior epoch, apply the realized values blockEpoch := f.computeEpochAtSlot(block.Block.Slot) currentEpoch := f.computeEpochAtSlot(f.Slot()) if blockEpoch < currentEpoch { f.updateCheckpoints(lastProcessedState.CurrentJustifiedCheckpoint().Copy(), lastProcessedState.FinalizedCheckpoint().Copy()) } return nil }