From 1619512acf9c9db68f7f5ecbe4c5faba6032d62d Mon Sep 17 00:00:00 2001 From: kewei Date: Tue, 11 Aug 2026 16:40:48 +0800 Subject: [PATCH 1/2] cl/sentinel, cl/phase1/forkchoice: serve req/resp chain data from fork_graph, not forkchoice Sentinel used only 7 of ForkChoiceStorageReader's 53 methods; 6 of them were one-line fork_graph re-exports and the 7th (GetPeerDas) fetched a PeerDasStateReader that sentinel already holds. Sentinel now depends on a 6-method ChainDataReader interface satisfied by fork_graph.ForkGraph, so its tests use a small map-backed fake instead of the 571-line ForkChoiceStorageMock, and adding methods to the fork-choice interface no longer touches sentinel. Also removes interface/mock surface with zero external callers (JustifiedSlot, ValidateOnAttestation, ShouldExtendPayload, GetPublicKeyForValidator) and adds a compile-time conformance assertion to ForkChoiceStorageMock. The fork_graph re-exports themselves stay on ForkChoiceStorageReader: beacon/handler and stages still call them; their migration is tracked separately. --- cl/phase1/forkchoice/forkchoice.go | 5 -- cl/phase1/forkchoice/interface.go | 5 -- .../mock_services/forkchoice_mock.go | 26 +------- cl/sentinel/handlers/blobs_test.go | 6 +- cl/sentinel/handlers/blocks.go | 5 +- cl/sentinel/handlers/blocks_by_head.go | 4 +- cl/sentinel/handlers/blocks_by_head_test.go | 16 ++--- cl/sentinel/handlers/blocks_by_range_test.go | 4 +- cl/sentinel/handlers/blocks_by_root_test.go | 4 +- cl/sentinel/handlers/chain_data_reader.go | 33 ++++++++++ .../handlers/data_column_sidecar_test.go | 4 +- .../handlers/execution_payload_envelopes.go | 8 +-- .../execution_payload_envelopes_test.go | 26 ++++---- cl/sentinel/handlers/handlers.go | 7 +- cl/sentinel/handlers/heartbeats_test.go | 12 ++-- cl/sentinel/handlers/light_client.go | 8 +-- cl/sentinel/handlers/light_client_test.go | 10 +-- .../mock_services/chain_data_reader_mock.go | 65 +++++++++++++++++++ .../handlers/rate_limiter_integration_test.go | 6 +- cl/sentinel/sentinel.go | 11 ++-- cl/sentinel/sentinel_requests_test.go | 4 +- cl/sentinel/service/start.go | 10 +-- cmd/caplin/caplin1/run.go | 5 +- 23 files changed, 174 insertions(+), 110 deletions(-) create mode 100644 cl/sentinel/handlers/chain_data_reader.go create mode 100644 cl/sentinel/handlers/mock_services/chain_data_reader_mock.go diff --git a/cl/phase1/forkchoice/forkchoice.go b/cl/phase1/forkchoice/forkchoice.go index 708fbae19ca..04c4f1ac6e2 100644 --- a/cl/phase1/forkchoice/forkchoice.go +++ b/cl/phase1/forkchoice/forkchoice.go @@ -543,11 +543,6 @@ func (f *ForkChoiceStore) JustifiedCheckpoint() solid.Checkpoint { return f.justifiedCheckpoint.Load().(solid.Checkpoint) } -// FinalizedCheckpoint returns justified checkpoint -func (f *ForkChoiceStore) JustifiedSlot() uint64 { - return f.computeStartSlotAtEpoch(f.justifiedCheckpoint.Load().(solid.Checkpoint).Epoch) -} - // getUnrealizedJustification returns the per-block unrealized justified checkpoint // (spec: store.unrealized_justifications[block_root]) func (f *ForkChoiceStore) getUnrealizedJustification(blockRoot common.Hash) (solid.Checkpoint, bool) { diff --git a/cl/phase1/forkchoice/interface.go b/cl/phase1/forkchoice/interface.go index e94aa336463..668bfb8c659 100644 --- a/cl/phase1/forkchoice/interface.go +++ b/cl/phase1/forkchoice/interface.go @@ -48,7 +48,6 @@ type ForkChoiceStorageReader interface { GetHead(auxilliaryState *state.CachingBeaconState) (common.Hash, uint64, error) HighestSeen() uint64 JustifiedCheckpoint() solid.Checkpoint - JustifiedSlot() uint64 ProposerBoostRoot() common.Hash GetStateAtBlockRoot( blockRoot common.Hash, @@ -90,9 +89,6 @@ type ForkChoiceStorageReader interface { // [New in Gloas:EIP7732] GetHeadPayloadStatus returns the payload status of the current // head node (FULL, EMPTY, or PENDING). Must be called after GetHead. GetHeadPayloadStatus() cltypes.PayloadStatus - // [New in Gloas:EIP7732] ShouldExtendPayload returns whether the payload for the given - // root should be extended. Used by prepare_execution_payload to decide FULL vs EMPTY path. - ShouldExtendPayload(root common.Hash) bool // [New in Gloas:EIP7732] ShouldBuildOnFull returns whether the proposer should build on // the full payload for the given head node. Used for proposer reorg of unavailable blocks. ShouldBuildOnFull(head ForkChoiceNode) bool @@ -109,7 +105,6 @@ type ForkChoiceStorageReader interface { GetPendingPartialWithdrawals(blockRoot common.Hash) (*solid.ListSSZ[*solid.PendingPartialWithdrawal], bool) GetProposerLookahead(slot uint64) (solid.Uint64VectorSSZ, bool) - ValidateOnAttestation(attestation *solid.Attestation) error IsRootOptimistic(root common.Hash) bool IsHeadOptimistic() bool GetPeerDas() das.PeerDas diff --git a/cl/phase1/forkchoice/mock_services/forkchoice_mock.go b/cl/phase1/forkchoice/mock_services/forkchoice_mock.go index 5c283b4a6a8..05992cb0672 100644 --- a/cl/phase1/forkchoice/mock_services/forkchoice_mock.go +++ b/cl/phase1/forkchoice/mock_services/forkchoice_mock.go @@ -39,6 +39,8 @@ import ( // Make mocks with maps and simple setters and getters, panic on methods from ForkChoiceStorageWriter +var _ forkchoice.ForkChoiceStorage = (*ForkChoiceStorageMock)(nil) + type ForkChoiceStorageMock struct { Ancestors map[uint64]forkchoice.ForkChoiceNode AnchorSlotVal uint64 @@ -51,7 +53,6 @@ type ForkChoiceStorageMock struct { HeadPayloadStatusVal cltypes.PayloadStatus HighestSeenVal uint64 JustifiedCheckpointVal solid.Checkpoint - JustifiedSlotVal uint64 ProposerBoostRootVal common.Hash SlotVal uint64 TimeVal uint64 @@ -86,8 +87,6 @@ type ForkChoiceStorageMock struct { // Mock for PeerDas MockPeerDas *mock_services.MockPeerDas - ShouldExtendPayloadVal bool - // [New in Gloas:EIP7732] Execution payload status by execution block hash ExecutionPayloadStatusMap map[common.Hash]execution_client.PayloadStatus PayloadStatusByRootMap map[common.Hash]execution_client.PayloadStatus @@ -203,7 +202,6 @@ func NewForkChoiceStorageMock(t *testing.T) *ForkChoiceStorageMock { HeadPayloadStatusVal: cltypes.PayloadStatusFull, HighestSeenVal: 0, JustifiedCheckpointVal: solid.Checkpoint{}, - JustifiedSlotVal: 0, ProposerBoostRootVal: common.Hash{}, SlotVal: 0, TimeVal: 0, @@ -218,7 +216,6 @@ func NewForkChoiceStorageMock(t *testing.T) *ForkChoiceStorageMock { Envelopes: make(map[common.Hash]*cltypes.SignedExecutionPayloadEnvelope), GetBeaconCommitteeMock: nil, Eth1Hashes: make(map[common.Hash]common.Hash), - ShouldExtendPayloadVal: true, SyncContributionPool: makeSyncContributionPoolMock(t), MockPeerDas: mockPeerDas, ExecutionPayloadStatusMap: make(map[common.Hash]execution_client.PayloadStatus), @@ -278,10 +275,6 @@ func (f *ForkChoiceStorageMock) JustifiedCheckpoint() solid.Checkpoint { return f.JustifiedCheckpointVal } -func (f *ForkChoiceStorageMock) JustifiedSlot() uint64 { - return f.JustifiedSlotVal -} - func (f *ForkChoiceStorageMock) ProposerBoostRoot() common.Hash { return f.ProposerBoostRootVal } @@ -462,10 +455,6 @@ func (f *ForkChoiceStorageMock) GetHeadPayloadStatus() cltypes.PayloadStatus { return f.HeadPayloadStatusVal } -func (f *ForkChoiceStorageMock) ShouldExtendPayload(root common.Hash) bool { - return f.ShouldExtendPayloadVal -} - func (f *ForkChoiceStorageMock) ShouldBuildOnFull(head forkchoice.ForkChoiceNode) bool { return true } @@ -498,13 +487,6 @@ func (f *ForkChoiceStorageMock) GetCurrentParticipationIndicies( panic("implement me") } -func (f *ForkChoiceStorageMock) GetPublicKeyForValidator( - blockRoot common.Hash, - idx uint64, -) (common.Bytes48, error) { - panic("implement me") -} - // func (f *ForkChoiceStorageMock) OnSignedContributionAndProof(signedContribution *cltypes.SignedContributionAndProof, test bool) error { // f.SyncContributionPool.AddSyncContribution(nil, signedContribution.Message.Contribution) // return nil @@ -514,10 +496,6 @@ func (f *ForkChoiceStorageMock) AddPreverifiedBlobSidecar(msg *cltypes.BlobSidec return nil } -func (f *ForkChoiceStorageMock) ValidateOnAttestation(attestation *solid.Attestation) error { - panic("implement me") -} - func (f *ForkChoiceStorageMock) ProcessAttestingIndicies( attestation *solid.Attestation, attestionIndicies []uint64, diff --git a/cl/sentinel/handlers/blobs_test.go b/cl/sentinel/handlers/blobs_test.go index 34310e2210c..21902f3417b 100644 --- a/cl/sentinel/handlers/blobs_test.go +++ b/cl/sentinel/handlers/blobs_test.go @@ -37,9 +37,9 @@ import ( "github.com/erigontech/erigon/cl/cltypes" "github.com/erigontech/erigon/cl/cltypes/solid" "github.com/erigontech/erigon/cl/persistence/blob_storage" - "github.com/erigontech/erigon/cl/phase1/forkchoice/mock_services" "github.com/erigontech/erigon/cl/sentinel/communication" "github.com/erigontech/erigon/cl/sentinel/communication/ssz_snappy" + "github.com/erigontech/erigon/cl/sentinel/handlers/mock_services" "github.com/erigontech/erigon/cl/sentinel/peers" "github.com/erigontech/erigon/cl/utils" "github.com/erigontech/erigon/common" @@ -113,7 +113,7 @@ func TestBlobsByRangeHandler(t *testing.T) { nil, beaconCfg, ethClock, - nil, &mock_services.ForkChoiceStorageMock{}, blobStorage, nil, nil, true, + nil, &mock_services.ChainDataReaderMock{}, blobStorage, nil, nil, true, ) c.Start() req := &cltypes.BlobsByRangeRequest{ @@ -236,7 +236,7 @@ func TestBlobsByIdentifiersHandler(t *testing.T) { nil, beaconCfg, ethClock, - nil, &mock_services.ForkChoiceStorageMock{}, blobStorage, nil, nil, true, + nil, &mock_services.ChainDataReaderMock{}, blobStorage, nil, nil, true, ) c.Start() req := solid.NewStaticListSSZ[*cltypes.BlobIdentifier](40269, 40) diff --git a/cl/sentinel/handlers/blocks.go b/cl/sentinel/handlers/blocks.go index 0f9078d6b6c..29a2cab8ce8 100644 --- a/cl/sentinel/handlers/blocks.go +++ b/cl/sentinel/handlers/blocks.go @@ -121,10 +121,9 @@ func (c *ConsensusHandlers) beaconBlocksByRootHandler(s network.Stream) error { if err != nil { return false } - // If the block is not in the database, check the fork choice store. // Recently received blocks (e.g. via gossip) may not have been persisted yet. - if block == nil && c.forkChoiceReader != nil { - block, _ = c.forkChoiceReader.GetBlock(blockRoot) + if block == nil && c.chainDataReader != nil { + block, _ = c.chainDataReader.GetBlock(blockRoot) } if block == nil { log.Debug("[Sentinel] beaconBlocksByRoot: block not found", "root", blockRoot) diff --git a/cl/sentinel/handlers/blocks_by_head.go b/cl/sentinel/handlers/blocks_by_head.go index fb64b0f10f3..b629d8d0027 100644 --- a/cl/sentinel/handlers/blocks_by_head.go +++ b/cl/sentinel/handlers/blocks_by_head.go @@ -52,8 +52,8 @@ func (c *ConsensusHandlers) beaconBlocksByHeadHandler(s network.Stream) error { if err != nil { return err } - if block == nil && c.forkChoiceReader != nil { - block, _ = c.forkChoiceReader.GetBlock(currentRoot) + if block == nil && c.chainDataReader != nil { + block, _ = c.chainDataReader.GetBlock(currentRoot) } if block == nil { log.Debug("[Sentinel] beaconBlocksByHead: block not found", "root", currentRoot) diff --git a/cl/sentinel/handlers/blocks_by_head_test.go b/cl/sentinel/handlers/blocks_by_head_test.go index e03c83b8dee..3295f99a5a3 100644 --- a/cl/sentinel/handlers/blocks_by_head_test.go +++ b/cl/sentinel/handlers/blocks_by_head_test.go @@ -34,9 +34,9 @@ import ( "github.com/erigontech/erigon/cl/antiquary/tests" "github.com/erigontech/erigon/cl/clparams" "github.com/erigontech/erigon/cl/cltypes" - "github.com/erigontech/erigon/cl/phase1/forkchoice/mock_services" "github.com/erigontech/erigon/cl/sentinel/communication" "github.com/erigontech/erigon/cl/sentinel/communication/ssz_snappy" + "github.com/erigontech/erigon/cl/sentinel/handlers/mock_services" "github.com/erigontech/erigon/cl/sentinel/peers" "github.com/erigontech/erigon/cl/utils" "github.com/erigontech/erigon/common" @@ -78,9 +78,9 @@ func TestBlocksByHeadParentChainTraversal(t *testing.T) { func TestBlocksByHeadForkChoiceFallback(t *testing.T) { blocks, roots := makeBlocksByHeadChain(t, 300, 1) - forkChoice := mock_services.NewForkChoiceStorageMock(t) - forkChoice.Blocks[roots[0]] = blocks[0] - _, stream := setupBlocksByHeadTest(t, nil, forkChoice) + chainData := mock_services.NewChainDataReaderMock() + chainData.Blocks[roots[0]] = blocks[0] + _, stream := setupBlocksByHeadTest(t, nil, chainData) writeBlocksByHeadRequest(t, stream, roots[0], 1) @@ -109,7 +109,7 @@ func TestBlocksByHeadZeroCount(t *testing.T) { func setupBlocksByHeadTest( t *testing.T, blocks []*cltypes.SignedBeaconBlock, - forkChoice *mock_services.ForkChoiceStorageMock, + chainData *mock_services.ChainDataReaderMock, ) (*tests.MockBlockReader, network.Stream) { t.Helper() @@ -136,8 +136,8 @@ func setupBlocksByHeadTest( for _, block := range blocks { store.U[block.Block.Slot] = block } - if forkChoice == nil { - forkChoice = mock_services.NewForkChoiceStorageMock(t) + if chainData == nil { + chainData = mock_services.NewChainDataReaderMock() } ethClock := getEthClock(t) @@ -152,7 +152,7 @@ func setupBlocksByHeadTest( nil, beaconCfg, ethClock, - nil, forkChoice, nil, nil, nil, true, + nil, chainData, nil, nil, nil, true, ) c.Start() diff --git a/cl/sentinel/handlers/blocks_by_range_test.go b/cl/sentinel/handlers/blocks_by_range_test.go index 2dddda52d0a..da0a392bead 100644 --- a/cl/sentinel/handlers/blocks_by_range_test.go +++ b/cl/sentinel/handlers/blocks_by_range_test.go @@ -33,9 +33,9 @@ import ( "github.com/erigontech/erigon/cl/antiquary/tests" "github.com/erigontech/erigon/cl/clparams" "github.com/erigontech/erigon/cl/cltypes" - "github.com/erigontech/erigon/cl/phase1/forkchoice/mock_services" "github.com/erigontech/erigon/cl/sentinel/communication" "github.com/erigontech/erigon/cl/sentinel/communication/ssz_snappy" + "github.com/erigontech/erigon/cl/sentinel/handlers/mock_services" "github.com/erigontech/erigon/cl/sentinel/peers" "github.com/erigontech/erigon/cl/utils" ) @@ -84,7 +84,7 @@ func TestBlocksByRootHandler(t *testing.T) { nil, beaconCfg, ethClock, - nil, &mock_services.ForkChoiceStorageMock{}, nil, nil, nil, true, + nil, &mock_services.ChainDataReaderMock{}, nil, nil, nil, true, ) c.Start() req := &cltypes.BeaconBlocksByRangeRequest{ diff --git a/cl/sentinel/handlers/blocks_by_root_test.go b/cl/sentinel/handlers/blocks_by_root_test.go index 0b7879137f5..0458b335f90 100644 --- a/cl/sentinel/handlers/blocks_by_root_test.go +++ b/cl/sentinel/handlers/blocks_by_root_test.go @@ -35,9 +35,9 @@ import ( "github.com/erigontech/erigon/cl/cltypes" "github.com/erigontech/erigon/cl/cltypes/solid" "github.com/erigontech/erigon/cl/persistence/beacon_indicies" - "github.com/erigontech/erigon/cl/phase1/forkchoice/mock_services" "github.com/erigontech/erigon/cl/sentinel/communication" "github.com/erigontech/erigon/cl/sentinel/communication/ssz_snappy" + "github.com/erigontech/erigon/cl/sentinel/handlers/mock_services" "github.com/erigontech/erigon/cl/sentinel/peers" "github.com/erigontech/erigon/cl/utils" "github.com/erigontech/erigon/common" @@ -88,7 +88,7 @@ func TestBlocksByRangeHandler(t *testing.T) { nil, beaconCfg, ethClock, - nil, &mock_services.ForkChoiceStorageMock{}, nil, nil, nil, true, + nil, &mock_services.ChainDataReaderMock{}, nil, nil, nil, true, ) c.Start() var req solid.HashListSSZ = solid.NewHashList(len(expBlocks)) diff --git a/cl/sentinel/handlers/chain_data_reader.go b/cl/sentinel/handlers/chain_data_reader.go new file mode 100644 index 00000000000..072f82d57c6 --- /dev/null +++ b/cl/sentinel/handlers/chain_data_reader.go @@ -0,0 +1,33 @@ +// Copyright 2026 The Erigon Authors +// This file is part of Erigon. +// +// Erigon is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Erigon is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Erigon. If not, see . + +package handlers + +import ( + "github.com/erigontech/erigon/cl/cltypes" + "github.com/erigontech/erigon/common" +) + +// ChainDataReader is the chain data the req/resp handlers serve to peers: +// recent blocks, light-client objects and execution payload envelopes. +type ChainDataReader interface { + GetBlock(blockRoot common.Hash) (*cltypes.SignedBeaconBlock, bool) + GetLightClientBootstrap(blockRoot common.Hash) (*cltypes.LightClientBootstrap, bool) + NewestLightClientUpdate() *cltypes.LightClientUpdate + GetLightClientUpdate(period uint64) (*cltypes.LightClientUpdate, bool) + HasEnvelope(blockRoot common.Hash) bool + ReadEnvelopeFromDisk(blockRoot common.Hash) (*cltypes.SignedExecutionPayloadEnvelope, error) +} diff --git a/cl/sentinel/handlers/data_column_sidecar_test.go b/cl/sentinel/handlers/data_column_sidecar_test.go index 2970df3858c..a27e9584a61 100644 --- a/cl/sentinel/handlers/data_column_sidecar_test.go +++ b/cl/sentinel/handlers/data_column_sidecar_test.go @@ -39,9 +39,9 @@ import ( "github.com/erigontech/erigon/cl/cltypes" "github.com/erigontech/erigon/cl/cltypes/solid" "github.com/erigontech/erigon/cl/persistence/blob_storage" - "github.com/erigontech/erigon/cl/phase1/forkchoice/mock_services" "github.com/erigontech/erigon/cl/sentinel/communication" "github.com/erigontech/erigon/cl/sentinel/communication/ssz_snappy" + "github.com/erigontech/erigon/cl/sentinel/handlers/mock_services" "github.com/erigontech/erigon/cl/sentinel/peers" "github.com/erigontech/erigon/common" "github.com/erigontech/erigon/db/kv" @@ -481,7 +481,7 @@ func setupDataColumnSidecarHandlerTestWithStore(t *testing.T, fuluForkEpoch uint nil, &beaconCfg, ethClock, - nil, &mock_services.ForkChoiceStorageMock{}, nil, columnStorage, nil, true, + nil, &mock_services.ChainDataReaderMock{}, nil, columnStorage, nil, true, ) c.Start() diff --git a/cl/sentinel/handlers/execution_payload_envelopes.go b/cl/sentinel/handlers/execution_payload_envelopes.go index a6804309700..73d6cee9232 100644 --- a/cl/sentinel/handlers/execution_payload_envelopes.go +++ b/cl/sentinel/handlers/execution_payload_envelopes.go @@ -102,11 +102,11 @@ func (c *ConsensusHandlers) executionPayloadEnvelopesByRangeHandler(s network.St continue } - if !c.forkChoiceReader.HasEnvelope(blockRoot) { + if !c.chainDataReader.HasEnvelope(blockRoot) { continue } - envelope, err := c.forkChoiceReader.ReadEnvelopeFromDisk(blockRoot) + envelope, err := c.chainDataReader.ReadEnvelopeFromDisk(blockRoot) if err != nil { log.Debug("failed to read envelope from disk", "blockRoot", blockRoot, "error", err) continue @@ -208,11 +208,11 @@ func (c *ConsensusHandlers) executionPayloadEnvelopesByRootHandler(s network.Str return true } - if !c.forkChoiceReader.HasEnvelope(blockRoot) { + if !c.chainDataReader.HasEnvelope(blockRoot) { return true } - envelope, err := c.forkChoiceReader.ReadEnvelopeFromDisk(blockRoot) + envelope, err := c.chainDataReader.ReadEnvelopeFromDisk(blockRoot) if err != nil { log.Debug("failed to read envelope from disk", "blockRoot", blockRoot, "error", err) return true diff --git a/cl/sentinel/handlers/execution_payload_envelopes_test.go b/cl/sentinel/handlers/execution_payload_envelopes_test.go index a88b8247e12..529712a5ed9 100644 --- a/cl/sentinel/handlers/execution_payload_envelopes_test.go +++ b/cl/sentinel/handlers/execution_payload_envelopes_test.go @@ -18,9 +18,9 @@ import ( "github.com/erigontech/erigon/cl/clparams/initial_state" "github.com/erigontech/erigon/cl/cltypes" "github.com/erigontech/erigon/cl/cltypes/solid" - "github.com/erigontech/erigon/cl/phase1/forkchoice/mock_services" "github.com/erigontech/erigon/cl/sentinel/communication" "github.com/erigontech/erigon/cl/sentinel/communication/ssz_snappy" + "github.com/erigontech/erigon/cl/sentinel/handlers/mock_services" "github.com/erigontech/erigon/cl/sentinel/peers" "github.com/erigontech/erigon/cl/utils" "github.com/erigontech/erigon/cl/utils/eth_clock" @@ -86,7 +86,7 @@ func TestExecutionPayloadEnvelopesByRangeHandler(t *testing.T) { require.NoError(t, tx.Commit()) // Create mock fork choice with envelopes - fcMock := mock_services.NewForkChoiceStorageMock(t) + chainDataMock := mock_services.NewChainDataReaderMock() // Create envelopes for each block and store them in mock. // The canonical block root is HashSSZ(header), computed by WriteBeaconBlockHeaderAndIndicies. @@ -124,7 +124,7 @@ func TestExecutionPayloadEnvelopesByRangeHandler(t *testing.T) { envelope.Message.BeaconBlockRoot = blockRoot envelope.Message.BuilderIndex = uint64(i) - fcMock.Envelopes[blockRoot] = envelope + chainDataMock.Envelopes[blockRoot] = envelope expEnvelopes = append(expEnvelopes, envelope) } @@ -138,7 +138,7 @@ func TestExecutionPayloadEnvelopesByRangeHandler(t *testing.T) { nil, beaconCfg, ethClock, - nil, fcMock, nil, nil, nil, true, + nil, chainDataMock, nil, nil, nil, true, ) c.Start() @@ -244,7 +244,7 @@ func TestExecutionPayloadEnvelopesByRootHandler(t *testing.T) { expBlocks := populateDatabaseWithBlocks(t, store, tx, startSlot, count) require.NoError(t, tx.Commit()) - fcMock := mock_services.NewForkChoiceStorageMock(t) + chainDataMock := mock_services.NewChainDataReaderMock() // Create envelopes keyed by block root expEnvelopes := make([]*cltypes.SignedExecutionPayloadEnvelope, 0, count) @@ -279,7 +279,7 @@ func TestExecutionPayloadEnvelopesByRootHandler(t *testing.T) { envelope.Message.BeaconBlockRoot = blockRoot envelope.Message.BuilderIndex = uint64(i) - fcMock.Envelopes[blockRoot] = envelope + chainDataMock.Envelopes[blockRoot] = envelope expEnvelopes = append(expEnvelopes, envelope) blockRoots = append(blockRoots, blockRoot) } @@ -294,7 +294,7 @@ func TestExecutionPayloadEnvelopesByRootHandler(t *testing.T) { nil, beaconCfg, ethClock, - nil, fcMock, nil, nil, nil, true, + nil, chainDataMock, nil, nil, nil, true, ) c.Start() @@ -380,7 +380,7 @@ func TestExecutionPayloadEnvelopesByRootHandler_PreGloas(t *testing.T) { ethClock := getEthClock(t) _, beaconCfg := clparams.GetConfigsByNetwork(1) - fcMock := mock_services.NewForkChoiceStorageMock(t) + chainDataMock := mock_services.NewChainDataReaderMock() c := NewConsensusHandlers( ctx, @@ -392,7 +392,7 @@ func TestExecutionPayloadEnvelopesByRootHandler_PreGloas(t *testing.T) { nil, beaconCfg, ethClock, - nil, fcMock, nil, nil, nil, true, + nil, chainDataMock, nil, nil, nil, true, ) c.Start() @@ -434,7 +434,7 @@ func TestExecutionPayloadEnvelopesByRootHandlerRejectsOverLimit(t *testing.T) { store := tests.NewMockBlockReader() ethClock, beaconCfg := getGloasEthClockAndConfig(t) beaconCfg.MaxRequestPayloads = 1 - fcMock := mock_services.NewForkChoiceStorageMock(t) + chainDataMock := mock_services.NewChainDataReaderMock() c := NewConsensusHandlers( ctx, @@ -446,7 +446,7 @@ func TestExecutionPayloadEnvelopesByRootHandlerRejectsOverLimit(t *testing.T) { nil, beaconCfg, ethClock, - nil, fcMock, nil, nil, nil, true, + nil, chainDataMock, nil, nil, nil, true, ) c.Start() @@ -492,7 +492,7 @@ func TestExecutionPayloadEnvelopesByRangeHandler_PreGloas(t *testing.T) { ethClock := getEthClock(t) _, beaconCfg := clparams.GetConfigsByNetwork(1) - fcMock := mock_services.NewForkChoiceStorageMock(t) + chainDataMock := mock_services.NewChainDataReaderMock() c := NewConsensusHandlers( ctx, @@ -504,7 +504,7 @@ func TestExecutionPayloadEnvelopesByRangeHandler_PreGloas(t *testing.T) { nil, beaconCfg, ethClock, - nil, fcMock, nil, nil, nil, true, + nil, chainDataMock, nil, nil, nil, true, ) c.Start() diff --git a/cl/sentinel/handlers/handlers.go b/cl/sentinel/handlers/handlers.go index 1cde5dd12df..b6aa66011eb 100644 --- a/cl/sentinel/handlers/handlers.go +++ b/cl/sentinel/handlers/handlers.go @@ -29,7 +29,6 @@ import ( "github.com/erigontech/erigon/cl/clparams" peerdasstate "github.com/erigontech/erigon/cl/das/state" "github.com/erigontech/erigon/cl/persistence/blob_storage" - "github.com/erigontech/erigon/cl/phase1/forkchoice" "github.com/erigontech/erigon/cl/sentinel/communication" "github.com/erigontech/erigon/cl/sentinel/communication/ssz_snappy" "github.com/erigontech/erigon/cl/sentinel/handshake" @@ -53,7 +52,7 @@ type ConsensusHandlers struct { indiciesDB kv.RoDB rateLimiter *peerRateLimiter - forkChoiceReader forkchoice.ForkChoiceStorageReader + chainDataReader ChainDataReader host host.Host me *enode.LocalNode netCfg *clparams.NetworkConfig @@ -81,7 +80,7 @@ func NewConsensusHandlers( beaconConfig *clparams.BeaconChainConfig, ethClock eth_clock.EthereumClock, hs *handshake.HandShaker, - forkChoiceReader forkchoice.ForkChoiceStorageReader, + chainDataReader ChainDataReader, blobsStorage blob_storage.BlobStorage, dataColumnStorage blob_storage.DataColumnStorage, peerDasStateReader peerdasstate.PeerDasStateReader, @@ -97,7 +96,7 @@ func NewConsensusHandlers( ctx: ctx, rateLimiter: newPeerRateLimiter(), enableBlocks: enabledBlocks, - forkChoiceReader: forkChoiceReader, + chainDataReader: chainDataReader, me: me, netCfg: netCfg, blobsStorage: blobsStorage, diff --git a/cl/sentinel/handlers/heartbeats_test.go b/cl/sentinel/handlers/heartbeats_test.go index 7b75bbf8db4..816aa67bfd6 100644 --- a/cl/sentinel/handlers/heartbeats_test.go +++ b/cl/sentinel/handlers/heartbeats_test.go @@ -32,9 +32,9 @@ import ( "github.com/erigontech/erigon/cl/clparams" "github.com/erigontech/erigon/cl/cltypes" peerdasstatemock "github.com/erigontech/erigon/cl/das/state/mock_services" - forkchoicemock "github.com/erigontech/erigon/cl/phase1/forkchoice/mock_services" "github.com/erigontech/erigon/cl/sentinel/communication" "github.com/erigontech/erigon/cl/sentinel/communication/ssz_snappy" + "github.com/erigontech/erigon/cl/sentinel/handlers/mock_services" "github.com/erigontech/erigon/cl/sentinel/handshake" "github.com/erigontech/erigon/cl/sentinel/peers" "github.com/erigontech/erigon/common" @@ -90,7 +90,7 @@ func TestPing(t *testing.T) { peersPool := peers.NewPool(host) beaconDB, indiciesDB := setupStore(t) - f := forkchoicemock.NewForkChoiceStorageMock(t) + f := mock_services.NewChainDataReaderMock() ethClock := getEthClock(t) _, beaconCfg := clparams.GetConfigsByNetwork(1) @@ -145,7 +145,7 @@ func TestGoodbye(t *testing.T) { peersPool := peers.NewPool(host) beaconDB, indiciesDB := setupStore(t) - f := forkchoicemock.NewForkChoiceStorageMock(t) + f := mock_services.NewChainDataReaderMock() ethClock := getEthClock(t) _, beaconCfg := clparams.GetConfigsByNetwork(1) c := NewConsensusHandlers( @@ -205,7 +205,7 @@ func TestMetadataV2(t *testing.T) { peersPool := peers.NewPool(host) beaconDB, indiciesDB := setupStore(t) - f := forkchoicemock.NewForkChoiceStorageMock(t) + f := mock_services.NewChainDataReaderMock() ethClock := getEthClock(t) nc := clparams.NetworkConfigs[chainspec.MainnetChainID] _, beaconCfg := clparams.GetConfigsByNetwork(1) @@ -263,7 +263,7 @@ func TestMetadataV1(t *testing.T) { peersPool := peers.NewPool(host) beaconDB, indiciesDB := setupStore(t) - f := forkchoicemock.NewForkChoiceStorageMock(t) + f := mock_services.NewChainDataReaderMock() nc := clparams.NetworkConfigs[chainspec.MainnetChainID] ethClock := getEthClock(t) @@ -321,7 +321,7 @@ func TestStatus(t *testing.T) { peersPool := peers.NewPool(host) beaconDB, indiciesDB := setupStore(t) - f := forkchoicemock.NewForkChoiceStorageMock(t) + f := mock_services.NewChainDataReaderMock() // Create mock for PeerDasStateReader ctrl := gomock.NewController(t) diff --git a/cl/sentinel/handlers/light_client.go b/cl/sentinel/handlers/light_client.go index 3ae272f7d6b..31b0e774449 100644 --- a/cl/sentinel/handlers/light_client.go +++ b/cl/sentinel/handlers/light_client.go @@ -26,7 +26,7 @@ import ( const maxLightClientsPerRequest = 100 func (c *ConsensusHandlers) optimisticLightClientUpdateHandler(s network.Stream) error { - lc := c.forkChoiceReader.NewestLightClientUpdate() + lc := c.chainDataReader.NewestLightClientUpdate() if lc == nil { return ssz_snappy.EncodeAndWrite(s, &emptyString{}, ResourceUnavailablePrefix) } @@ -47,7 +47,7 @@ func (c *ConsensusHandlers) optimisticLightClientUpdateHandler(s network.Stream) } func (c *ConsensusHandlers) finalityLightClientUpdateHandler(s network.Stream) error { - lc := c.forkChoiceReader.NewestLightClientUpdate() + lc := c.chainDataReader.NewestLightClientUpdate() if lc == nil { return ssz_snappy.EncodeAndWrite(s, &emptyString{}, ResourceUnavailablePrefix) } @@ -74,7 +74,7 @@ func (c *ConsensusHandlers) lightClientBootstrapHandler(s network.Stream) error return err } - lc, has := c.forkChoiceReader.GetLightClientBootstrap(root.Root) + lc, has := c.chainDataReader.GetLightClientBootstrap(root.Root) if !has { return ssz_snappy.EncodeAndWrite(s, &emptyString{}, ResourceUnavailablePrefix) } @@ -112,7 +112,7 @@ func (c *ConsensusHandlers) lightClientUpdatesByRangeHandler(s network.Stream) e notFoundPrev := false // Fetch from [start_period, start_period + count] for i := req.StartPeriod; i < endPeriod; i++ { - update, has := c.forkChoiceReader.GetLightClientUpdate(i) + update, has := c.chainDataReader.GetLightClientUpdate(i) if !has { notFoundPrev = true continue diff --git a/cl/sentinel/handlers/light_client_test.go b/cl/sentinel/handlers/light_client_test.go index d6a0f6328a2..97cde845076 100644 --- a/cl/sentinel/handlers/light_client_test.go +++ b/cl/sentinel/handlers/light_client_test.go @@ -33,9 +33,9 @@ import ( "github.com/erigontech/erigon/cl/clparams" "github.com/erigontech/erigon/cl/cltypes" "github.com/erigontech/erigon/cl/cltypes/solid" - "github.com/erigontech/erigon/cl/phase1/forkchoice/mock_services" "github.com/erigontech/erigon/cl/sentinel/communication" "github.com/erigontech/erigon/cl/sentinel/communication/ssz_snappy" + "github.com/erigontech/erigon/cl/sentinel/handlers/mock_services" "github.com/erigontech/erigon/cl/sentinel/peers" "github.com/erigontech/erigon/cl/utils" "github.com/erigontech/erigon/common" @@ -63,7 +63,7 @@ func TestLightClientOptimistic(t *testing.T) { peersPool := peers.NewPool(host) beaconDB, indiciesDB := setupStore(t) - f := mock_services.NewForkChoiceStorageMock(t) + f := mock_services.NewChainDataReaderMock() f.NewestLCUpdate = &cltypes.LightClientUpdate{ AttestedHeader: cltypes.NewLightClientHeader(clparams.AltairVersion), @@ -134,7 +134,7 @@ func TestLightClientFinality(t *testing.T) { peersPool := peers.NewPool(host) beaconDB, indiciesDB := setupStore(t) - f := mock_services.NewForkChoiceStorageMock(t) + f := mock_services.NewChainDataReaderMock() f.NewestLCUpdate = &cltypes.LightClientUpdate{ AttestedHeader: cltypes.NewLightClientHeader(clparams.AltairVersion), @@ -207,7 +207,7 @@ func TestLightClientBootstrap(t *testing.T) { peersPool := peers.NewPool(host) beaconDB, indiciesDB := setupStore(t) - f := mock_services.NewForkChoiceStorageMock(t) + f := mock_services.NewChainDataReaderMock() f.NewestLCUpdate = &cltypes.LightClientUpdate{ AttestedHeader: cltypes.NewLightClientHeader(clparams.AltairVersion), @@ -290,7 +290,7 @@ func TestLightClientUpdates(t *testing.T) { peersPool := peers.NewPool(host) beaconDB, indiciesDB := setupStore(t) - f := mock_services.NewForkChoiceStorageMock(t) + f := mock_services.NewChainDataReaderMock() ethClock := getEthClock(t) _, beaconCfg := clparams.GetConfigsByNetwork(1) diff --git a/cl/sentinel/handlers/mock_services/chain_data_reader_mock.go b/cl/sentinel/handlers/mock_services/chain_data_reader_mock.go new file mode 100644 index 00000000000..debe214820f --- /dev/null +++ b/cl/sentinel/handlers/mock_services/chain_data_reader_mock.go @@ -0,0 +1,65 @@ +// Copyright 2026 The Erigon Authors +// This file is part of Erigon. +// +// Erigon is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Erigon is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Erigon. If not, see . + +package mock_services + +import ( + "github.com/erigontech/erigon/cl/cltypes" + "github.com/erigontech/erigon/common" +) + +// ChainDataReaderMock is a map-backed fake for handlers.ChainDataReader. +type ChainDataReaderMock struct { + Blocks map[common.Hash]*cltypes.SignedBeaconBlock + LightClientBootstraps map[common.Hash]*cltypes.LightClientBootstrap + NewestLCUpdate *cltypes.LightClientUpdate + LCUpdates map[uint64]*cltypes.LightClientUpdate + Envelopes map[common.Hash]*cltypes.SignedExecutionPayloadEnvelope +} + +func NewChainDataReaderMock() *ChainDataReaderMock { + return &ChainDataReaderMock{ + Blocks: make(map[common.Hash]*cltypes.SignedBeaconBlock), + LightClientBootstraps: make(map[common.Hash]*cltypes.LightClientBootstrap), + LCUpdates: make(map[uint64]*cltypes.LightClientUpdate), + Envelopes: make(map[common.Hash]*cltypes.SignedExecutionPayloadEnvelope), + } +} + +func (m *ChainDataReaderMock) GetBlock(blockRoot common.Hash) (*cltypes.SignedBeaconBlock, bool) { + return m.Blocks[blockRoot], m.Blocks[blockRoot] != nil +} + +func (m *ChainDataReaderMock) GetLightClientBootstrap(blockRoot common.Hash) (*cltypes.LightClientBootstrap, bool) { + return m.LightClientBootstraps[blockRoot], m.LightClientBootstraps[blockRoot] != nil +} + +func (m *ChainDataReaderMock) NewestLightClientUpdate() *cltypes.LightClientUpdate { + return m.NewestLCUpdate +} + +func (m *ChainDataReaderMock) GetLightClientUpdate(period uint64) (*cltypes.LightClientUpdate, bool) { + return m.LCUpdates[period], m.LCUpdates[period] != nil +} + +func (m *ChainDataReaderMock) HasEnvelope(blockRoot common.Hash) bool { + _, ok := m.Envelopes[blockRoot] + return ok +} + +func (m *ChainDataReaderMock) ReadEnvelopeFromDisk(blockRoot common.Hash) (*cltypes.SignedExecutionPayloadEnvelope, error) { + return m.Envelopes[blockRoot], nil +} diff --git a/cl/sentinel/handlers/rate_limiter_integration_test.go b/cl/sentinel/handlers/rate_limiter_integration_test.go index a85207512e7..c253a3452e7 100644 --- a/cl/sentinel/handlers/rate_limiter_integration_test.go +++ b/cl/sentinel/handlers/rate_limiter_integration_test.go @@ -31,9 +31,9 @@ import ( "github.com/erigontech/erigon/cl/antiquary/tests" "github.com/erigontech/erigon/cl/clparams" "github.com/erigontech/erigon/cl/cltypes" - "github.com/erigontech/erigon/cl/phase1/forkchoice/mock_services" "github.com/erigontech/erigon/cl/sentinel/communication" "github.com/erigontech/erigon/cl/sentinel/communication/ssz_snappy" + "github.com/erigontech/erigon/cl/sentinel/handlers/mock_services" "github.com/erigontech/erigon/cl/sentinel/peers" ) @@ -62,7 +62,7 @@ func TestPingRateLimit(t *testing.T) { c := NewConsensusHandlers( ctx, beaconDB, indiciesDB, server, peersPool, &clparams.NetworkConfig{}, testLocalNode(t), beaconCfg, ethClock, - nil, &mock_services.ForkChoiceStorageMock{}, nil, nil, nil, true, + nil, &mock_services.ChainDataReaderMock{}, nil, nil, nil, true, ) c.Start() @@ -128,7 +128,7 @@ func TestBlocksByRangeRateLimit(t *testing.T) { c := NewConsensusHandlers( ctx, store, indiciesDB, server, peersPool, &clparams.NetworkConfig{}, nil, beaconCfg, ethClock, - nil, &mock_services.ForkChoiceStorageMock{}, nil, nil, nil, true, + nil, &mock_services.ChainDataReaderMock{}, nil, nil, nil, true, ) c.Start() diff --git a/cl/sentinel/sentinel.go b/cl/sentinel/sentinel.go index b0829241801..e381a2b6626 100644 --- a/cl/sentinel/sentinel.go +++ b/cl/sentinel/sentinel.go @@ -36,7 +36,6 @@ import ( peerdasstate "github.com/erigontech/erigon/cl/das/state" "github.com/erigontech/erigon/cl/p2p" "github.com/erigontech/erigon/cl/persistence/blob_storage" - "github.com/erigontech/erigon/cl/phase1/forkchoice" "github.com/erigontech/erigon/cl/sentinel/handlers" "github.com/erigontech/erigon/cl/sentinel/handshake" "github.com/erigontech/erigon/cl/sentinel/httpreqresp" @@ -75,7 +74,7 @@ type Sentinel struct { subManager *GossipManager metrics bool logger log.Logger - forkChoiceReader forkchoice.ForkChoiceStorageReader + chainDataReader handlers.ChainDataReader pidToEnr sync.Map pidToEnodeId sync.Map ethClock eth_clock.EthereumClock @@ -101,7 +100,7 @@ func New( blobStorage blob_storage.BlobStorage, indiciesDB kv.RoDB, logger log.Logger, - forkChoiceReader forkchoice.ForkChoiceStorageReader, + chainDataReader handlers.ChainDataReader, dataColumnStorage blob_storage.DataColumnStorage, peerDasStateReader peerdasstate.PeerDasStateReader, p2p p2p.P2PManager, @@ -129,7 +128,7 @@ func New( indiciesDB: indiciesDB, metrics: true, logger: logger, - forkChoiceReader: forkChoiceReader, + chainDataReader: chainDataReader, blobStorage: blobStorage, ethClock: ethClock, dataColumnStorage: dataColumnStorage, @@ -177,7 +176,7 @@ func (s *Sentinel) Start() (*enode.LocalNode, error) { s.peers, s.cfg.NetworkConfig, s.p2p.UDPv5Listener().LocalNode(), - s.cfg.BeaconConfig, s.ethClock, s.handshaker, s.forkChoiceReader, s.blobStorage, s.dataColumnStorage, s.peerDasStateReader, s.cfg.EnableBlocks, + s.cfg.BeaconConfig, s.ethClock, s.handshaker, s.chainDataReader, s.blobStorage, s.dataColumnStorage, s.peerDasStateReader, s.cfg.EnableBlocks, ).Start() /*if err := s.connectToBootnodes(); err != nil { @@ -332,7 +331,7 @@ func (s *Sentinel) Identity() (pid, enrStr string, p2pAddresses, discoveryAddres if err := s.listener.LocalNode().Node().Load(syncNetEnr); err != nil { s.logger.Debug("[IDENTITY] Could not load sync subnet", "err", err) } - cgc := s.forkChoiceReader.GetPeerDas().StateReader().GetAdvertisedCgc() + cgc := s.peerDasStateReader.GetAdvertisedCgc() metadata = &cltypes.Metadata{ SeqNumber: s.listener.LocalNode().Seq(), Attnets: [8]byte(subnetField), diff --git a/cl/sentinel/sentinel_requests_test.go b/cl/sentinel/sentinel_requests_test.go index 2194fd6ebc3..ee27b7029b3 100644 --- a/cl/sentinel/sentinel_requests_test.go +++ b/cl/sentinel/sentinel_requests_test.go @@ -41,9 +41,9 @@ import ( "github.com/erigontech/erigon/cl/p2p" state_accessors "github.com/erigontech/erigon/cl/persistence/state" "github.com/erigontech/erigon/cl/phase1/core/state" - "github.com/erigontech/erigon/cl/phase1/forkchoice/mock_services" "github.com/erigontech/erigon/cl/sentinel/communication" "github.com/erigontech/erigon/cl/sentinel/communication/ssz_snappy" + "github.com/erigontech/erigon/cl/sentinel/handlers/mock_services" "github.com/erigontech/erigon/cl/utils" "github.com/erigontech/erigon/cl/utils/eth_clock" "github.com/erigontech/erigon/common" @@ -144,7 +144,7 @@ func newTestSentinel(t *testing.T, ethClock eth_clock.EthereumClock, reader free MaxPeerCount: 100, }, EnableBlocks: true, - }, ethClock, reader, nil, db, log.New(), &mock_services.ForkChoiceStorageMock{}, nil, mockPeerDasStateReader, pm) + }, ethClock, reader, nil, db, log.New(), &mock_services.ChainDataReaderMock{}, nil, mockPeerDasStateReader, pm) noErr(err) t.Cleanup(func() { sent.Stop() }) diff --git a/cl/sentinel/service/start.go b/cl/sentinel/service/start.go index dd5cf77a596..23b808abc4b 100644 --- a/cl/sentinel/service/start.go +++ b/cl/sentinel/service/start.go @@ -27,8 +27,8 @@ import ( peerdasstate "github.com/erigontech/erigon/cl/das/state" "github.com/erigontech/erigon/cl/p2p" "github.com/erigontech/erigon/cl/persistence/blob_storage" - "github.com/erigontech/erigon/cl/phase1/forkchoice" "github.com/erigontech/erigon/cl/sentinel" + "github.com/erigontech/erigon/cl/sentinel/handlers" "github.com/erigontech/erigon/cl/utils/eth_clock" "github.com/erigontech/erigon/common/log/v3" "github.com/erigontech/erigon/db/kv" @@ -53,7 +53,7 @@ func createSentinel( blockReader freezeblocks.BeaconSnapshotReader, blobStorage blob_storage.BlobStorage, indiciesDB kv.RwDB, - forkChoiceReader forkchoice.ForkChoiceStorageReader, + chainDataReader handlers.ChainDataReader, ethClock eth_clock.EthereumClock, dataColumnStorage blob_storage.DataColumnStorage, peerDasStateReader peerdasstate.PeerDasStateReader, @@ -69,7 +69,7 @@ func createSentinel( blobStorage, indiciesDB, logger, - forkChoiceReader, + chainDataReader, dataColumnStorage, peerDasStateReader, p2p, @@ -99,7 +99,7 @@ func StartSentinelService( indiciesDB kv.RwDB, srvCfg *ServerConfig, ethClock eth_clock.EthereumClock, - forkChoiceReader forkchoice.ForkChoiceStorageReader, + chainDataReader handlers.ChainDataReader, dataColumnStorage blob_storage.DataColumnStorage, peerDasStateReader peerdasstate.PeerDasStateReader, p2p p2p.P2PManager, @@ -111,7 +111,7 @@ func StartSentinelService( blockReader, blobStorage, indiciesDB, - forkChoiceReader, + chainDataReader, ethClock, dataColumnStorage, peerDasStateReader, diff --git a/cmd/caplin/caplin1/run.go b/cmd/caplin/caplin1/run.go index a744357bc8e..9a8f100080b 100644 --- a/cmd/caplin/caplin1/run.go +++ b/cmd/caplin/caplin1/run.go @@ -359,8 +359,9 @@ func RunCaplinService(ctx context.Context, engine execution_client.ExecutionEngi // create the public keys registry pksRegistry := public_keys_registry.NewHeadViewPublicKeysRegistry(syncedDataManager) validatorParameters := validator_params.NewValidatorParams() + forkGraph := fork_graph.NewForkGraphDisk(state, syncedDataManager, fcuFs, config.BeaconAPIRouter) forkChoice, err := forkchoice.NewForkChoiceStore( - ethClock, state, engine, pool, fork_graph.NewForkGraphDisk(state, syncedDataManager, fcuFs, config.BeaconAPIRouter), + ethClock, state, engine, pool, forkGraph, emitters, syncedDataManager, blobStorage, pksRegistry, validatorParameters, doLMDSampling, indexDB) if err != nil { logger.Error("Could not create forkchoice", "err", err) @@ -450,7 +451,7 @@ func RunCaplinService(ctx context.Context, engine execution_client.ExecutionEngi HeadRoot: anchorRoot, } }(), - }, ethClock, forkChoice, columnStorage, peerDasState, p2p, logger) + }, ethClock, forkGraph, columnStorage, peerDasState, p2p, logger) if err != nil { return err } From 84a7c5dc2079ddf885b32b4dcf9e99f8ce147b06 Mon Sep 17 00:00:00 2001 From: kewei Date: Tue, 11 Aug 2026 21:46:33 +0800 Subject: [PATCH 2/2] cl/sentinel: cover Identity custody group count, finish chain-data naming Review follow-ups: add a test pinning Identity()'s CustodyGroupCount to the injected PeerDasStateReader value (the one behavioral edge of the seam change), rename leftover fork-choice-flavored test identifiers, and use the comma-ok idiom in ChainDataReaderMock getters. --- cl/sentinel/handlers/blocks_by_head_test.go | 2 +- cl/sentinel/handlers/heartbeats_test.go | 20 +++---- cl/sentinel/handlers/light_client_test.go | 54 +++++++++---------- .../mock_services/chain_data_reader_mock.go | 9 ++-- cl/sentinel/sentinel_requests_test.go | 28 ++++++++-- 5 files changed, 67 insertions(+), 46 deletions(-) diff --git a/cl/sentinel/handlers/blocks_by_head_test.go b/cl/sentinel/handlers/blocks_by_head_test.go index 3295f99a5a3..0b7db7560b9 100644 --- a/cl/sentinel/handlers/blocks_by_head_test.go +++ b/cl/sentinel/handlers/blocks_by_head_test.go @@ -76,7 +76,7 @@ func TestBlocksByHeadParentChainTraversal(t *testing.T) { require.Equal(t, roots[0], got[2].Block.ParentRoot) } -func TestBlocksByHeadForkChoiceFallback(t *testing.T) { +func TestBlocksByHeadChainDataFallback(t *testing.T) { blocks, roots := makeBlocksByHeadChain(t, 300, 1) chainData := mock_services.NewChainDataReaderMock() chainData.Blocks[roots[0]] = blocks[0] diff --git a/cl/sentinel/handlers/heartbeats_test.go b/cl/sentinel/handlers/heartbeats_test.go index 816aa67bfd6..718f29f7eee 100644 --- a/cl/sentinel/handlers/heartbeats_test.go +++ b/cl/sentinel/handlers/heartbeats_test.go @@ -90,7 +90,7 @@ func TestPing(t *testing.T) { peersPool := peers.NewPool(host) beaconDB, indiciesDB := setupStore(t) - f := mock_services.NewChainDataReaderMock() + chainData := mock_services.NewChainDataReaderMock() ethClock := getEthClock(t) _, beaconCfg := clparams.GetConfigsByNetwork(1) @@ -104,7 +104,7 @@ func TestPing(t *testing.T) { testLocalNode(t), beaconCfg, ethClock, - nil, f, nil, nil, nil, true, + nil, chainData, nil, nil, nil, true, ) c.Start() @@ -145,7 +145,7 @@ func TestGoodbye(t *testing.T) { peersPool := peers.NewPool(host) beaconDB, indiciesDB := setupStore(t) - f := mock_services.NewChainDataReaderMock() + chainData := mock_services.NewChainDataReaderMock() ethClock := getEthClock(t) _, beaconCfg := clparams.GetConfigsByNetwork(1) c := NewConsensusHandlers( @@ -158,7 +158,7 @@ func TestGoodbye(t *testing.T) { testLocalNode(t), beaconCfg, ethClock, - nil, f, nil, nil, nil, true, + nil, chainData, nil, nil, nil, true, ) c.Start() @@ -205,7 +205,7 @@ func TestMetadataV2(t *testing.T) { peersPool := peers.NewPool(host) beaconDB, indiciesDB := setupStore(t) - f := mock_services.NewChainDataReaderMock() + chainData := mock_services.NewChainDataReaderMock() ethClock := getEthClock(t) nc := clparams.NetworkConfigs[chainspec.MainnetChainID] _, beaconCfg := clparams.GetConfigsByNetwork(1) @@ -219,7 +219,7 @@ func TestMetadataV2(t *testing.T) { testLocalNode(t), beaconCfg, ethClock, - nil, f, nil, nil, nil, true, + nil, chainData, nil, nil, nil, true, ) c.Start() @@ -263,7 +263,7 @@ func TestMetadataV1(t *testing.T) { peersPool := peers.NewPool(host) beaconDB, indiciesDB := setupStore(t) - f := mock_services.NewChainDataReaderMock() + chainData := mock_services.NewChainDataReaderMock() nc := clparams.NetworkConfigs[chainspec.MainnetChainID] ethClock := getEthClock(t) @@ -278,7 +278,7 @@ func TestMetadataV1(t *testing.T) { testLocalNode(t), beaconCfg, ethClock, - nil, f, nil, nil, nil, true, + nil, chainData, nil, nil, nil, true, ) c.Start() @@ -321,7 +321,7 @@ func TestStatus(t *testing.T) { peersPool := peers.NewPool(host) beaconDB, indiciesDB := setupStore(t) - f := mock_services.NewChainDataReaderMock() + chainData := mock_services.NewChainDataReaderMock() // Create mock for PeerDasStateReader ctrl := gomock.NewController(t) @@ -368,7 +368,7 @@ func TestStatus(t *testing.T) { testLocalNode(t), beaconCfg, getEthClock(t), - hs, f, nil, nil, mockPeerDasStateReader, true, + hs, chainData, nil, nil, mockPeerDasStateReader, true, ) c.Start() diff --git a/cl/sentinel/handlers/light_client_test.go b/cl/sentinel/handlers/light_client_test.go index 97cde845076..aef77f6a757 100644 --- a/cl/sentinel/handlers/light_client_test.go +++ b/cl/sentinel/handlers/light_client_test.go @@ -63,9 +63,9 @@ func TestLightClientOptimistic(t *testing.T) { peersPool := peers.NewPool(host) beaconDB, indiciesDB := setupStore(t) - f := mock_services.NewChainDataReaderMock() + chainData := mock_services.NewChainDataReaderMock() - f.NewestLCUpdate = &cltypes.LightClientUpdate{ + chainData.NewestLCUpdate = &cltypes.LightClientUpdate{ AttestedHeader: cltypes.NewLightClientHeader(clparams.AltairVersion), NextSyncCommittee: solid.NewSyncCommittee(), SignatureSlot: 1234, @@ -75,7 +75,7 @@ func TestLightClientOptimistic(t *testing.T) { FinalityBranch: solid.NewHashVector(8), NextSyncCommitteeBranch: solid.NewHashVector(8), } - f.NewestLCUpdate.AttestedHeader.Beacon.Slot = altairSlot + chainData.NewestLCUpdate.AttestedHeader.Beacon.Slot = altairSlot ethClock := getEthClock(t) _, beaconCfg := clparams.GetConfigsByNetwork(1) @@ -89,7 +89,7 @@ func TestLightClientOptimistic(t *testing.T) { nil, beaconCfg, ethClock, - nil, f, nil, nil, nil, true, + nil, chainData, nil, nil, nil, true, ) c.Start() @@ -109,9 +109,9 @@ func TestLightClientOptimistic(t *testing.T) { err = ssz_snappy.DecodeAndRead(stream, optimistic, &clparams.MainnetBeaconConfig, ethClock) require.NoError(t, err) - require.Equal(t, f.NewestLCUpdate.AttestedHeader, optimistic.AttestedHeader) - require.Equal(t, f.NewestLCUpdate.SignatureSlot, optimistic.SignatureSlot) - require.Equal(t, f.NewestLCUpdate.SyncAggregate, optimistic.SyncAggregate) + require.Equal(t, chainData.NewestLCUpdate.AttestedHeader, optimistic.AttestedHeader) + require.Equal(t, chainData.NewestLCUpdate.SignatureSlot, optimistic.SignatureSlot) + require.Equal(t, chainData.NewestLCUpdate.SyncAggregate, optimistic.SyncAggregate) } func TestLightClientFinality(t *testing.T) { @@ -134,9 +134,9 @@ func TestLightClientFinality(t *testing.T) { peersPool := peers.NewPool(host) beaconDB, indiciesDB := setupStore(t) - f := mock_services.NewChainDataReaderMock() + chainData := mock_services.NewChainDataReaderMock() - f.NewestLCUpdate = &cltypes.LightClientUpdate{ + chainData.NewestLCUpdate = &cltypes.LightClientUpdate{ AttestedHeader: cltypes.NewLightClientHeader(clparams.AltairVersion), NextSyncCommittee: solid.NewSyncCommittee(), SignatureSlot: altairSlot, @@ -145,7 +145,7 @@ func TestLightClientFinality(t *testing.T) { FinalityBranch: solid.NewHashVector(cltypes.FinalizedBranchSize), NextSyncCommitteeBranch: solid.NewHashVector(cltypes.SyncCommitteeBranchSize), } - f.NewestLCUpdate.AttestedHeader.Beacon.Slot = altairSlot + chainData.NewestLCUpdate.AttestedHeader.Beacon.Slot = altairSlot ethClock := getEthClock(t) _, beaconCfg := clparams.GetConfigsByNetwork(1) @@ -159,7 +159,7 @@ func TestLightClientFinality(t *testing.T) { nil, beaconCfg, ethClock, - nil, f, nil, nil, nil, true, + nil, chainData, nil, nil, nil, true, ) c.Start() @@ -179,11 +179,11 @@ func TestLightClientFinality(t *testing.T) { err = ssz_snappy.DecodeAndRead(stream, got, &clparams.MainnetBeaconConfig, ethClock) require.NoError(t, err) - require.Equal(t, got.AttestedHeader, f.NewestLCUpdate.AttestedHeader) - require.Equal(t, got.SyncAggregate, f.NewestLCUpdate.SyncAggregate) - require.Equal(t, got.FinalizedHeader, f.NewestLCUpdate.FinalizedHeader) - require.Equal(t, got.FinalityBranch, f.NewestLCUpdate.FinalityBranch) - require.Equal(t, got.SignatureSlot, f.NewestLCUpdate.SignatureSlot) + require.Equal(t, got.AttestedHeader, chainData.NewestLCUpdate.AttestedHeader) + require.Equal(t, got.SyncAggregate, chainData.NewestLCUpdate.SyncAggregate) + require.Equal(t, got.FinalizedHeader, chainData.NewestLCUpdate.FinalizedHeader) + require.Equal(t, got.FinalityBranch, chainData.NewestLCUpdate.FinalityBranch) + require.Equal(t, got.SignatureSlot, chainData.NewestLCUpdate.SignatureSlot) } func TestLightClientBootstrap(t *testing.T) { @@ -207,9 +207,9 @@ func TestLightClientBootstrap(t *testing.T) { peersPool := peers.NewPool(host) beaconDB, indiciesDB := setupStore(t) - f := mock_services.NewChainDataReaderMock() + chainData := mock_services.NewChainDataReaderMock() - f.NewestLCUpdate = &cltypes.LightClientUpdate{ + chainData.NewestLCUpdate = &cltypes.LightClientUpdate{ AttestedHeader: cltypes.NewLightClientHeader(clparams.AltairVersion), NextSyncCommittee: solid.NewSyncCommittee(), SignatureSlot: altairSlot, @@ -218,14 +218,14 @@ func TestLightClientBootstrap(t *testing.T) { FinalityBranch: solid.NewHashVector(cltypes.FinalizedBranchSize), NextSyncCommitteeBranch: solid.NewHashVector(cltypes.SyncCommitteeBranchSize), } - f.NewestLCUpdate.AttestedHeader.Beacon.Slot = altairSlot + chainData.NewestLCUpdate.AttestedHeader.Beacon.Slot = altairSlot reqRoot := common.Hash{1, 2, 3} - f.LightClientBootstraps[reqRoot] = &cltypes.LightClientBootstrap{ + chainData.LightClientBootstraps[reqRoot] = &cltypes.LightClientBootstrap{ Header: cltypes.NewLightClientHeader(clparams.AltairVersion), CurrentSyncCommittee: solid.NewSyncCommittee(), CurrentSyncCommitteeBranch: solid.NewHashVector(cltypes.SyncCommitteeBranchSize), } - f.LightClientBootstraps[reqRoot].Header.Beacon.Slot = altairSlot + chainData.LightClientBootstraps[reqRoot].Header.Beacon.Slot = altairSlot _, beaconCfg := clparams.GetConfigsByNetwork(1) c := NewConsensusHandlers( ctx, @@ -237,7 +237,7 @@ func TestLightClientBootstrap(t *testing.T) { nil, beaconCfg, ethClock, - nil, f, nil, nil, nil, true, + nil, chainData, nil, nil, nil, true, ) c.Start() @@ -264,7 +264,7 @@ func TestLightClientBootstrap(t *testing.T) { err = ssz_snappy.DecodeAndRead(stream, got, &clparams.MainnetBeaconConfig, ethClock) require.NoError(t, err) - expected := f.LightClientBootstraps[reqRoot] + expected := chainData.LightClientBootstraps[reqRoot] require.Equal(t, expected.Header, got.Header) require.Equal(t, expected.CurrentSyncCommittee, got.CurrentSyncCommittee) require.Equal(t, expected.CurrentSyncCommitteeBranch, got.CurrentSyncCommitteeBranch) @@ -290,7 +290,7 @@ func TestLightClientUpdates(t *testing.T) { peersPool := peers.NewPool(host) beaconDB, indiciesDB := setupStore(t) - f := mock_services.NewChainDataReaderMock() + chainData := mock_services.NewChainDataReaderMock() ethClock := getEthClock(t) _, beaconCfg := clparams.GetConfigsByNetwork(1) @@ -300,7 +300,7 @@ func TestLightClientUpdates(t *testing.T) { upC := cltypes.NewLightClientUpdate(clparams.AltairVersion, beaconCfg) upC.AttestedHeader.Beacon.Slot = altairSlot upC.SignatureSlot = uint64(i) - f.LCUpdates[uint64(i)] = upC + chainData.LCUpdates[uint64(i)] = upC } c := NewConsensusHandlers( ctx, @@ -312,7 +312,7 @@ func TestLightClientUpdates(t *testing.T) { nil, beaconCfg, ethClock, - nil, f, nil, nil, nil, true, + nil, chainData, nil, nil, nil, true, ) c.Start() @@ -378,7 +378,7 @@ func TestLightClientUpdates(t *testing.T) { require.NoError(t, err) return } - require.Equal(t, f.LCUpdates[uint64(currentPeriod)], update) + require.Equal(t, chainData.LCUpdates[uint64(currentPeriod)], update) currentPeriod++ stream.Read(make([]byte, 1)) diff --git a/cl/sentinel/handlers/mock_services/chain_data_reader_mock.go b/cl/sentinel/handlers/mock_services/chain_data_reader_mock.go index debe214820f..da7b81cc273 100644 --- a/cl/sentinel/handlers/mock_services/chain_data_reader_mock.go +++ b/cl/sentinel/handlers/mock_services/chain_data_reader_mock.go @@ -40,11 +40,13 @@ func NewChainDataReaderMock() *ChainDataReaderMock { } func (m *ChainDataReaderMock) GetBlock(blockRoot common.Hash) (*cltypes.SignedBeaconBlock, bool) { - return m.Blocks[blockRoot], m.Blocks[blockRoot] != nil + b, ok := m.Blocks[blockRoot] + return b, ok } func (m *ChainDataReaderMock) GetLightClientBootstrap(blockRoot common.Hash) (*cltypes.LightClientBootstrap, bool) { - return m.LightClientBootstraps[blockRoot], m.LightClientBootstraps[blockRoot] != nil + b, ok := m.LightClientBootstraps[blockRoot] + return b, ok } func (m *ChainDataReaderMock) NewestLightClientUpdate() *cltypes.LightClientUpdate { @@ -52,7 +54,8 @@ func (m *ChainDataReaderMock) NewestLightClientUpdate() *cltypes.LightClientUpda } func (m *ChainDataReaderMock) GetLightClientUpdate(period uint64) (*cltypes.LightClientUpdate, bool) { - return m.LCUpdates[period], m.LCUpdates[period] != nil + u, ok := m.LCUpdates[period] + return u, ok } func (m *ChainDataReaderMock) HasEnvelope(blockRoot common.Hash) bool { diff --git a/cl/sentinel/sentinel_requests_test.go b/cl/sentinel/sentinel_requests_test.go index ee27b7029b3..50b93007df2 100644 --- a/cl/sentinel/sentinel_requests_test.go +++ b/cl/sentinel/sentinel_requests_test.go @@ -153,12 +153,12 @@ func newTestSentinel(t *testing.T, ethClock eth_clock.EthereumClock, reader free return sent } -func newMockPeerDasStateReader(t *testing.T) *peerdasstatemock.MockPeerDasStateReader { +func newMockPeerDasStateReader(t *testing.T, advertisedCgc uint64) *peerdasstatemock.MockPeerDasStateReader { ctrl := gomock.NewController(t) m := peerdasstatemock.NewMockPeerDasStateReader(ctrl) m.EXPECT().GetEarliestAvailableSlot().Return(uint64(0)).AnyTimes() m.EXPECT().GetRealCgc().Return(uint64(0)).AnyTimes() - m.EXPECT().GetAdvertisedCgc().Return(uint64(0)).AnyTimes() + m.EXPECT().GetAdvertisedCgc().Return(advertisedCgc).AnyTimes() return m } @@ -168,7 +168,7 @@ func testSentinelBlocksByRange(t *testing.T) { db, blocks, _, _, reader := loadChain(t) _, beaconConfig := clparams.GetConfigsByNetwork(chainspec.MainnetChainID) - sent := newTestSentinel(t, ethClock, reader, db, newMockPeerDasStateReader(t)) + sent := newTestSentinel(t, ethClock, reader, db, newMockPeerDasStateReader(t, 0)) h := sent.Host() host1, err := libp2p.New(libp2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/0")) @@ -244,7 +244,7 @@ func testSentinelBlocksByRoots(t *testing.T) { ethClock := getEthClock(t) _, beaconConfig := clparams.GetConfigsByNetwork(chainspec.MainnetChainID) - sent := newTestSentinel(t, ethClock, reader, db, newMockPeerDasStateReader(t)) + sent := newTestSentinel(t, ethClock, reader, db, newMockPeerDasStateReader(t, 0)) h := sent.Host() host1, err := libp2p.New(libp2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/0")) @@ -323,7 +323,7 @@ func testSentinelStatusRequest(t *testing.T) { db, blocks, _, _, reader := loadChain(t) ethClock := getEthClock(t) - sent := newTestSentinel(t, ethClock, reader, db, newMockPeerDasStateReader(t)) + sent := newTestSentinel(t, ethClock, reader, db, newMockPeerDasStateReader(t, 0)) h := sent.Host() host1, err := libp2p.New(libp2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/0")) @@ -360,6 +360,24 @@ func testSentinelStatusRequest(t *testing.T) { assertPanic(req.FinalizedEpoch == resp.FinalizedEpoch, "FinalizedEpoch mismatch: %v != %v", req.FinalizedEpoch, resp.FinalizedEpoch) } +func testSentinelIdentityCustodyGroupCount(t *testing.T) { + const expectedCgc = uint64(73) + ethClock := getEthClock(t) + db, _, _, _, reader := loadChain(t) + + sent := newTestSentinel(t, ethClock, reader, db, newMockPeerDasStateReader(t, expectedCgc)) + + pid, enrStr, _, _, metadata := sent.Identity() + assertPanic(pid != "", "expected non-empty pid") + assertPanic(enrStr != "", "expected non-empty enr") + assertPanic(metadata.CustodyGroupCount != nil, "expected non-nil CustodyGroupCount") + assertPanic(*metadata.CustodyGroupCount == expectedCgc, "expected CustodyGroupCount=%d, got %d", expectedCgc, *metadata.CustodyGroupCount) +} + +func TestSentinelIdentityCustodyGroupCount(t *testing.T) { + retryTestFunc(t, 3, func() { testSentinelIdentityCustodyGroupCount(t) }) +} + func TestSentinelBlocksByRange(t *testing.T) { retryTestFunc(t, 3, func() { testSentinelBlocksByRange(t) }) }