mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 21:08:10 -05:00
889 lines
33 KiB
Go
889 lines
33 KiB
Go
package kv
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"testing"
|
|
|
|
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
|
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
|
|
"github.com/OffchainLabs/prysm/v7/config/params"
|
|
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
|
|
light_client "github.com/OffchainLabs/prysm/v7/consensus-types/light-client"
|
|
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
|
enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1"
|
|
pb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
|
"github.com/OffchainLabs/prysm/v7/runtime/version"
|
|
"github.com/OffchainLabs/prysm/v7/testing/require"
|
|
"github.com/OffchainLabs/prysm/v7/testing/util"
|
|
"github.com/OffchainLabs/prysm/v7/time/slots"
|
|
bolt "go.etcd.io/bbolt"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
func createUpdate(t *testing.T, v int) (interfaces.LightClientUpdate, error) {
|
|
config := params.BeaconConfig()
|
|
var slot primitives.Slot
|
|
var header interfaces.LightClientHeader
|
|
var st state.BeaconState
|
|
var err error
|
|
|
|
sampleRoot := make([]byte, 32)
|
|
for i := range 32 {
|
|
sampleRoot[i] = byte(i)
|
|
}
|
|
|
|
sampleExecutionBranch := make([][]byte, fieldparams.ExecutionBranchDepth)
|
|
for i := range 4 {
|
|
sampleExecutionBranch[i] = make([]byte, 32)
|
|
for j := range 32 {
|
|
sampleExecutionBranch[i][j] = byte(i + j)
|
|
}
|
|
}
|
|
|
|
switch v {
|
|
case version.Altair:
|
|
slot = primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)
|
|
header, err = light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{
|
|
Beacon: &pb.BeaconBlockHeader{
|
|
Slot: slot,
|
|
ProposerIndex: primitives.ValidatorIndex(rand.Int()),
|
|
ParentRoot: sampleRoot,
|
|
StateRoot: sampleRoot,
|
|
BodyRoot: sampleRoot,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
st, err = util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
case version.Bellatrix:
|
|
slot = primitives.Slot(config.BellatrixForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)
|
|
header, err = light_client.NewWrappedHeader(&pb.LightClientHeaderAltair{
|
|
Beacon: &pb.BeaconBlockHeader{
|
|
Slot: slot,
|
|
ProposerIndex: primitives.ValidatorIndex(rand.Int()),
|
|
ParentRoot: sampleRoot,
|
|
StateRoot: sampleRoot,
|
|
BodyRoot: sampleRoot,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
st, err = util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
case version.Capella:
|
|
slot = primitives.Slot(config.CapellaForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)
|
|
header, err = light_client.NewWrappedHeader(&pb.LightClientHeaderCapella{
|
|
Beacon: &pb.BeaconBlockHeader{
|
|
Slot: slot,
|
|
ProposerIndex: primitives.ValidatorIndex(rand.Int()),
|
|
ParentRoot: sampleRoot,
|
|
StateRoot: sampleRoot,
|
|
BodyRoot: sampleRoot,
|
|
},
|
|
Execution: &enginev1.ExecutionPayloadHeaderCapella{
|
|
ParentHash: make([]byte, fieldparams.RootLength),
|
|
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
|
StateRoot: make([]byte, fieldparams.RootLength),
|
|
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
|
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
|
|
PrevRandao: make([]byte, fieldparams.RootLength),
|
|
ExtraData: make([]byte, 0),
|
|
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
|
BlockHash: make([]byte, fieldparams.RootLength),
|
|
TransactionsRoot: make([]byte, fieldparams.RootLength),
|
|
WithdrawalsRoot: make([]byte, fieldparams.RootLength),
|
|
},
|
|
ExecutionBranch: sampleExecutionBranch,
|
|
})
|
|
require.NoError(t, err)
|
|
st, err = util.NewBeaconStateCapella()
|
|
require.NoError(t, err)
|
|
case version.Deneb:
|
|
slot = primitives.Slot(config.DenebForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)
|
|
header, err = light_client.NewWrappedHeader(&pb.LightClientHeaderDeneb{
|
|
Beacon: &pb.BeaconBlockHeader{
|
|
Slot: slot,
|
|
ProposerIndex: primitives.ValidatorIndex(rand.Int()),
|
|
ParentRoot: sampleRoot,
|
|
StateRoot: sampleRoot,
|
|
BodyRoot: sampleRoot,
|
|
},
|
|
Execution: &enginev1.ExecutionPayloadHeaderDeneb{
|
|
ParentHash: make([]byte, fieldparams.RootLength),
|
|
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
|
StateRoot: make([]byte, fieldparams.RootLength),
|
|
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
|
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
|
|
PrevRandao: make([]byte, fieldparams.RootLength),
|
|
ExtraData: make([]byte, 0),
|
|
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
|
BlockHash: make([]byte, fieldparams.RootLength),
|
|
TransactionsRoot: make([]byte, fieldparams.RootLength),
|
|
WithdrawalsRoot: make([]byte, fieldparams.RootLength),
|
|
},
|
|
ExecutionBranch: sampleExecutionBranch,
|
|
})
|
|
require.NoError(t, err)
|
|
st, err = util.NewBeaconStateDeneb()
|
|
require.NoError(t, err)
|
|
case version.Electra:
|
|
slot = primitives.Slot(config.ElectraForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)
|
|
header, err = light_client.NewWrappedHeader(&pb.LightClientHeaderDeneb{
|
|
Beacon: &pb.BeaconBlockHeader{
|
|
Slot: slot,
|
|
ProposerIndex: primitives.ValidatorIndex(rand.Int()),
|
|
ParentRoot: sampleRoot,
|
|
StateRoot: sampleRoot,
|
|
BodyRoot: sampleRoot,
|
|
},
|
|
Execution: &enginev1.ExecutionPayloadHeaderDeneb{
|
|
ParentHash: make([]byte, fieldparams.RootLength),
|
|
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
|
StateRoot: make([]byte, fieldparams.RootLength),
|
|
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
|
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
|
|
PrevRandao: make([]byte, fieldparams.RootLength),
|
|
ExtraData: make([]byte, 0),
|
|
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
|
BlockHash: make([]byte, fieldparams.RootLength),
|
|
TransactionsRoot: make([]byte, fieldparams.RootLength),
|
|
WithdrawalsRoot: make([]byte, fieldparams.RootLength),
|
|
},
|
|
ExecutionBranch: sampleExecutionBranch,
|
|
})
|
|
require.NoError(t, err)
|
|
st, err = util.NewBeaconStateElectra()
|
|
require.NoError(t, err)
|
|
case version.Fulu:
|
|
slot = primitives.Slot(config.FuluForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1)
|
|
header, err = light_client.NewWrappedHeader(&pb.LightClientHeaderDeneb{
|
|
Beacon: &pb.BeaconBlockHeader{
|
|
Slot: slot,
|
|
ProposerIndex: primitives.ValidatorIndex(rand.Int()),
|
|
ParentRoot: sampleRoot,
|
|
StateRoot: sampleRoot,
|
|
BodyRoot: sampleRoot,
|
|
},
|
|
Execution: &enginev1.ExecutionPayloadHeaderDeneb{
|
|
ParentHash: make([]byte, fieldparams.RootLength),
|
|
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
|
StateRoot: make([]byte, fieldparams.RootLength),
|
|
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
|
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
|
|
PrevRandao: make([]byte, fieldparams.RootLength),
|
|
ExtraData: make([]byte, 0),
|
|
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
|
BlockHash: make([]byte, fieldparams.RootLength),
|
|
TransactionsRoot: make([]byte, fieldparams.RootLength),
|
|
WithdrawalsRoot: make([]byte, fieldparams.RootLength),
|
|
},
|
|
ExecutionBranch: sampleExecutionBranch,
|
|
})
|
|
require.NoError(t, err)
|
|
st, err = util.NewBeaconStateFulu()
|
|
require.NoError(t, err)
|
|
default:
|
|
return nil, fmt.Errorf("unsupported version %s", version.String(v))
|
|
}
|
|
|
|
update, err := createDefaultLightClientUpdate(slot, st)
|
|
require.NoError(t, err)
|
|
update.SetSignatureSlot(slot - 1)
|
|
syncCommitteeBits := make([]byte, 64)
|
|
syncCommitteeSignature := make([]byte, 96)
|
|
update.SetSyncAggregate(&pb.SyncAggregate{
|
|
SyncCommitteeBits: syncCommitteeBits,
|
|
SyncCommitteeSignature: syncCommitteeSignature,
|
|
})
|
|
|
|
require.NoError(t, update.SetAttestedHeader(header))
|
|
require.NoError(t, update.SetFinalizedHeader(header))
|
|
|
|
return update, nil
|
|
}
|
|
|
|
func TestStore_LightClientUpdate_CanSaveRetrieve(t *testing.T) {
|
|
params.SetupTestConfigCleanup(t)
|
|
cfg := params.BeaconConfig()
|
|
cfg.AltairForkEpoch = 0
|
|
cfg.BellatrixForkEpoch = 1
|
|
cfg.CapellaForkEpoch = 2
|
|
cfg.DenebForkEpoch = 3
|
|
cfg.ElectraForkEpoch = 4
|
|
cfg.FuluForkEpoch = 5
|
|
params.OverrideBeaconConfig(cfg)
|
|
|
|
db := setupDB(t)
|
|
ctx := t.Context()
|
|
for _, testVersion := range version.All()[1:] {
|
|
if testVersion == version.Gloas {
|
|
// TODO(16027): Unskip light client tests for Gloas
|
|
continue
|
|
}
|
|
t.Run(version.String(testVersion), func(t *testing.T) {
|
|
update, err := createUpdate(t, testVersion)
|
|
require.NoError(t, err)
|
|
period := uint64(1)
|
|
|
|
err = db.SaveLightClientUpdate(ctx, period, update)
|
|
require.NoError(t, err)
|
|
|
|
retrievedUpdate, err := db.LightClientUpdate(ctx, period)
|
|
require.NoError(t, err)
|
|
require.DeepEqual(t, update, retrievedUpdate, "retrieved update does not match saved update")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStore_LightClientUpdates_canRetrieveRange(t *testing.T) {
|
|
db := setupDB(t)
|
|
ctx := t.Context()
|
|
updates := make([]interfaces.LightClientUpdate, 0, 3)
|
|
for i := 1; i <= 3; i++ {
|
|
update, err := createUpdate(t, version.Altair)
|
|
require.NoError(t, err)
|
|
updates = append(updates, update)
|
|
}
|
|
|
|
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.DeepEqual(t, update, retrievedUpdatesMap[uint64(i+1)], "retrieved update does not match saved update")
|
|
}
|
|
|
|
}
|
|
|
|
func TestStore_LightClientUpdate_EndPeriodSmallerThanStartPeriod(t *testing.T) {
|
|
db := setupDB(t)
|
|
ctx := t.Context()
|
|
updates := make([]interfaces.LightClientUpdate, 0, 3)
|
|
for i := 1; i <= 3; i++ {
|
|
update, err := createUpdate(t, version.Altair)
|
|
require.NoError(t, err)
|
|
updates = append(updates, update)
|
|
}
|
|
|
|
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 := t.Context()
|
|
updates := make([]interfaces.LightClientUpdate, 0, 3)
|
|
for i := 1; i <= 3; i++ {
|
|
update, err := createUpdate(t, version.Altair)
|
|
require.NoError(t, err)
|
|
updates = append(updates, update)
|
|
}
|
|
|
|
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.DeepEqual(t, updates[1], retrievedUpdates[2], "retrieved update does not match saved update")
|
|
}
|
|
|
|
func TestStore_LightClientUpdate_StartPeriodBeforeFirstUpdate(t *testing.T) {
|
|
db := setupDB(t)
|
|
ctx := t.Context()
|
|
updates := make([]interfaces.LightClientUpdate, 0, 3)
|
|
for i := 1; i <= 3; i++ {
|
|
update, err := createUpdate(t, version.Altair)
|
|
require.NoError(t, err)
|
|
updates = append(updates, update)
|
|
}
|
|
|
|
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, 0, 4)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 3, len(retrievedUpdates))
|
|
for i, update := range updates {
|
|
require.DeepEqual(t, update, retrievedUpdates[uint64(i+1)], "retrieved update does not match saved update")
|
|
}
|
|
}
|
|
|
|
func TestStore_LightClientUpdate_EndPeriodAfterLastUpdate(t *testing.T) {
|
|
db := setupDB(t)
|
|
ctx := t.Context()
|
|
updates := make([]interfaces.LightClientUpdate, 0, 3)
|
|
for i := 1; i <= 3; i++ {
|
|
update, err := createUpdate(t, version.Altair)
|
|
require.NoError(t, err)
|
|
updates = append(updates, update)
|
|
}
|
|
|
|
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.DeepEqual(t, update, retrievedUpdates[uint64(i+1)], "retrieved update does not match saved update")
|
|
}
|
|
}
|
|
|
|
func TestStore_LightClientUpdate_PartialUpdates(t *testing.T) {
|
|
db := setupDB(t)
|
|
ctx := t.Context()
|
|
updates := make([]interfaces.LightClientUpdate, 0, 3)
|
|
for i := 1; i <= 3; i++ {
|
|
update, err := createUpdate(t, version.Altair)
|
|
require.NoError(t, err)
|
|
updates = append(updates, update)
|
|
}
|
|
|
|
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.DeepEqual(t, update, retrievedUpdates[uint64(i+1)], "retrieved update does not match saved update")
|
|
}
|
|
}
|
|
|
|
func TestStore_LightClientUpdate_MissingPeriods_SimpleData(t *testing.T) {
|
|
db := setupDB(t)
|
|
ctx := t.Context()
|
|
updates := make([]interfaces.LightClientUpdate, 0, 4)
|
|
for i := 1; i <= 4; i++ {
|
|
update, err := createUpdate(t, version.Altair)
|
|
require.NoError(t, err)
|
|
updates = append(updates, update)
|
|
}
|
|
|
|
for i, update := range updates {
|
|
if i == 1 || i == 2 {
|
|
continue
|
|
}
|
|
err := db.SaveLightClientUpdate(ctx, uint64(i+1), update)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Retrieve the updates
|
|
retrievedUpdates, err := db.LightClientUpdates(ctx, 1, 4)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 2, len(retrievedUpdates))
|
|
require.DeepEqual(t, updates[0], retrievedUpdates[uint64(1)], "retrieved update does not match saved update")
|
|
require.DeepEqual(t, updates[3], retrievedUpdates[uint64(4)], "retrieved update does not match saved update")
|
|
|
|
// Retrieve the updates from the middle
|
|
retrievedUpdates, err = db.LightClientUpdates(ctx, 2, 4)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(retrievedUpdates))
|
|
require.DeepEqual(t, updates[3], retrievedUpdates[4], "retrieved update does not match saved update")
|
|
|
|
// Retrieve the updates from after the missing period
|
|
retrievedUpdates, err = db.LightClientUpdates(ctx, 4, 4)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(retrievedUpdates))
|
|
require.DeepEqual(t, updates[3], retrievedUpdates[4], "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, 0, 6)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 2, len(retrievedUpdates))
|
|
require.DeepEqual(t, updates[0], retrievedUpdates[uint64(1)], "retrieved update does not match saved update")
|
|
require.DeepEqual(t, updates[3], retrievedUpdates[uint64(4)], "retrieved update does not match saved update")
|
|
}
|
|
|
|
func TestStore_LightClientUpdate_EmptyDB(t *testing.T) {
|
|
db := setupDB(t)
|
|
ctx := t.Context()
|
|
|
|
// Retrieve the updates
|
|
retrievedUpdates, err := db.LightClientUpdates(ctx, 1, 3)
|
|
require.IsNil(t, err)
|
|
require.Equal(t, 0, len(retrievedUpdates))
|
|
}
|
|
|
|
func TestStore_LightClientUpdate_RetrieveMissingPeriodDistributed(t *testing.T) {
|
|
db := setupDB(t)
|
|
ctx := t.Context()
|
|
updates := make([]interfaces.LightClientUpdate, 0, 5)
|
|
for i := 1; i <= 5; i++ {
|
|
update, err := createUpdate(t, version.Altair)
|
|
require.NoError(t, err)
|
|
updates = append(updates, update)
|
|
}
|
|
|
|
for i, update := range updates {
|
|
if i == 1 || i == 3 {
|
|
continue
|
|
}
|
|
err := db.SaveLightClientUpdate(ctx, uint64(i+1), update)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Retrieve the updates
|
|
retrievedUpdates, err := db.LightClientUpdates(ctx, 0, 7)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 3, len(retrievedUpdates))
|
|
require.DeepEqual(t, updates[0], retrievedUpdates[uint64(1)], "retrieved update does not match saved update")
|
|
require.DeepEqual(t, updates[2], retrievedUpdates[uint64(3)], "retrieved update does not match saved update")
|
|
require.DeepEqual(t, updates[4], retrievedUpdates[uint64(5)], "retrieved update does not match saved update")
|
|
}
|
|
|
|
func createDefaultLightClientUpdate(currentSlot primitives.Slot, attestedState state.BeaconState) (interfaces.LightClientUpdate, error) {
|
|
currentEpoch := slots.ToEpoch(currentSlot)
|
|
|
|
syncCommitteeSize := params.BeaconConfig().SyncCommitteeSize
|
|
pubKeys := make([][]byte, syncCommitteeSize)
|
|
for i := range syncCommitteeSize {
|
|
pubKeys[i] = make([]byte, fieldparams.BLSPubkeyLength)
|
|
}
|
|
nextSyncCommittee := &pb.SyncCommittee{
|
|
Pubkeys: pubKeys,
|
|
AggregatePubkey: make([]byte, fieldparams.BLSPubkeyLength),
|
|
}
|
|
|
|
var nextSyncCommitteeBranch [][]byte
|
|
if attestedState.Version() >= version.Electra {
|
|
nextSyncCommitteeBranch = make([][]byte, fieldparams.SyncCommitteeBranchDepthElectra)
|
|
} else {
|
|
nextSyncCommitteeBranch = make([][]byte, fieldparams.SyncCommitteeBranchDepth)
|
|
}
|
|
for i := 0; i < len(nextSyncCommitteeBranch); i++ {
|
|
nextSyncCommitteeBranch[i] = make([]byte, fieldparams.RootLength)
|
|
}
|
|
|
|
executionBranch := make([][]byte, fieldparams.ExecutionBranchDepth)
|
|
for i := range fieldparams.ExecutionBranchDepth {
|
|
executionBranch[i] = make([]byte, 32)
|
|
}
|
|
|
|
var finalityBranch [][]byte
|
|
if attestedState.Version() >= version.Electra {
|
|
finalityBranch = make([][]byte, fieldparams.FinalityBranchDepthElectra)
|
|
} else {
|
|
finalityBranch = make([][]byte, fieldparams.FinalityBranchDepth)
|
|
}
|
|
for i := 0; i < len(finalityBranch); i++ {
|
|
finalityBranch[i] = make([]byte, 32)
|
|
}
|
|
|
|
var m proto.Message
|
|
if currentEpoch < params.BeaconConfig().CapellaForkEpoch {
|
|
m = &pb.LightClientUpdateAltair{
|
|
AttestedHeader: &pb.LightClientHeaderAltair{},
|
|
NextSyncCommittee: nextSyncCommittee,
|
|
NextSyncCommitteeBranch: nextSyncCommitteeBranch,
|
|
FinalityBranch: finalityBranch,
|
|
}
|
|
} else if currentEpoch < params.BeaconConfig().DenebForkEpoch {
|
|
m = &pb.LightClientUpdateCapella{
|
|
AttestedHeader: &pb.LightClientHeaderCapella{
|
|
Beacon: &pb.BeaconBlockHeader{},
|
|
Execution: &enginev1.ExecutionPayloadHeaderCapella{},
|
|
ExecutionBranch: executionBranch,
|
|
},
|
|
NextSyncCommittee: nextSyncCommittee,
|
|
NextSyncCommitteeBranch: nextSyncCommitteeBranch,
|
|
FinalityBranch: finalityBranch,
|
|
}
|
|
} else if currentEpoch < params.BeaconConfig().ElectraForkEpoch {
|
|
m = &pb.LightClientUpdateDeneb{
|
|
AttestedHeader: &pb.LightClientHeaderDeneb{
|
|
Beacon: &pb.BeaconBlockHeader{},
|
|
Execution: &enginev1.ExecutionPayloadHeaderDeneb{},
|
|
ExecutionBranch: executionBranch,
|
|
},
|
|
NextSyncCommittee: nextSyncCommittee,
|
|
NextSyncCommitteeBranch: nextSyncCommitteeBranch,
|
|
FinalityBranch: finalityBranch,
|
|
}
|
|
} else {
|
|
if attestedState.Version() >= version.Electra {
|
|
m = &pb.LightClientUpdateElectra{
|
|
AttestedHeader: &pb.LightClientHeaderDeneb{
|
|
Beacon: &pb.BeaconBlockHeader{},
|
|
Execution: &enginev1.ExecutionPayloadHeaderDeneb{},
|
|
ExecutionBranch: executionBranch,
|
|
},
|
|
NextSyncCommittee: nextSyncCommittee,
|
|
NextSyncCommitteeBranch: nextSyncCommitteeBranch,
|
|
FinalityBranch: finalityBranch,
|
|
}
|
|
} else {
|
|
m = &pb.LightClientUpdateDeneb{
|
|
AttestedHeader: &pb.LightClientHeaderDeneb{
|
|
Beacon: &pb.BeaconBlockHeader{},
|
|
Execution: &enginev1.ExecutionPayloadHeaderDeneb{},
|
|
ExecutionBranch: executionBranch,
|
|
},
|
|
NextSyncCommittee: nextSyncCommittee,
|
|
NextSyncCommitteeBranch: nextSyncCommitteeBranch,
|
|
FinalityBranch: finalityBranch,
|
|
}
|
|
}
|
|
}
|
|
|
|
return light_client.NewWrappedUpdate(m)
|
|
}
|
|
|
|
func TestStore_LightClientBootstrap_CanSaveRetrieve(t *testing.T) {
|
|
params.SetupTestConfigCleanup(t)
|
|
cfg := params.BeaconConfig()
|
|
cfg.AltairForkEpoch = 0
|
|
cfg.BellatrixForkEpoch = 1
|
|
cfg.CapellaForkEpoch = 2
|
|
cfg.DenebForkEpoch = 3
|
|
cfg.ElectraForkEpoch = 4
|
|
cfg.EpochsPerSyncCommitteePeriod = 1
|
|
params.OverrideBeaconConfig(cfg)
|
|
|
|
db := setupDB(t)
|
|
ctx := t.Context()
|
|
|
|
t.Run("Nil", func(t *testing.T) {
|
|
retrievedBootstrap, err := db.LightClientBootstrap(ctx, []byte("NilBlockRoot"))
|
|
require.NoError(t, err)
|
|
require.IsNil(t, retrievedBootstrap)
|
|
})
|
|
for _, testVersion := range version.All()[1:] {
|
|
if testVersion == version.Gloas {
|
|
// TODO(16027): Unskip light client tests for Gloas
|
|
continue
|
|
}
|
|
t.Run(version.String(testVersion), func(t *testing.T) {
|
|
bootstrap, err := createDefaultLightClientBootstrap(primitives.Slot(uint64(params.BeaconConfig().VersionToForkEpochMap()[testVersion]) * uint64(params.BeaconConfig().SlotsPerEpoch)))
|
|
require.NoError(t, err)
|
|
|
|
err = bootstrap.SetCurrentSyncCommittee(createRandomSyncCommittee())
|
|
require.NoError(t, err)
|
|
|
|
blockRoot := []byte("blockRootAltair" + version.String(testVersion))
|
|
|
|
err = db.SaveLightClientBootstrap(ctx, blockRoot, bootstrap)
|
|
require.NoError(t, err)
|
|
|
|
retrievedBootstrap, err := db.LightClientBootstrap(ctx, blockRoot)
|
|
require.NoError(t, err)
|
|
require.DeepEqual(t, bootstrap.Header(), retrievedBootstrap.Header(), "retrieved bootstrap header does not match saved bootstrap header")
|
|
require.DeepEqual(t, bootstrap.CurrentSyncCommittee(), retrievedBootstrap.CurrentSyncCommittee(), "retrieved bootstrap sync committee does not match saved bootstrap sync committee")
|
|
if testVersion >= version.Electra {
|
|
savedBranch, err := bootstrap.CurrentSyncCommitteeBranchElectra()
|
|
require.NoError(t, err)
|
|
retrievedBranch, err := retrievedBootstrap.CurrentSyncCommitteeBranchElectra()
|
|
require.NoError(t, err)
|
|
require.DeepEqual(t, savedBranch, retrievedBranch, "retrieved bootstrap sync committee branch does not match saved bootstrap sync committee branch")
|
|
} else {
|
|
savedBranch, err := bootstrap.CurrentSyncCommitteeBranch()
|
|
require.NoError(t, err)
|
|
retrievedBranch, err := retrievedBootstrap.CurrentSyncCommitteeBranch()
|
|
require.NoError(t, err)
|
|
require.DeepEqual(t, savedBranch, retrievedBranch, "retrieved bootstrap sync committee branch does not match saved bootstrap sync committee branch")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStore_LightClientBootstrap_MultipleBootstrapsWithSameSyncCommittee(t *testing.T) {
|
|
params.SetupTestConfigCleanup(t)
|
|
cfg := params.BeaconConfig()
|
|
cfg.AltairForkEpoch = 0
|
|
cfg.CapellaForkEpoch = 1
|
|
cfg.DenebForkEpoch = 2
|
|
cfg.ElectraForkEpoch = 3
|
|
cfg.EpochsPerSyncCommitteePeriod = 1
|
|
params.OverrideBeaconConfig(cfg)
|
|
|
|
db := setupDB(t)
|
|
ctx := t.Context()
|
|
|
|
bootstrap1, err := createDefaultLightClientBootstrap(primitives.Slot(uint64(params.BeaconConfig().AltairForkEpoch) * uint64(params.BeaconConfig().SlotsPerEpoch)))
|
|
require.NoError(t, err)
|
|
bootstrap2, err := createDefaultLightClientBootstrap(primitives.Slot(uint64(params.BeaconConfig().AltairForkEpoch) * uint64(params.BeaconConfig().SlotsPerEpoch)))
|
|
require.NoError(t, err)
|
|
|
|
randomSyncCommittee := createRandomSyncCommittee()
|
|
|
|
err = bootstrap1.SetCurrentSyncCommittee(randomSyncCommittee)
|
|
require.NoError(t, err)
|
|
err = bootstrap2.SetCurrentSyncCommittee(randomSyncCommittee)
|
|
require.NoError(t, err)
|
|
|
|
err = db.SaveLightClientBootstrap(ctx, []byte("blockRootAltair1"), bootstrap1)
|
|
require.NoError(t, err)
|
|
err = db.SaveLightClientBootstrap(ctx, []byte("blockRootAltair2"), bootstrap2)
|
|
require.NoError(t, err)
|
|
|
|
retrievedBootstrap1, err := db.LightClientBootstrap(ctx, []byte("blockRootAltair1"))
|
|
require.NoError(t, err)
|
|
retrievedBootstrap2, err := db.LightClientBootstrap(ctx, []byte("blockRootAltair2"))
|
|
require.NoError(t, err)
|
|
|
|
require.DeepEqual(t, bootstrap1.Header(), retrievedBootstrap1.Header(), "retrieved bootstrap1 header does not match saved bootstrap1 header")
|
|
require.DeepEqual(t, randomSyncCommittee, retrievedBootstrap1.CurrentSyncCommittee(), "retrieved bootstrap1 sync committee does not match saved bootstrap1 sync committee")
|
|
savedBranch, err := bootstrap1.CurrentSyncCommitteeBranch()
|
|
require.NoError(t, err)
|
|
retrievedBranch, err := retrievedBootstrap1.CurrentSyncCommitteeBranch()
|
|
require.NoError(t, err)
|
|
require.DeepEqual(t, savedBranch, retrievedBranch, "retrieved bootstrap1 sync committee branch does not match saved bootstrap1 sync committee branch")
|
|
|
|
require.DeepEqual(t, bootstrap2.Header(), retrievedBootstrap2.Header(), "retrieved bootstrap1 header does not match saved bootstrap1 header")
|
|
require.DeepEqual(t, randomSyncCommittee, retrievedBootstrap2.CurrentSyncCommittee(), "retrieved bootstrap1 sync committee does not match saved bootstrap1 sync committee")
|
|
savedBranch2, err := bootstrap2.CurrentSyncCommitteeBranch()
|
|
require.NoError(t, err)
|
|
retrievedBranch2, err := retrievedBootstrap2.CurrentSyncCommitteeBranch()
|
|
require.NoError(t, err)
|
|
require.DeepEqual(t, savedBranch2, retrievedBranch2, "retrieved bootstrap1 sync committee branch does not match saved bootstrap1 sync committee branch")
|
|
|
|
// Ensure that the sync committee is only stored once
|
|
err = db.db.View(func(tx *bolt.Tx) error {
|
|
bucket := tx.Bucket(lightClientSyncCommitteeBucket)
|
|
require.NotNil(t, bucket)
|
|
count := bucket.Stats().KeyN
|
|
require.Equal(t, 1, count)
|
|
return nil
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestStore_LightClientBootstrap_MultipleBootstrapsWithDifferentSyncCommittees(t *testing.T) {
|
|
params.SetupTestConfigCleanup(t)
|
|
cfg := params.BeaconConfig()
|
|
cfg.AltairForkEpoch = 0
|
|
cfg.CapellaForkEpoch = 1
|
|
cfg.DenebForkEpoch = 2
|
|
cfg.ElectraForkEpoch = 3
|
|
cfg.EpochsPerSyncCommitteePeriod = 1
|
|
params.OverrideBeaconConfig(cfg)
|
|
|
|
db := setupDB(t)
|
|
ctx := t.Context()
|
|
|
|
bootstrap1, err := createDefaultLightClientBootstrap(primitives.Slot(uint64(params.BeaconConfig().AltairForkEpoch) * uint64(params.BeaconConfig().SlotsPerEpoch)))
|
|
require.NoError(t, err)
|
|
bootstrap2, err := createDefaultLightClientBootstrap(primitives.Slot(uint64(params.BeaconConfig().AltairForkEpoch) * uint64(params.BeaconConfig().SlotsPerEpoch)))
|
|
require.NoError(t, err)
|
|
|
|
err = bootstrap1.SetCurrentSyncCommittee(createRandomSyncCommittee())
|
|
require.NoError(t, err)
|
|
err = bootstrap2.SetCurrentSyncCommittee(createRandomSyncCommittee())
|
|
require.NoError(t, err)
|
|
|
|
err = db.SaveLightClientBootstrap(ctx, []byte("blockRootAltair1"), bootstrap1)
|
|
require.NoError(t, err)
|
|
err = db.SaveLightClientBootstrap(ctx, []byte("blockRootAltair2"), bootstrap2)
|
|
require.NoError(t, err)
|
|
|
|
retrievedBootstrap1, err := db.LightClientBootstrap(ctx, []byte("blockRootAltair1"))
|
|
require.NoError(t, err)
|
|
retrievedBootstrap2, err := db.LightClientBootstrap(ctx, []byte("blockRootAltair2"))
|
|
require.NoError(t, err)
|
|
|
|
require.DeepEqual(t, bootstrap1.Header(), retrievedBootstrap1.Header(), "retrieved bootstrap1 header does not match saved bootstrap1 header")
|
|
require.DeepEqual(t, bootstrap1.CurrentSyncCommittee(), retrievedBootstrap1.CurrentSyncCommittee(), "retrieved bootstrap1 sync committee does not match saved bootstrap1 sync committee")
|
|
savedBranch, err := bootstrap1.CurrentSyncCommitteeBranch()
|
|
require.NoError(t, err)
|
|
retrievedBranch, err := retrievedBootstrap1.CurrentSyncCommitteeBranch()
|
|
require.NoError(t, err)
|
|
require.DeepEqual(t, savedBranch, retrievedBranch, "retrieved bootstrap1 sync committee branch does not match saved bootstrap1 sync committee branch")
|
|
|
|
require.DeepEqual(t, bootstrap2.Header(), retrievedBootstrap2.Header(), "retrieved bootstrap1 header does not match saved bootstrap1 header")
|
|
require.DeepEqual(t, bootstrap2.CurrentSyncCommittee(), retrievedBootstrap2.CurrentSyncCommittee(), "retrieved bootstrap1 sync committee does not match saved bootstrap1 sync committee")
|
|
savedBranch2, err := bootstrap2.CurrentSyncCommitteeBranch()
|
|
require.NoError(t, err)
|
|
retrievedBranch2, err := retrievedBootstrap2.CurrentSyncCommitteeBranch()
|
|
require.NoError(t, err)
|
|
require.DeepEqual(t, savedBranch2, retrievedBranch2, "retrieved bootstrap1 sync committee branch does not match saved bootstrap1 sync committee branch")
|
|
|
|
// Ensure that the sync committee is stored twice
|
|
err = db.db.View(func(tx *bolt.Tx) error {
|
|
bucket := tx.Bucket(lightClientSyncCommitteeBucket)
|
|
require.NotNil(t, bucket)
|
|
count := bucket.Stats().KeyN
|
|
require.Equal(t, 2, count)
|
|
return nil
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func createDefaultLightClientBootstrap(currentSlot primitives.Slot) (interfaces.LightClientBootstrap, error) {
|
|
currentEpoch := slots.ToEpoch(currentSlot)
|
|
syncCommitteeSize := params.BeaconConfig().SyncCommitteeSize
|
|
pubKeys := make([][]byte, syncCommitteeSize)
|
|
for i := range syncCommitteeSize {
|
|
pubKeys[i] = make([]byte, fieldparams.BLSPubkeyLength)
|
|
}
|
|
currentSyncCommittee := &pb.SyncCommittee{
|
|
Pubkeys: pubKeys,
|
|
AggregatePubkey: make([]byte, fieldparams.BLSPubkeyLength),
|
|
}
|
|
|
|
var currentSyncCommitteeBranch [][]byte
|
|
if currentEpoch >= params.BeaconConfig().ElectraForkEpoch {
|
|
currentSyncCommitteeBranch = make([][]byte, fieldparams.SyncCommitteeBranchDepthElectra)
|
|
} else {
|
|
currentSyncCommitteeBranch = make([][]byte, fieldparams.SyncCommitteeBranchDepth)
|
|
}
|
|
for i := 0; i < len(currentSyncCommitteeBranch); i++ {
|
|
currentSyncCommitteeBranch[i] = make([]byte, fieldparams.RootLength)
|
|
}
|
|
|
|
executionBranch := make([][]byte, fieldparams.ExecutionBranchDepth)
|
|
for i := range fieldparams.ExecutionBranchDepth {
|
|
executionBranch[i] = make([]byte, 32)
|
|
}
|
|
|
|
// TODO: can this be based on the current epoch?
|
|
var m proto.Message
|
|
if currentEpoch < params.BeaconConfig().CapellaForkEpoch {
|
|
m = &pb.LightClientBootstrapAltair{
|
|
Header: &pb.LightClientHeaderAltair{
|
|
Beacon: &pb.BeaconBlockHeader{
|
|
Slot: currentSlot,
|
|
ParentRoot: make([]byte, 32),
|
|
StateRoot: make([]byte, 32),
|
|
BodyRoot: make([]byte, 32),
|
|
},
|
|
},
|
|
CurrentSyncCommittee: currentSyncCommittee,
|
|
CurrentSyncCommitteeBranch: currentSyncCommitteeBranch,
|
|
}
|
|
} else if currentEpoch < params.BeaconConfig().DenebForkEpoch {
|
|
m = &pb.LightClientBootstrapCapella{
|
|
Header: &pb.LightClientHeaderCapella{
|
|
Beacon: &pb.BeaconBlockHeader{
|
|
Slot: currentSlot,
|
|
ParentRoot: make([]byte, 32),
|
|
StateRoot: make([]byte, 32),
|
|
BodyRoot: make([]byte, 32),
|
|
},
|
|
Execution: &enginev1.ExecutionPayloadHeaderCapella{
|
|
ParentHash: make([]byte, fieldparams.RootLength),
|
|
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
|
StateRoot: make([]byte, fieldparams.RootLength),
|
|
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
|
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
|
|
PrevRandao: make([]byte, fieldparams.RootLength),
|
|
ExtraData: make([]byte, 0),
|
|
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
|
BlockHash: make([]byte, fieldparams.RootLength),
|
|
TransactionsRoot: make([]byte, fieldparams.RootLength),
|
|
WithdrawalsRoot: make([]byte, fieldparams.RootLength),
|
|
},
|
|
ExecutionBranch: executionBranch,
|
|
},
|
|
CurrentSyncCommittee: currentSyncCommittee,
|
|
CurrentSyncCommitteeBranch: currentSyncCommitteeBranch,
|
|
}
|
|
} else if currentEpoch < params.BeaconConfig().ElectraForkEpoch {
|
|
m = &pb.LightClientBootstrapDeneb{
|
|
Header: &pb.LightClientHeaderDeneb{
|
|
Beacon: &pb.BeaconBlockHeader{
|
|
Slot: currentSlot,
|
|
ParentRoot: make([]byte, 32),
|
|
StateRoot: make([]byte, 32),
|
|
BodyRoot: make([]byte, 32),
|
|
},
|
|
Execution: &enginev1.ExecutionPayloadHeaderDeneb{
|
|
ParentHash: make([]byte, fieldparams.RootLength),
|
|
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
|
StateRoot: make([]byte, fieldparams.RootLength),
|
|
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
|
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
|
|
PrevRandao: make([]byte, fieldparams.RootLength),
|
|
ExtraData: make([]byte, 0),
|
|
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
|
BlockHash: make([]byte, fieldparams.RootLength),
|
|
TransactionsRoot: make([]byte, fieldparams.RootLength),
|
|
WithdrawalsRoot: make([]byte, fieldparams.RootLength),
|
|
GasLimit: 0,
|
|
GasUsed: 0,
|
|
},
|
|
ExecutionBranch: executionBranch,
|
|
},
|
|
CurrentSyncCommittee: currentSyncCommittee,
|
|
CurrentSyncCommitteeBranch: currentSyncCommitteeBranch,
|
|
}
|
|
} else {
|
|
m = &pb.LightClientBootstrapElectra{
|
|
Header: &pb.LightClientHeaderDeneb{
|
|
Beacon: &pb.BeaconBlockHeader{
|
|
Slot: currentSlot,
|
|
ParentRoot: make([]byte, 32),
|
|
StateRoot: make([]byte, 32),
|
|
BodyRoot: make([]byte, 32),
|
|
},
|
|
Execution: &enginev1.ExecutionPayloadHeaderDeneb{
|
|
ParentHash: make([]byte, fieldparams.RootLength),
|
|
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
|
StateRoot: make([]byte, fieldparams.RootLength),
|
|
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
|
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
|
|
PrevRandao: make([]byte, fieldparams.RootLength),
|
|
ExtraData: make([]byte, 0),
|
|
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
|
BlockHash: make([]byte, fieldparams.RootLength),
|
|
TransactionsRoot: make([]byte, fieldparams.RootLength),
|
|
WithdrawalsRoot: make([]byte, fieldparams.RootLength),
|
|
GasLimit: 0,
|
|
GasUsed: 0,
|
|
},
|
|
ExecutionBranch: executionBranch,
|
|
},
|
|
CurrentSyncCommittee: currentSyncCommittee,
|
|
CurrentSyncCommitteeBranch: currentSyncCommitteeBranch,
|
|
}
|
|
}
|
|
|
|
return light_client.NewWrappedBootstrap(m)
|
|
}
|
|
|
|
func createRandomSyncCommittee() *pb.SyncCommittee {
|
|
// random number between 2 and 128
|
|
base := rand.Int()%127 + 2
|
|
|
|
syncCom := make([][]byte, params.BeaconConfig().SyncCommitteeSize)
|
|
for i := 0; uint64(i) < params.BeaconConfig().SyncCommitteeSize; i++ {
|
|
if i%base == 0 {
|
|
syncCom[i] = make([]byte, fieldparams.BLSPubkeyLength)
|
|
syncCom[i][0] = 1
|
|
continue
|
|
}
|
|
syncCom[i] = make([]byte, fieldparams.BLSPubkeyLength)
|
|
}
|
|
|
|
return &pb.SyncCommittee{
|
|
Pubkeys: syncCom,
|
|
AggregatePubkey: make([]byte, fieldparams.BLSPubkeyLength),
|
|
}
|
|
}
|