mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 21:38:05 -05:00
Read LC Bootstraps from DB (#14718)
* fix bootstrap handler * add tests for get bootstrap handler * changelog * make CreateDefaultLightClientBootstrap private * remove fulu test --------- Co-authored-by: Radosław Kapka <rkapka@wp.pl>
This commit is contained in:
@@ -40,6 +40,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve
|
|||||||
- Update our `go-libp2p-pubsub` dependency.
|
- Update our `go-libp2p-pubsub` dependency.
|
||||||
- Re-organize the content of files to ease the creation of a new fork boilerplate.
|
- Re-organize the content of files to ease the creation of a new fork boilerplate.
|
||||||
- Fixed Metadata errors for peers connected via QUIC.
|
- Fixed Metadata errors for peers connected via QUIC.
|
||||||
|
- Process light client finality updates only for new finalized epochs instead of doing it for every block.
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ func (s *Service) postBlockProcess(cfg *postBlockProcessConfig) error {
|
|||||||
if features.Get().EnableLightClient && slots.ToEpoch(s.CurrentSlot()) >= params.BeaconConfig().AltairForkEpoch {
|
if features.Get().EnableLightClient && slots.ToEpoch(s.CurrentSlot()) >= params.BeaconConfig().AltairForkEpoch {
|
||||||
defer s.processLightClientUpdates(cfg)
|
defer s.processLightClientUpdates(cfg)
|
||||||
defer s.saveLightClientUpdate(cfg)
|
defer s.saveLightClientUpdate(cfg)
|
||||||
|
defer s.saveLightClientBootstrap(cfg)
|
||||||
}
|
}
|
||||||
defer s.sendStateFeedOnBlock(cfg)
|
defer s.sendStateFeedOnBlock(cfg)
|
||||||
defer reportProcessingTime(startTime)
|
defer reportProcessingTime(startTime)
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/prysmaticlabs/prysm/v5/api"
|
"github.com/prysmaticlabs/prysm/v5/api"
|
||||||
"github.com/prysmaticlabs/prysm/v5/api/server/structs"
|
"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/beacon-chain/rpc/eth/shared"
|
||||||
"github.com/prysmaticlabs/prysm/v5/config/features"
|
"github.com/prysmaticlabs/prysm/v5/config/features"
|
||||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||||
@@ -35,41 +34,34 @@ func (s *Server) GetLightClientBootstrap(w http.ResponseWriter, req *http.Reques
|
|||||||
// Get the block
|
// Get the block
|
||||||
blockRootParam, err := hexutil.Decode(req.PathValue("block_root"))
|
blockRootParam, err := hexutil.Decode(req.PathValue("block_root"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httputil.HandleError(w, "invalid block root: "+err.Error(), http.StatusBadRequest)
|
httputil.HandleError(w, "Invalid block root: "+err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
blockRoot := bytesutil.ToBytes32(blockRootParam)
|
blockRoot := bytesutil.ToBytes32(blockRootParam)
|
||||||
blk, err := s.Blocker.Block(ctx, blockRoot[:])
|
bootstrap, err := s.BeaconDB.LightClientBootstrap(ctx, blockRoot[:])
|
||||||
if !shared.WriteBlockFetchError(w, blk, err) {
|
if err != nil {
|
||||||
|
httputil.HandleError(w, "Could not get light client bootstrap: "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if bootstrap == nil {
|
||||||
|
httputil.HandleError(w, "Light client bootstrap not found", http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the state
|
|
||||||
state, err := s.Stater.StateBySlot(ctx, blk.Block().Slot())
|
|
||||||
if err != nil {
|
|
||||||
httputil.HandleError(w, "could not get state: "+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)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.Header().Set(api.VersionHeader, version.String(bootstrap.Version()))
|
w.Header().Set(api.VersionHeader, version.String(bootstrap.Version()))
|
||||||
|
|
||||||
if httputil.RespondWithSsz(req) {
|
if httputil.RespondWithSsz(req) {
|
||||||
ssz, err := bootstrap.MarshalSSZ()
|
ssz, err := bootstrap.MarshalSSZ()
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
httputil.WriteSsz(w, ssz, "light_client_bootstrap.ssz")
|
httputil.WriteSsz(w, ssz, "light_client_bootstrap.ssz")
|
||||||
} else {
|
} else {
|
||||||
data, err := structs.LightClientBootstrapFromConsensus(bootstrap)
|
data, err := structs.LightClientBootstrapFromConsensus(bootstrap)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
response := &structs.LightClientBootstrapResponse{
|
response := &structs.LightClientBootstrapResponse{
|
||||||
|
|||||||
@@ -53,27 +53,28 @@ func TestLightClientHandler_GetLightClientBootstrap(t *testing.T) {
|
|||||||
l := util.NewTestLightClient(t).SetupTestAltair()
|
l := util.NewTestLightClient(t).SetupTestAltair()
|
||||||
|
|
||||||
slot := primitives.Slot(params.BeaconConfig().AltairForkEpoch * primitives.Epoch(params.BeaconConfig().SlotsPerEpoch)).Add(1)
|
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)
|
require.NoError(t, err)
|
||||||
|
|
||||||
mockBlocker := &testutil.MockBlocker{BlockToReturn: l.Block}
|
|
||||||
mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot}
|
|
||||||
mockChainInfoFetcher := &mock.ChainService{Slot: &slot}
|
|
||||||
s := &Server{
|
s := &Server{
|
||||||
Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{
|
BeaconDB: db,
|
||||||
slot: l.State,
|
|
||||||
}},
|
|
||||||
Blocker: mockBlocker,
|
|
||||||
HeadFetcher: mockChainService,
|
|
||||||
ChainInfoFetcher: mockChainInfoFetcher,
|
|
||||||
}
|
}
|
||||||
request := httptest.NewRequest("GET", "http://foo.com/", nil)
|
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 := httptest.NewRecorder()
|
||||||
writer.Body = &bytes.Buffer{}
|
writer.Body = &bytes.Buffer{}
|
||||||
|
|
||||||
s.GetLightClientBootstrap(writer, request)
|
s.GetLightClientBootstrap(writer, request)
|
||||||
require.Equal(t, http.StatusOK, writer.Code)
|
require.Equal(t, http.StatusOK, writer.Code)
|
||||||
|
|
||||||
var resp structs.LightClientBootstrapResponse
|
var resp structs.LightClientBootstrapResponse
|
||||||
err = json.Unmarshal(writer.Body.Bytes(), &resp)
|
err = json.Unmarshal(writer.Body.Bytes(), &resp)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -90,6 +91,32 @@ func TestLightClientHandler_GetLightClientBootstrap(t *testing.T) {
|
|||||||
require.NotNil(t, resp.Data.CurrentSyncCommittee)
|
require.NotNil(t, resp.Data.CurrentSyncCommittee)
|
||||||
require.NotNil(t, resp.Data.CurrentSyncCommitteeBranch)
|
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) {
|
t.Run("bellatrix", func(t *testing.T) {
|
||||||
l := util.NewTestLightClient(t).SetupTestBellatrix()
|
l := util.NewTestLightClient(t).SetupTestBellatrix()
|
||||||
|
|
||||||
@@ -97,16 +124,16 @@ func TestLightClientHandler_GetLightClientBootstrap(t *testing.T) {
|
|||||||
blockRoot, err := l.Block.Block().HashTreeRoot()
|
blockRoot, err := l.Block.Block().HashTreeRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
mockBlocker := &testutil.MockBlocker{BlockToReturn: l.Block}
|
bootstrap, err := lightclient.NewLightClientBootstrapFromBeaconState(l.Ctx, slot, l.State, l.Block)
|
||||||
mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot}
|
require.NoError(t, err)
|
||||||
mockChainInfoFetcher := &mock.ChainService{Slot: &slot}
|
|
||||||
|
db := dbtesting.SetupDB(t)
|
||||||
|
|
||||||
|
err = db.SaveLightClientBootstrap(l.Ctx, blockRoot[:], bootstrap)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
s := &Server{
|
s := &Server{
|
||||||
Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{
|
BeaconDB: db,
|
||||||
slot: l.State,
|
|
||||||
}},
|
|
||||||
Blocker: mockBlocker,
|
|
||||||
HeadFetcher: mockChainService,
|
|
||||||
ChainInfoFetcher: mockChainInfoFetcher,
|
|
||||||
}
|
}
|
||||||
request := httptest.NewRequest("GET", "http://foo.com/", nil)
|
request := httptest.NewRequest("GET", "http://foo.com/", nil)
|
||||||
request.SetPathValue("block_root", hexutil.Encode(blockRoot[:]))
|
request.SetPathValue("block_root", hexutil.Encode(blockRoot[:]))
|
||||||
@@ -138,16 +165,16 @@ func TestLightClientHandler_GetLightClientBootstrap(t *testing.T) {
|
|||||||
blockRoot, err := l.Block.Block().HashTreeRoot()
|
blockRoot, err := l.Block.Block().HashTreeRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
mockBlocker := &testutil.MockBlocker{BlockToReturn: l.Block}
|
bootstrap, err := lightclient.NewLightClientBootstrapFromBeaconState(l.Ctx, slot, l.State, l.Block)
|
||||||
mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot}
|
require.NoError(t, err)
|
||||||
mockChainInfoFetcher := &mock.ChainService{Slot: &slot}
|
|
||||||
|
db := dbtesting.SetupDB(t)
|
||||||
|
|
||||||
|
err = db.SaveLightClientBootstrap(l.Ctx, blockRoot[:], bootstrap)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
s := &Server{
|
s := &Server{
|
||||||
Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{
|
BeaconDB: db,
|
||||||
slot: l.State,
|
|
||||||
}},
|
|
||||||
Blocker: mockBlocker,
|
|
||||||
HeadFetcher: mockChainService,
|
|
||||||
ChainInfoFetcher: mockChainInfoFetcher,
|
|
||||||
}
|
}
|
||||||
request := httptest.NewRequest("GET", "http://foo.com/", nil)
|
request := httptest.NewRequest("GET", "http://foo.com/", nil)
|
||||||
request.SetPathValue("block_root", hexutil.Encode(blockRoot[:]))
|
request.SetPathValue("block_root", hexutil.Encode(blockRoot[:]))
|
||||||
@@ -179,16 +206,16 @@ func TestLightClientHandler_GetLightClientBootstrap(t *testing.T) {
|
|||||||
blockRoot, err := l.Block.Block().HashTreeRoot()
|
blockRoot, err := l.Block.Block().HashTreeRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
mockBlocker := &testutil.MockBlocker{BlockToReturn: l.Block}
|
bootstrap, err := lightclient.NewLightClientBootstrapFromBeaconState(l.Ctx, slot, l.State, l.Block)
|
||||||
mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot}
|
require.NoError(t, err)
|
||||||
mockChainInfoFetcher := &mock.ChainService{Slot: &slot}
|
|
||||||
|
db := dbtesting.SetupDB(t)
|
||||||
|
|
||||||
|
err = db.SaveLightClientBootstrap(l.Ctx, blockRoot[:], bootstrap)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
s := &Server{
|
s := &Server{
|
||||||
Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{
|
BeaconDB: db,
|
||||||
slot: l.State,
|
|
||||||
}},
|
|
||||||
Blocker: mockBlocker,
|
|
||||||
HeadFetcher: mockChainService,
|
|
||||||
ChainInfoFetcher: mockChainInfoFetcher,
|
|
||||||
}
|
}
|
||||||
request := httptest.NewRequest("GET", "http://foo.com/", nil)
|
request := httptest.NewRequest("GET", "http://foo.com/", nil)
|
||||||
request.SetPathValue("block_root", hexutil.Encode(blockRoot[:]))
|
request.SetPathValue("block_root", hexutil.Encode(blockRoot[:]))
|
||||||
@@ -220,57 +247,16 @@ func TestLightClientHandler_GetLightClientBootstrap(t *testing.T) {
|
|||||||
blockRoot, err := l.Block.Block().HashTreeRoot()
|
blockRoot, err := l.Block.Block().HashTreeRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
mockBlocker := &testutil.MockBlocker{BlockToReturn: l.Block}
|
bootstrap, err := lightclient.NewLightClientBootstrapFromBeaconState(l.Ctx, slot, l.State, l.Block)
|
||||||
mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot}
|
require.NoError(t, err)
|
||||||
mockChainInfoFetcher := &mock.ChainService{Slot: &slot}
|
|
||||||
|
db := dbtesting.SetupDB(t)
|
||||||
|
|
||||||
|
err = db.SaveLightClientBootstrap(l.Ctx, blockRoot[:], bootstrap)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
s := &Server{
|
s := &Server{
|
||||||
Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{
|
BeaconDB: db,
|
||||||
slot: l.State,
|
|
||||||
}},
|
|
||||||
Blocker: mockBlocker,
|
|
||||||
HeadFetcher: mockChainService,
|
|
||||||
ChainInfoFetcher: mockChainInfoFetcher,
|
|
||||||
}
|
|
||||||
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.StatusOK, writer.Code)
|
|
||||||
var resp structs.LightClientBootstrapResponse
|
|
||||||
err = json.Unmarshal(writer.Body.Bytes(), &resp)
|
|
||||||
require.NoError(t, err)
|
|
||||||
var respHeader structs.LightClientHeader
|
|
||||||
err = json.Unmarshal(resp.Data.Header, &respHeader)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, "electra", resp.Version)
|
|
||||||
|
|
||||||
blockHeader, err := l.Block.Header()
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, hexutil.Encode(blockHeader.Header.BodyRoot), respHeader.Beacon.BodyRoot)
|
|
||||||
require.Equal(t, strconv.FormatUint(uint64(blockHeader.Header.Slot), 10), respHeader.Beacon.Slot)
|
|
||||||
|
|
||||||
require.NotNil(t, resp.Data.CurrentSyncCommittee)
|
|
||||||
require.NotNil(t, resp.Data.CurrentSyncCommitteeBranch)
|
|
||||||
})
|
|
||||||
t.Run("fulu", func(t *testing.T) {
|
|
||||||
l := util.NewTestLightClient(t).SetupTestFulu(false) // result is same for true and false
|
|
||||||
|
|
||||||
slot := primitives.Slot(params.BeaconConfig().FuluForkEpoch * primitives.Epoch(params.BeaconConfig().SlotsPerEpoch)).Add(1)
|
|
||||||
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}
|
|
||||||
s := &Server{
|
|
||||||
Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{
|
|
||||||
slot: l.State,
|
|
||||||
}},
|
|
||||||
Blocker: mockBlocker,
|
|
||||||
HeadFetcher: mockChainService,
|
|
||||||
ChainInfoFetcher: mockChainInfoFetcher,
|
|
||||||
}
|
}
|
||||||
request := httptest.NewRequest("GET", "http://foo.com/", nil)
|
request := httptest.NewRequest("GET", "http://foo.com/", nil)
|
||||||
request.SetPathValue("block_root", hexutil.Encode(blockRoot[:]))
|
request.SetPathValue("block_root", hexutil.Encode(blockRoot[:]))
|
||||||
|
|||||||
Reference in New Issue
Block a user