diff --git a/CHANGELOG.md b/CHANGELOG.md index c6dccfa6194..0a38213d5bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -81,6 +81,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Check kzg commitments align with blobs and proofs for beacon api end point. - Revert "Proposer checks gas limit before accepting builder's bid". - Updated quic-go to v0.48.2 . +- Updated `GetLightClientBootstrap` RPC handler to read the bootstraps from the DB. - Process light client finality updates only for new finalized epochs instead of doing it for every block. ### Deprecated diff --git a/beacon-chain/blockchain/process_block.go b/beacon-chain/blockchain/process_block.go index 80e21466786..774b996eaf9 100644 --- a/beacon-chain/blockchain/process_block.go +++ b/beacon-chain/blockchain/process_block.go @@ -70,6 +70,7 @@ func (s *Service) postBlockProcess(cfg *postBlockProcessConfig) error { if features.Get().EnableLightClient && slots.ToEpoch(s.CurrentSlot()) >= params.BeaconConfig().AltairForkEpoch { defer s.processLightClientUpdates(cfg) defer s.saveLightClientUpdate(cfg) + defer s.saveLightClientBootstrap(cfg) } defer s.sendStateFeedOnBlock(cfg) defer reportProcessingTime(startTime) diff --git a/beacon-chain/rpc/eth/light-client/handlers.go b/beacon-chain/rpc/eth/light-client/handlers.go index 7e0d7a1d7a1..979aa9522ff 100644 --- a/beacon-chain/rpc/eth/light-client/handlers.go +++ b/beacon-chain/rpc/eth/light-client/handlers.go @@ -10,7 +10,6 @@ import ( "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v5/api" "github.com/prysmaticlabs/prysm/v5/api/server/structs" - lightclient "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/light-client" "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/shared" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces" @@ -29,41 +28,34 @@ func (s *Server) GetLightClientBootstrap(w http.ResponseWriter, req *http.Reques // Get the block blockRootParam, err := hexutil.Decode(req.PathValue("block_root")) if err != nil { - httputil.HandleError(w, "invalid block root: "+err.Error(), http.StatusBadRequest) + httputil.HandleError(w, "Invalid block root: "+err.Error(), http.StatusBadRequest) return } blockRoot := bytesutil.ToBytes32(blockRootParam) - blk, err := s.Blocker.Block(ctx, blockRoot[:]) - if !shared.WriteBlockFetchError(w, blk, err) { - return - } - - // Get the state - state, err := s.Stater.StateBySlot(ctx, blk.Block().Slot()) + bootstrap, err := s.BeaconDB.LightClientBootstrap(ctx, blockRoot[:]) if err != nil { - httputil.HandleError(w, "could not get state: "+err.Error(), http.StatusInternalServerError) + httputil.HandleError(w, "Could not get light client bootstrap: "+err.Error(), http.StatusInternalServerError) return } - - bootstrap, err := lightclient.NewLightClientBootstrapFromBeaconState(ctx, s.ChainInfoFetcher.CurrentSlot(), state, blk) - if err != nil { - httputil.HandleError(w, "could not get light client bootstrap: "+err.Error(), http.StatusInternalServerError) + if bootstrap == nil { + httputil.HandleError(w, "Light client bootstrap not found", http.StatusNotFound) return } + w.Header().Set(api.VersionHeader, version.String(bootstrap.Version())) if httputil.RespondWithSsz(req) { ssz, err := bootstrap.MarshalSSZ() if err != nil { - httputil.HandleError(w, "could not marshal bootstrap to SSZ: "+err.Error(), http.StatusInternalServerError) + httputil.HandleError(w, "Could not marshal bootstrap to SSZ: "+err.Error(), http.StatusInternalServerError) return } httputil.WriteSsz(w, ssz, "light_client_bootstrap.ssz") } else { data, err := structs.LightClientBootstrapFromConsensus(bootstrap) if err != nil { - httputil.HandleError(w, "could not marshal bootstrap to JSON: "+err.Error(), http.StatusInternalServerError) + httputil.HandleError(w, "Could not marshal bootstrap to JSON: "+err.Error(), http.StatusInternalServerError) return } response := &structs.LightClientBootstrapResponse{ diff --git a/beacon-chain/rpc/eth/light-client/handlers_test.go b/beacon-chain/rpc/eth/light-client/handlers_test.go index 984cd7add01..a546f815114 100644 --- a/beacon-chain/rpc/eth/light-client/handlers_test.go +++ b/beacon-chain/rpc/eth/light-client/handlers_test.go @@ -46,27 +46,28 @@ func TestLightClientHandler_GetLightClientBootstrap(t *testing.T) { l := util.NewTestLightClient(t).SetupTestAltair() slot := primitives.Slot(params.BeaconConfig().AltairForkEpoch * primitives.Epoch(params.BeaconConfig().SlotsPerEpoch)).Add(1) - stateRoot, err := l.State.HashTreeRoot(l.Ctx) + blockRoot, err := l.Block.Block().HashTreeRoot() + require.NoError(t, err) + + bootstrap, err := lightclient.NewLightClientBootstrapFromBeaconState(l.Ctx, slot, l.State, l.Block) + require.NoError(t, err) + + db := dbtesting.SetupDB(t) + + err = db.SaveLightClientBootstrap(l.Ctx, blockRoot[:], bootstrap) require.NoError(t, err) - mockBlocker := &testutil.MockBlocker{BlockToReturn: l.Block} - mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot} - mockChainInfoFetcher := &mock.ChainService{Slot: &slot} s := &Server{ - Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ - slot: l.State, - }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, - ChainInfoFetcher: mockChainInfoFetcher, + BeaconDB: db, } request := httptest.NewRequest("GET", "http://foo.com/", nil) - request.SetPathValue("block_root", hexutil.Encode(stateRoot[:])) + request.SetPathValue("block_root", hexutil.Encode(blockRoot[:])) writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} s.GetLightClientBootstrap(writer, request) require.Equal(t, http.StatusOK, writer.Code) + var resp structs.LightClientBootstrapResponse err = json.Unmarshal(writer.Body.Bytes(), &resp) require.NoError(t, err) @@ -83,6 +84,32 @@ func TestLightClientHandler_GetLightClientBootstrap(t *testing.T) { require.NotNil(t, resp.Data.CurrentSyncCommittee) require.NotNil(t, resp.Data.CurrentSyncCommitteeBranch) }) + t.Run("altair - no bootstrap found", func(t *testing.T) { + l := util.NewTestLightClient(t).SetupTestAltair() + + slot := primitives.Slot(params.BeaconConfig().AltairForkEpoch * primitives.Epoch(params.BeaconConfig().SlotsPerEpoch)).Add(1) + blockRoot, err := l.Block.Block().HashTreeRoot() + require.NoError(t, err) + + bootstrap, err := lightclient.NewLightClientBootstrapFromBeaconState(l.Ctx, slot, l.State, l.Block) + require.NoError(t, err) + + db := dbtesting.SetupDB(t) + + err = db.SaveLightClientBootstrap(l.Ctx, blockRoot[1:], bootstrap) + require.NoError(t, err) + + s := &Server{ + BeaconDB: db, + } + request := httptest.NewRequest("GET", "http://foo.com/", nil) + request.SetPathValue("block_root", hexutil.Encode(blockRoot[:])) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.GetLightClientBootstrap(writer, request) + require.Equal(t, http.StatusNotFound, writer.Code) + }) t.Run("bellatrix", func(t *testing.T) { l := util.NewTestLightClient(t).SetupTestBellatrix() @@ -90,16 +117,16 @@ func TestLightClientHandler_GetLightClientBootstrap(t *testing.T) { blockRoot, err := l.Block.Block().HashTreeRoot() require.NoError(t, err) - mockBlocker := &testutil.MockBlocker{BlockToReturn: l.Block} - mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot} - mockChainInfoFetcher := &mock.ChainService{Slot: &slot} + bootstrap, err := lightclient.NewLightClientBootstrapFromBeaconState(l.Ctx, slot, l.State, l.Block) + require.NoError(t, err) + + db := dbtesting.SetupDB(t) + + err = db.SaveLightClientBootstrap(l.Ctx, blockRoot[:], bootstrap) + require.NoError(t, err) + s := &Server{ - Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ - slot: l.State, - }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, - ChainInfoFetcher: mockChainInfoFetcher, + BeaconDB: db, } request := httptest.NewRequest("GET", "http://foo.com/", nil) request.SetPathValue("block_root", hexutil.Encode(blockRoot[:])) @@ -131,16 +158,16 @@ func TestLightClientHandler_GetLightClientBootstrap(t *testing.T) { blockRoot, err := l.Block.Block().HashTreeRoot() require.NoError(t, err) - mockBlocker := &testutil.MockBlocker{BlockToReturn: l.Block} - mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot} - mockChainInfoFetcher := &mock.ChainService{Slot: &slot} + bootstrap, err := lightclient.NewLightClientBootstrapFromBeaconState(l.Ctx, slot, l.State, l.Block) + require.NoError(t, err) + + db := dbtesting.SetupDB(t) + + err = db.SaveLightClientBootstrap(l.Ctx, blockRoot[:], bootstrap) + require.NoError(t, err) + s := &Server{ - Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ - slot: l.State, - }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, - ChainInfoFetcher: mockChainInfoFetcher, + BeaconDB: db, } request := httptest.NewRequest("GET", "http://foo.com/", nil) request.SetPathValue("block_root", hexutil.Encode(blockRoot[:])) @@ -172,16 +199,16 @@ func TestLightClientHandler_GetLightClientBootstrap(t *testing.T) { blockRoot, err := l.Block.Block().HashTreeRoot() require.NoError(t, err) - mockBlocker := &testutil.MockBlocker{BlockToReturn: l.Block} - mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot} - mockChainInfoFetcher := &mock.ChainService{Slot: &slot} + bootstrap, err := lightclient.NewLightClientBootstrapFromBeaconState(l.Ctx, slot, l.State, l.Block) + require.NoError(t, err) + + db := dbtesting.SetupDB(t) + + err = db.SaveLightClientBootstrap(l.Ctx, blockRoot[:], bootstrap) + require.NoError(t, err) + s := &Server{ - Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ - slot: l.State, - }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, - ChainInfoFetcher: mockChainInfoFetcher, + BeaconDB: db, } request := httptest.NewRequest("GET", "http://foo.com/", nil) request.SetPathValue("block_root", hexutil.Encode(blockRoot[:])) @@ -213,16 +240,16 @@ func TestLightClientHandler_GetLightClientBootstrap(t *testing.T) { blockRoot, err := l.Block.Block().HashTreeRoot() require.NoError(t, err) - mockBlocker := &testutil.MockBlocker{BlockToReturn: l.Block} - mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot} - mockChainInfoFetcher := &mock.ChainService{Slot: &slot} + bootstrap, err := lightclient.NewLightClientBootstrapFromBeaconState(l.Ctx, slot, l.State, l.Block) + require.NoError(t, err) + + db := dbtesting.SetupDB(t) + + err = db.SaveLightClientBootstrap(l.Ctx, blockRoot[:], bootstrap) + require.NoError(t, err) + s := &Server{ - Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ - slot: l.State, - }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, - ChainInfoFetcher: mockChainInfoFetcher, + BeaconDB: db, } request := httptest.NewRequest("GET", "http://foo.com/", nil) request.SetPathValue("block_root", hexutil.Encode(blockRoot[:]))