mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 21:38:05 -05:00
**What type of PR is this?** Bug fix **What does this PR do? Why is it needed?** This PR fixes the LC p2p `fork version not recognized` bug. It adds object mappings for the LC types for Fulu, and fixes tests to cover such cases in the future.
287 lines
11 KiB
Go
287 lines
11 KiB
Go
package sync
|
|
|
|
import (
|
|
"bytes"
|
|
"math"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/OffchainLabs/prysm/v7/async/event"
|
|
mock "github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/testing"
|
|
testDB "github.com/OffchainLabs/prysm/v7/beacon-chain/db/testing"
|
|
lightClient "github.com/OffchainLabs/prysm/v7/beacon-chain/light-client"
|
|
"github.com/OffchainLabs/prysm/v7/beacon-chain/p2p"
|
|
p2ptest "github.com/OffchainLabs/prysm/v7/beacon-chain/p2p/testing"
|
|
"github.com/OffchainLabs/prysm/v7/beacon-chain/startup"
|
|
mockSync "github.com/OffchainLabs/prysm/v7/beacon-chain/sync/initial-sync/testing"
|
|
"github.com/OffchainLabs/prysm/v7/config/params"
|
|
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
|
|
"github.com/OffchainLabs/prysm/v7/runtime/version"
|
|
"github.com/OffchainLabs/prysm/v7/testing/require"
|
|
"github.com/OffchainLabs/prysm/v7/testing/util"
|
|
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
|
pb "github.com/libp2p/go-libp2p-pubsub/pb"
|
|
)
|
|
|
|
func TestValidateLightClientOptimisticUpdate_NilMessageOrTopic(t *testing.T) {
|
|
params.SetupTestConfigCleanup(t)
|
|
ctx := t.Context()
|
|
p := p2ptest.NewTestP2P(t)
|
|
lcStore := lightClient.NewLightClientStore(&p2ptest.FakeP2P{}, new(event.Feed), testDB.SetupDB(t))
|
|
s := &Service{cfg: &config{p2p: p, initialSync: &mockSync.Sync{}}, lcStore: lcStore}
|
|
mockUpdate, err := util.MockOptimisticUpdate()
|
|
require.NoError(t, err)
|
|
s.lcStore.SetLastOptimisticUpdate(mockUpdate, false)
|
|
|
|
_, err = s.validateLightClientOptimisticUpdate(ctx, "", nil)
|
|
require.ErrorIs(t, err, errNilPubsubMessage)
|
|
|
|
_, err = s.validateLightClientOptimisticUpdate(ctx, "", &pubsub.Message{Message: &pb.Message{}})
|
|
require.ErrorIs(t, err, errNilPubsubMessage)
|
|
|
|
emptyTopic := ""
|
|
_, err = s.validateLightClientOptimisticUpdate(ctx, "", &pubsub.Message{Message: &pb.Message{
|
|
Topic: &emptyTopic,
|
|
}})
|
|
require.ErrorIs(t, err, errNilPubsubMessage)
|
|
}
|
|
|
|
func TestValidateLightClientOptimisticUpdate(t *testing.T) {
|
|
params.SetupTestConfigCleanup(t)
|
|
cfg := params.BeaconConfig().Copy()
|
|
cfg.AltairForkEpoch = 1
|
|
cfg.BellatrixForkEpoch = 2
|
|
cfg.CapellaForkEpoch = 3
|
|
cfg.DenebForkEpoch = 4
|
|
cfg.ElectraForkEpoch = 5
|
|
cfg.FuluForkEpoch = 6
|
|
cfg.ForkVersionSchedule[[4]byte{1, 0, 0, 0}] = 1
|
|
cfg.ForkVersionSchedule[[4]byte{2, 0, 0, 0}] = 2
|
|
cfg.ForkVersionSchedule[[4]byte{3, 0, 0, 0}] = 3
|
|
cfg.ForkVersionSchedule[[4]byte{4, 0, 0, 0}] = 4
|
|
cfg.ForkVersionSchedule[[4]byte{5, 0, 0, 0}] = 5
|
|
cfg.ForkVersionSchedule[[4]byte{6, 0, 0, 0}] = 6
|
|
params.OverrideBeaconConfig(cfg)
|
|
|
|
secondsPerSlot := int(params.BeaconConfig().SecondsPerSlot)
|
|
slotIntervals := int(params.BeaconConfig().IntervalsPerSlot)
|
|
slotsPerEpoch := int(params.BeaconConfig().SlotsPerEpoch)
|
|
|
|
tests := []struct {
|
|
name string
|
|
genesisDrift int
|
|
oldUpdateOptions []util.LightClientOption
|
|
newUpdateOptions []util.LightClientOption
|
|
expectedResult pubsub.ValidationResult
|
|
expectedErr error
|
|
}{
|
|
{
|
|
name: "no previous update",
|
|
oldUpdateOptions: nil,
|
|
newUpdateOptions: []util.LightClientOption{},
|
|
expectedResult: pubsub.ValidationIgnore,
|
|
},
|
|
{
|
|
name: "not enough time passed",
|
|
genesisDrift: -int(math.Ceil(float64(params.BeaconConfig().SlotComponentDuration(params.BeaconConfig().SyncMessageDueBPS)) / float64(time.Second))),
|
|
oldUpdateOptions: []util.LightClientOption{},
|
|
newUpdateOptions: []util.LightClientOption{},
|
|
expectedResult: pubsub.ValidationIgnore,
|
|
},
|
|
{
|
|
name: "new update is the same",
|
|
oldUpdateOptions: []util.LightClientOption{},
|
|
newUpdateOptions: []util.LightClientOption{},
|
|
expectedResult: pubsub.ValidationAccept,
|
|
},
|
|
{
|
|
name: "new update is different",
|
|
oldUpdateOptions: []util.LightClientOption{},
|
|
newUpdateOptions: []util.LightClientOption{util.WithIncreasedAttestedSlot(1)},
|
|
expectedResult: pubsub.ValidationIgnore,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
for v := range version.All() {
|
|
if v == version.Phase0 {
|
|
continue
|
|
}
|
|
t.Run(test.name+"_"+version.String(v), func(t *testing.T) {
|
|
ctx := t.Context()
|
|
p := p2ptest.NewTestP2P(t)
|
|
// drift back appropriate number of epochs based on fork + 2 slots for signature slot + time for gossip propagation + any extra drift
|
|
genesisDrift := v*slotsPerEpoch*secondsPerSlot + 2*secondsPerSlot + secondsPerSlot/slotIntervals + test.genesisDrift
|
|
chainService := &mock.ChainService{Genesis: time.Unix(time.Now().Unix()-int64(genesisDrift), 0)}
|
|
lcStore := lightClient.NewLightClientStore(&p2ptest.FakeP2P{}, new(event.Feed), testDB.SetupDB(t))
|
|
s := &Service{cfg: &config{p2p: p, initialSync: &mockSync.Sync{}, clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot)}, lcStore: lcStore}
|
|
|
|
var oldUpdate interfaces.LightClientOptimisticUpdate
|
|
if test.oldUpdateOptions != nil {
|
|
l := util.NewTestLightClient(t, v, test.oldUpdateOptions...)
|
|
var err error
|
|
oldUpdate, err = lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, l.AttestedBlock)
|
|
require.NoError(t, err)
|
|
|
|
s.lcStore.SetLastOptimisticUpdate(oldUpdate, false)
|
|
}
|
|
|
|
l := util.NewTestLightClient(t, v, test.newUpdateOptions...)
|
|
newUpdate, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, l.AttestedBlock)
|
|
require.NoError(t, err)
|
|
buf := new(bytes.Buffer)
|
|
_, err = p.Encoding().EncodeGossip(buf, newUpdate)
|
|
require.NoError(t, err)
|
|
|
|
topic := p2p.LightClientOptimisticUpdateTopicFormat
|
|
digest, err := s.currentForkDigest()
|
|
require.NoError(t, err)
|
|
topic = s.addDigestToTopic(topic, digest)
|
|
|
|
r, err := s.validateLightClientOptimisticUpdate(ctx, "", &pubsub.Message{
|
|
Message: &pb.Message{
|
|
Data: buf.Bytes(),
|
|
Topic: &topic,
|
|
}})
|
|
if test.expectedErr != nil {
|
|
require.ErrorIs(t, err, test.expectedErr)
|
|
} else {
|
|
require.NoError(t, err)
|
|
require.Equal(t, test.expectedResult, r)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestValidateLightClientFinalityUpdate_NilMessageOrTopic(t *testing.T) {
|
|
params.SetupTestConfigCleanup(t)
|
|
ctx := t.Context()
|
|
p := p2ptest.NewTestP2P(t)
|
|
lcStore := lightClient.NewLightClientStore(&p2ptest.FakeP2P{}, new(event.Feed), testDB.SetupDB(t))
|
|
s := &Service{cfg: &config{p2p: p, initialSync: &mockSync.Sync{}}, lcStore: lcStore}
|
|
mockUpdate, err := util.MockFinalityUpdate()
|
|
require.NoError(t, err)
|
|
s.lcStore.SetLastFinalityUpdate(mockUpdate, false)
|
|
|
|
_, err = s.validateLightClientFinalityUpdate(ctx, "", nil)
|
|
require.ErrorIs(t, err, errNilPubsubMessage)
|
|
|
|
_, err = s.validateLightClientFinalityUpdate(ctx, "", &pubsub.Message{Message: &pb.Message{}})
|
|
require.ErrorIs(t, err, errNilPubsubMessage)
|
|
|
|
emptyTopic := ""
|
|
_, err = s.validateLightClientFinalityUpdate(ctx, "", &pubsub.Message{Message: &pb.Message{
|
|
Topic: &emptyTopic,
|
|
}})
|
|
require.ErrorIs(t, err, errNilPubsubMessage)
|
|
}
|
|
|
|
func TestValidateLightClientFinalityUpdate(t *testing.T) {
|
|
params.SetupTestConfigCleanup(t)
|
|
cfg := params.BeaconConfig().Copy()
|
|
cfg.AltairForkEpoch = 1
|
|
cfg.BellatrixForkEpoch = 2
|
|
cfg.CapellaForkEpoch = 3
|
|
cfg.DenebForkEpoch = 4
|
|
cfg.ElectraForkEpoch = 5
|
|
cfg.FuluForkEpoch = 6
|
|
cfg.ForkVersionSchedule[[4]byte{1, 0, 0, 0}] = 1
|
|
cfg.ForkVersionSchedule[[4]byte{2, 0, 0, 0}] = 2
|
|
cfg.ForkVersionSchedule[[4]byte{3, 0, 0, 0}] = 3
|
|
cfg.ForkVersionSchedule[[4]byte{4, 0, 0, 0}] = 4
|
|
cfg.ForkVersionSchedule[[4]byte{5, 0, 0, 0}] = 5
|
|
cfg.ForkVersionSchedule[[4]byte{6, 0, 0, 0}] = 6
|
|
params.OverrideBeaconConfig(cfg)
|
|
|
|
secondsPerSlot := int(params.BeaconConfig().SecondsPerSlot)
|
|
slotIntervals := int(params.BeaconConfig().IntervalsPerSlot)
|
|
slotsPerEpoch := int(params.BeaconConfig().SlotsPerEpoch)
|
|
|
|
tests := []struct {
|
|
name string
|
|
genesisDrift int
|
|
oldUpdateOptions []util.LightClientOption
|
|
newUpdateOptions []util.LightClientOption
|
|
expectedResult pubsub.ValidationResult
|
|
expectedErr error
|
|
}{
|
|
{
|
|
name: "no previous update",
|
|
oldUpdateOptions: nil,
|
|
newUpdateOptions: []util.LightClientOption{},
|
|
expectedResult: pubsub.ValidationIgnore,
|
|
},
|
|
{
|
|
name: "not enough time passed",
|
|
genesisDrift: -int(math.Ceil(float64(params.BeaconConfig().SlotComponentDuration(params.BeaconConfig().SyncMessageDueBPS)) / float64(time.Second))),
|
|
oldUpdateOptions: []util.LightClientOption{},
|
|
newUpdateOptions: []util.LightClientOption{},
|
|
expectedResult: pubsub.ValidationIgnore,
|
|
},
|
|
{
|
|
name: "new update is the same",
|
|
oldUpdateOptions: []util.LightClientOption{},
|
|
newUpdateOptions: []util.LightClientOption{},
|
|
expectedResult: pubsub.ValidationAccept,
|
|
},
|
|
{
|
|
name: "new update is different",
|
|
oldUpdateOptions: []util.LightClientOption{},
|
|
newUpdateOptions: []util.LightClientOption{util.WithIncreasedFinalizedSlot(1)},
|
|
expectedResult: pubsub.ValidationIgnore,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
for v := range version.All() {
|
|
if v == version.Phase0 {
|
|
continue
|
|
}
|
|
t.Run(test.name+"_"+version.String(v), func(t *testing.T) {
|
|
ctx := t.Context()
|
|
p := p2ptest.NewTestP2P(t)
|
|
// drift back appropriate number of epochs based on fork + 2 slots for signature slot + time for gossip propagation + any extra drift
|
|
genesisDrift := v*slotsPerEpoch*secondsPerSlot + 2*secondsPerSlot + secondsPerSlot/slotIntervals + test.genesisDrift
|
|
chainService := &mock.ChainService{Genesis: time.Unix(time.Now().Unix()-int64(genesisDrift), 0)}
|
|
lcStore := lightClient.NewLightClientStore(&p2ptest.FakeP2P{}, new(event.Feed), testDB.SetupDB(t))
|
|
s := &Service{cfg: &config{p2p: p, initialSync: &mockSync.Sync{}, clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot)}, lcStore: lcStore}
|
|
|
|
var oldUpdate interfaces.LightClientFinalityUpdate
|
|
if test.oldUpdateOptions != nil {
|
|
l := util.NewTestLightClient(t, v, test.oldUpdateOptions...)
|
|
var err error
|
|
oldUpdate, err = lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, l.AttestedBlock, l.FinalizedBlock)
|
|
require.NoError(t, err)
|
|
|
|
s.lcStore.SetLastFinalityUpdate(oldUpdate, false)
|
|
}
|
|
|
|
l := util.NewTestLightClient(t, v, test.newUpdateOptions...)
|
|
newUpdate, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, l.AttestedBlock, l.FinalizedBlock)
|
|
require.NoError(t, err)
|
|
buf := new(bytes.Buffer)
|
|
_, err = p.Encoding().EncodeGossip(buf, newUpdate)
|
|
require.NoError(t, err)
|
|
|
|
topic := p2p.LightClientFinalityUpdateTopicFormat
|
|
digest, err := s.currentForkDigest()
|
|
require.NoError(t, err)
|
|
topic = s.addDigestToTopic(topic, digest)
|
|
|
|
r, err := s.validateLightClientFinalityUpdate(ctx, "", &pubsub.Message{
|
|
Message: &pb.Message{
|
|
Data: buf.Bytes(),
|
|
Topic: &topic,
|
|
}})
|
|
if test.expectedErr != nil {
|
|
require.ErrorIs(t, err, test.expectedErr)
|
|
} else {
|
|
require.NoError(t, err)
|
|
require.Equal(t, test.expectedResult, r)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|