mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 22:07:59 -05:00
Compare commits
6 Commits
verify-wit
...
potuz_patc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7880f2887f | ||
|
|
6735c921f8 | ||
|
|
02fb1534e1 | ||
|
|
619334115a | ||
|
|
de0825f880 | ||
|
|
7794a77ae6 |
@@ -79,6 +79,7 @@ type HeadFetcher interface {
|
||||
HeadPublicKeyToValidatorIndex(pubKey [fieldparams.BLSPubkeyLength]byte) (primitives.ValidatorIndex, bool)
|
||||
HeadValidatorIndexToPublicKey(ctx context.Context, index primitives.ValidatorIndex) ([fieldparams.BLSPubkeyLength]byte, error)
|
||||
ChainHeads() ([][32]byte, []primitives.Slot)
|
||||
DependentRootForEpoch([32]byte, primitives.Epoch) ([32]byte, error)
|
||||
TargetRootForEpoch([32]byte, primitives.Epoch) ([32]byte, error)
|
||||
HeadSyncCommitteeFetcher
|
||||
HeadDomainFetcher
|
||||
@@ -470,6 +471,13 @@ func (s *Service) IsOptimisticForRoot(ctx context.Context, root [32]byte) (bool,
|
||||
return !isCanonical, nil
|
||||
}
|
||||
|
||||
// DependentRootForEpoch wraps the corresponding method in forkchoice
|
||||
func (s *Service) DependentRootForEpoch(root [32]byte, epoch primitives.Epoch) ([32]byte, error) {
|
||||
s.cfg.ForkChoiceStore.RLock()
|
||||
defer s.cfg.ForkChoiceStore.RUnlock()
|
||||
return s.cfg.ForkChoiceStore.DependentRootForEpoch(root, epoch)
|
||||
}
|
||||
|
||||
// TargetRootForEpoch wraps the corresponding method in forkchoice
|
||||
func (s *Service) TargetRootForEpoch(root [32]byte, epoch primitives.Epoch) ([32]byte, error) {
|
||||
s.cfg.ForkChoiceStore.RLock()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
@@ -34,11 +33,15 @@ func (s *Service) getRecentPreState(ctx context.Context, c *ethpb.Checkpoint) st
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
headTarget, err := s.cfg.ForkChoiceStore.TargetRootForEpoch([32]byte(headRoot), c.Epoch)
|
||||
headDependent, err := s.cfg.ForkChoiceStore.DependentRootForEpoch([32]byte(headRoot), c.Epoch)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if !bytes.Equal(c.Root, headTarget[:]) {
|
||||
targetDependent, err := s.cfg.ForkChoiceStore.DependentRootForEpoch([32]byte(c.Root), c.Epoch)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if targetDependent != headDependent {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -2804,7 +2804,7 @@ func TestProcessLightClientUpdate(t *testing.T) {
|
||||
require.NoError(t, s.cfg.BeaconDB.SaveState(ctx, headState, [32]byte{1, 2}))
|
||||
require.NoError(t, s.cfg.BeaconDB.SaveHeadBlockRoot(ctx, [32]byte{1, 2}))
|
||||
|
||||
for testVersion := version.Altair; testVersion <= version.Electra; testVersion++ {
|
||||
for _, testVersion := range version.All()[1:] {
|
||||
t.Run(version.String(testVersion), func(t *testing.T) {
|
||||
l := util.NewTestLightClient(t, testVersion)
|
||||
|
||||
|
||||
@@ -758,6 +758,11 @@ func (c *ChainService) ReceiveDataColumns(dcs []blocks.VerifiedRODataColumn) err
|
||||
return nil
|
||||
}
|
||||
|
||||
// DependentRootForEpoch mocks the same method in the chain service
|
||||
func (c *ChainService) DependentRootForEpoch(_ [32]byte, _ primitives.Epoch) ([32]byte, error) {
|
||||
return c.TargetRoot, nil
|
||||
}
|
||||
|
||||
// TargetRootForEpoch mocks the same method in the chain service
|
||||
func (c *ChainService) TargetRootForEpoch(_ [32]byte, _ primitives.Epoch) ([32]byte, error) {
|
||||
return c.TargetRoot, nil
|
||||
|
||||
@@ -181,6 +181,13 @@ func decodeLightClientBootstrap(enc []byte) (interfaces.LightClientBootstrap, []
|
||||
}
|
||||
m = bootstrap
|
||||
syncCommitteeHash = enc[len(ElectraKey) : len(ElectraKey)+32]
|
||||
case hasFuluKey(enc):
|
||||
bootstrap := ðpb.LightClientBootstrapElectra{}
|
||||
if err := bootstrap.UnmarshalSSZ(enc[len(fuluKey)+32:]); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "could not unmarshal Electra light client bootstrap")
|
||||
}
|
||||
m = bootstrap
|
||||
syncCommitteeHash = enc[len(fuluKey) : len(fuluKey)+32]
|
||||
default:
|
||||
return nil, nil, errors.New("decoding of saved light client bootstrap is unsupported")
|
||||
}
|
||||
@@ -296,6 +303,12 @@ func decodeLightClientUpdate(enc []byte) (interfaces.LightClientUpdate, error) {
|
||||
return nil, errors.Wrap(err, "could not unmarshal Electra light client update")
|
||||
}
|
||||
m = update
|
||||
case hasFuluKey(enc):
|
||||
update := ðpb.LightClientUpdateElectra{}
|
||||
if err := update.UnmarshalSSZ(enc[len(fuluKey):]); err != nil {
|
||||
return nil, errors.Wrap(err, "could not unmarshal Fulu light client update")
|
||||
}
|
||||
m = update
|
||||
default:
|
||||
return nil, errors.New("decoding of saved light client update is unsupported")
|
||||
}
|
||||
@@ -304,6 +317,8 @@ func decodeLightClientUpdate(enc []byte) (interfaces.LightClientUpdate, error) {
|
||||
|
||||
func keyForLightClientUpdate(v int) ([]byte, error) {
|
||||
switch v {
|
||||
case version.Fulu:
|
||||
return fuluKey, nil
|
||||
case version.Electra:
|
||||
return ElectraKey, nil
|
||||
case version.Deneb:
|
||||
|
||||
@@ -215,8 +215,7 @@ func TestStore_LightClientUpdate_CanSaveRetrieve(t *testing.T) {
|
||||
|
||||
db := setupDB(t)
|
||||
ctx := t.Context()
|
||||
|
||||
for testVersion := version.Altair; testVersion <= version.Electra; testVersion++ {
|
||||
for _, testVersion := range version.All()[1:] {
|
||||
t.Run(version.String(testVersion), func(t *testing.T) {
|
||||
update, err := createUpdate(t, testVersion)
|
||||
require.NoError(t, err)
|
||||
@@ -572,8 +571,7 @@ func TestStore_LightClientBootstrap_CanSaveRetrieve(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.IsNil(t, retrievedBootstrap)
|
||||
})
|
||||
|
||||
for testVersion := version.Altair; testVersion <= version.Electra; testVersion++ {
|
||||
for _, testVersion := range version.All()[1:] {
|
||||
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)
|
||||
|
||||
@@ -100,6 +100,7 @@ go_test(
|
||||
deps = [
|
||||
"//async/event:go_default_library",
|
||||
"//beacon-chain/blockchain/kzg:go_default_library",
|
||||
"//beacon-chain/blockchain/testing:go_default_library",
|
||||
"//beacon-chain/cache/depositsnapshot:go_default_library",
|
||||
"//beacon-chain/core/feed:go_default_library",
|
||||
"//beacon-chain/core/feed/state:go_default_library",
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/async/event"
|
||||
chainMock "github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/testing"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/cache/depositsnapshot"
|
||||
dbutil "github.com/OffchainLabs/prysm/v7/beacon-chain/db/testing"
|
||||
mockExecution "github.com/OffchainLabs/prysm/v7/beacon-chain/execution/testing"
|
||||
@@ -99,7 +100,7 @@ func TestStart_OK(t *testing.T) {
|
||||
c := startup.NewClockSynchronizer()
|
||||
require.NoError(t, c.SetClock(startup.NewClock(time.Unix(0, 0), [32]byte{})))
|
||||
waiter := verification.NewInitializerWaiter(
|
||||
c, forkchoice.NewROForkChoice(nil), nil)
|
||||
c, forkchoice.NewROForkChoice(nil), nil, &chainMock.ChainService{})
|
||||
|
||||
web3Service, err := NewService(t.Context(),
|
||||
WithHttpEndpoint(endpoint),
|
||||
|
||||
@@ -626,21 +626,26 @@ func (f *ForkChoice) Slot(root [32]byte) (primitives.Slot, error) {
|
||||
|
||||
// DependentRoot returns the last root of the epoch prior to the requested ecoch in the canonical chain.
|
||||
func (f *ForkChoice) DependentRoot(epoch primitives.Epoch) ([32]byte, error) {
|
||||
tr, err := f.TargetRootForEpoch(f.CachedHeadRoot(), epoch)
|
||||
return f.DependentRootForEpoch(f.CachedHeadRoot(), epoch)
|
||||
}
|
||||
|
||||
// DependentRootForEpoch return the last root of the epoch prior to the requested ecoch for the given root.
|
||||
func (f *ForkChoice) DependentRootForEpoch(root [32]byte, epoch primitives.Epoch) ([32]byte, error) {
|
||||
tr, err := f.TargetRootForEpoch(root, epoch)
|
||||
if err != nil {
|
||||
return [32]byte{}, err
|
||||
}
|
||||
if tr == [32]byte{} {
|
||||
return [32]byte{}, nil
|
||||
}
|
||||
n, ok := f.store.nodeByRoot[tr]
|
||||
if !ok || n == nil {
|
||||
node, ok := f.store.nodeByRoot[tr]
|
||||
if !ok || node == nil {
|
||||
return [32]byte{}, ErrNilNode
|
||||
}
|
||||
if slots.ToEpoch(n.slot) == epoch && n.parent != nil {
|
||||
n = n.parent
|
||||
if slots.ToEpoch(node.slot) >= epoch && node.parent != nil {
|
||||
node = node.parent
|
||||
}
|
||||
return n.root, nil
|
||||
return node.root, nil
|
||||
}
|
||||
|
||||
// TargetRootForEpoch returns the root of the target block for a given epoch.
|
||||
|
||||
@@ -608,6 +608,96 @@ func TestStore_TargetRootForEpoch(t *testing.T) {
|
||||
require.Equal(t, blk4.Root(), target)
|
||||
}
|
||||
|
||||
func TestStore_DependentRootForEpoch(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
f := setup(1, 1)
|
||||
|
||||
// Build the following tree structure:
|
||||
// /------------37
|
||||
// 0<--31<---32 <---33 <--- 35 <-------- 65 <--- 66
|
||||
// \-- 36 ------------- 38
|
||||
|
||||
// Insert block at slot 31 (epoch 0)
|
||||
state, blk31, err := prepareForkchoiceState(ctx, 31, [32]byte{31}, params.BeaconConfig().ZeroHash, params.BeaconConfig().ZeroHash, 1, 1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blk31))
|
||||
|
||||
// Insert block at slot 32 (epoch 1)
|
||||
state, blk32, err := prepareForkchoiceState(ctx, 32, [32]byte{32}, blk31.Root(), params.BeaconConfig().ZeroHash, 1, 1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blk32))
|
||||
|
||||
// Insert block at slot 33 (epoch 1)
|
||||
state, blk33, err := prepareForkchoiceState(ctx, 33, [32]byte{33}, blk32.Root(), params.BeaconConfig().ZeroHash, 1, 1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blk33))
|
||||
|
||||
// Insert block at slot 35 (epoch 1)
|
||||
state, blk35, err := prepareForkchoiceState(ctx, 35, [32]byte{35}, blk33.Root(), params.BeaconConfig().ZeroHash, 1, 1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blk35))
|
||||
|
||||
// Insert fork: block at slot 36 (epoch 1) descending from block 32
|
||||
state, blk36, err := prepareForkchoiceState(ctx, 36, [32]byte{36}, blk32.Root(), params.BeaconConfig().ZeroHash, 1, 1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blk36))
|
||||
|
||||
// Insert block at slot 37 (epoch 1) descending from block 33
|
||||
state, blk37, err := prepareForkchoiceState(ctx, 37, [32]byte{37}, blk33.Root(), params.BeaconConfig().ZeroHash, 1, 1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blk37))
|
||||
|
||||
// Insert block at slot 38 (epoch 1) descending from block 36
|
||||
state, blk38, err := prepareForkchoiceState(ctx, 38, [32]byte{38}, blk36.Root(), params.BeaconConfig().ZeroHash, 1, 1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blk38))
|
||||
|
||||
// Insert block at slot 65 (epoch 2) descending from block 35
|
||||
state, blk65, err := prepareForkchoiceState(ctx, 65, [32]byte{65}, blk35.Root(), params.BeaconConfig().ZeroHash, 1, 1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blk65))
|
||||
|
||||
// Insert block at slot 66 (epoch 2) descending from block 65
|
||||
state, blk66, err := prepareForkchoiceState(ctx, 66, [32]byte{66}, blk65.Root(), params.BeaconConfig().ZeroHash, 1, 1)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blk66))
|
||||
|
||||
// Test dependent root for block 32 at epoch 1 - should be block 31
|
||||
dependent, err := f.DependentRootForEpoch(blk32.Root(), 1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, blk31.Root(), dependent)
|
||||
|
||||
// Test dependent root for block 32 at epoch 2 - should be block 32
|
||||
dependent, err = f.DependentRootForEpoch(blk32.Root(), 2)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, blk32.Root(), dependent)
|
||||
|
||||
// Test dependent root for block 33 at epoch 1 - should be block 31
|
||||
dependent, err = f.DependentRootForEpoch(blk33.Root(), 1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, blk31.Root(), dependent)
|
||||
|
||||
// Test dependent root for block 38 at epoch 1 - should be block 31
|
||||
dependent, err = f.DependentRootForEpoch(blk38.Root(), 1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, blk31.Root(), dependent)
|
||||
|
||||
// Test dependent root for block 36 at epoch 2 - should be block 36
|
||||
dependent, err = f.DependentRootForEpoch(blk36.Root(), 2)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, blk36.Root(), dependent)
|
||||
|
||||
// Test dependent root for block 66 at epoch 1 - should be block 31
|
||||
dependent, err = f.DependentRootForEpoch(blk66.Root(), 1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, blk31.Root(), dependent)
|
||||
|
||||
// Test dependent root for block 66 at epoch 2 - should be block 35
|
||||
dependent, err = f.DependentRootForEpoch(blk66.Root(), 2)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, blk35.Root(), dependent)
|
||||
}
|
||||
|
||||
func TestStore_CleanupInserting(t *testing.T) {
|
||||
f := setup(0, 0)
|
||||
ctx := t.Context()
|
||||
|
||||
@@ -81,6 +81,7 @@ type FastGetter interface {
|
||||
ShouldOverrideFCU() bool
|
||||
Slot([32]byte) (primitives.Slot, error)
|
||||
DependentRoot(primitives.Epoch) ([32]byte, error)
|
||||
DependentRootForEpoch([32]byte, primitives.Epoch) ([32]byte, error)
|
||||
TargetRootForEpoch([32]byte, primitives.Epoch) ([32]byte, error)
|
||||
UnrealizedJustifiedPayloadBlockHash() [32]byte
|
||||
Weight(root [32]byte) (uint64, error)
|
||||
|
||||
@@ -177,6 +177,13 @@ func (ro *ROForkChoice) DependentRoot(epoch primitives.Epoch) ([32]byte, error)
|
||||
return ro.getter.DependentRoot(epoch)
|
||||
}
|
||||
|
||||
// DependentRootForEpoch delegates to the underlying forkchoice call, under a lock.
|
||||
func (ro *ROForkChoice) DependentRootForEpoch(root [32]byte, epoch primitives.Epoch) ([32]byte, error) {
|
||||
ro.l.RLock()
|
||||
defer ro.l.RUnlock()
|
||||
return ro.getter.DependentRootForEpoch(root, epoch)
|
||||
}
|
||||
|
||||
// TargetRootForEpoch delegates to the underlying forkchoice call, under a lock.
|
||||
func (ro *ROForkChoice) TargetRootForEpoch(root [32]byte, epoch primitives.Epoch) ([32]byte, error) {
|
||||
ro.l.RLock()
|
||||
|
||||
@@ -40,6 +40,7 @@ const (
|
||||
targetRootForEpochCalled
|
||||
parentRootCalled
|
||||
dependentRootCalled
|
||||
dependentRootForEpochCalled
|
||||
)
|
||||
|
||||
func _discard(t *testing.T, e error) {
|
||||
@@ -305,6 +306,12 @@ func (ro *mockROForkchoice) DependentRoot(_ primitives.Epoch) ([32]byte, error)
|
||||
return [32]byte{}, nil
|
||||
}
|
||||
|
||||
// DependentRootForEpoch implements FastGetter.
|
||||
func (ro *mockROForkchoice) DependentRootForEpoch(_ [32]byte, _ primitives.Epoch) ([32]byte, error) {
|
||||
ro.calls = append(ro.calls, dependentRootForEpochCalled)
|
||||
return [32]byte{}, nil
|
||||
}
|
||||
|
||||
// TargetRootForEpoch implements FastGetter.
|
||||
func (ro *mockROForkchoice) TargetRootForEpoch(_ [32]byte, _ primitives.Epoch) ([32]byte, error) {
|
||||
ro.calls = append(ro.calls, targetRootForEpochCalled)
|
||||
|
||||
@@ -29,58 +29,22 @@ func TestLightClient_NewLightClientOptimisticUpdateFromBeaconState(t *testing.T)
|
||||
cfg.CapellaForkEpoch = 3
|
||||
cfg.DenebForkEpoch = 4
|
||||
cfg.ElectraForkEpoch = 5
|
||||
cfg.FuluForkEpoch = 6
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
t.Run("Altair", func(t *testing.T) {
|
||||
l := util.NewTestLightClient(t, version.Altair)
|
||||
for _, testVersion := range version.All()[1:] {
|
||||
t.Run(version.String(testVersion), func(t *testing.T) {
|
||||
l := util.NewTestLightClient(t, testVersion)
|
||||
|
||||
update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, l.AttestedBlock)
|
||||
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")
|
||||
update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, l.AttestedBlock)
|
||||
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.SyncAggregate())
|
||||
l.CheckAttestedHeader(update.AttestedHeader())
|
||||
})
|
||||
|
||||
t.Run("Capella", func(t *testing.T) {
|
||||
l := util.NewTestLightClient(t, version.Capella)
|
||||
|
||||
update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, l.AttestedBlock)
|
||||
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.SyncAggregate())
|
||||
l.CheckAttestedHeader(update.AttestedHeader())
|
||||
})
|
||||
|
||||
t.Run("Deneb", func(t *testing.T) {
|
||||
l := util.NewTestLightClient(t, version.Deneb)
|
||||
|
||||
update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, l.AttestedBlock)
|
||||
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.SyncAggregate())
|
||||
l.CheckAttestedHeader(update.AttestedHeader())
|
||||
})
|
||||
|
||||
t.Run("Electra", func(t *testing.T) {
|
||||
l := util.NewTestLightClient(t, version.Electra)
|
||||
|
||||
update, err := lightClient.NewLightClientOptimisticUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, l.AttestedBlock)
|
||||
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.SyncAggregate())
|
||||
l.CheckAttestedHeader(update.AttestedHeader())
|
||||
})
|
||||
l.CheckSyncAggregate(update.SyncAggregate())
|
||||
l.CheckAttestedHeader(update.AttestedHeader())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) {
|
||||
@@ -91,6 +55,7 @@ func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) {
|
||||
cfg.CapellaForkEpoch = 3
|
||||
cfg.DenebForkEpoch = 4
|
||||
cfg.ElectraForkEpoch = 5
|
||||
cfg.FuluForkEpoch = 6
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
t.Run("Altair", func(t *testing.T) {
|
||||
@@ -538,6 +503,157 @@ func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) {
|
||||
require.DeepSSZEqual(t, execution, updateExecution.Proto(), "Finalized Block Execution is not equal")
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Fulu", func(t *testing.T) {
|
||||
t.Run("FinalizedBlock Not Nil", func(t *testing.T) {
|
||||
l := util.NewTestLightClient(t, version.Fulu)
|
||||
|
||||
update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, l.AttestedBlock, l.FinalizedBlock)
|
||||
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.SyncAggregate())
|
||||
l.CheckAttestedHeader(update.AttestedHeader())
|
||||
|
||||
//zeroHash := params.BeaconConfig().ZeroHash[:]
|
||||
finalizedBlockHeader, err := l.FinalizedBlock.Header()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, update.FinalizedHeader(), "Finalized header is nil")
|
||||
updateFinalizedHeaderBeacon := update.FinalizedHeader().Beacon()
|
||||
require.Equal(t, finalizedBlockHeader.Header.Slot, updateFinalizedHeaderBeacon.Slot, "Finalized header slot is not equal")
|
||||
require.Equal(t, finalizedBlockHeader.Header.ProposerIndex, updateFinalizedHeaderBeacon.ProposerIndex, "Finalized header proposer index is not equal")
|
||||
require.DeepSSZEqual(t, finalizedBlockHeader.Header.ParentRoot, updateFinalizedHeaderBeacon.ParentRoot, "Finalized header parent root is not equal")
|
||||
require.DeepSSZEqual(t, finalizedBlockHeader.Header.StateRoot, updateFinalizedHeaderBeacon.StateRoot, "Finalized header state root is not equal")
|
||||
require.DeepSSZEqual(t, finalizedBlockHeader.Header.BodyRoot, updateFinalizedHeaderBeacon.BodyRoot, "Finalized header body root is not equal")
|
||||
fb, err := update.FinalityBranchElectra()
|
||||
require.NoError(t, err)
|
||||
proof, err := l.AttestedState.FinalizedRootProof(l.Ctx)
|
||||
require.NoError(t, err)
|
||||
for i, leaf := range fb {
|
||||
require.DeepSSZEqual(t, proof[i], leaf[:], "Leaf is not equal")
|
||||
}
|
||||
|
||||
// Check Execution BlockHash
|
||||
payloadInterface, err := l.FinalizedBlock.Block().Body().Execution()
|
||||
require.NoError(t, err)
|
||||
transactionsRoot, err := payloadInterface.TransactionsRoot()
|
||||
if errors.Is(err, consensustypes.ErrUnsupportedField) {
|
||||
transactions, err := payloadInterface.Transactions()
|
||||
require.NoError(t, err)
|
||||
transactionsRootArray, err := ssz.TransactionsRoot(transactions)
|
||||
require.NoError(t, err)
|
||||
transactionsRoot = transactionsRootArray[:]
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
withdrawalsRoot, err := payloadInterface.WithdrawalsRoot()
|
||||
if errors.Is(err, consensustypes.ErrUnsupportedField) {
|
||||
withdrawals, err := payloadInterface.Withdrawals()
|
||||
require.NoError(t, err)
|
||||
withdrawalsRootArray, err := ssz.WithdrawalSliceRoot(withdrawals, fieldparams.MaxWithdrawalsPerPayload)
|
||||
require.NoError(t, err)
|
||||
withdrawalsRoot = withdrawalsRootArray[:]
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
execution := &v11.ExecutionPayloadHeaderDeneb{
|
||||
ParentHash: payloadInterface.ParentHash(),
|
||||
FeeRecipient: payloadInterface.FeeRecipient(),
|
||||
StateRoot: payloadInterface.StateRoot(),
|
||||
ReceiptsRoot: payloadInterface.ReceiptsRoot(),
|
||||
LogsBloom: payloadInterface.LogsBloom(),
|
||||
PrevRandao: payloadInterface.PrevRandao(),
|
||||
BlockNumber: payloadInterface.BlockNumber(),
|
||||
GasLimit: payloadInterface.GasLimit(),
|
||||
GasUsed: payloadInterface.GasUsed(),
|
||||
Timestamp: payloadInterface.Timestamp(),
|
||||
ExtraData: payloadInterface.ExtraData(),
|
||||
BaseFeePerGas: payloadInterface.BaseFeePerGas(),
|
||||
BlockHash: payloadInterface.BlockHash(),
|
||||
TransactionsRoot: transactionsRoot,
|
||||
WithdrawalsRoot: withdrawalsRoot,
|
||||
}
|
||||
updateExecution, err := update.FinalizedHeader().Execution()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, execution, updateExecution.Proto(), "Finalized Block Execution is not equal")
|
||||
})
|
||||
|
||||
t.Run("FinalizedBlock In Previous Fork", func(t *testing.T) {
|
||||
l := util.NewTestLightClient(t, version.Fulu, util.WithFinalizedCheckpointInPrevFork())
|
||||
|
||||
update, err := lightClient.NewLightClientFinalityUpdateFromBeaconState(l.Ctx, l.State, l.Block, l.AttestedState, l.AttestedBlock, l.FinalizedBlock)
|
||||
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.SyncAggregate())
|
||||
l.CheckAttestedHeader(update.AttestedHeader())
|
||||
|
||||
finalizedBlockHeader, err := l.FinalizedBlock.Header()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, update.FinalizedHeader(), "Finalized header is nil")
|
||||
updateFinalizedHeaderBeacon := update.FinalizedHeader().Beacon()
|
||||
require.Equal(t, finalizedBlockHeader.Header.Slot, updateFinalizedHeaderBeacon.Slot, "Finalized header slot is not equal")
|
||||
require.Equal(t, finalizedBlockHeader.Header.ProposerIndex, updateFinalizedHeaderBeacon.ProposerIndex, "Finalized header proposer index is not equal")
|
||||
require.DeepSSZEqual(t, finalizedBlockHeader.Header.ParentRoot, updateFinalizedHeaderBeacon.ParentRoot, "Finalized header parent root is not equal")
|
||||
require.DeepSSZEqual(t, finalizedBlockHeader.Header.StateRoot, updateFinalizedHeaderBeacon.StateRoot, "Finalized header state root is not equal")
|
||||
require.DeepSSZEqual(t, finalizedBlockHeader.Header.BodyRoot, updateFinalizedHeaderBeacon.BodyRoot, "Finalized header body root is not equal")
|
||||
fb, err := update.FinalityBranchElectra()
|
||||
require.NoError(t, err)
|
||||
proof, err := l.AttestedState.FinalizedRootProof(l.Ctx)
|
||||
require.NoError(t, err)
|
||||
for i, leaf := range fb {
|
||||
require.DeepSSZEqual(t, proof[i], leaf[:], "Leaf is not equal")
|
||||
}
|
||||
|
||||
// Check Execution BlockHash
|
||||
payloadInterface, err := l.FinalizedBlock.Block().Body().Execution()
|
||||
require.NoError(t, err)
|
||||
transactionsRoot, err := payloadInterface.TransactionsRoot()
|
||||
if errors.Is(err, consensustypes.ErrUnsupportedField) {
|
||||
transactions, err := payloadInterface.Transactions()
|
||||
require.NoError(t, err)
|
||||
transactionsRootArray, err := ssz.TransactionsRoot(transactions)
|
||||
require.NoError(t, err)
|
||||
transactionsRoot = transactionsRootArray[:]
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
withdrawalsRoot, err := payloadInterface.WithdrawalsRoot()
|
||||
if errors.Is(err, consensustypes.ErrUnsupportedField) {
|
||||
withdrawals, err := payloadInterface.Withdrawals()
|
||||
require.NoError(t, err)
|
||||
withdrawalsRootArray, err := ssz.WithdrawalSliceRoot(withdrawals, fieldparams.MaxWithdrawalsPerPayload)
|
||||
require.NoError(t, err)
|
||||
withdrawalsRoot = withdrawalsRootArray[:]
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
execution := &v11.ExecutionPayloadHeaderDeneb{
|
||||
ParentHash: payloadInterface.ParentHash(),
|
||||
FeeRecipient: payloadInterface.FeeRecipient(),
|
||||
StateRoot: payloadInterface.StateRoot(),
|
||||
ReceiptsRoot: payloadInterface.ReceiptsRoot(),
|
||||
LogsBloom: payloadInterface.LogsBloom(),
|
||||
PrevRandao: payloadInterface.PrevRandao(),
|
||||
BlockNumber: payloadInterface.BlockNumber(),
|
||||
GasLimit: payloadInterface.GasLimit(),
|
||||
GasUsed: payloadInterface.GasUsed(),
|
||||
Timestamp: payloadInterface.Timestamp(),
|
||||
ExtraData: payloadInterface.ExtraData(),
|
||||
BaseFeePerGas: payloadInterface.BaseFeePerGas(),
|
||||
BlockHash: payloadInterface.BlockHash(),
|
||||
TransactionsRoot: transactionsRoot,
|
||||
WithdrawalsRoot: withdrawalsRoot,
|
||||
}
|
||||
updateExecution, err := update.FinalizedHeader().Execution()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, execution, updateExecution.Proto(), "Finalized Block Execution is not equal")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestLightClient_BlockToLightClientHeader(t *testing.T) {
|
||||
@@ -983,6 +1099,138 @@ func TestLightClient_BlockToLightClientHeader(t *testing.T) {
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Fulu", func(t *testing.T) {
|
||||
t.Run("Non-Blinded Beacon Block", func(t *testing.T) {
|
||||
l := util.NewTestLightClient(t, version.Fulu)
|
||||
|
||||
header, err := lightClient.BlockToLightClientHeader(l.Ctx, version.Fulu, l.Block)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, header, "header is nil")
|
||||
|
||||
parentRoot := l.Block.Block().ParentRoot()
|
||||
stateRoot := l.Block.Block().StateRoot()
|
||||
bodyRoot, err := l.Block.Block().Body().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
payload, err := l.Block.Block().Body().Execution()
|
||||
require.NoError(t, err)
|
||||
|
||||
transactionsRoot, err := lightClient.ComputeTransactionsRoot(payload)
|
||||
require.NoError(t, err)
|
||||
|
||||
withdrawalsRoot, err := lightClient.ComputeWithdrawalsRoot(payload)
|
||||
require.NoError(t, err)
|
||||
|
||||
blobGasUsed, err := payload.BlobGasUsed()
|
||||
require.NoError(t, err)
|
||||
|
||||
excessBlobGas, err := payload.ExcessBlobGas()
|
||||
require.NoError(t, err)
|
||||
|
||||
executionHeader := &v11.ExecutionPayloadHeaderDeneb{
|
||||
ParentHash: payload.ParentHash(),
|
||||
FeeRecipient: payload.FeeRecipient(),
|
||||
StateRoot: payload.StateRoot(),
|
||||
ReceiptsRoot: payload.ReceiptsRoot(),
|
||||
LogsBloom: payload.LogsBloom(),
|
||||
PrevRandao: payload.PrevRandao(),
|
||||
BlockNumber: payload.BlockNumber(),
|
||||
GasLimit: payload.GasLimit(),
|
||||
GasUsed: payload.GasUsed(),
|
||||
Timestamp: payload.Timestamp(),
|
||||
ExtraData: payload.ExtraData(),
|
||||
BaseFeePerGas: payload.BaseFeePerGas(),
|
||||
BlockHash: payload.BlockHash(),
|
||||
TransactionsRoot: transactionsRoot,
|
||||
WithdrawalsRoot: withdrawalsRoot,
|
||||
BlobGasUsed: blobGasUsed,
|
||||
ExcessBlobGas: excessBlobGas,
|
||||
}
|
||||
|
||||
executionPayloadProof, err := blocks.PayloadProof(l.Ctx, l.Block.Block())
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, l.Block.Block().Slot(), header.Beacon().Slot, "Slot is not equal")
|
||||
require.Equal(t, l.Block.Block().ProposerIndex(), header.Beacon().ProposerIndex, "Proposer index is not equal")
|
||||
require.DeepSSZEqual(t, parentRoot[:], header.Beacon().ParentRoot, "Parent root is not equal")
|
||||
require.DeepSSZEqual(t, stateRoot[:], header.Beacon().StateRoot, "State root is not equal")
|
||||
require.DeepSSZEqual(t, bodyRoot[:], header.Beacon().BodyRoot, "Body root is not equal")
|
||||
|
||||
headerExecution, err := header.Execution()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, executionHeader, headerExecution.Proto(), "Execution headers are not equal")
|
||||
|
||||
headerExecutionBranch, err := header.ExecutionBranch()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, executionPayloadProof, convertArrayToSlice(headerExecutionBranch), "Execution payload proofs are not equal")
|
||||
})
|
||||
|
||||
t.Run("Blinded Beacon Block", func(t *testing.T) {
|
||||
l := util.NewTestLightClient(t, version.Fulu, util.WithBlinded())
|
||||
|
||||
header, err := lightClient.BlockToLightClientHeader(l.Ctx, version.Fulu, l.Block)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, header, "header is nil")
|
||||
|
||||
parentRoot := l.Block.Block().ParentRoot()
|
||||
stateRoot := l.Block.Block().StateRoot()
|
||||
bodyRoot, err := l.Block.Block().Body().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
payload, err := l.Block.Block().Body().Execution()
|
||||
require.NoError(t, err)
|
||||
|
||||
transactionsRoot, err := payload.TransactionsRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
withdrawalsRoot, err := payload.WithdrawalsRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
blobGasUsed, err := payload.BlobGasUsed()
|
||||
require.NoError(t, err)
|
||||
|
||||
excessBlobGas, err := payload.ExcessBlobGas()
|
||||
require.NoError(t, err)
|
||||
|
||||
executionHeader := &v11.ExecutionPayloadHeaderDeneb{
|
||||
ParentHash: payload.ParentHash(),
|
||||
FeeRecipient: payload.FeeRecipient(),
|
||||
StateRoot: payload.StateRoot(),
|
||||
ReceiptsRoot: payload.ReceiptsRoot(),
|
||||
LogsBloom: payload.LogsBloom(),
|
||||
PrevRandao: payload.PrevRandao(),
|
||||
BlockNumber: payload.BlockNumber(),
|
||||
GasLimit: payload.GasLimit(),
|
||||
GasUsed: payload.GasUsed(),
|
||||
Timestamp: payload.Timestamp(),
|
||||
ExtraData: payload.ExtraData(),
|
||||
BaseFeePerGas: payload.BaseFeePerGas(),
|
||||
BlockHash: payload.BlockHash(),
|
||||
TransactionsRoot: transactionsRoot,
|
||||
WithdrawalsRoot: withdrawalsRoot,
|
||||
BlobGasUsed: blobGasUsed,
|
||||
ExcessBlobGas: excessBlobGas,
|
||||
}
|
||||
|
||||
executionPayloadProof, err := blocks.PayloadProof(l.Ctx, l.Block.Block())
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, l.Block.Block().Slot(), header.Beacon().Slot, "Slot is not equal")
|
||||
require.Equal(t, l.Block.Block().ProposerIndex(), header.Beacon().ProposerIndex, "Proposer index is not equal")
|
||||
require.DeepSSZEqual(t, parentRoot[:], header.Beacon().ParentRoot, "Parent root is not equal")
|
||||
require.DeepSSZEqual(t, stateRoot[:], header.Beacon().StateRoot, "State root is not equal")
|
||||
require.DeepSSZEqual(t, bodyRoot[:], header.Beacon().BodyRoot, "Body root is not equal")
|
||||
|
||||
headerExecution, err := header.Execution()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, executionHeader, headerExecution.Proto(), "Execution headers are not equal")
|
||||
|
||||
headerExecutionBranch, err := header.ExecutionBranch()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, executionPayloadProof, convertArrayToSlice(headerExecutionBranch), "Execution payload proofs are not equal")
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Capella fork with Altair block", func(t *testing.T) {
|
||||
l := util.NewTestLightClient(t, version.Altair)
|
||||
|
||||
|
||||
@@ -124,6 +124,7 @@ type BeaconNode struct {
|
||||
DataColumnStorage *filesystem.DataColumnStorage
|
||||
DataColumnStorageOptions []filesystem.DataColumnStorageOption
|
||||
verifyInitWaiter *verification.InitializerWaiter
|
||||
lhsp *verification.LazyHeadStateProvider
|
||||
syncChecker *initialsync.SyncChecker
|
||||
slasherEnabled bool
|
||||
lcStore *lightclient.Store
|
||||
@@ -230,8 +231,9 @@ func New(cliCtx *cli.Context, cancel context.CancelFunc, opts ...Option) (*Beaco
|
||||
return nil, errors.Wrap(err, "could not start modules")
|
||||
}
|
||||
|
||||
beacon.lhsp = &verification.LazyHeadStateProvider{}
|
||||
beacon.verifyInitWaiter = verification.NewInitializerWaiter(
|
||||
beacon.clockWaiter, forkchoice.NewROForkChoice(beacon.forkChoicer), beacon.stateGen)
|
||||
beacon.clockWaiter, forkchoice.NewROForkChoice(beacon.forkChoicer), beacon.stateGen, beacon.lhsp)
|
||||
|
||||
beacon.BackfillOpts = append(
|
||||
beacon.BackfillOpts,
|
||||
@@ -749,6 +751,7 @@ func (b *BeaconNode) registerBlockchainService(fc forkchoice.ForkChoicer, gs *st
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not register blockchain service")
|
||||
}
|
||||
b.lhsp.HeadStateProvider = blockchainService
|
||||
return b.services.RegisterService(blockchainService)
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ func TestLightClientHandler_GetLightClientBootstrap(t *testing.T) {
|
||||
cfg.FuluForkEpoch = 5
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
for testVersion := version.Altair; testVersion <= version.Electra; testVersion++ {
|
||||
for _, testVersion := range version.All()[1:] {
|
||||
t.Run(version.String(testVersion), func(t *testing.T) {
|
||||
l := util.NewTestLightClient(t, testVersion)
|
||||
|
||||
@@ -131,7 +131,7 @@ func TestLightClientHandler_GetLightClientBootstrap(t *testing.T) {
|
||||
resp = &pb.LightClientBootstrapCapella{}
|
||||
case version.Deneb:
|
||||
resp = &pb.LightClientBootstrapDeneb{}
|
||||
case version.Electra:
|
||||
case version.Electra, version.Fulu:
|
||||
resp = &pb.LightClientBootstrapElectra{}
|
||||
default:
|
||||
t.Fatalf("Unsupported version %s", version.String(testVersion))
|
||||
@@ -173,10 +173,11 @@ func TestLightClientHandler_GetLightClientByRange(t *testing.T) {
|
||||
config.CapellaForkEpoch = 2
|
||||
config.DenebForkEpoch = 3
|
||||
config.ElectraForkEpoch = 4
|
||||
config.FuluForkEpoch = 5
|
||||
params.OverrideBeaconConfig(config)
|
||||
|
||||
t.Run("can save retrieve", func(t *testing.T) {
|
||||
for testVersion := version.Altair; testVersion <= version.Electra; testVersion++ {
|
||||
for _, testVersion := range version.All()[1:] {
|
||||
t.Run(version.String(testVersion), func(t *testing.T) {
|
||||
|
||||
slot := primitives.Slot(params.BeaconConfig().VersionToForkEpochMap()[testVersion] * primitives.Epoch(config.SlotsPerEpoch)).Add(1)
|
||||
@@ -252,7 +253,7 @@ func TestLightClientHandler_GetLightClientByRange(t *testing.T) {
|
||||
resp = &pb.LightClientUpdateCapella{}
|
||||
case version.Deneb:
|
||||
resp = &pb.LightClientUpdateDeneb{}
|
||||
case version.Electra:
|
||||
case version.Electra, version.Fulu:
|
||||
resp = &pb.LightClientUpdateElectra{}
|
||||
default:
|
||||
t.Fatalf("Unsupported version %s", version.String(testVersion))
|
||||
@@ -313,7 +314,7 @@ func TestLightClientHandler_GetLightClientByRange(t *testing.T) {
|
||||
resp = &pb.LightClientUpdateCapella{}
|
||||
case version.Deneb:
|
||||
resp = &pb.LightClientUpdateDeneb{}
|
||||
case version.Electra:
|
||||
case version.Electra, version.Fulu:
|
||||
resp = &pb.LightClientUpdateElectra{}
|
||||
default:
|
||||
t.Fatalf("Unsupported version %s", version.String(testVersion))
|
||||
@@ -730,7 +731,7 @@ func TestLightClientHandler_GetLightClientFinalityUpdate(t *testing.T) {
|
||||
require.Equal(t, http.StatusNotFound, writer.Code)
|
||||
})
|
||||
|
||||
for testVersion := 1; testVersion < 6; testVersion++ {
|
||||
for _, testVersion := range version.All()[1:] {
|
||||
t.Run(version.String(testVersion), func(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
@@ -793,7 +794,7 @@ func TestLightClientHandler_GetLightClientFinalityUpdate(t *testing.T) {
|
||||
resp = &pb.LightClientFinalityUpdateCapella{}
|
||||
case version.Deneb:
|
||||
resp = &pb.LightClientFinalityUpdateDeneb{}
|
||||
case version.Electra:
|
||||
case version.Electra, version.Fulu:
|
||||
resp = &pb.LightClientFinalityUpdateElectra{}
|
||||
default:
|
||||
t.Fatalf("Unsupported version %s", version.String(testVersion))
|
||||
@@ -825,7 +826,7 @@ func TestLightClientHandler_GetLightClientOptimisticUpdate(t *testing.T) {
|
||||
require.Equal(t, http.StatusNotFound, writer.Code)
|
||||
})
|
||||
|
||||
for testVersion := 1; testVersion < 6; testVersion++ {
|
||||
for _, testVersion := range version.All()[1:] {
|
||||
t.Run(version.String(testVersion), func(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
l := util.NewTestLightClient(t, testVersion)
|
||||
@@ -886,7 +887,7 @@ func TestLightClientHandler_GetLightClientOptimisticUpdate(t *testing.T) {
|
||||
resp = &pb.LightClientOptimisticUpdateCapella{}
|
||||
case version.Deneb:
|
||||
resp = &pb.LightClientOptimisticUpdateDeneb{}
|
||||
case version.Electra:
|
||||
case version.Electra, version.Fulu:
|
||||
resp = &pb.LightClientOptimisticUpdateDeneb{}
|
||||
default:
|
||||
t.Fatalf("Unsupported version %s", version.String(testVersion))
|
||||
|
||||
@@ -154,7 +154,7 @@ func TestFetchDataColumnSidecars(t *testing.T) {
|
||||
err = gs.SetClock(startup.NewClock(time.Unix(4113849600, 0), [fieldparams.RootLength]byte{}))
|
||||
require.NoError(t, err)
|
||||
|
||||
waiter := verification.NewInitializerWaiter(gs, nil, nil)
|
||||
waiter := verification.NewInitializerWaiter(gs, nil, nil, nil)
|
||||
initializer, err := waiter.WaitForInitializer(t.Context())
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -787,7 +787,7 @@ func TestVerifyDataColumnSidecarsByPeer(t *testing.T) {
|
||||
err := gs.SetClock(startup.NewClock(time.Unix(4113849600, 0), [fieldparams.RootLength]byte{}))
|
||||
require.NoError(t, err)
|
||||
|
||||
waiter := verification.NewInitializerWaiter(gs, nil, nil)
|
||||
waiter := verification.NewInitializerWaiter(gs, nil, nil, nil)
|
||||
initializer, err := waiter.WaitForInitializer(t.Context())
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -832,7 +832,7 @@ func TestVerifyDataColumnSidecarsByPeer(t *testing.T) {
|
||||
err := gs.SetClock(startup.NewClock(time.Unix(4113849600, 0), [fieldparams.RootLength]byte{}))
|
||||
require.NoError(t, err)
|
||||
|
||||
waiter := verification.NewInitializerWaiter(gs, nil, nil)
|
||||
waiter := verification.NewInitializerWaiter(gs, nil, nil, nil)
|
||||
initializer, err := waiter.WaitForInitializer(t.Context())
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@@ -216,6 +216,9 @@ func (s *Service) fetchOriginSidecars(peers []peer.ID) error {
|
||||
if errors.Is(err, db.ErrNotFoundOriginBlockRoot) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error fetching origin checkpoint blockroot")
|
||||
}
|
||||
|
||||
block, err := s.cfg.DB.Block(s.ctx, blockRoot)
|
||||
if err != nil {
|
||||
|
||||
@@ -174,7 +174,7 @@ func TestService_InitStartStop(t *testing.T) {
|
||||
StateNotifier: &mock.MockStateNotifier{},
|
||||
InitialSyncComplete: make(chan struct{}),
|
||||
})
|
||||
s.verifierWaiter = verification.NewInitializerWaiter(gs, nil, nil)
|
||||
s.verifierWaiter = verification.NewInitializerWaiter(gs, nil, nil, nil)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
assert.NotNil(t, s)
|
||||
if tt.setGenesis != nil {
|
||||
@@ -217,7 +217,7 @@ func TestService_waitForStateInitialization(t *testing.T) {
|
||||
counter: ratecounter.NewRateCounter(counterSeconds * time.Second),
|
||||
genesisChan: make(chan time.Time),
|
||||
}
|
||||
s.verifierWaiter = verification.NewInitializerWaiter(cs, nil, nil)
|
||||
s.verifierWaiter = verification.NewInitializerWaiter(cs, nil, nil, nil)
|
||||
return s, cs
|
||||
}
|
||||
|
||||
@@ -786,7 +786,7 @@ func TestFetchOriginColumns(t *testing.T) {
|
||||
err = gs.SetClock(startup.NewClock(time.Unix(4113849600, 0), [fieldparams.RootLength]byte{}))
|
||||
require.NoError(t, err)
|
||||
|
||||
waiter := verification.NewInitializerWaiter(gs, nil, nil)
|
||||
waiter := verification.NewInitializerWaiter(gs, nil, nil, nil)
|
||||
initializer, err := waiter.WaitForInitializer(t.Context())
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@@ -332,7 +332,7 @@ func TestHandshakeHandlers_Roundtrip(t *testing.T) {
|
||||
markInitSyncComplete(t, r)
|
||||
clock := startup.NewClockSynchronizer()
|
||||
require.NoError(t, clock.SetClock(startup.NewClock(time.Now(), [32]byte{})))
|
||||
r.verifierWaiter = verification.NewInitializerWaiter(clock, chain.ForkChoiceStore, r.cfg.stateGen)
|
||||
r.verifierWaiter = verification.NewInitializerWaiter(clock, chain.ForkChoiceStore, r.cfg.stateGen, chain)
|
||||
p1.Digest, err = r.currentForkDigest()
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -354,7 +354,7 @@ func TestHandshakeHandlers_Roundtrip(t *testing.T) {
|
||||
markInitSyncComplete(t, r2)
|
||||
clock = startup.NewClockSynchronizer()
|
||||
require.NoError(t, clock.SetClock(startup.NewClock(time.Now(), [32]byte{})))
|
||||
r2.verifierWaiter = verification.NewInitializerWaiter(clock, chain2.ForkChoiceStore, r2.cfg.stateGen)
|
||||
r2.verifierWaiter = verification.NewInitializerWaiter(clock, chain2.ForkChoiceStore, r2.cfg.stateGen, chain2)
|
||||
|
||||
p2.Digest, err = r.currentForkDigest()
|
||||
require.NoError(t, err)
|
||||
@@ -948,7 +948,7 @@ func TestStatusRPCRequest_BadPeerHandshake(t *testing.T) {
|
||||
markInitSyncComplete(t, r)
|
||||
clock := startup.NewClockSynchronizer()
|
||||
require.NoError(t, clock.SetClock(startup.NewClock(time.Now(), [32]byte{})))
|
||||
r.verifierWaiter = verification.NewInitializerWaiter(clock, chain.ForkChoiceStore, r.cfg.stateGen)
|
||||
r.verifierWaiter = verification.NewInitializerWaiter(clock, chain.ForkChoiceStore, r.cfg.stateGen, chain)
|
||||
|
||||
go r.Start()
|
||||
|
||||
|
||||
@@ -337,17 +337,17 @@ func (s *Service) blockVerifyingState(ctx context.Context, blk interfaces.ReadOn
|
||||
}
|
||||
return transition.ProcessSlotsUsingNextSlotCache(ctx, headState, headRoot, blockSlot)
|
||||
}
|
||||
// If head and block are in the same epoch and head is compatible with the parent's target, then use head
|
||||
// If head and block are in the same epoch and head is compatible with the parent's dependent root, then use head
|
||||
if blockEpoch == headEpoch {
|
||||
headTarget, err := s.cfg.chain.TargetRootForEpoch([32]byte(headRoot), blockEpoch)
|
||||
headDependent, err := s.cfg.chain.DependentRootForEpoch([32]byte(headRoot), blockEpoch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parentTarget, err := s.cfg.chain.TargetRootForEpoch([32]byte(parentRoot), blockEpoch)
|
||||
parentDependent, err := s.cfg.chain.DependentRootForEpoch([32]byte(parentRoot), blockEpoch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bytes.Equal(headTarget[:], parentTarget[:]) {
|
||||
if bytes.Equal(headDependent[:], parentDependent[:]) {
|
||||
return s.cfg.chain.HeadStateReadOnly(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,7 +433,7 @@ func TestService_ValidateBlsToExecutionChange(t *testing.T) {
|
||||
tt.clock = startup.NewClock(time.Now(), [32]byte{})
|
||||
}
|
||||
require.NoError(t, cw.SetClock(tt.clock))
|
||||
svc.verifierWaiter = verification.NewInitializerWaiter(cw, chainService.ForkChoiceStore, svc.cfg.stateGen)
|
||||
svc.verifierWaiter = verification.NewInitializerWaiter(cw, chainService.ForkChoiceStore, svc.cfg.stateGen, chainService)
|
||||
go svc.Start()
|
||||
|
||||
marshalledObj, err := tt.args.msg.MarshalSSZ()
|
||||
|
||||
@@ -411,7 +411,7 @@ func TestService_ValidateSyncCommitteeMessage(t *testing.T) {
|
||||
svc, tt.args.topic, clock = tt.setupSvc(svc, tt.args.msg, tt.args.topic)
|
||||
markInitSyncComplete(t, svc)
|
||||
require.NoError(t, cw.SetClock(clock))
|
||||
svc.verifierWaiter = verification.NewInitializerWaiter(cw, chainService.ForkChoiceStore, svc.cfg.stateGen)
|
||||
svc.verifierWaiter = verification.NewInitializerWaiter(cw, chainService.ForkChoiceStore, svc.cfg.stateGen, chainService)
|
||||
go svc.Start()
|
||||
|
||||
marshalledObj, err := tt.args.msg.MarshalSSZ()
|
||||
|
||||
@@ -855,7 +855,7 @@ func TestService_ValidateSyncContributionAndProof(t *testing.T) {
|
||||
var clock *startup.Clock
|
||||
svc, clock = tt.setupSvc(svc, tt.args.msg)
|
||||
require.NoError(t, cw.SetClock(clock))
|
||||
svc.verifierWaiter = verification.NewInitializerWaiter(cw, chainService.ForkChoiceStore, svc.cfg.stateGen)
|
||||
svc.verifierWaiter = verification.NewInitializerWaiter(cw, chainService.ForkChoiceStore, svc.cfg.stateGen, chainService)
|
||||
markInitSyncComplete(t, svc)
|
||||
go svc.Start()
|
||||
marshalledObj, err := tt.args.msg.MarshalSSZ()
|
||||
|
||||
@@ -69,6 +69,7 @@ go_test(
|
||||
"//beacon-chain/forkchoice/types:go_default_library",
|
||||
"//beacon-chain/startup:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/state-native:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/blocks:go_default_library",
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
forkchoicetypes "github.com/OffchainLabs/prysm/v7/beacon-chain/forkchoice/types"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/startup"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
||||
state_native "github.com/OffchainLabs/prysm/v7/beacon-chain/state/state-native"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
@@ -547,11 +548,12 @@ func TestRequirementSatisfaction(t *testing.T) {
|
||||
}
|
||||
|
||||
type mockForkchoicer struct {
|
||||
FinalizedCheckpointCB func() *forkchoicetypes.Checkpoint
|
||||
HasNodeCB func([32]byte) bool
|
||||
IsCanonicalCB func(root [32]byte) bool
|
||||
SlotCB func([32]byte) (primitives.Slot, error)
|
||||
TargetRootForEpochCB func([32]byte, primitives.Epoch) ([32]byte, error)
|
||||
FinalizedCheckpointCB func() *forkchoicetypes.Checkpoint
|
||||
HasNodeCB func([32]byte) bool
|
||||
IsCanonicalCB func(root [32]byte) bool
|
||||
SlotCB func([32]byte) (primitives.Slot, error)
|
||||
DependentRootForEpochCB func([32]byte, primitives.Epoch) ([32]byte, error)
|
||||
TargetRootForEpochCB func([32]byte, primitives.Epoch) ([32]byte, error)
|
||||
}
|
||||
|
||||
var _ Forkchoicer = &mockForkchoicer{}
|
||||
@@ -572,6 +574,10 @@ func (m *mockForkchoicer) Slot(root [32]byte) (primitives.Slot, error) {
|
||||
return m.SlotCB(root)
|
||||
}
|
||||
|
||||
func (m *mockForkchoicer) DependentRootForEpoch(root [32]byte, epoch primitives.Epoch) ([32]byte, error) {
|
||||
return m.DependentRootForEpochCB(root, epoch)
|
||||
}
|
||||
|
||||
func (m *mockForkchoicer) TargetRootForEpoch(root [32]byte, epoch primitives.Epoch) ([32]byte, error) {
|
||||
return m.TargetRootForEpochCB(root, epoch)
|
||||
}
|
||||
@@ -626,6 +632,45 @@ func (sbr *mockStateByRooter) StateByRoot(ctx context.Context, root [32]byte) (s
|
||||
|
||||
var _ StateByRooter = &mockStateByRooter{}
|
||||
|
||||
type mockHeadStateProvider struct {
|
||||
headRoot []byte
|
||||
headSlot primitives.Slot
|
||||
headState state.BeaconState
|
||||
headStateReadOnly state.ReadOnlyBeaconState
|
||||
}
|
||||
|
||||
func (m *mockHeadStateProvider) HeadRoot(_ context.Context) ([]byte, error) {
|
||||
if m.headRoot != nil {
|
||||
return m.headRoot, nil
|
||||
}
|
||||
root := make([]byte, 32)
|
||||
root[0] = 0xff
|
||||
return root, nil
|
||||
}
|
||||
|
||||
func (m *mockHeadStateProvider) HeadSlot() primitives.Slot {
|
||||
if m.headSlot == 0 {
|
||||
return 1000
|
||||
}
|
||||
return m.headSlot
|
||||
}
|
||||
|
||||
func (m *mockHeadStateProvider) HeadState(_ context.Context) (state.BeaconState, error) {
|
||||
if m.headState == nil {
|
||||
return nil, errors.New("head state not available")
|
||||
}
|
||||
return m.headState, nil
|
||||
}
|
||||
|
||||
func (m *mockHeadStateProvider) HeadStateReadOnly(_ context.Context) (state.ReadOnlyBeaconState, error) {
|
||||
if m.headStateReadOnly == nil {
|
||||
return nil, errors.New("head state read only not available")
|
||||
}
|
||||
return m.headStateReadOnly, nil
|
||||
}
|
||||
|
||||
var _ HeadStateProvider = &mockHeadStateProvider{}
|
||||
|
||||
func sbrErrorIfCalled(t *testing.T) sbrfunc {
|
||||
return func(_ context.Context, _ [32]byte) (state.BeaconState, error) {
|
||||
t.Error("StateByRoot should not have been called")
|
||||
@@ -643,15 +688,56 @@ func sbrNotFound(t *testing.T, expectedRoot [32]byte) *mockStateByRooter {
|
||||
}
|
||||
|
||||
func sbrForValOverride(idx primitives.ValidatorIndex, val *ethpb.Validator) *mockStateByRooter {
|
||||
return sbrForValOverrideWithT(nil, idx, val)
|
||||
}
|
||||
|
||||
func sbrForValOverrideWithT(t testing.TB, idx primitives.ValidatorIndex, val *ethpb.Validator) *mockStateByRooter {
|
||||
return &mockStateByRooter{sbr: func(_ context.Context, root [32]byte) (state.BeaconState, error) {
|
||||
return &validxStateOverride{vals: map[primitives.ValidatorIndex]*ethpb.Validator{
|
||||
idx: val,
|
||||
}}, nil
|
||||
// Use a real deterministic state so that helpers.BeaconProposerIndexAtSlot works correctly
|
||||
numValidators := uint64(idx + 1)
|
||||
if numValidators < 64 {
|
||||
numValidators = 64
|
||||
}
|
||||
|
||||
var st state.BeaconState
|
||||
var err error
|
||||
if t != nil {
|
||||
st, _ = util.DeterministicGenesisStateFulu(t, numValidators)
|
||||
} else {
|
||||
// Fallback for blob tests that don't need the full state
|
||||
return &validxStateOverride{
|
||||
slot: 0,
|
||||
vals: map[primitives.ValidatorIndex]*ethpb.Validator{
|
||||
idx: val,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Override the specific validator if provided
|
||||
if val != nil {
|
||||
vals := st.Validators()
|
||||
if idx < primitives.ValidatorIndex(len(vals)) {
|
||||
vals[idx] = val
|
||||
// Ensure the validator is active
|
||||
if vals[idx].ActivationEpoch > 0 {
|
||||
vals[idx].ActivationEpoch = 0
|
||||
}
|
||||
if vals[idx].ExitEpoch == 0 || vals[idx].ExitEpoch < params.BeaconConfig().FarFutureEpoch {
|
||||
vals[idx].ExitEpoch = params.BeaconConfig().FarFutureEpoch
|
||||
}
|
||||
if vals[idx].EffectiveBalance == 0 {
|
||||
vals[idx].EffectiveBalance = params.BeaconConfig().MaxEffectiveBalance
|
||||
}
|
||||
_ = st.SetValidators(vals)
|
||||
}
|
||||
}
|
||||
return st, err
|
||||
}}
|
||||
}
|
||||
|
||||
type validxStateOverride struct {
|
||||
state.BeaconState
|
||||
slot primitives.Slot
|
||||
vals map[primitives.ValidatorIndex]*ethpb.Validator
|
||||
}
|
||||
|
||||
@@ -665,6 +751,105 @@ func (v *validxStateOverride) ValidatorAtIndex(idx primitives.ValidatorIndex) (*
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func (v *validxStateOverride) Slot() primitives.Slot {
|
||||
return v.slot
|
||||
}
|
||||
|
||||
func (v *validxStateOverride) Version() int {
|
||||
// Return Fulu version (6) as default for tests
|
||||
return 6
|
||||
}
|
||||
|
||||
func (v *validxStateOverride) Validators() []*ethpb.Validator {
|
||||
// Return all validators in the map as a slice
|
||||
maxIdx := primitives.ValidatorIndex(0)
|
||||
for idx := range v.vals {
|
||||
if idx > maxIdx {
|
||||
maxIdx = idx
|
||||
}
|
||||
}
|
||||
// Ensure we have at least 64 validators for a valid beacon state
|
||||
numValidators := maxIdx + 1
|
||||
if numValidators < 64 {
|
||||
numValidators = 64
|
||||
}
|
||||
validators := make([]*ethpb.Validator, numValidators)
|
||||
for i := range validators {
|
||||
if val, ok := v.vals[primitives.ValidatorIndex(i)]; ok {
|
||||
validators[i] = val
|
||||
} else {
|
||||
// Default validator for indices we don't care about
|
||||
validators[i] = ðpb.Validator{
|
||||
ActivationEpoch: 0,
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
||||
}
|
||||
}
|
||||
}
|
||||
return validators
|
||||
}
|
||||
|
||||
func (v *validxStateOverride) RandaoMixAtIndex(idx uint64) ([]byte, error) {
|
||||
// Return a zero mix for simplicity in tests
|
||||
return make([]byte, 32), nil
|
||||
}
|
||||
|
||||
func (v *validxStateOverride) NumValidators() int {
|
||||
return len(v.Validators())
|
||||
}
|
||||
|
||||
func (v *validxStateOverride) ValidatorAtIndexReadOnly(idx primitives.ValidatorIndex) (state.ReadOnlyValidator, error) {
|
||||
validators := v.Validators()
|
||||
if idx >= primitives.ValidatorIndex(len(validators)) {
|
||||
return nil, fmt.Errorf("validator index %d out of range", idx)
|
||||
}
|
||||
return state_native.NewValidator(validators[idx])
|
||||
}
|
||||
|
||||
func (v *validxStateOverride) IsNil() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (v *validxStateOverride) LatestBlockHeader() *ethpb.BeaconBlockHeader {
|
||||
// Return a minimal block header for tests
|
||||
return ðpb.BeaconBlockHeader{
|
||||
Slot: v.slot,
|
||||
ProposerIndex: 0,
|
||||
ParentRoot: make([]byte, 32),
|
||||
StateRoot: make([]byte, 32),
|
||||
BodyRoot: make([]byte, 32),
|
||||
}
|
||||
}
|
||||
|
||||
func (v *validxStateOverride) HashTreeRoot(ctx context.Context) ([32]byte, error) {
|
||||
// Return a zero hash for tests
|
||||
return [32]byte{}, nil
|
||||
}
|
||||
|
||||
func (v *validxStateOverride) UpdateStateRootAtIndex(idx uint64, stateRoot [32]byte) error {
|
||||
// No-op for mock - we don't track state roots
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *validxStateOverride) SetLatestBlockHeader(val *ethpb.BeaconBlockHeader) error {
|
||||
// No-op for mock - we don't track block headers
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *validxStateOverride) ReadFromEveryValidator(f func(idx int, val state.ReadOnlyValidator) error) error {
|
||||
validators := v.Validators()
|
||||
for i, val := range validators {
|
||||
rov, err := state_native.NewValidator(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f(i, rov); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type mockProposerCache struct {
|
||||
ComputeProposerCB func(ctx context.Context, root [32]byte, slot primitives.Slot, pst state.BeaconState) (primitives.ValidatorIndex, error)
|
||||
ProposerCB func(c *forkchoicetypes.Checkpoint, slot primitives.Slot) (primitives.ValidatorIndex, bool)
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package verification
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/helpers"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/peerdas"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/transition"
|
||||
forkchoicetypes "github.com/OffchainLabs/prysm/v7/beacon-chain/forkchoice/types"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
||||
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
|
||||
@@ -66,6 +69,12 @@ var (
|
||||
errBadTopic = errors.New("topic is not of the one expected")
|
||||
)
|
||||
|
||||
type LazyHeadStateProvider struct {
|
||||
HeadStateProvider
|
||||
}
|
||||
|
||||
var _ HeadStateProvider = &LazyHeadStateProvider{}
|
||||
|
||||
type (
|
||||
RODataColumnsVerifier struct {
|
||||
*sharedResources
|
||||
@@ -262,14 +271,14 @@ func (dv *RODataColumnsVerifier) ValidProposerSignature(ctx context.Context) (er
|
||||
if _, err, _ = dv.sg.Do(signatureData.concat(), func() (any, error) {
|
||||
columnVerificationProposerSignatureCache.WithLabelValues("miss").Inc()
|
||||
|
||||
// Retrieve the parent state.
|
||||
parentState, err := dv.state(ctx, dataColumn.ParentRoot())
|
||||
// Retrieve a state compatible with the data column for verification.
|
||||
verifyingState, err := dv.getVerifyingState(ctx, dataColumn)
|
||||
if err != nil {
|
||||
return nil, columnErrBuilder(errors.Wrap(err, "parent state"))
|
||||
return nil, columnErrBuilder(errors.Wrap(err, "verifying state"))
|
||||
}
|
||||
|
||||
// Full verification, which will subsequently be cached for anything sharing the signature cache.
|
||||
if err = dv.sc.VerifySignature(signatureData, parentState); err != nil {
|
||||
if err = dv.sc.VerifySignature(signatureData, verifyingState); err != nil {
|
||||
return nil, columnErrBuilder(errors.Wrap(err, "verify signature"))
|
||||
}
|
||||
|
||||
@@ -282,6 +291,61 @@ func (dv *RODataColumnsVerifier) ValidProposerSignature(ctx context.Context) (er
|
||||
return nil
|
||||
}
|
||||
|
||||
// getVerifyingState returns a state that is compatible with the column sidecar and can be used to verify signature and proposer index.
|
||||
// The returned state is guaranteed to be at the same epoch as the data column's epoch, and have the same randao mix and active
|
||||
// validator indices as the data column's parent state advanced to the data column's slot.
|
||||
func (dv *RODataColumnsVerifier) getVerifyingState(ctx context.Context, dataColumn blocks.RODataColumn) (state.ReadOnlyBeaconState, error) {
|
||||
headRoot, err := dv.hsp.HeadRoot(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parentRoot := dataColumn.ParentRoot()
|
||||
dataColumnSlot := dataColumn.Slot()
|
||||
dataColumnEpoch := slots.ToEpoch(dataColumnSlot)
|
||||
headSlot := dv.hsp.HeadSlot()
|
||||
headEpoch := slots.ToEpoch(headSlot)
|
||||
|
||||
// Use head if it's the parent
|
||||
if bytes.Equal(parentRoot[:], headRoot) {
|
||||
// If they are in the same epoch, then we can return the head state directly
|
||||
if dataColumnEpoch == headEpoch {
|
||||
return dv.hsp.HeadStateReadOnly(ctx)
|
||||
}
|
||||
// Otherwise, we need to process the head state to the data column's slot
|
||||
headState, err := dv.hsp.HeadState(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return transition.ProcessSlotsUsingNextSlotCache(ctx, headState, headRoot, dataColumnSlot)
|
||||
}
|
||||
|
||||
// If head and data column are in the same epoch and head is compatible with the parent's depdendent root, then use head
|
||||
if dataColumnEpoch == headEpoch {
|
||||
headDependent, err := dv.fc.DependentRootForEpoch(bytesutil.ToBytes32(headRoot), dataColumnEpoch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parentDependent, err := dv.fc.DependentRootForEpoch(parentRoot, dataColumnEpoch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bytes.Equal(headDependent[:], parentDependent[:]) {
|
||||
return dv.hsp.HeadStateReadOnly(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise retrieve the parent state and advance it to the data column's slot
|
||||
parentState, err := dv.sr.StateByRoot(ctx, parentRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parentEpoch := slots.ToEpoch(parentState.Slot())
|
||||
if dataColumnEpoch == parentEpoch {
|
||||
return parentState, nil
|
||||
}
|
||||
return transition.ProcessSlotsUsingNextSlotCache(ctx, parentState, parentRoot[:], dataColumnSlot)
|
||||
}
|
||||
|
||||
func (dv *RODataColumnsVerifier) SidecarParentSeen(parentSeen func([fieldparams.RootLength]byte) bool) (err error) {
|
||||
if ok, err := dv.results.cached(RequireSidecarParentSeen); ok {
|
||||
return err
|
||||
@@ -482,14 +546,12 @@ func (dv *RODataColumnsVerifier) SidecarProposerExpected(ctx context.Context) (e
|
||||
// Ensure the expensive index computation is only performed once for
|
||||
// concurrent requests for the same signature data.
|
||||
idxAny, err, _ := dv.sg.Do(concatRootSlot(parentRoot, dataColumnSlot), func() (any, error) {
|
||||
// Retrieve the parent state.
|
||||
parentState, err := dv.state(ctx, parentRoot)
|
||||
verifyingState, err := dv.getVerifyingState(ctx, dataColumn)
|
||||
if err != nil {
|
||||
return nil, columnErrBuilder(errors.Wrap(err, "parent state"))
|
||||
return nil, columnErrBuilder(errors.Wrap(err, "verifying state"))
|
||||
}
|
||||
|
||||
// Compute the proposer index.
|
||||
idx, err = dv.pc.ComputeProposer(ctx, parentRoot, dataColumnSlot, parentState)
|
||||
idx, err = helpers.BeaconProposerIndexAtSlot(ctx, verifyingState, dataColumnSlot)
|
||||
if err != nil {
|
||||
return nil, columnErrBuilder(errors.Wrap(err, "compute proposer"))
|
||||
}
|
||||
@@ -514,25 +576,6 @@ func (dv *RODataColumnsVerifier) SidecarProposerExpected(ctx context.Context) (e
|
||||
return nil
|
||||
}
|
||||
|
||||
// state retrieves the state of the corresponding root from the cache if possible, else retrieves it from the state by rooter.
|
||||
func (dv *RODataColumnsVerifier) state(ctx context.Context, root [fieldparams.RootLength]byte) (state.BeaconState, error) {
|
||||
// If the parent root is already in the cache, return it.
|
||||
if st, ok := dv.stateByRoot[root]; ok {
|
||||
return st, nil
|
||||
}
|
||||
|
||||
// Retrieve the parent state from the state by rooter.
|
||||
st, err := dv.sr.StateByRoot(ctx, root)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "state by root")
|
||||
}
|
||||
|
||||
// Store the parent state in the cache.
|
||||
dv.stateByRoot[root] = st
|
||||
|
||||
return st, nil
|
||||
}
|
||||
|
||||
func columnToSignatureData(d blocks.RODataColumn) signatureData {
|
||||
return signatureData{
|
||||
Root: d.BlockRoot(),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package verification
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -11,7 +10,6 @@ import (
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/peerdas"
|
||||
forkchoicetypes "github.com/OffchainLabs/prysm/v7/beacon-chain/forkchoice/types"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/startup"
|
||||
"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/blocks"
|
||||
@@ -330,7 +328,7 @@ func TestValidProposerSignature(t *testing.T) {
|
||||
svcbError: nil,
|
||||
vscbShouldError: false,
|
||||
vscbError: nil,
|
||||
stateByRooter: sbrForValOverride(firstColumn.ProposerIndex(), validator),
|
||||
stateByRooter: sbrForValOverrideWithT(t, firstColumn.ProposerIndex(), validator),
|
||||
isError: false,
|
||||
},
|
||||
{
|
||||
@@ -348,7 +346,7 @@ func TestValidProposerSignature(t *testing.T) {
|
||||
svcbError: nil,
|
||||
vscbShouldError: false,
|
||||
vscbError: errors.New("signature, not so good!"),
|
||||
stateByRooter: sbrForValOverride(firstColumn.ProposerIndex(), validator),
|
||||
stateByRooter: sbrForValOverrideWithT(t, firstColumn.ProposerIndex(), validator),
|
||||
isError: true,
|
||||
},
|
||||
}
|
||||
@@ -378,8 +376,12 @@ func TestValidProposerSignature(t *testing.T) {
|
||||
|
||||
initializer := Initializer{
|
||||
shared: &sharedResources{
|
||||
sc: signatureCache,
|
||||
sr: tc.stateByRooter,
|
||||
sc: signatureCache,
|
||||
sr: tc.stateByRooter,
|
||||
hsp: &mockHeadStateProvider{},
|
||||
fc: &mockForkchoicer{
|
||||
TargetRootForEpochCB: fcReturnsTargetRoot([fieldparams.RootLength]byte{}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -796,20 +798,7 @@ func TestDataColumnsSidecarProposerExpected(t *testing.T) {
|
||||
parentRoot := [fieldparams.RootLength]byte{}
|
||||
columns := GenerateTestDataColumns(t, parentRoot, columnSlot, blobCount)
|
||||
firstColumn := columns[0]
|
||||
|
||||
newColumns := GenerateTestDataColumns(t, parentRoot, 2*params.BeaconConfig().SlotsPerEpoch, blobCount)
|
||||
firstNewColumn := newColumns[0]
|
||||
|
||||
validator := ðpb.Validator{}
|
||||
|
||||
commonComputeProposerCB := func(_ context.Context, root [fieldparams.RootLength]byte, slot primitives.Slot, _ state.BeaconState) (primitives.ValidatorIndex, error) {
|
||||
require.Equal(t, firstColumn.ParentRoot(), root)
|
||||
require.Equal(t, firstColumn.Slot(), slot)
|
||||
return firstColumn.ProposerIndex(), nil
|
||||
}
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
stateByRooter StateByRooter
|
||||
@@ -841,66 +830,7 @@ func TestDataColumnsSidecarProposerExpected(t *testing.T) {
|
||||
ProposerCB: pcReturnsNotFound(),
|
||||
},
|
||||
columns: columns,
|
||||
error: "state by root",
|
||||
},
|
||||
{
|
||||
name: "Not cached, proposer matches",
|
||||
stateByRooter: sbrForValOverride(firstColumn.ProposerIndex(), validator),
|
||||
proposerCache: &mockProposerCache{
|
||||
ProposerCB: pcReturnsNotFound(),
|
||||
ComputeProposerCB: commonComputeProposerCB,
|
||||
},
|
||||
columns: columns,
|
||||
},
|
||||
{
|
||||
name: "Not cached, proposer matches",
|
||||
stateByRooter: sbrForValOverride(firstColumn.ProposerIndex(), validator),
|
||||
proposerCache: &mockProposerCache{
|
||||
ProposerCB: pcReturnsNotFound(),
|
||||
ComputeProposerCB: commonComputeProposerCB,
|
||||
},
|
||||
columns: columns,
|
||||
},
|
||||
{
|
||||
name: "Not cached, proposer matches for next epoch",
|
||||
stateByRooter: sbrForValOverride(firstNewColumn.ProposerIndex(), validator),
|
||||
proposerCache: &mockProposerCache{
|
||||
ProposerCB: pcReturnsNotFound(),
|
||||
ComputeProposerCB: func(_ context.Context, root [32]byte, slot primitives.Slot, _ state.BeaconState) (primitives.ValidatorIndex, error) {
|
||||
require.Equal(t, firstNewColumn.ParentRoot(), root)
|
||||
require.Equal(t, firstNewColumn.Slot(), slot)
|
||||
return firstColumn.ProposerIndex(), nil
|
||||
},
|
||||
},
|
||||
columns: newColumns,
|
||||
},
|
||||
{
|
||||
name: "Not cached, proposer does not match",
|
||||
stateByRooter: sbrForValOverride(firstColumn.ProposerIndex(), validator),
|
||||
proposerCache: &mockProposerCache{
|
||||
ProposerCB: pcReturnsNotFound(),
|
||||
ComputeProposerCB: func(_ context.Context, root [32]byte, slot primitives.Slot, _ state.BeaconState) (primitives.ValidatorIndex, error) {
|
||||
require.Equal(t, firstColumn.ParentRoot(), root)
|
||||
require.Equal(t, firstColumn.Slot(), slot)
|
||||
return firstColumn.ProposerIndex() + 1, nil
|
||||
},
|
||||
},
|
||||
columns: columns,
|
||||
error: errSidecarUnexpectedProposer.Error(),
|
||||
},
|
||||
{
|
||||
name: "Not cached, ComputeProposer fails",
|
||||
stateByRooter: sbrForValOverride(firstColumn.ProposerIndex(), validator),
|
||||
proposerCache: &mockProposerCache{
|
||||
ProposerCB: pcReturnsNotFound(),
|
||||
ComputeProposerCB: func(_ context.Context, root [32]byte, slot primitives.Slot, _ state.BeaconState) (primitives.ValidatorIndex, error) {
|
||||
require.Equal(t, firstColumn.ParentRoot(), root)
|
||||
require.Equal(t, firstColumn.Slot(), slot)
|
||||
return 0, errors.New("ComputeProposer failed")
|
||||
},
|
||||
},
|
||||
columns: columns,
|
||||
error: "compute proposer",
|
||||
error: "verifying state",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -908,8 +838,9 @@ func TestDataColumnsSidecarProposerExpected(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
initializer := Initializer{
|
||||
shared: &sharedResources{
|
||||
sr: tc.stateByRooter,
|
||||
pc: tc.proposerCache,
|
||||
sr: tc.stateByRooter,
|
||||
pc: tc.proposerCache,
|
||||
hsp: &mockHeadStateProvider{},
|
||||
fc: &mockForkchoicer{
|
||||
TargetRootForEpochCB: fcReturnsTargetRoot([fieldparams.RootLength]byte{}),
|
||||
},
|
||||
|
||||
@@ -25,6 +25,7 @@ type Forkchoicer interface {
|
||||
HasNode([32]byte) bool
|
||||
IsCanonical(root [32]byte) bool
|
||||
Slot([32]byte) (primitives.Slot, error)
|
||||
DependentRootForEpoch([32]byte, primitives.Epoch) ([32]byte, error)
|
||||
TargetRootForEpoch([32]byte, primitives.Epoch) ([32]byte, error)
|
||||
}
|
||||
|
||||
@@ -33,6 +34,16 @@ type StateByRooter interface {
|
||||
StateByRoot(ctx context.Context, blockRoot [32]byte) (state.BeaconState, error)
|
||||
}
|
||||
|
||||
// HeadStateProvider describes a type that can provide access to the current head state and related methods.
|
||||
// This interface matches blockchain.HeadFetcher but is defined here to avoid import cycles
|
||||
// (blockchain package imports verification package).
|
||||
type HeadStateProvider interface {
|
||||
HeadRoot(ctx context.Context) ([]byte, error)
|
||||
HeadSlot() primitives.Slot
|
||||
HeadState(ctx context.Context) (state.BeaconState, error)
|
||||
HeadStateReadOnly(ctx context.Context) (state.ReadOnlyBeaconState, error)
|
||||
}
|
||||
|
||||
// sharedResources provides access to resources that are required by different verification types.
|
||||
// for example, sidecar verification and block verification share the block signature verification cache.
|
||||
type sharedResources struct {
|
||||
@@ -41,6 +52,7 @@ type sharedResources struct {
|
||||
sc signatureCache
|
||||
pc proposerCache
|
||||
sr StateByRooter
|
||||
hsp HeadStateProvider
|
||||
ic *inclusionProofCache
|
||||
sg singleflight.Group
|
||||
}
|
||||
@@ -96,14 +108,15 @@ func WithForkLookup(fl forkLookup) InitializerOption {
|
||||
}
|
||||
|
||||
// NewInitializerWaiter creates an InitializerWaiter which can be used to obtain an Initializer once async dependencies are ready.
|
||||
func NewInitializerWaiter(cw startup.ClockWaiter, fc Forkchoicer, sr StateByRooter, opts ...InitializerOption) *InitializerWaiter {
|
||||
func NewInitializerWaiter(cw startup.ClockWaiter, fc Forkchoicer, sr StateByRooter, hsp HeadStateProvider, opts ...InitializerOption) *InitializerWaiter {
|
||||
pc := newPropCache()
|
||||
// signature cache is initialized in WaitForInitializer, since we need the genesis validators root, which can be obtained from startup.Clock.
|
||||
shared := &sharedResources{
|
||||
fc: fc,
|
||||
pc: pc,
|
||||
sr: sr,
|
||||
ic: newInclusionProofCache(defaultInclusionProofCacheSize),
|
||||
fc: fc,
|
||||
pc: pc,
|
||||
sr: sr,
|
||||
hsp: hsp,
|
||||
ic: newInclusionProofCache(defaultInclusionProofCacheSize),
|
||||
}
|
||||
iw := &InitializerWaiter{cw: cw, ini: &Initializer{shared: shared}}
|
||||
for _, o := range opts {
|
||||
|
||||
@@ -18,7 +18,7 @@ func TestInitializerWaiter(t *testing.T) {
|
||||
cs := startup.NewClockSynchronizer()
|
||||
require.NoError(t, cs.SetClock(c))
|
||||
|
||||
w := NewInitializerWaiter(cs, &mockForkchoicer{}, &mockStateByRooter{})
|
||||
w := NewInitializerWaiter(cs, &mockForkchoicer{}, &mockStateByRooter{}, &mockHeadStateProvider{})
|
||||
ini, err := w.WaitForInitializer(ctx)
|
||||
require.NoError(t, err)
|
||||
csc, ok := ini.shared.sc.(*sigCache)
|
||||
|
||||
3
changelog/bastin_update-lc-for-fulu.md
Normal file
3
changelog/bastin_update-lc-for-fulu.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Added
|
||||
|
||||
- add fulu support to light client processing.
|
||||
2
changelog/dbxe_fulu-interop.md
Normal file
2
changelog/dbxe_fulu-interop.md
Normal file
@@ -0,0 +1,2 @@
|
||||
### Ignored
|
||||
- Add osaka fork timestamp derivation to interop genesis
|
||||
2
changelog/muzry_improving_slashing_import.md
Normal file
2
changelog/muzry_improving_slashing_import.md
Normal file
@@ -0,0 +1,2 @@
|
||||
### Changed
|
||||
- Improve readability in slashing import and remove duplicated code
|
||||
3
changelog/potuz_err_check.md
Normal file
3
changelog/potuz_err_check.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Ignored
|
||||
|
||||
- Add error check in origin sidecars fetching.
|
||||
3
changelog/potuz_use_dependent_root_instead_of_target.md
Normal file
3
changelog/potuz_use_dependent_root_instead_of_target.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Changed
|
||||
|
||||
- Use dependent root instead of target when possible.
|
||||
3
changelog/potuz_use_head_datacolumn.md
Normal file
3
changelog/potuz_use_head_datacolumn.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Changed
|
||||
|
||||
- Use head state readonly when possible to validate data column sidecars.
|
||||
@@ -264,6 +264,7 @@ func generateGenesis(ctx context.Context) (state.BeaconState, error) {
|
||||
gen.Config.ShanghaiTime = interop.GethShanghaiTime(genesis, params.BeaconConfig())
|
||||
gen.Config.CancunTime = interop.GethCancunTime(genesis, params.BeaconConfig())
|
||||
gen.Config.PragueTime = interop.GethPragueTime(genesis, params.BeaconConfig())
|
||||
gen.Config.OsakaTime = interop.GethOsakaTime(genesis, params.BeaconConfig())
|
||||
|
||||
fields := logrus.Fields{}
|
||||
if gen.Config.ShanghaiTime != nil {
|
||||
@@ -275,6 +276,9 @@ func generateGenesis(ctx context.Context) (state.BeaconState, error) {
|
||||
if gen.Config.PragueTime != nil {
|
||||
fields["prague"] = fmt.Sprintf("%d", *gen.Config.PragueTime)
|
||||
}
|
||||
if gen.Config.OsakaTime != nil {
|
||||
fields["osaka"] = fmt.Sprintf("%d", *gen.Config.OsakaTime)
|
||||
}
|
||||
log.WithFields(fields).Info("Setting fork geth times")
|
||||
if v > version.Altair {
|
||||
// set ttd to zero so EL goes post-merge immediately
|
||||
|
||||
@@ -61,20 +61,13 @@ func importSlashingProtectionJSON(cliCtx *cli.Context) error {
|
||||
if isDatabaseMinimal {
|
||||
databaseFileDir = filesystem.DatabaseDirName
|
||||
}
|
||||
return fmt.Errorf("%s (validator database) was not found at path %s, so nothing to export", databaseFileDir, dataDir)
|
||||
} else {
|
||||
if !isDatabaseMinimal {
|
||||
matchPath = filepath.Dir(matchPath) // strip the file name
|
||||
}
|
||||
dataDir = matchPath
|
||||
log.Infof("Found validator database at path %s", dataDir)
|
||||
return fmt.Errorf("%s (validator database) was not found at path %s, so nothing to import", databaseFileDir, dataDir)
|
||||
}
|
||||
message := "Found existing database inside of %s"
|
||||
if !found {
|
||||
message = "Did not find existing database inside of %s, creating a new one"
|
||||
if !isDatabaseMinimal {
|
||||
matchPath = filepath.Dir(matchPath) // strip the file name
|
||||
}
|
||||
|
||||
log.Infof(message, dataDir)
|
||||
dataDir = matchPath
|
||||
log.Infof("Found validator database at path %s", dataDir)
|
||||
|
||||
// Open the validator database.
|
||||
if isDatabaseMinimal {
|
||||
|
||||
@@ -113,6 +113,21 @@ func GethPragueTime(genesisTime time.Time, cfg *clparams.BeaconChainConfig) *uin
|
||||
return pragueTime
|
||||
}
|
||||
|
||||
// GethOsakaTime calculates the absolute time of the osaka (aka fulu) fork block
|
||||
// by adding the relative time of the capella the fork epoch to the given genesis timestamp.
|
||||
func GethOsakaTime(genesisTime time.Time, cfg *clparams.BeaconChainConfig) *uint64 {
|
||||
var osakaTime *uint64
|
||||
if cfg.FuluForkEpoch != math.MaxUint64 {
|
||||
startSlot, err := slots.EpochStart(cfg.FuluForkEpoch)
|
||||
if err == nil {
|
||||
startTime := slots.UnsafeStartTime(genesisTime, startSlot)
|
||||
newTime := uint64(startTime.Unix())
|
||||
osakaTime = &newTime
|
||||
}
|
||||
}
|
||||
return osakaTime
|
||||
}
|
||||
|
||||
// GethTestnetGenesis creates a genesis.json for eth1 clients with a set of defaults suitable for ephemeral testnets,
|
||||
// like in an e2e test. The parameters are minimal but the full value is returned unmarshaled so that it can be
|
||||
// customized as desired.
|
||||
@@ -130,6 +145,10 @@ func GethTestnetGenesis(genesis time.Time, cfg *clparams.BeaconChainConfig) *cor
|
||||
if cfg.ElectraForkEpoch == 0 {
|
||||
pragueTime = &genesisTime
|
||||
}
|
||||
osakaTime := GethOsakaTime(genesis, cfg)
|
||||
if cfg.FuluForkEpoch == 0 {
|
||||
osakaTime = &genesisTime
|
||||
}
|
||||
cc := ¶ms.ChainConfig{
|
||||
ChainID: big.NewInt(defaultTestChainId),
|
||||
HomesteadBlock: bigz,
|
||||
@@ -151,6 +170,7 @@ func GethTestnetGenesis(genesis time.Time, cfg *clparams.BeaconChainConfig) *cor
|
||||
ShanghaiTime: shanghaiTime,
|
||||
CancunTime: cancunTime,
|
||||
PragueTime: pragueTime,
|
||||
OsakaTime: osakaTime,
|
||||
DepositContractAddress: common.HexToAddress(cfg.DepositContractAddress),
|
||||
BlobScheduleConfig: ¶ms.BlobScheduleConfig{
|
||||
Cancun: ¶ms.BlobConfig{
|
||||
|
||||
@@ -41,7 +41,7 @@ func NewBuilder(t testing.TB, initialState state.BeaconState, initialBlock inter
|
||||
getFork := func(targetEpoch primitives.Epoch) (*ethpb.Fork, error) {
|
||||
return initialState.Fork(), nil
|
||||
}
|
||||
bvw := verification.NewInitializerWaiter(cw, fc, sg, verification.WithForkLookup(getFork))
|
||||
bvw := verification.NewInitializerWaiter(cw, fc, sg, service, verification.WithForkLookup(getFork))
|
||||
return &Builder{
|
||||
service: service,
|
||||
execMock: execMock,
|
||||
|
||||
@@ -62,6 +62,8 @@ func NewTestLightClient(t *testing.T, forkVersion int, options ...LightClientOpt
|
||||
return l.setupTestDeneb()
|
||||
case version.Electra:
|
||||
return l.setupTestElectra()
|
||||
case version.Fulu:
|
||||
return l.setupTestFulu()
|
||||
default:
|
||||
l.T.Fatalf("Unsupported version %s", version.String(l.version))
|
||||
return nil
|
||||
@@ -955,6 +957,184 @@ func (l *TestLightClient) setupTestElectra() *TestLightClient {
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *TestLightClient) setupTestFulu() *TestLightClient {
|
||||
ctx := context.Background()
|
||||
|
||||
attestedSlot := primitives.Slot(uint64(params.BeaconConfig().FuluForkEpoch) * uint64(params.BeaconConfig().SlotsPerEpoch)).Add(1)
|
||||
if l.increaseAttestedSlotBy > 0 {
|
||||
attestedSlot = attestedSlot.Add(l.increaseAttestedSlotBy)
|
||||
}
|
||||
|
||||
signatureSlot := attestedSlot.Add(1)
|
||||
if l.increaseSignatureSlotBy > 0 {
|
||||
signatureSlot = signatureSlot.Add(l.increaseSignatureSlotBy)
|
||||
}
|
||||
|
||||
// Attested State & Block
|
||||
attestedState, err := NewBeaconStateFulu()
|
||||
require.NoError(l.T, err)
|
||||
require.NoError(l.T, attestedState.SetSlot(attestedSlot))
|
||||
|
||||
var signedFinalizedBlock interfaces.SignedBeaconBlock
|
||||
var finalizedState state.BeaconState
|
||||
// Finalized checkpoint
|
||||
if !l.noFinalizedCheckpoint {
|
||||
var finalizedSlot primitives.Slot
|
||||
|
||||
if l.finalizedCheckpointInPrevFork {
|
||||
finalizedSlot = primitives.Slot(uint64(params.BeaconConfig().ElectraForkEpoch) * uint64(params.BeaconConfig().SlotsPerEpoch))
|
||||
if l.increaseFinalizedSlotBy > 0 {
|
||||
finalizedSlot = finalizedSlot.Add(l.increaseFinalizedSlotBy)
|
||||
}
|
||||
|
||||
finalizedState, err = NewBeaconStateElectra()
|
||||
require.NoError(l.T, err)
|
||||
require.NoError(l.T, finalizedState.SetSlot(finalizedSlot))
|
||||
|
||||
finalizedBlock := NewBeaconBlockElectra()
|
||||
require.NoError(l.T, err)
|
||||
finalizedBlock.Block.Slot = finalizedSlot
|
||||
signedFinalizedBlock, err = blocks.NewSignedBeaconBlock(finalizedBlock)
|
||||
require.NoError(l.T, err)
|
||||
finalizedHeader, err := signedFinalizedBlock.Header()
|
||||
require.NoError(l.T, err)
|
||||
require.NoError(l.T, finalizedState.SetLatestBlockHeader(finalizedHeader.Header))
|
||||
finalizedStateRoot, err := finalizedState.HashTreeRoot(ctx)
|
||||
require.NoError(l.T, err)
|
||||
finalizedBlock.Block.StateRoot = finalizedStateRoot[:]
|
||||
signedFinalizedBlock, err = blocks.NewSignedBeaconBlock(finalizedBlock)
|
||||
require.NoError(l.T, err)
|
||||
} else {
|
||||
finalizedSlot = primitives.Slot(uint64(params.BeaconConfig().FuluForkEpoch) * uint64(params.BeaconConfig().SlotsPerEpoch))
|
||||
if l.increaseFinalizedSlotBy > 0 {
|
||||
finalizedSlot = finalizedSlot.Add(l.increaseFinalizedSlotBy)
|
||||
}
|
||||
|
||||
finalizedState, err = NewBeaconStateFulu()
|
||||
require.NoError(l.T, err)
|
||||
require.NoError(l.T, finalizedState.SetSlot(finalizedSlot))
|
||||
|
||||
finalizedBlock := NewBeaconBlockFulu()
|
||||
require.NoError(l.T, err)
|
||||
finalizedBlock.Block.Slot = finalizedSlot
|
||||
signedFinalizedBlock, err = blocks.NewSignedBeaconBlock(finalizedBlock)
|
||||
require.NoError(l.T, err)
|
||||
finalizedHeader, err := signedFinalizedBlock.Header()
|
||||
require.NoError(l.T, err)
|
||||
require.NoError(l.T, finalizedState.SetLatestBlockHeader(finalizedHeader.Header))
|
||||
finalizedStateRoot, err := finalizedState.HashTreeRoot(ctx)
|
||||
require.NoError(l.T, err)
|
||||
finalizedBlock.Block.StateRoot = finalizedStateRoot[:]
|
||||
signedFinalizedBlock, err = blocks.NewSignedBeaconBlock(finalizedBlock)
|
||||
require.NoError(l.T, err)
|
||||
}
|
||||
|
||||
// Set the finalized checkpoint
|
||||
finalizedBlockRoot, err := signedFinalizedBlock.Block().HashTreeRoot()
|
||||
require.NoError(l.T, err)
|
||||
finalizedCheckpoint := ðpb.Checkpoint{
|
||||
Epoch: slots.ToEpoch(finalizedSlot),
|
||||
Root: finalizedBlockRoot[:],
|
||||
}
|
||||
require.NoError(l.T, attestedState.SetFinalizedCheckpoint(finalizedCheckpoint))
|
||||
}
|
||||
|
||||
// Attested Block
|
||||
attestedBlock := NewBeaconBlockFulu()
|
||||
attestedBlock.Block.Slot = attestedSlot
|
||||
attestedBlock.Block.ParentRoot = l.attestedParentRoot[:]
|
||||
signedAttestedBlock, err := blocks.NewSignedBeaconBlock(attestedBlock)
|
||||
require.NoError(l.T, err)
|
||||
attestedBlockHeader, err := signedAttestedBlock.Header()
|
||||
require.NoError(l.T, err)
|
||||
require.NoError(l.T, attestedState.SetLatestBlockHeader(attestedBlockHeader.Header))
|
||||
attestedStateRoot, err := attestedState.HashTreeRoot(ctx)
|
||||
require.NoError(l.T, err)
|
||||
attestedBlock.Block.StateRoot = attestedStateRoot[:]
|
||||
signedAttestedBlock, err = blocks.NewSignedBeaconBlock(attestedBlock)
|
||||
require.NoError(l.T, err)
|
||||
|
||||
// Signature State & Block
|
||||
signatureState, err := NewBeaconStateFulu()
|
||||
require.NoError(l.T, err)
|
||||
require.NoError(l.T, signatureState.SetSlot(signatureSlot))
|
||||
|
||||
var signedSignatureBlock interfaces.SignedBeaconBlock
|
||||
if l.blinded {
|
||||
signatureBlock := NewBlindedBeaconBlockFulu()
|
||||
signatureBlock.Message.Slot = signatureSlot
|
||||
attestedBlockRoot, err := signedAttestedBlock.Block().HashTreeRoot()
|
||||
require.NoError(l.T, err)
|
||||
signatureBlock.Message.ParentRoot = attestedBlockRoot[:]
|
||||
|
||||
var trueBitNum uint64
|
||||
if l.supermajority {
|
||||
trueBitNum = uint64((float64(params.BeaconConfig().SyncCommitteeSize) * 2.0 / 3.0) + 1 + float64(l.increaseActiveParticipantsBy))
|
||||
} else {
|
||||
trueBitNum = params.BeaconConfig().MinSyncCommitteeParticipants
|
||||
}
|
||||
for i := uint64(0); i < trueBitNum; i++ {
|
||||
signatureBlock.Message.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true)
|
||||
}
|
||||
|
||||
signedSignatureBlock, err = blocks.NewSignedBeaconBlock(signatureBlock)
|
||||
require.NoError(l.T, err)
|
||||
|
||||
signatureBlockHeader, err := signedSignatureBlock.Header()
|
||||
require.NoError(l.T, err)
|
||||
|
||||
err = signatureState.SetLatestBlockHeader(signatureBlockHeader.Header)
|
||||
require.NoError(l.T, err)
|
||||
stateRoot, err := signatureState.HashTreeRoot(ctx)
|
||||
require.NoError(l.T, err)
|
||||
|
||||
signatureBlock.Message.StateRoot = stateRoot[:]
|
||||
signedSignatureBlock, err = blocks.NewSignedBeaconBlock(signatureBlock)
|
||||
require.NoError(l.T, err)
|
||||
} else {
|
||||
signatureBlock := NewBeaconBlockFulu()
|
||||
signatureBlock.Block.Slot = signatureSlot
|
||||
attestedBlockRoot, err := signedAttestedBlock.Block().HashTreeRoot()
|
||||
require.NoError(l.T, err)
|
||||
signatureBlock.Block.ParentRoot = attestedBlockRoot[:]
|
||||
|
||||
var trueBitNum uint64
|
||||
if l.supermajority {
|
||||
trueBitNum = uint64((float64(params.BeaconConfig().SyncCommitteeSize) * 2.0 / 3.0) + 1 + float64(l.increaseActiveParticipantsBy))
|
||||
} else {
|
||||
trueBitNum = params.BeaconConfig().MinSyncCommitteeParticipants
|
||||
}
|
||||
for i := uint64(0); i < trueBitNum; i++ {
|
||||
signatureBlock.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true)
|
||||
}
|
||||
|
||||
signedSignatureBlock, err = blocks.NewSignedBeaconBlock(signatureBlock)
|
||||
require.NoError(l.T, err)
|
||||
|
||||
signatureBlockHeader, err := signedSignatureBlock.Header()
|
||||
require.NoError(l.T, err)
|
||||
|
||||
err = signatureState.SetLatestBlockHeader(signatureBlockHeader.Header)
|
||||
require.NoError(l.T, err)
|
||||
signatureStateRoot, err := signatureState.HashTreeRoot(ctx)
|
||||
require.NoError(l.T, err)
|
||||
|
||||
signatureBlock.Block.StateRoot = signatureStateRoot[:]
|
||||
signedSignatureBlock, err = blocks.NewSignedBeaconBlock(signatureBlock)
|
||||
require.NoError(l.T, err)
|
||||
}
|
||||
|
||||
l.State = signatureState
|
||||
l.AttestedState = attestedState
|
||||
l.AttestedBlock = signedAttestedBlock
|
||||
l.Block = signedSignatureBlock
|
||||
l.Ctx = ctx
|
||||
l.FinalizedBlock = signedFinalizedBlock
|
||||
l.FinalizedState = finalizedState
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *TestLightClient) CheckAttestedHeader(header interfaces.LightClientHeader) {
|
||||
updateAttestedHeaderBeacon := header.Beacon()
|
||||
testAttestedHeader, err := l.AttestedBlock.Header()
|
||||
|
||||
Reference in New Issue
Block a user