diff --git a/beacon-chain/blockchain/BUILD.bazel b/beacon-chain/blockchain/BUILD.bazel index b60a763d0f..8bbd65338c 100644 --- a/beacon-chain/blockchain/BUILD.bazel +++ b/beacon-chain/blockchain/BUILD.bazel @@ -13,7 +13,6 @@ go_library( "head.go", "head_sync_committee_info.go", "init_sync_process_block.go", - "lightclient.go", "log.go", "merge_ascii_art.go", "metrics.go", @@ -48,6 +47,7 @@ go_library( "//beacon-chain/core/feed:go_default_library", "//beacon-chain/core/feed/state:go_default_library", "//beacon-chain/core/helpers:go_default_library", + "//beacon-chain/core/light-client:go_default_library", "//beacon-chain/core/signing:go_default_library", "//beacon-chain/core/time:go_default_library", "//beacon-chain/core/transition:go_default_library", @@ -84,7 +84,6 @@ go_library( "//proto/engine/v1:go_default_library", "//proto/eth/v1:go_default_library", "//proto/eth/v2:go_default_library", - "//proto/migration:go_default_library", "//proto/prysm/v1alpha1:go_default_library", "//proto/prysm/v1alpha1/attestation:go_default_library", "//runtime/version:go_default_library", @@ -117,7 +116,6 @@ go_test( "head_test.go", "init_sync_process_block_test.go", "init_test.go", - "lightclient_test.go", "log_test.go", "metrics_test.go", "mock_test.go", @@ -174,7 +172,6 @@ go_test( "//encoding/bytesutil:go_default_library", "//proto/engine/v1:go_default_library", "//proto/eth/v1:go_default_library", - "//proto/eth/v2:go_default_library", "//proto/prysm/v1alpha1:go_default_library", "//runtime/version:go_default_library", "//testing/assert:go_default_library", diff --git a/beacon-chain/blockchain/lightclient_test.go b/beacon-chain/blockchain/lightclient_test.go deleted file mode 100644 index 41a3a7cfac..0000000000 --- a/beacon-chain/blockchain/lightclient_test.go +++ /dev/null @@ -1,160 +0,0 @@ -package blockchain - -import ( - "context" - "testing" - - "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" - "github.com/prysmaticlabs/prysm/v5/config/params" - "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" - "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces" - "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" - v1 "github.com/prysmaticlabs/prysm/v5/proto/eth/v1" - ethpbv2 "github.com/prysmaticlabs/prysm/v5/proto/eth/v2" - ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" - "github.com/prysmaticlabs/prysm/v5/testing/require" - "github.com/prysmaticlabs/prysm/v5/testing/util" -) - -type testlc struct { - t *testing.T - ctx context.Context - state state.BeaconState - block interfaces.ReadOnlySignedBeaconBlock - attestedState state.BeaconState - attestedHeader *ethpb.BeaconBlockHeader -} - -func newTestLc(t *testing.T) *testlc { - return &testlc{t: t} -} - -func (l *testlc) setupTest() *testlc { - ctx := context.Background() - - slot := primitives.Slot(params.BeaconConfig().AltairForkEpoch * primitives.Epoch(params.BeaconConfig().SlotsPerEpoch)).Add(1) - - attestedState, err := util.NewBeaconStateCapella() - require.NoError(l.t, err) - err = attestedState.SetSlot(slot) - require.NoError(l.t, err) - - parent := util.NewBeaconBlockCapella() - parent.Block.Slot = slot - - signedParent, err := blocks.NewSignedBeaconBlock(parent) - require.NoError(l.t, err) - - parentHeader, err := signedParent.Header() - require.NoError(l.t, err) - attestedHeader := parentHeader.Header - - err = attestedState.SetLatestBlockHeader(attestedHeader) - require.NoError(l.t, err) - attestedStateRoot, err := attestedState.HashTreeRoot(ctx) - require.NoError(l.t, err) - - // get a new signed block so the root is updated with the new state root - parent.Block.StateRoot = attestedStateRoot[:] - signedParent, err = blocks.NewSignedBeaconBlock(parent) - require.NoError(l.t, err) - - state, err := util.NewBeaconStateCapella() - require.NoError(l.t, err) - err = state.SetSlot(slot) - require.NoError(l.t, err) - - parentRoot, err := signedParent.Block().HashTreeRoot() - require.NoError(l.t, err) - - block := util.NewBeaconBlockCapella() - block.Block.Slot = slot - block.Block.ParentRoot = parentRoot[:] - - for i := uint64(0); i < params.BeaconConfig().MinSyncCommitteeParticipants; i++ { - block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) - } - - signedBlock, err := blocks.NewSignedBeaconBlock(block) - require.NoError(l.t, err) - - h, err := signedBlock.Header() - require.NoError(l.t, err) - - err = state.SetLatestBlockHeader(h.Header) - require.NoError(l.t, err) - stateRoot, err := state.HashTreeRoot(ctx) - require.NoError(l.t, err) - - // get a new signed block so the root is updated with the new state root - block.Block.StateRoot = stateRoot[:] - signedBlock, err = blocks.NewSignedBeaconBlock(block) - require.NoError(l.t, err) - - l.state = state - l.attestedState = attestedState - l.attestedHeader = attestedHeader - l.block = signedBlock - l.ctx = ctx - - return l -} - -func (l *testlc) checkAttestedHeader(update *ethpbv2.LightClientUpdate) { - require.Equal(l.t, l.attestedHeader.Slot, update.AttestedHeader.Slot, "Attested header slot is not equal") - require.Equal(l.t, l.attestedHeader.ProposerIndex, update.AttestedHeader.ProposerIndex, "Attested header proposer index is not equal") - require.DeepSSZEqual(l.t, l.attestedHeader.ParentRoot, update.AttestedHeader.ParentRoot, "Attested header parent root is not equal") - require.DeepSSZEqual(l.t, l.attestedHeader.BodyRoot, update.AttestedHeader.BodyRoot, "Attested header body root is not equal") - - attestedStateRoot, err := l.attestedState.HashTreeRoot(l.ctx) - require.NoError(l.t, err) - require.DeepSSZEqual(l.t, attestedStateRoot[:], update.AttestedHeader.StateRoot, "Attested header state root is not equal") -} - -func (l *testlc) checkSyncAggregate(update *ethpbv2.LightClientUpdate) { - syncAggregate, err := l.block.Block().Body().SyncAggregate() - require.NoError(l.t, err) - require.DeepSSZEqual(l.t, syncAggregate.SyncCommitteeBits, update.SyncAggregate.SyncCommitteeBits, "SyncAggregate bits is not equal") - require.DeepSSZEqual(l.t, syncAggregate.SyncCommitteeSignature, update.SyncAggregate.SyncCommitteeSignature, "SyncAggregate signature is not equal") -} - -func TestLightClient_NewLightClientOptimisticUpdateFromBeaconState(t *testing.T) { - l := newTestLc(t).setupTest() - - update, err := NewLightClientOptimisticUpdateFromBeaconState(l.ctx, l.state, l.block, l.attestedState) - require.NoError(t, err) - require.NotNil(t, update, "update is nil") - - require.Equal(t, l.block.Block().Slot(), update.SignatureSlot, "Signature slot is not equal") - - l.checkSyncAggregate(update) - l.checkAttestedHeader(update) - - require.Equal(t, (*v1.BeaconBlockHeader)(nil), update.FinalizedHeader, "Finalized header is not nil") - require.DeepSSZEqual(t, ([][]byte)(nil), update.FinalityBranch, "Finality branch is not nil") -} - -func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) { - l := newTestLc(t).setupTest() - - update, err := NewLightClientFinalityUpdateFromBeaconState(l.ctx, l.state, l.block, l.attestedState, nil) - require.NoError(t, err) - require.NotNil(t, update, "update is nil") - - require.Equal(t, l.block.Block().Slot(), update.SignatureSlot, "Signature slot is not equal") - - l.checkSyncAggregate(update) - l.checkAttestedHeader(update) - - zeroHash := params.BeaconConfig().ZeroHash[:] - require.NotNil(t, update.FinalizedHeader, "Finalized header is nil") - require.Equal(t, primitives.Slot(0), update.FinalizedHeader.Slot, "Finalized header slot is not zero") - require.Equal(t, primitives.ValidatorIndex(0), update.FinalizedHeader.ProposerIndex, "Finalized header proposer index is not zero") - require.DeepSSZEqual(t, zeroHash, update.FinalizedHeader.ParentRoot, "Finalized header parent root is not zero") - require.DeepSSZEqual(t, zeroHash, update.FinalizedHeader.StateRoot, "Finalized header state root is not zero") - require.DeepSSZEqual(t, zeroHash, update.FinalizedHeader.BodyRoot, "Finalized header body root is not zero") - require.Equal(t, FinalityBranchNumOfLeaves, len(update.FinalityBranch), "Invalid finality branch leaves") - for _, leaf := range update.FinalityBranch { - require.DeepSSZEqual(t, zeroHash, leaf, "Leaf is not zero") - } -} diff --git a/beacon-chain/blockchain/process_block_helpers.go b/beacon-chain/blockchain/process_block_helpers.go index 9e07cb4e17..b878b95e6a 100644 --- a/beacon-chain/blockchain/process_block_helpers.go +++ b/beacon-chain/blockchain/process_block_helpers.go @@ -5,6 +5,8 @@ import ( "fmt" "time" + lightclient "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/light-client" + "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/feed" @@ -176,7 +178,7 @@ func (s *Service) sendLightClientFinalityUpdate(ctx context.Context, signed inte } } - update, err := NewLightClientFinalityUpdateFromBeaconState( + update, err := lightclient.NewLightClientFinalityUpdateFromBeaconState( ctx, postState, signed, @@ -191,7 +193,7 @@ func (s *Service) sendLightClientFinalityUpdate(ctx context.Context, signed inte // Return the result result := ðpbv2.LightClientFinalityUpdateWithVersion{ Version: ethpbv2.Version(signed.Version()), - Data: CreateLightClientFinalityUpdate(update), + Data: lightclient.CreateLightClientFinalityUpdate(update), } // Send event @@ -211,7 +213,7 @@ func (s *Service) sendLightClientOptimisticUpdate(ctx context.Context, signed in return 0, errors.Wrap(err, "could not get attested state") } - update, err := NewLightClientOptimisticUpdateFromBeaconState( + update, err := lightclient.NewLightClientOptimisticUpdateFromBeaconState( ctx, postState, signed, @@ -225,7 +227,7 @@ func (s *Service) sendLightClientOptimisticUpdate(ctx context.Context, signed in // Return the result result := ðpbv2.LightClientOptimisticUpdateWithVersion{ Version: ethpbv2.Version(signed.Version()), - Data: CreateLightClientOptimisticUpdate(update), + Data: lightclient.CreateLightClientOptimisticUpdate(update), } return s.cfg.StateNotifier.StateFeed().Send(&feed.Event{ diff --git a/beacon-chain/core/light-client/BUILD.bazel b/beacon-chain/core/light-client/BUILD.bazel new file mode 100644 index 0000000000..bd96ab1774 --- /dev/null +++ b/beacon-chain/core/light-client/BUILD.bazel @@ -0,0 +1,31 @@ +load("@prysm//tools/go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["lightclient.go"], + importpath = "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/light-client", + visibility = ["//visibility:public"], + deps = [ + "//beacon-chain/state:go_default_library", + "//config/params:go_default_library", + "//consensus-types/interfaces:go_default_library", + "//encoding/bytesutil:go_default_library", + "//proto/eth/v1:go_default_library", + "//proto/eth/v2:go_default_library", + "//proto/migration:go_default_library", + "//time/slots:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["lightclient_test.go"], + deps = [ + ":go_default_library", + "//config/params:go_default_library", + "//consensus-types/primitives:go_default_library", + "//proto/eth/v1:go_default_library", + "//testing/require:go_default_library", + "//testing/util:go_default_library", + ], +) diff --git a/beacon-chain/blockchain/lightclient.go b/beacon-chain/core/light-client/lightclient.go similarity index 99% rename from beacon-chain/blockchain/lightclient.go rename to beacon-chain/core/light-client/lightclient.go index ade4258cbb..009d07b432 100644 --- a/beacon-chain/blockchain/lightclient.go +++ b/beacon-chain/core/light-client/lightclient.go @@ -1,18 +1,20 @@ -package blockchain +package light_client import ( "bytes" - "context" "fmt" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces" - "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" ethpbv1 "github.com/prysmaticlabs/prysm/v5/proto/eth/v1" ethpbv2 "github.com/prysmaticlabs/prysm/v5/proto/eth/v2" "github.com/prysmaticlabs/prysm/v5/proto/migration" "github.com/prysmaticlabs/prysm/v5/time/slots" + + "context" + + "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" ) const ( diff --git a/beacon-chain/core/light-client/lightclient_test.go b/beacon-chain/core/light-client/lightclient_test.go new file mode 100644 index 0000000000..729a51416a --- /dev/null +++ b/beacon-chain/core/light-client/lightclient_test.go @@ -0,0 +1,54 @@ +package light_client_test + +import ( + "testing" + + lightClient "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/light-client" + "github.com/prysmaticlabs/prysm/v5/config/params" + "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" + "github.com/prysmaticlabs/prysm/v5/testing/require" + "github.com/prysmaticlabs/prysm/v5/testing/util" + + v1 "github.com/prysmaticlabs/prysm/v5/proto/eth/v1" +) + +func TestLightClient_NewLightClientOptimisticUpdateFromBeaconState(t *testing.T) { + l := util.NewTestLightClient(t).SetupTest() + + update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState) + require.NoError(t, err) + require.NotNil(t, update, "update is nil") + + require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot, "Signature slot is not equal") + + l.CheckSyncAggregate(update) + l.CheckAttestedHeader(update) + + require.Equal(t, (*v1.BeaconBlockHeader)(nil), update.FinalizedHeader, "Finalized header is not nil") + require.DeepSSZEqual(t, ([][]byte)(nil), update.FinalityBranch, "Finality branch is not nil") +} + +func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) { + l := util.NewTestLightClient(t).SetupTest() + + update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, nil) + require.NoError(t, err) + require.NotNil(t, update, "update is nil") + + require.Equal(t, l.Block.Block().Slot(), update.SignatureSlot, "Signature slot is not equal") + + l.CheckSyncAggregate(update) + l.CheckAttestedHeader(update) + + zeroHash := params.BeaconConfig().ZeroHash[:] + require.NotNil(t, update.FinalizedHeader, "Finalized header is nil") + require.Equal(t, primitives.Slot(0), update.FinalizedHeader.Slot, "Finalized header slot is not zero") + require.Equal(t, primitives.ValidatorIndex(0), update.FinalizedHeader.ProposerIndex, "Finalized header proposer index is not zero") + require.DeepSSZEqual(t, zeroHash, update.FinalizedHeader.ParentRoot, "Finalized header parent root is not zero") + require.DeepSSZEqual(t, zeroHash, update.FinalizedHeader.StateRoot, "Finalized header state root is not zero") + require.DeepSSZEqual(t, zeroHash, update.FinalizedHeader.BodyRoot, "Finalized header body root is not zero") + require.Equal(t, lightClient.FinalityBranchNumOfLeaves, len(update.FinalityBranch), "Invalid finality branch leaves") + for _, leaf := range update.FinalityBranch { + require.DeepSSZEqual(t, zeroHash, leaf, "Leaf is not zero") + } +} diff --git a/beacon-chain/db/iface/BUILD.bazel b/beacon-chain/db/iface/BUILD.bazel index 81929a26a4..993d1fd84c 100644 --- a/beacon-chain/db/iface/BUILD.bazel +++ b/beacon-chain/db/iface/BUILD.bazel @@ -18,6 +18,7 @@ go_library( "//consensus-types/primitives:go_default_library", "//monitoring/backup:go_default_library", "//proto/dbval:go_default_library", + "//proto/eth/v2:go_default_library", "//proto/prysm/v1alpha1:go_default_library", "@com_github_ethereum_go_ethereum//common:go_default_library", ], diff --git a/beacon-chain/db/iface/interface.go b/beacon-chain/db/iface/interface.go index 82e5a019f7..b75960ef55 100644 --- a/beacon-chain/db/iface/interface.go +++ b/beacon-chain/db/iface/interface.go @@ -7,6 +7,8 @@ import ( "context" "io" + ethpbv2 "github.com/prysmaticlabs/prysm/v5/proto/eth/v2" + "github.com/ethereum/go-ethereum/common" "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/filters" slashertypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/slasher/types" @@ -56,6 +58,9 @@ type ReadOnlyDatabase interface { // Fee recipients operations. FeeRecipientByValidatorID(ctx context.Context, id primitives.ValidatorIndex) (common.Address, error) RegistrationByValidatorID(ctx context.Context, id primitives.ValidatorIndex) (*ethpb.ValidatorRegistrationV1, error) + // light client operations + LightClientUpdates(ctx context.Context, startPeriod, endPeriod uint64) (map[uint64]*ethpbv2.LightClientUpdateWithVersion, error) + LightClientUpdate(ctx context.Context, period uint64) (*ethpbv2.LightClientUpdateWithVersion, error) // origin checkpoint sync support OriginCheckpointBlockRoot(ctx context.Context) ([32]byte, error) @@ -92,6 +97,8 @@ type NoHeadAccessDatabase interface { // Fee recipients operations. SaveFeeRecipientsByValidatorIDs(ctx context.Context, ids []primitives.ValidatorIndex, addrs []common.Address) error SaveRegistrationsByValidatorIDs(ctx context.Context, ids []primitives.ValidatorIndex, regs []*ethpb.ValidatorRegistrationV1) error + // light client operations + SaveLightClientUpdate(ctx context.Context, period uint64, update *ethpbv2.LightClientUpdateWithVersion) error CleanUpDirtyStates(ctx context.Context, slotsPerArchivedPoint primitives.Slot) error } diff --git a/beacon-chain/db/kv/BUILD.bazel b/beacon-chain/db/kv/BUILD.bazel index 464a6c82e1..1d634375f3 100644 --- a/beacon-chain/db/kv/BUILD.bazel +++ b/beacon-chain/db/kv/BUILD.bazel @@ -16,6 +16,7 @@ go_library( "genesis.go", "key.go", "kv.go", + "lightclient.go", "log.go", "migration.go", "migration_archived_index.go", @@ -51,6 +52,7 @@ go_library( "//monitoring/progress:go_default_library", "//monitoring/tracing:go_default_library", "//proto/dbval:go_default_library", + "//proto/eth/v2:go_default_library", "//proto/prysm/v1alpha1:go_default_library", "//runtime/version:go_default_library", "//time:go_default_library", @@ -87,6 +89,7 @@ go_test( "genesis_test.go", "init_test.go", "kv_test.go", + "lightclient_test.go", "migration_archived_index_test.go", "migration_block_slot_index_test.go", "migration_state_validators_test.go", @@ -113,6 +116,7 @@ go_test( "//encoding/bytesutil:go_default_library", "//proto/dbval:go_default_library", "//proto/engine/v1:go_default_library", + "//proto/eth/v2:go_default_library", "//proto/prysm/v1alpha1:go_default_library", "//proto/testing:go_default_library", "//runtime/version:go_default_library", diff --git a/beacon-chain/db/kv/kv.go b/beacon-chain/db/kv/kv.go index 90f01a63df..63e49e3048 100644 --- a/beacon-chain/db/kv/kv.go +++ b/beacon-chain/db/kv/kv.go @@ -107,6 +107,7 @@ var Buckets = [][]byte{ powchainBucket, stateSummaryBucket, stateValidatorsBucket, + lightClientUpdatesBucket, // Indices buckets. blockSlotIndicesBucket, stateSlotIndicesBucket, diff --git a/beacon-chain/db/kv/lightclient.go b/beacon-chain/db/kv/lightclient.go new file mode 100644 index 0000000000..4677dc6adb --- /dev/null +++ b/beacon-chain/db/kv/lightclient.go @@ -0,0 +1,79 @@ +package kv + +import ( + "context" + "encoding/binary" + "fmt" + + "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" + ethpbv2 "github.com/prysmaticlabs/prysm/v5/proto/eth/v2" + bolt "go.etcd.io/bbolt" + "go.opencensus.io/trace" +) + +func (s *Store) SaveLightClientUpdate(ctx context.Context, period uint64, update *ethpbv2.LightClientUpdateWithVersion) error { + ctx, span := trace.StartSpan(ctx, "BeaconDB.saveLightClientUpdate") + defer span.End() + + return s.db.Update(func(tx *bolt.Tx) error { + bkt := tx.Bucket(lightClientUpdatesBucket) + updateMarshalled, err := encode(ctx, update) + if err != nil { + return err + } + return bkt.Put(bytesutil.Uint64ToBytesBigEndian(period), updateMarshalled) + }) +} + +func (s *Store) LightClientUpdates(ctx context.Context, startPeriod, endPeriod uint64) (map[uint64]*ethpbv2.LightClientUpdateWithVersion, error) { + ctx, span := trace.StartSpan(ctx, "BeaconDB.LightClientUpdates") + defer span.End() + + if startPeriod > endPeriod { + return nil, fmt.Errorf("start period %d is greater than end period %d", startPeriod, endPeriod) + } + + updates := make(map[uint64]*ethpbv2.LightClientUpdateWithVersion) + err := s.db.View(func(tx *bolt.Tx) error { + bkt := tx.Bucket(lightClientUpdatesBucket) + c := bkt.Cursor() + + firstPeriodInDb, _ := c.First() + if firstPeriodInDb == nil { + return nil + } + + for k, v := c.Seek(bytesutil.Uint64ToBytesBigEndian(startPeriod)); k != nil && binary.BigEndian.Uint64(k) <= endPeriod; k, v = c.Next() { + currentPeriod := binary.BigEndian.Uint64(k) + + var update ethpbv2.LightClientUpdateWithVersion + if err := decode(ctx, v, &update); err != nil { + return err + } + updates[currentPeriod] = &update + } + + return nil + }) + + if err != nil { + return nil, err + } + return updates, err +} + +func (s *Store) LightClientUpdate(ctx context.Context, period uint64) (*ethpbv2.LightClientUpdateWithVersion, error) { + ctx, span := trace.StartSpan(ctx, "BeaconDB.LightClientUpdate") + defer span.End() + + var update ethpbv2.LightClientUpdateWithVersion + err := s.db.View(func(tx *bolt.Tx) error { + bkt := tx.Bucket(lightClientUpdatesBucket) + updateBytes := bkt.Get(bytesutil.Uint64ToBytesBigEndian(period)) + if updateBytes == nil { + return nil + } + return decode(ctx, updateBytes, &update) + }) + return &update, err +} diff --git a/beacon-chain/db/kv/lightclient_test.go b/beacon-chain/db/kv/lightclient_test.go new file mode 100644 index 0000000000..528e55023d --- /dev/null +++ b/beacon-chain/db/kv/lightclient_test.go @@ -0,0 +1,648 @@ +package kv + +import ( + "context" + "testing" + + "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" + ethpbv2 "github.com/prysmaticlabs/prysm/v5/proto/eth/v2" + "github.com/prysmaticlabs/prysm/v5/testing/require" +) + +func TestStore_LightclientUpdate_CanSaveRetrieve(t *testing.T) { + db := setupDB(t) + ctx := context.Background() + update := ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: 7, + } + + period := uint64(1) + err := db.SaveLightClientUpdate(ctx, period, ðpbv2.LightClientUpdateWithVersion{ + Version: 1, + Data: update, + }) + require.NoError(t, err) + + // Retrieve the update + retrievedUpdate, err := db.LightClientUpdate(ctx, period) + require.NoError(t, err) + require.Equal(t, update.SignatureSlot, retrievedUpdate.Data.SignatureSlot, "retrieved update does not match saved update") + +} + +func TestStore_LightclientUpdates_canRetrieveRange(t *testing.T) { + db := setupDB(t) + ctx := context.Background() + updates := []*ethpbv2.LightClientUpdateWithVersion{ + { + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: 7, + }, + }, + { + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: 8, + }, + }, + { + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: 9, + }, + }, + } + + for i, update := range updates { + err := db.SaveLightClientUpdate(ctx, uint64(i+1), update) + require.NoError(t, err) + } + + // Retrieve the updates + retrievedUpdatesMap, err := db.LightClientUpdates(ctx, 1, 3) + require.NoError(t, err) + require.Equal(t, len(updates), len(retrievedUpdatesMap), "retrieved updates do not match saved updates") + for i, update := range updates { + require.Equal(t, update.Data.SignatureSlot, retrievedUpdatesMap[uint64(i+1)].Data.SignatureSlot, "retrieved update does not match saved update") + } + +} + +func TestStore_LightClientUpdate_EndPeriodSmallerThanStartPeriod(t *testing.T) { + db := setupDB(t) + ctx := context.Background() + updates := []*ethpbv2.LightClientUpdateWithVersion{ + { + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: 7, + }, + }, + { + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: 8, + }, + }, + { + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: 9, + }, + }, + } + + for i, update := range updates { + err := db.SaveLightClientUpdate(ctx, uint64(i+1), update) + require.NoError(t, err) + } + + // Retrieve the updates + retrievedUpdates, err := db.LightClientUpdates(ctx, 3, 1) + require.NotNil(t, err) + require.Equal(t, err.Error(), "start period 3 is greater than end period 1") + require.IsNil(t, retrievedUpdates) + +} + +func TestStore_LightClientUpdate_EndPeriodEqualToStartPeriod(t *testing.T) { + db := setupDB(t) + ctx := context.Background() + updates := []*ethpbv2.LightClientUpdateWithVersion{ + { + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: 7, + }, + }, + { + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: 8, + }, + }, + { + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: 9, + }, + }, + } + + for i, update := range updates { + err := db.SaveLightClientUpdate(ctx, uint64(i+1), update) + require.NoError(t, err) + } + + // Retrieve the updates + retrievedUpdates, err := db.LightClientUpdates(ctx, 2, 2) + require.NoError(t, err) + require.Equal(t, 1, len(retrievedUpdates)) + require.Equal(t, updates[1].Data.SignatureSlot, retrievedUpdates[2].Data.SignatureSlot, "retrieved update does not match saved update") +} + +func TestStore_LightClientUpdate_StartPeriodBeforeFirstUpdate(t *testing.T) { + db := setupDB(t) + ctx := context.Background() + updates := []*ethpbv2.LightClientUpdateWithVersion{ + { + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: 7, + }, + }, + { + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: 8, + }, + }, + { + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: 9, + }, + }, + } + + for i, update := range updates { + err := db.SaveLightClientUpdate(ctx, uint64(i+2), update) + require.NoError(t, err) + } + + // Retrieve the updates + retrievedUpdates, err := db.LightClientUpdates(ctx, 0, 4) + require.NoError(t, err) + require.Equal(t, 3, len(retrievedUpdates)) + for i, update := range updates { + require.Equal(t, update.Data.SignatureSlot, retrievedUpdates[uint64(i+2)].Data.SignatureSlot, "retrieved update does not match saved update") + } +} + +func TestStore_LightClientUpdate_EndPeriodAfterLastUpdate(t *testing.T) { + db := setupDB(t) + ctx := context.Background() + updates := []*ethpbv2.LightClientUpdateWithVersion{ + { + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: 7, + }, + }, + { + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: 8, + }, + }, + { + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: 9, + }, + }, + } + + for i, update := range updates { + err := db.SaveLightClientUpdate(ctx, uint64(i+1), update) + require.NoError(t, err) + } + + // Retrieve the updates + retrievedUpdates, err := db.LightClientUpdates(ctx, 1, 6) + require.NoError(t, err) + require.Equal(t, 3, len(retrievedUpdates)) + for i, update := range updates { + require.Equal(t, update.Data.SignatureSlot, retrievedUpdates[uint64(i+1)].Data.SignatureSlot, "retrieved update does not match saved update") + } +} + +func TestStore_LightClientUpdate_PartialUpdates(t *testing.T) { + db := setupDB(t) + ctx := context.Background() + updates := []*ethpbv2.LightClientUpdateWithVersion{ + { + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: 7, + }, + }, + { + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: 8, + }, + }, + { + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: 9, + }, + }, + } + + for i, update := range updates { + err := db.SaveLightClientUpdate(ctx, uint64(i+1), update) + require.NoError(t, err) + } + + // Retrieve the updates + retrievedUpdates, err := db.LightClientUpdates(ctx, 1, 2) + require.NoError(t, err) + require.Equal(t, 2, len(retrievedUpdates)) + for i, update := range updates[:2] { + require.Equal(t, update.Data.SignatureSlot, retrievedUpdates[uint64(i+1)].Data.SignatureSlot, "retrieved update does not match saved update") + } +} + +func TestStore_LightClientUpdate_MissingPeriods_SimpleData(t *testing.T) { + db := setupDB(t) + ctx := context.Background() + updates := []*ethpbv2.LightClientUpdateWithVersion{ + { + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: 7, + }, + }, + { + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: 8, + }, + }, + { + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: 11, + }, + }, + { + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: 12, + }, + }, + } + + for _, update := range updates { + err := db.SaveLightClientUpdate(ctx, uint64(update.Data.SignatureSlot), update) + require.NoError(t, err) + } + + // Retrieve the updates + retrievedUpdates, err := db.LightClientUpdates(ctx, 7, 12) + require.NoError(t, err) + require.Equal(t, 4, len(retrievedUpdates)) + for _, update := range updates { + require.Equal(t, update.Data.SignatureSlot, retrievedUpdates[uint64(update.Data.SignatureSlot)].Data.SignatureSlot, "retrieved update does not match saved update") + } + + // Retrieve the updates from the middle + retrievedUpdates, err = db.LightClientUpdates(ctx, 8, 12) + require.NoError(t, err) + require.Equal(t, 3, len(retrievedUpdates)) + require.Equal(t, updates[1].Data.SignatureSlot, retrievedUpdates[8].Data.SignatureSlot, "retrieved update does not match saved update") + require.Equal(t, updates[2].Data.SignatureSlot, retrievedUpdates[11].Data.SignatureSlot, "retrieved update does not match saved update") + require.Equal(t, updates[3].Data.SignatureSlot, retrievedUpdates[12].Data.SignatureSlot, "retrieved update does not match saved update") + + // Retrieve the updates from after the missing period + retrievedUpdates, err = db.LightClientUpdates(ctx, 11, 12) + require.NoError(t, err) + require.Equal(t, 2, len(retrievedUpdates)) + require.Equal(t, updates[2].Data.SignatureSlot, retrievedUpdates[11].Data.SignatureSlot, "retrieved update does not match saved update") + require.Equal(t, updates[3].Data.SignatureSlot, retrievedUpdates[12].Data.SignatureSlot, "retrieved update does not match saved update") + + //retrieve the updates from before the missing period to after the missing period + retrievedUpdates, err = db.LightClientUpdates(ctx, 3, 15) + require.NoError(t, err) + require.Equal(t, 4, len(retrievedUpdates)) + require.Equal(t, updates[0].Data.SignatureSlot, retrievedUpdates[7].Data.SignatureSlot, "retrieved update does not match saved update") + require.Equal(t, updates[1].Data.SignatureSlot, retrievedUpdates[8].Data.SignatureSlot, "retrieved update does not match saved update") + require.Equal(t, updates[2].Data.SignatureSlot, retrievedUpdates[11].Data.SignatureSlot, "retrieved update does not match saved update") + require.Equal(t, updates[3].Data.SignatureSlot, retrievedUpdates[12].Data.SignatureSlot, "retrieved update does not match saved update") +} + +func TestStore_LightClientUpdate_EmptyDB(t *testing.T) { + db := setupDB(t) + ctx := context.Background() + + // Retrieve the updates + retrievedUpdates, err := db.LightClientUpdates(ctx, 1, 3) + require.IsNil(t, err) + require.Equal(t, 0, len(retrievedUpdates)) +} + +func TestStore_LightClientUpdate_MissingPeriodsAtTheEnd_SimpleData(t *testing.T) { + db := setupDB(t) + ctx := context.Background() + + for i := 1; i < 4; i++ { + update := ðpbv2.LightClientUpdateWithVersion{ + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: primitives.Slot(uint64(i)), + }, + } + err := db.SaveLightClientUpdate(ctx, uint64(i), update) + require.NoError(t, err) + } + for i := 7; i < 10; i++ { + update := ðpbv2.LightClientUpdateWithVersion{ + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: primitives.Slot(uint64(i)), + }, + } + err := db.SaveLightClientUpdate(ctx, uint64(i), update) + require.NoError(t, err) + } + + // Retrieve the updates from 1 to 5 + retrievedUpdates, err := db.LightClientUpdates(ctx, 1, 5) + require.NoError(t, err) + require.Equal(t, 3, len(retrievedUpdates)) + require.Equal(t, primitives.Slot(1), retrievedUpdates[1].Data.SignatureSlot, "retrieved update does not match saved update") + require.Equal(t, primitives.Slot(2), retrievedUpdates[2].Data.SignatureSlot, "retrieved update does not match saved update") + require.Equal(t, primitives.Slot(3), retrievedUpdates[3].Data.SignatureSlot, "retrieved update does not match saved update") + +} + +func setupLightClientTestDB(t *testing.T) (*Store, context.Context) { + db := setupDB(t) + ctx := context.Background() + + for i := 10; i < 101; i++ { // 10 to 100 + update := ðpbv2.LightClientUpdateWithVersion{ + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: primitives.Slot(uint64(i)), + }, + } + err := db.SaveLightClientUpdate(ctx, uint64(i), update) + require.NoError(t, err) + } + + for i := 110; i < 201; i++ { // 110 to 200 + update := ðpbv2.LightClientUpdateWithVersion{ + Version: 1, + Data: ðpbv2.LightClientUpdate{ + AttestedHeader: nil, + NextSyncCommittee: nil, + NextSyncCommitteeBranch: nil, + FinalizedHeader: nil, + FinalityBranch: nil, + SyncAggregate: nil, + SignatureSlot: primitives.Slot(uint64(i)), + }, + } + err := db.SaveLightClientUpdate(ctx, uint64(i), update) + require.NoError(t, err) + } + + return db, ctx +} + +func TestStore_LightClientUpdate_MissingPeriodsInTheMiddleDistributed(t *testing.T) { + db, ctx := setupLightClientTestDB(t) + + // Retrieve the updates - should fail because of missing periods in the middle + retrievedUpdates, err := db.LightClientUpdates(ctx, 1, 300) + require.NoError(t, err) + require.Equal(t, 91*2, len(retrievedUpdates)) + for i := 10; i < 101; i++ { + require.Equal(t, primitives.Slot(uint64(i)), retrievedUpdates[uint64(i)].Data.SignatureSlot, "retrieved update does not match saved update") + } + for i := 110; i < 201; i++ { + require.Equal(t, primitives.Slot(uint64(i)), retrievedUpdates[uint64(i)].Data.SignatureSlot, "retrieved update does not match saved update") + } + +} + +func TestStore_LightClientUpdate_RetrieveValidRangeFromStart(t *testing.T) { + db, ctx := setupLightClientTestDB(t) + + // retrieve 1 to 100 - should work because all periods are present after the firstPeriodInDB > startPeriod + retrievedUpdates, err := db.LightClientUpdates(ctx, 1, 100) + require.NoError(t, err) + require.Equal(t, 91, len(retrievedUpdates)) + for i := 10; i < 101; i++ { + require.Equal(t, primitives.Slot(uint64(i)), retrievedUpdates[uint64(i)].Data.SignatureSlot, "retrieved update does not match saved update") + } +} + +func TestStore_LightClientUpdate_RetrieveValidRangeInTheMiddle(t *testing.T) { + db, ctx := setupLightClientTestDB(t) + + // retrieve 110 to 200 - should work because all periods are present + retrievedUpdates, err := db.LightClientUpdates(ctx, 110, 200) + require.NoError(t, err) + require.Equal(t, 91, len(retrievedUpdates)) + for i := 110; i < 201; i++ { + require.Equal(t, primitives.Slot(uint64(i)), retrievedUpdates[uint64(i)].Data.SignatureSlot, "retrieved update does not match saved update") + } +} + +func TestStore_LightClientUpdate_MissingPeriodInTheMiddleConcentrated(t *testing.T) { + db, ctx := setupLightClientTestDB(t) + + // retrieve 100 to 200 + retrievedUpdates, err := db.LightClientUpdates(ctx, 100, 200) + require.NoError(t, err) + require.Equal(t, 92, len(retrievedUpdates)) + require.Equal(t, primitives.Slot(100), retrievedUpdates[100].Data.SignatureSlot, "retrieved update does not match saved update") + for i := 110; i < 201; i++ { + require.Equal(t, primitives.Slot(uint64(i)), retrievedUpdates[uint64(i)].Data.SignatureSlot, "retrieved update does not match saved update") + } +} + +func TestStore_LightClientUpdate_MissingPeriodsAtTheEnd(t *testing.T) { + db, ctx := setupLightClientTestDB(t) + + // retrieve 10 to 109 + retrievedUpdates, err := db.LightClientUpdates(ctx, 10, 109) + require.NoError(t, err) + require.Equal(t, 91, len(retrievedUpdates)) + for i := 10; i < 101; i++ { + require.Equal(t, primitives.Slot(uint64(i)), retrievedUpdates[uint64(i)].Data.SignatureSlot, "retrieved update does not match saved update") + } +} + +func TestStore_LightClientUpdate_MissingPeriodsAtTheBeginning(t *testing.T) { + db, ctx := setupLightClientTestDB(t) + + // retrieve 105 to 200 + retrievedUpdates, err := db.LightClientUpdates(ctx, 105, 200) + require.NoError(t, err) + require.Equal(t, 91, len(retrievedUpdates)) + for i := 110; i < 201; i++ { + require.Equal(t, primitives.Slot(uint64(i)), retrievedUpdates[uint64(i)].Data.SignatureSlot, "retrieved update does not match saved update") + } +} + +func TestStore_LightClientUpdate_StartPeriodGreaterThanLastPeriod(t *testing.T) { + db, ctx := setupLightClientTestDB(t) + + // retrieve 300 to 400 + retrievedUpdates, err := db.LightClientUpdates(ctx, 300, 400) + require.NoError(t, err) + require.Equal(t, 0, len(retrievedUpdates)) + +} diff --git a/beacon-chain/db/kv/schema.go b/beacon-chain/db/kv/schema.go index 108849e9f6..30d950514c 100644 --- a/beacon-chain/db/kv/schema.go +++ b/beacon-chain/db/kv/schema.go @@ -17,6 +17,9 @@ var ( feeRecipientBucket = []byte("fee-recipient") registrationBucket = []byte("registration") + // Light Client Updates Bucket + lightClientUpdatesBucket = []byte("light-client-updates") + // Deprecated: This bucket was migrated in PR 6461. Do not use, except for migrations. slotsHasObjectBucket = []byte("slots-has-objects") // Deprecated: This bucket was migrated in PR 6461. Do not use, except for migrations. diff --git a/beacon-chain/rpc/endpoints.go b/beacon-chain/rpc/endpoints.go index 9a67d2d8f3..622e6be912 100644 --- a/beacon-chain/rpc/endpoints.go +++ b/beacon-chain/rpc/endpoints.go @@ -815,6 +815,7 @@ func (s *Service) lightClientEndpoints(blocker lookup.Blocker, stater lookup.Sta Blocker: blocker, Stater: stater, HeadFetcher: s.cfg.HeadFetcher, + BeaconDB: s.cfg.BeaconDB, } const namespace = "lightclient" diff --git a/beacon-chain/rpc/eth/light-client/BUILD.bazel b/beacon-chain/rpc/eth/light-client/BUILD.bazel index db4370fd4e..a954168141 100644 --- a/beacon-chain/rpc/eth/light-client/BUILD.bazel +++ b/beacon-chain/rpc/eth/light-client/BUILD.bazel @@ -12,6 +12,8 @@ go_library( deps = [ "//api/server/structs:go_default_library", "//beacon-chain/blockchain:go_default_library", + "//beacon-chain/core/light-client:go_default_library", + "//beacon-chain/db:go_default_library", "//beacon-chain/rpc/eth/shared:go_default_library", "//beacon-chain/rpc/lookup:go_default_library", "//beacon-chain/state:go_default_library", @@ -41,9 +43,9 @@ go_test( embed = [":go_default_library"], deps = [ "//api/server/structs:go_default_library", - "//beacon-chain/blockchain:go_default_library", "//beacon-chain/blockchain/testing:go_default_library", "//beacon-chain/core/helpers:go_default_library", + "//beacon-chain/core/light-client:go_default_library", "//beacon-chain/rpc/testutil:go_default_library", "//beacon-chain/state:go_default_library", "//config/fieldparams:go_default_library", diff --git a/beacon-chain/rpc/eth/light-client/helpers.go b/beacon-chain/rpc/eth/light-client/helpers.go index d6fe7a22f9..eb87610d78 100644 --- a/beacon-chain/rpc/eth/light-client/helpers.go +++ b/beacon-chain/rpc/eth/light-client/helpers.go @@ -6,9 +6,10 @@ import ( "reflect" "strconv" + lightclient "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/light-client" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/prysmaticlabs/prysm/v5/api/server/structs" - "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" "github.com/prysmaticlabs/prysm/v5/config/params" @@ -152,7 +153,7 @@ func createLightClientUpdate( block interfaces.ReadOnlySignedBeaconBlock, attestedState state.BeaconState, finalizedBlock interfaces.ReadOnlySignedBeaconBlock) (*structs.LightClientUpdate, error) { - result, err := blockchain.NewLightClientFinalityUpdateFromBeaconState(ctx, state, block, attestedState, finalizedBlock) + result, err := lightclient.NewLightClientFinalityUpdateFromBeaconState(ctx, state, block, attestedState, finalizedBlock) if err != nil { return nil, err } @@ -210,7 +211,7 @@ func newLightClientFinalityUpdateFromBeaconState( block interfaces.ReadOnlySignedBeaconBlock, attestedState state.BeaconState, finalizedBlock interfaces.ReadOnlySignedBeaconBlock) (*structs.LightClientUpdate, error) { - result, err := blockchain.NewLightClientFinalityUpdateFromBeaconState(ctx, state, block, attestedState, finalizedBlock) + result, err := lightclient.NewLightClientFinalityUpdateFromBeaconState(ctx, state, block, attestedState, finalizedBlock) if err != nil { return nil, err } @@ -223,7 +224,7 @@ func newLightClientOptimisticUpdateFromBeaconState( state state.BeaconState, block interfaces.ReadOnlySignedBeaconBlock, attestedState state.BeaconState) (*structs.LightClientUpdate, error) { - result, err := blockchain.NewLightClientOptimisticUpdateFromBeaconState(ctx, state, block, attestedState) + result, err := lightclient.NewLightClientOptimisticUpdateFromBeaconState(ctx, state, block, attestedState) if err != nil { return nil, err } @@ -319,7 +320,7 @@ func IsSyncCommitteeUpdate(update *v2.LightClientUpdate) bool { } func IsFinalityUpdate(update *v2.LightClientUpdate) bool { - finalityBranch := make([][]byte, blockchain.FinalityBranchNumOfLeaves) + finalityBranch := make([][]byte, lightclient.FinalityBranchNumOfLeaves) return !reflect.DeepEqual(update.FinalityBranch, finalityBranch) } diff --git a/beacon-chain/rpc/eth/light-client/helpers_test.go b/beacon-chain/rpc/eth/light-client/helpers_test.go index 97a817717c..f93a49e25f 100644 --- a/beacon-chain/rpc/eth/light-client/helpers_test.go +++ b/beacon-chain/rpc/eth/light-client/helpers_test.go @@ -3,7 +3,8 @@ package lightclient import ( "testing" - "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain" + lightclient "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/light-client" + fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" ethpbv1 "github.com/prysmaticlabs/prysm/v5/proto/eth/v1" ethpbv2 "github.com/prysmaticlabs/prysm/v5/proto/eth/v2" @@ -19,7 +20,7 @@ func createNonEmptySyncCommitteeBranch() [][]byte { // When the update has finality func createNonEmptyFinalityBranch() [][]byte { - res := make([][]byte, blockchain.FinalityBranchNumOfLeaves) + res := make([][]byte, lightclient.FinalityBranchNumOfLeaves) res[0] = []byte("xyz") return res } @@ -146,7 +147,7 @@ func TestIsBetterUpdate(t *testing.T) { }, NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), SignatureSlot: 9999, - FinalityBranch: make([][]byte, blockchain.FinalityBranchNumOfLeaves), + FinalityBranch: make([][]byte, lightclient.FinalityBranchNumOfLeaves), }, newUpdate: ðpbv2.LightClientUpdate{ SyncAggregate: ðpbv1.SyncAggregate{ @@ -183,7 +184,7 @@ func TestIsBetterUpdate(t *testing.T) { }, NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), SignatureSlot: 9999, - FinalityBranch: make([][]byte, blockchain.FinalityBranchNumOfLeaves), + FinalityBranch: make([][]byte, lightclient.FinalityBranchNumOfLeaves), }, expectedResult: false, }, diff --git a/beacon-chain/rpc/eth/light-client/server.go b/beacon-chain/rpc/eth/light-client/server.go index f01100a259..e0773a3064 100644 --- a/beacon-chain/rpc/eth/light-client/server.go +++ b/beacon-chain/rpc/eth/light-client/server.go @@ -2,6 +2,7 @@ package lightclient import ( "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/db" "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/lookup" ) @@ -9,4 +10,5 @@ type Server struct { Blocker lookup.Blocker Stater lookup.Stater HeadFetcher blockchain.HeadFetcher + BeaconDB db.HeadAccessDatabase } diff --git a/proto/eth/v2/beacon_lightclient.pb.go b/proto/eth/v2/beacon_lightclient.pb.go index 812355cdbd..33daa3abaa 100755 --- a/proto/eth/v2/beacon_lightclient.pb.go +++ b/proto/eth/v2/beacon_lightclient.pb.go @@ -434,6 +434,61 @@ func (x *LightClientOptimisticUpdate) GetSignatureSlot() github_com_prysmaticlab return github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.Slot(0) } +type LightClientUpdateWithVersion struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Version Version `protobuf:"varint,1,opt,name=version,proto3,enum=ethereum.eth.v2.Version" json:"version,omitempty"` + Data *LightClientUpdate `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *LightClientUpdateWithVersion) Reset() { + *x = LightClientUpdateWithVersion{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_eth_v2_beacon_lightclient_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LightClientUpdateWithVersion) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LightClientUpdateWithVersion) ProtoMessage() {} + +func (x *LightClientUpdateWithVersion) ProtoReflect() protoreflect.Message { + mi := &file_proto_eth_v2_beacon_lightclient_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LightClientUpdateWithVersion.ProtoReflect.Descriptor instead. +func (*LightClientUpdateWithVersion) Descriptor() ([]byte, []int) { + return file_proto_eth_v2_beacon_lightclient_proto_rawDescGZIP(), []int{6} +} + +func (x *LightClientUpdateWithVersion) GetVersion() Version { + if x != nil { + return x.Version + } + return Version_PHASE0 +} + +func (x *LightClientUpdateWithVersion) GetData() *LightClientUpdate { + if x != nil { + return x.Data + } + return nil +} + var File_proto_eth_v2_beacon_lightclient_proto protoreflect.FileDescriptor var file_proto_eth_v2_beacon_lightclient_proto_rawDesc = []byte{ @@ -560,16 +615,25 @@ var file_proto_eth_v2_beacon_lightclient_proto_rawDesc = []byte{ 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x35, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x42, 0x83, 0x01, 0x0a, 0x13, 0x6f, 0x72, 0x67, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, - 0x42, 0x12, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, - 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x35, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, - 0x65, 0x74, 0x68, 0x2f, 0x76, 0x32, 0x3b, 0x65, 0x74, 0x68, 0xaa, 0x02, 0x0f, 0x45, 0x74, 0x68, - 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45, 0x74, 0x68, 0x2e, 0x56, 0x32, 0xca, 0x02, 0x0f, 0x45, - 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45, 0x74, 0x68, 0x5c, 0x76, 0x32, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x75, 0x72, 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x22, 0x8a, 0x01, 0x0a, 0x1c, 0x4c, 0x69, 0x67, + 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x69, + 0x74, 0x68, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x65, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, + 0x67, 0x68, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x42, 0x83, 0x01, 0x0a, 0x13, 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x42, 0x12, 0x53, + 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x50, 0x01, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, + 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x35, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x74, 0x68, + 0x2f, 0x76, 0x32, 0x3b, 0x65, 0x74, 0x68, 0xaa, 0x02, 0x0f, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x45, 0x74, 0x68, 0x2e, 0x56, 0x32, 0xca, 0x02, 0x0f, 0x45, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45, 0x74, 0x68, 0x5c, 0x76, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -584,7 +648,7 @@ func file_proto_eth_v2_beacon_lightclient_proto_rawDescGZIP() []byte { return file_proto_eth_v2_beacon_lightclient_proto_rawDescData } -var file_proto_eth_v2_beacon_lightclient_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_proto_eth_v2_beacon_lightclient_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_proto_eth_v2_beacon_lightclient_proto_goTypes = []interface{}{ (*LightClientBootstrap)(nil), // 0: ethereum.eth.v2.LightClientBootstrap (*LightClientUpdate)(nil), // 1: ethereum.eth.v2.LightClientUpdate @@ -592,32 +656,35 @@ var file_proto_eth_v2_beacon_lightclient_proto_goTypes = []interface{}{ (*LightClientFinalityUpdate)(nil), // 3: ethereum.eth.v2.LightClientFinalityUpdate (*LightClientOptimisticUpdateWithVersion)(nil), // 4: ethereum.eth.v2.LightClientOptimisticUpdateWithVersion (*LightClientOptimisticUpdate)(nil), // 5: ethereum.eth.v2.LightClientOptimisticUpdate - (*v1.BeaconBlockHeader)(nil), // 6: ethereum.eth.v1.BeaconBlockHeader - (*SyncCommittee)(nil), // 7: ethereum.eth.v2.SyncCommittee - (*v1.SyncAggregate)(nil), // 8: ethereum.eth.v1.SyncAggregate - (Version)(0), // 9: ethereum.eth.v2.Version + (*LightClientUpdateWithVersion)(nil), // 6: ethereum.eth.v2.LightClientUpdateWithVersion + (*v1.BeaconBlockHeader)(nil), // 7: ethereum.eth.v1.BeaconBlockHeader + (*SyncCommittee)(nil), // 8: ethereum.eth.v2.SyncCommittee + (*v1.SyncAggregate)(nil), // 9: ethereum.eth.v1.SyncAggregate + (Version)(0), // 10: ethereum.eth.v2.Version } var file_proto_eth_v2_beacon_lightclient_proto_depIdxs = []int32{ - 6, // 0: ethereum.eth.v2.LightClientBootstrap.header:type_name -> ethereum.eth.v1.BeaconBlockHeader - 7, // 1: ethereum.eth.v2.LightClientBootstrap.current_sync_committee:type_name -> ethereum.eth.v2.SyncCommittee - 6, // 2: ethereum.eth.v2.LightClientUpdate.attested_header:type_name -> ethereum.eth.v1.BeaconBlockHeader - 7, // 3: ethereum.eth.v2.LightClientUpdate.next_sync_committee:type_name -> ethereum.eth.v2.SyncCommittee - 6, // 4: ethereum.eth.v2.LightClientUpdate.finalized_header:type_name -> ethereum.eth.v1.BeaconBlockHeader - 8, // 5: ethereum.eth.v2.LightClientUpdate.sync_aggregate:type_name -> ethereum.eth.v1.SyncAggregate - 9, // 6: ethereum.eth.v2.LightClientFinalityUpdateWithVersion.version:type_name -> ethereum.eth.v2.Version + 7, // 0: ethereum.eth.v2.LightClientBootstrap.header:type_name -> ethereum.eth.v1.BeaconBlockHeader + 8, // 1: ethereum.eth.v2.LightClientBootstrap.current_sync_committee:type_name -> ethereum.eth.v2.SyncCommittee + 7, // 2: ethereum.eth.v2.LightClientUpdate.attested_header:type_name -> ethereum.eth.v1.BeaconBlockHeader + 8, // 3: ethereum.eth.v2.LightClientUpdate.next_sync_committee:type_name -> ethereum.eth.v2.SyncCommittee + 7, // 4: ethereum.eth.v2.LightClientUpdate.finalized_header:type_name -> ethereum.eth.v1.BeaconBlockHeader + 9, // 5: ethereum.eth.v2.LightClientUpdate.sync_aggregate:type_name -> ethereum.eth.v1.SyncAggregate + 10, // 6: ethereum.eth.v2.LightClientFinalityUpdateWithVersion.version:type_name -> ethereum.eth.v2.Version 3, // 7: ethereum.eth.v2.LightClientFinalityUpdateWithVersion.data:type_name -> ethereum.eth.v2.LightClientFinalityUpdate - 6, // 8: ethereum.eth.v2.LightClientFinalityUpdate.attested_header:type_name -> ethereum.eth.v1.BeaconBlockHeader - 6, // 9: ethereum.eth.v2.LightClientFinalityUpdate.finalized_header:type_name -> ethereum.eth.v1.BeaconBlockHeader - 8, // 10: ethereum.eth.v2.LightClientFinalityUpdate.sync_aggregate:type_name -> ethereum.eth.v1.SyncAggregate - 9, // 11: ethereum.eth.v2.LightClientOptimisticUpdateWithVersion.version:type_name -> ethereum.eth.v2.Version + 7, // 8: ethereum.eth.v2.LightClientFinalityUpdate.attested_header:type_name -> ethereum.eth.v1.BeaconBlockHeader + 7, // 9: ethereum.eth.v2.LightClientFinalityUpdate.finalized_header:type_name -> ethereum.eth.v1.BeaconBlockHeader + 9, // 10: ethereum.eth.v2.LightClientFinalityUpdate.sync_aggregate:type_name -> ethereum.eth.v1.SyncAggregate + 10, // 11: ethereum.eth.v2.LightClientOptimisticUpdateWithVersion.version:type_name -> ethereum.eth.v2.Version 5, // 12: ethereum.eth.v2.LightClientOptimisticUpdateWithVersion.data:type_name -> ethereum.eth.v2.LightClientOptimisticUpdate - 6, // 13: ethereum.eth.v2.LightClientOptimisticUpdate.attested_header:type_name -> ethereum.eth.v1.BeaconBlockHeader - 8, // 14: ethereum.eth.v2.LightClientOptimisticUpdate.sync_aggregate:type_name -> ethereum.eth.v1.SyncAggregate - 15, // [15:15] is the sub-list for method output_type - 15, // [15:15] is the sub-list for method input_type - 15, // [15:15] is the sub-list for extension type_name - 15, // [15:15] is the sub-list for extension extendee - 0, // [0:15] is the sub-list for field type_name + 7, // 13: ethereum.eth.v2.LightClientOptimisticUpdate.attested_header:type_name -> ethereum.eth.v1.BeaconBlockHeader + 9, // 14: ethereum.eth.v2.LightClientOptimisticUpdate.sync_aggregate:type_name -> ethereum.eth.v1.SyncAggregate + 10, // 15: ethereum.eth.v2.LightClientUpdateWithVersion.version:type_name -> ethereum.eth.v2.Version + 1, // 16: ethereum.eth.v2.LightClientUpdateWithVersion.data:type_name -> ethereum.eth.v2.LightClientUpdate + 17, // [17:17] is the sub-list for method output_type + 17, // [17:17] is the sub-list for method input_type + 17, // [17:17] is the sub-list for extension type_name + 17, // [17:17] is the sub-list for extension extendee + 0, // [0:17] is the sub-list for field type_name } func init() { file_proto_eth_v2_beacon_lightclient_proto_init() } @@ -700,6 +767,18 @@ func file_proto_eth_v2_beacon_lightclient_proto_init() { return nil } } + file_proto_eth_v2_beacon_lightclient_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LightClientUpdateWithVersion); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -707,7 +786,7 @@ func file_proto_eth_v2_beacon_lightclient_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_eth_v2_beacon_lightclient_proto_rawDesc, NumEnums: 0, - NumMessages: 6, + NumMessages: 7, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/eth/v2/beacon_lightclient.proto b/proto/eth/v2/beacon_lightclient.proto index 6f3bcd62f7..c967e61883 100644 --- a/proto/eth/v2/beacon_lightclient.proto +++ b/proto/eth/v2/beacon_lightclient.proto @@ -30,41 +30,46 @@ option php_namespace = "Ethereum\\Eth\\v2"; // Beacon LightClient API related messages. message LightClientBootstrap { - v1.BeaconBlockHeader header = 1; - SyncCommittee current_sync_committee = 2; - repeated bytes current_sync_committee_branch = 3; + v1.BeaconBlockHeader header = 1; + SyncCommittee current_sync_committee = 2; + repeated bytes current_sync_committee_branch = 3; } message LightClientUpdate { - v1.BeaconBlockHeader attested_header = 1; - SyncCommittee next_sync_committee = 2; - repeated bytes next_sync_committee_branch = 3; - v1.BeaconBlockHeader finalized_header = 4; - repeated bytes finality_branch = 5; - v1.SyncAggregate sync_aggregate = 6; - uint64 signature_slot = 7 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.Slot"]; + v1.BeaconBlockHeader attested_header = 1; + SyncCommittee next_sync_committee = 2; + repeated bytes next_sync_committee_branch = 3; + v1.BeaconBlockHeader finalized_header = 4; + repeated bytes finality_branch = 5; + v1.SyncAggregate sync_aggregate = 6; + uint64 signature_slot = 7 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.Slot"]; } message LightClientFinalityUpdateWithVersion { - v2.Version version = 1; - LightClientFinalityUpdate data = 2; + v2.Version version = 1; + LightClientFinalityUpdate data = 2; } message LightClientFinalityUpdate { - v1.BeaconBlockHeader attested_header = 1; - v1.BeaconBlockHeader finalized_header = 2; - repeated bytes finality_branch = 3; - v1.SyncAggregate sync_aggregate = 4; - uint64 signature_slot = 5 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.Slot"]; + v1.BeaconBlockHeader attested_header = 1; + v1.BeaconBlockHeader finalized_header = 2; + repeated bytes finality_branch = 3; + v1.SyncAggregate sync_aggregate = 4; + uint64 signature_slot = 5 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.Slot"]; } message LightClientOptimisticUpdateWithVersion { - v2.Version version = 1; - LightClientOptimisticUpdate data = 2; + v2.Version version = 1; + LightClientOptimisticUpdate data = 2; } message LightClientOptimisticUpdate { - v1.BeaconBlockHeader attested_header = 1; - v1.SyncAggregate sync_aggregate = 2; - uint64 signature_slot = 3 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.Slot"]; + v1.BeaconBlockHeader attested_header = 1; + v1.SyncAggregate sync_aggregate = 2; + uint64 signature_slot = 3 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.Slot"]; } + +message LightClientUpdateWithVersion { + v2.Version version = 1; + LightClientUpdate data = 2; +} \ No newline at end of file diff --git a/testing/util/BUILD.bazel b/testing/util/BUILD.bazel index 8456b8d178..f41aa3bc2e 100644 --- a/testing/util/BUILD.bazel +++ b/testing/util/BUILD.bazel @@ -20,6 +20,7 @@ go_library( "electra_block.go", "electra_state.go", "helpers.go", + "lightclient.go", "merge.go", "state.go", "sync_aggregate.go", diff --git a/testing/util/lightclient.go b/testing/util/lightclient.go new file mode 100644 index 0000000000..5c6ac63567 --- /dev/null +++ b/testing/util/lightclient.go @@ -0,0 +1,117 @@ +package util + +import ( + "context" + "testing" + + "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" + "github.com/prysmaticlabs/prysm/v5/config/params" + "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" + "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces" + "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" + ethpbv2 "github.com/prysmaticlabs/prysm/v5/proto/eth/v2" + ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v5/testing/require" +) + +type TestLightClient struct { + T *testing.T + Ctx context.Context + State state.BeaconState + Block interfaces.ReadOnlySignedBeaconBlock + AttestedState state.BeaconState + AttestedHeader *ethpb.BeaconBlockHeader +} + +func NewTestLightClient(t *testing.T) *TestLightClient { + return &TestLightClient{T: t} +} + +func (l *TestLightClient) SetupTest() *TestLightClient { + ctx := context.Background() + + slot := primitives.Slot(params.BeaconConfig().AltairForkEpoch * primitives.Epoch(params.BeaconConfig().SlotsPerEpoch)).Add(1) + + attestedState, err := NewBeaconStateCapella() + require.NoError(l.T, err) + err = attestedState.SetSlot(slot) + require.NoError(l.T, err) + + parent := NewBeaconBlockCapella() + parent.Block.Slot = slot + + signedParent, err := blocks.NewSignedBeaconBlock(parent) + require.NoError(l.T, err) + + parentHeader, err := signedParent.Header() + require.NoError(l.T, err) + attestedHeader := parentHeader.Header + + err = attestedState.SetLatestBlockHeader(attestedHeader) + require.NoError(l.T, err) + attestedStateRoot, err := attestedState.HashTreeRoot(ctx) + require.NoError(l.T, err) + + // get a new signed block so the root is updated with the new state root + parent.Block.StateRoot = attestedStateRoot[:] + signedParent, err = blocks.NewSignedBeaconBlock(parent) + require.NoError(l.T, err) + + state, err := NewBeaconStateCapella() + require.NoError(l.T, err) + err = state.SetSlot(slot) + require.NoError(l.T, err) + + parentRoot, err := signedParent.Block().HashTreeRoot() + require.NoError(l.T, err) + + block := NewBeaconBlockCapella() + block.Block.Slot = slot + block.Block.ParentRoot = parentRoot[:] + + for i := uint64(0); i < params.BeaconConfig().MinSyncCommitteeParticipants; i++ { + block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) + } + + signedBlock, err := blocks.NewSignedBeaconBlock(block) + require.NoError(l.T, err) + + h, err := signedBlock.Header() + require.NoError(l.T, err) + + err = state.SetLatestBlockHeader(h.Header) + require.NoError(l.T, err) + stateRoot, err := state.HashTreeRoot(ctx) + require.NoError(l.T, err) + + // get a new signed block so the root is updated with the new state root + block.Block.StateRoot = stateRoot[:] + signedBlock, err = blocks.NewSignedBeaconBlock(block) + require.NoError(l.T, err) + + l.State = state + l.AttestedState = attestedState + l.AttestedHeader = attestedHeader + l.Block = signedBlock + l.Ctx = ctx + + return l +} + +func (l *TestLightClient) CheckAttestedHeader(update *ethpbv2.LightClientUpdate) { + require.Equal(l.T, l.AttestedHeader.Slot, update.AttestedHeader.Slot, "Attested header slot is not equal") + require.Equal(l.T, l.AttestedHeader.ProposerIndex, update.AttestedHeader.ProposerIndex, "Attested header proposer index is not equal") + require.DeepSSZEqual(l.T, l.AttestedHeader.ParentRoot, update.AttestedHeader.ParentRoot, "Attested header parent root is not equal") + require.DeepSSZEqual(l.T, l.AttestedHeader.BodyRoot, update.AttestedHeader.BodyRoot, "Attested header body root is not equal") + + attestedStateRoot, err := l.AttestedState.HashTreeRoot(l.Ctx) + require.NoError(l.T, err) + require.DeepSSZEqual(l.T, attestedStateRoot[:], update.AttestedHeader.StateRoot, "Attested header state root is not equal") +} + +func (l *TestLightClient) CheckSyncAggregate(update *ethpbv2.LightClientUpdate) { + syncAggregate, err := l.Block.Block().Body().SyncAggregate() + require.NoError(l.T, err) + require.DeepSSZEqual(l.T, syncAggregate.SyncCommitteeBits, update.SyncAggregate.SyncCommitteeBits, "SyncAggregate bits is not equal") + require.DeepSSZEqual(l.T, syncAggregate.SyncCommitteeSignature, update.SyncAggregate.SyncCommitteeSignature, "SyncAggregate signature is not equal") +}