mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-06 22:23:56 -05:00
* Ran gopls modernize to fix everything go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest -fix -test ./... * Override rules_go provided dependency for golang.org/x/tools to v0.38.0. To update this, checked out rules_go, then ran `bazel run //go/tools/releaser -- upgrade-dep -mirror=false org_golang_x_tools` and copied the patches. * Fix buildtag violations and ignore buildtag violations in external * Introduce modernize analyzer package. * Add modernize "any" analyzer. * Fix violations of any analyzer * Add modernize "appendclipped" analyzer. * Fix violations of appendclipped * Add modernize "bloop" analyzer. * Add modernize "fmtappendf" analyzer. * Add modernize "forvar" analyzer. * Add modernize "mapsloop" analyzer. * Add modernize "minmax" analyzer. * Fix violations of minmax analyzer * Add modernize "omitzero" analyzer. * Add modernize "rangeint" analyzer. * Fix violations of rangeint. * Add modernize "reflecttypefor" analyzer. * Fix violations of reflecttypefor analyzer. * Add modernize "slicescontains" analyzer. * Add modernize "slicessort" analyzer. * Add modernize "slicesdelete" analyzer. This is disabled by default for now. See https://go.dev/issue/73686. * Add modernize "stringscutprefix" analyzer. * Add modernize "stringsbuilder" analyzer. * Fix violations of stringsbuilder analyzer. * Add modernize "stringsseq" analyzer. * Add modernize "testingcontext" analyzer. * Add modernize "waitgroup" analyzer. * Changelog fragment * gofmt * gazelle * Add modernize "newexpr" analyzer. * Disable newexpr until go1.26 * Add more details in WORKSPACE on how to update the override * @nalepae feedback on min() * gofmt * Fix violations of forvar
614 lines
21 KiB
Go
614 lines
21 KiB
Go
package blockchain
|
|
|
|
import (
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
blockchainTesting "github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/testing"
|
|
"github.com/OffchainLabs/prysm/v7/beacon-chain/cache"
|
|
statefeed "github.com/OffchainLabs/prysm/v7/beacon-chain/core/feed/state"
|
|
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/helpers"
|
|
"github.com/OffchainLabs/prysm/v7/beacon-chain/das"
|
|
forkchoicetypes "github.com/OffchainLabs/prysm/v7/beacon-chain/forkchoice/types"
|
|
lightClient "github.com/OffchainLabs/prysm/v7/beacon-chain/light-client"
|
|
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/voluntaryexits"
|
|
"github.com/OffchainLabs/prysm/v7/config/features"
|
|
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
|
|
"github.com/OffchainLabs/prysm/v7/config/params"
|
|
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
|
|
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
|
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
|
|
ethpbv1 "github.com/OffchainLabs/prysm/v7/proto/eth/v1"
|
|
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
|
"github.com/OffchainLabs/prysm/v7/runtime/version"
|
|
"github.com/OffchainLabs/prysm/v7/testing/assert"
|
|
"github.com/OffchainLabs/prysm/v7/testing/require"
|
|
"github.com/OffchainLabs/prysm/v7/testing/util"
|
|
"github.com/OffchainLabs/prysm/v7/time/slots"
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
logTest "github.com/sirupsen/logrus/hooks/test"
|
|
)
|
|
|
|
func TestService_ReceiveBlock(t *testing.T) {
|
|
ctx := t.Context()
|
|
|
|
genesis, keys := util.DeterministicGenesisState(t, 64)
|
|
copiedGen := genesis.Copy()
|
|
genFullBlock := func(t *testing.T, conf *util.BlockGenConfig, slot primitives.Slot) *ethpb.SignedBeaconBlock {
|
|
blk, err := util.GenerateFullBlock(copiedGen.Copy(), keys, conf, slot)
|
|
require.NoError(t, err)
|
|
return blk
|
|
}
|
|
//params.SetupTestConfigCleanupWithLock(t)
|
|
bc := params.BeaconConfig().Copy()
|
|
bc.ShardCommitteePeriod = 0 // Required for voluntary exits test in reasonable time.
|
|
params.OverrideBeaconConfig(bc)
|
|
|
|
badBlock := genFullBlock(t, util.DefaultBlockGenConfig(), 101)
|
|
badRoot, err := badBlock.Block.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
badRoots := make(map[[32]byte]struct{})
|
|
badRoots[badRoot] = struct{}{}
|
|
resetCfg := features.InitWithReset(&features.Flags{
|
|
BlacklistedRoots: badRoots,
|
|
})
|
|
defer resetCfg()
|
|
|
|
type args struct {
|
|
block *ethpb.SignedBeaconBlock
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
wantedErr string
|
|
check func(*testing.T, *Service)
|
|
}{
|
|
{
|
|
name: "applies block with state transition",
|
|
args: args{
|
|
block: genFullBlock(t, util.DefaultBlockGenConfig(), 2 /*slot*/),
|
|
},
|
|
check: func(t *testing.T, s *Service) {
|
|
if hs := s.head.state.Slot(); hs != 2 {
|
|
t.Errorf("Unexpected state slot. Got %d but wanted %d", hs, 2)
|
|
}
|
|
if bs := s.head.block.Block().Slot(); bs != 2 {
|
|
t.Errorf("Unexpected head block slot. Got %d but wanted %d", bs, 2)
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "saves attestations to pool",
|
|
args: args{
|
|
block: genFullBlock(t,
|
|
&util.BlockGenConfig{
|
|
NumProposerSlashings: 0,
|
|
NumAttesterSlashings: 0,
|
|
NumAttestations: 2,
|
|
NumDeposits: 0,
|
|
NumVoluntaryExits: 0,
|
|
},
|
|
1, /*slot*/
|
|
),
|
|
},
|
|
check: func(t *testing.T, s *Service) {
|
|
if baCount := len(s.cfg.AttPool.BlockAttestations()); baCount != 0 {
|
|
t.Errorf("Did not get the correct number of block attestations saved to the pool. "+
|
|
"Got %d but wanted %d", baCount, 0)
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "updates exit pool",
|
|
args: args{
|
|
block: genFullBlock(t, &util.BlockGenConfig{
|
|
NumProposerSlashings: 0,
|
|
NumAttesterSlashings: 0,
|
|
NumAttestations: 0,
|
|
NumDeposits: 0,
|
|
NumVoluntaryExits: 3,
|
|
},
|
|
1, /*slot*/
|
|
),
|
|
},
|
|
check: func(t *testing.T, s *Service) {
|
|
pending, err := s.cfg.ExitPool.PendingExits()
|
|
require.NoError(t, err)
|
|
if len(pending) != 0 {
|
|
t.Errorf(
|
|
"Did not mark the correct number of exits. Got %d pending but wanted %d",
|
|
len(pending),
|
|
0,
|
|
)
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "notifies block processed on state feed",
|
|
args: args{
|
|
block: genFullBlock(t, util.DefaultBlockGenConfig(), 1 /*slot*/),
|
|
},
|
|
check: func(t *testing.T, s *Service) {
|
|
// Hacky sleep, should use a better way to be able to resolve the race
|
|
// between event being sent out and processed.
|
|
time.Sleep(100 * time.Millisecond)
|
|
if recvd := len(s.cfg.StateNotifier.(*blockchainTesting.MockStateNotifier).ReceivedEvents()); recvd < 1 {
|
|
t.Errorf("Received %d state notifications, expected at least 1", recvd)
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "The block is blacklisted",
|
|
args: args{
|
|
block: badBlock,
|
|
},
|
|
wantedErr: errBlacklistedRoot.Error(),
|
|
},
|
|
}
|
|
wg := new(sync.WaitGroup)
|
|
for _, tt := range tests {
|
|
wg.Add(1)
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
defer func() {
|
|
wg.Done()
|
|
}()
|
|
genesis = genesis.Copy()
|
|
s, tr := minimalTestService(t,
|
|
WithFinalizedStateAtStartUp(genesis),
|
|
WithExitPool(voluntaryexits.NewPool()),
|
|
WithStateNotifier(&blockchainTesting.MockStateNotifier{RecordEvents: true}),
|
|
WithTrackedValidatorsCache(cache.NewTrackedValidatorsCache()),
|
|
)
|
|
|
|
beaconDB := tr.db
|
|
genesisBlockRoot := bytesutil.ToBytes32(nil)
|
|
require.NoError(t, beaconDB.SaveState(ctx, genesis, genesisBlockRoot))
|
|
|
|
// Initialize it here.
|
|
_ = s.cfg.StateNotifier.StateFeed()
|
|
require.NoError(t, s.saveGenesisData(ctx, genesis))
|
|
root, err := tt.args.block.Block.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
wsb, err := blocks.NewSignedBeaconBlock(tt.args.block)
|
|
require.NoError(t, err)
|
|
err = s.ReceiveBlock(ctx, wsb, root, nil)
|
|
if tt.wantedErr != "" {
|
|
assert.ErrorContains(t, tt.wantedErr, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
tt.check(t, s)
|
|
}
|
|
})
|
|
}
|
|
wg.Wait()
|
|
}
|
|
func TestHandleDA(t *testing.T) {
|
|
signedBeaconBlock, err := blocks.NewSignedBeaconBlock(ðpb.SignedBeaconBlock{
|
|
Block: ðpb.BeaconBlock{
|
|
Body: ðpb.BeaconBlockBody{},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
s, _ := minimalTestService(t)
|
|
block, err := blocks.NewROBlockWithRoot(signedBeaconBlock, [32]byte{})
|
|
require.NoError(t, err)
|
|
elapsed, err := s.handleDA(t.Context(), nil, block)
|
|
require.NoError(t, err)
|
|
require.Equal(t, true, elapsed > 0, "Elapsed time should be greater than 0")
|
|
}
|
|
|
|
func TestService_ReceiveBlockUpdateHead(t *testing.T) {
|
|
s, tr := minimalTestService(t,
|
|
WithExitPool(voluntaryexits.NewPool()),
|
|
WithStateNotifier(&blockchainTesting.MockStateNotifier{RecordEvents: true}))
|
|
ctx, beaconDB := tr.ctx, tr.db
|
|
genesis, keys := util.DeterministicGenesisState(t, 64)
|
|
b, err := util.GenerateFullBlock(genesis, keys, util.DefaultBlockGenConfig(), 1)
|
|
assert.NoError(t, err)
|
|
genesisBlockRoot := bytesutil.ToBytes32(nil)
|
|
require.NoError(t, beaconDB.SaveState(ctx, genesis, genesisBlockRoot))
|
|
|
|
// Initialize it here.
|
|
_ = s.cfg.StateNotifier.StateFeed()
|
|
require.NoError(t, s.saveGenesisData(ctx, genesis))
|
|
root, err := b.Block.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
wg := sync.WaitGroup{}
|
|
wg.Go(func() {
|
|
wsb, err := blocks.NewSignedBeaconBlock(b)
|
|
require.NoError(t, err)
|
|
require.NoError(t, s.ReceiveBlock(ctx, wsb, root, nil))
|
|
})
|
|
wg.Wait()
|
|
time.Sleep(100 * time.Millisecond)
|
|
if recvd := len(s.cfg.StateNotifier.(*blockchainTesting.MockStateNotifier).ReceivedEvents()); recvd < 1 {
|
|
t.Errorf("Received %d state notifications, expected at least 1", recvd)
|
|
}
|
|
// Verify fork choice has processed the block. (Genesis block and the new block)
|
|
assert.Equal(t, 2, s.cfg.ForkChoiceStore.NodeCount())
|
|
}
|
|
|
|
func TestService_ReceiveBlockBatch(t *testing.T) {
|
|
ctx := t.Context()
|
|
|
|
genesis, keys := util.DeterministicGenesisState(t, 64)
|
|
genFullBlock := func(t *testing.T, conf *util.BlockGenConfig, slot primitives.Slot) *ethpb.SignedBeaconBlock {
|
|
blk, err := util.GenerateFullBlock(genesis, keys, conf, slot)
|
|
assert.NoError(t, err)
|
|
return blk
|
|
}
|
|
|
|
type args struct {
|
|
block *ethpb.SignedBeaconBlock
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
wantedErr string
|
|
check func(*testing.T, *Service)
|
|
}{
|
|
{
|
|
name: "applies block with state transition",
|
|
args: args{
|
|
block: genFullBlock(t, util.DefaultBlockGenConfig(), 2 /*slot*/),
|
|
},
|
|
check: func(t *testing.T, s *Service) {
|
|
assert.Equal(t, primitives.Slot(2), s.head.state.Slot(), "Incorrect head state slot")
|
|
assert.Equal(t, primitives.Slot(2), s.head.block.Block().Slot(), "Incorrect head block slot")
|
|
},
|
|
},
|
|
{
|
|
name: "notifies block processed on state feed",
|
|
args: args{
|
|
block: genFullBlock(t, util.DefaultBlockGenConfig(), 1 /*slot*/),
|
|
},
|
|
check: func(t *testing.T, s *Service) {
|
|
time.Sleep(100 * time.Millisecond)
|
|
if recvd := len(s.cfg.StateNotifier.(*blockchainTesting.MockStateNotifier).ReceivedEvents()); recvd < 1 {
|
|
t.Errorf("Received %d state notifications, expected at least 1", recvd)
|
|
}
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
s, _ := minimalTestService(t, WithStateNotifier(&blockchainTesting.MockStateNotifier{RecordEvents: true}))
|
|
err := s.saveGenesisData(ctx, genesis)
|
|
require.NoError(t, err)
|
|
wsb, err := blocks.NewSignedBeaconBlock(tt.args.block)
|
|
require.NoError(t, err)
|
|
rwsb, err := blocks.NewROBlock(wsb)
|
|
require.NoError(t, err)
|
|
err = s.ReceiveBlockBatch(ctx, []blocks.ROBlock{rwsb}, &das.MockAvailabilityStore{})
|
|
if tt.wantedErr != "" {
|
|
assert.ErrorContains(t, tt.wantedErr, err)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
tt.check(t, s)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestService_HasBlock(t *testing.T) {
|
|
s, _ := minimalTestService(t)
|
|
r := [32]byte{'a'}
|
|
if s.HasBlock(t.Context(), r) {
|
|
t.Error("Should not have block")
|
|
}
|
|
wsb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())
|
|
require.NoError(t, err)
|
|
require.NoError(t, s.saveInitSyncBlock(t.Context(), r, wsb))
|
|
if !s.HasBlock(t.Context(), r) {
|
|
t.Error("Should have block")
|
|
}
|
|
b := util.NewBeaconBlock()
|
|
b.Block.Slot = 1
|
|
util.SaveBlock(t, t.Context(), s.cfg.BeaconDB, b)
|
|
r, err = b.Block.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
require.Equal(t, true, s.HasBlock(t.Context(), r))
|
|
err = s.blockBeingSynced.set(r)
|
|
require.NoError(t, err)
|
|
err = s.blockBeingSynced.set(r)
|
|
require.ErrorIs(t, err, errBlockBeingSynced)
|
|
require.Equal(t, false, s.HasBlock(t.Context(), r))
|
|
}
|
|
|
|
func TestCheckSaveHotStateDB_Enabling(t *testing.T) {
|
|
hook := logTest.NewGlobal()
|
|
s, _ := minimalTestService(t)
|
|
st := params.BeaconConfig().SlotsPerEpoch.Mul(uint64(epochsSinceFinalitySaveHotStateDB))
|
|
s.genesisTime = time.Now().Add(time.Duration(-1*int64(st)*int64(params.BeaconConfig().SecondsPerSlot)) * time.Second)
|
|
|
|
require.NoError(t, s.checkSaveHotStateDB(t.Context()))
|
|
assert.LogsContain(t, hook, "Entering mode to save hot states in DB")
|
|
}
|
|
|
|
func TestCheckSaveHotStateDB_Disabling(t *testing.T) {
|
|
hook := logTest.NewGlobal()
|
|
|
|
s, _ := minimalTestService(t)
|
|
|
|
st := params.BeaconConfig().SlotsPerEpoch.Mul(uint64(epochsSinceFinalitySaveHotStateDB))
|
|
s.genesisTime = time.Now().Add(time.Duration(-1*int64(st)*int64(params.BeaconConfig().SecondsPerSlot)) * time.Second)
|
|
require.NoError(t, s.checkSaveHotStateDB(t.Context()))
|
|
s.genesisTime = time.Now()
|
|
|
|
require.NoError(t, s.checkSaveHotStateDB(t.Context()))
|
|
assert.LogsContain(t, hook, "Exiting mode to save hot states in DB")
|
|
}
|
|
|
|
func TestCheckSaveHotStateDB_Overflow(t *testing.T) {
|
|
hook := logTest.NewGlobal()
|
|
s, _ := minimalTestService(t)
|
|
s.SetGenesisTime(time.Now())
|
|
|
|
require.NoError(t, s.checkSaveHotStateDB(t.Context()))
|
|
assert.LogsDoNotContain(t, hook, "Entering mode to save hot states in DB")
|
|
}
|
|
|
|
func TestHandleCaches_EnablingLargeSize(t *testing.T) {
|
|
hook := logTest.NewGlobal()
|
|
s, _ := minimalTestService(t)
|
|
st := params.BeaconConfig().SlotsPerEpoch.Mul(uint64(epochsSinceFinalitySaveHotStateDB))
|
|
s.SetGenesisTime(time.Now().Add(time.Duration(-1*int64(st)*int64(params.BeaconConfig().SecondsPerSlot)) * time.Second))
|
|
|
|
helpers.ClearCache()
|
|
require.NoError(t, s.handleCaches())
|
|
assert.LogsContain(t, hook, "Expanding committee cache size")
|
|
}
|
|
|
|
func TestHandleCaches_DisablingLargeSize(t *testing.T) {
|
|
hook := logTest.NewGlobal()
|
|
s, _ := minimalTestService(t)
|
|
|
|
st := params.BeaconConfig().SlotsPerEpoch.Mul(uint64(epochsSinceFinalitySaveHotStateDB))
|
|
s.genesisTime = time.Now().Add(time.Duration(-1*int64(st)*int64(params.BeaconConfig().SecondsPerSlot)) * time.Second)
|
|
require.NoError(t, s.handleCaches())
|
|
s.genesisTime = time.Now()
|
|
|
|
require.NoError(t, s.handleCaches())
|
|
assert.LogsContain(t, hook, "Reducing committee cache size")
|
|
}
|
|
|
|
func TestHandleBlockBLSToExecutionChanges(t *testing.T) {
|
|
service, tr := minimalTestService(t)
|
|
pool := tr.blsPool
|
|
|
|
t.Run("pre Capella block", func(t *testing.T) {
|
|
body := ðpb.BeaconBlockBodyBellatrix{}
|
|
pbb := ðpb.BeaconBlockBellatrix{
|
|
Body: body,
|
|
}
|
|
blk, err := blocks.NewBeaconBlock(pbb)
|
|
require.NoError(t, err)
|
|
require.NoError(t, service.markIncludedBlockBLSToExecChanges(blk))
|
|
})
|
|
|
|
t.Run("Post Capella no changes", func(t *testing.T) {
|
|
body := ðpb.BeaconBlockBodyCapella{}
|
|
pbb := ðpb.BeaconBlockCapella{
|
|
Body: body,
|
|
}
|
|
blk, err := blocks.NewBeaconBlock(pbb)
|
|
require.NoError(t, err)
|
|
require.NoError(t, service.markIncludedBlockBLSToExecChanges(blk))
|
|
})
|
|
|
|
t.Run("Post Capella some changes", func(t *testing.T) {
|
|
idx := primitives.ValidatorIndex(123)
|
|
change := ðpb.BLSToExecutionChange{
|
|
ValidatorIndex: idx,
|
|
}
|
|
signedChange := ðpb.SignedBLSToExecutionChange{
|
|
Message: change,
|
|
}
|
|
body := ðpb.BeaconBlockBodyCapella{
|
|
BlsToExecutionChanges: []*ethpb.SignedBLSToExecutionChange{signedChange},
|
|
}
|
|
pbb := ðpb.BeaconBlockCapella{
|
|
Body: body,
|
|
}
|
|
blk, err := blocks.NewBeaconBlock(pbb)
|
|
require.NoError(t, err)
|
|
|
|
pool.InsertBLSToExecChange(signedChange)
|
|
require.Equal(t, true, pool.ValidatorExists(idx))
|
|
require.NoError(t, service.markIncludedBlockBLSToExecChanges(blk))
|
|
require.Equal(t, false, pool.ValidatorExists(idx))
|
|
})
|
|
}
|
|
|
|
func Test_sendNewFinalizedEvent(t *testing.T) {
|
|
s, _ := minimalTestService(t)
|
|
notifier := &blockchainTesting.MockStateNotifier{RecordEvents: true}
|
|
s.cfg.StateNotifier = notifier
|
|
finalizedSt, err := util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
finalizedStRoot, err := finalizedSt.HashTreeRoot(s.ctx)
|
|
require.NoError(t, err)
|
|
b := util.NewBeaconBlock()
|
|
b.Block.StateRoot = finalizedStRoot[:]
|
|
sbb, err := blocks.NewSignedBeaconBlock(b)
|
|
require.NoError(t, err)
|
|
sbbRoot, err := sbb.Block().HashTreeRoot()
|
|
require.NoError(t, err)
|
|
require.NoError(t, s.cfg.BeaconDB.SaveBlock(s.ctx, sbb))
|
|
st, err := util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
require.NoError(t, st.SetFinalizedCheckpoint(ðpb.Checkpoint{
|
|
Epoch: 123,
|
|
Root: sbbRoot[:],
|
|
}))
|
|
|
|
s.sendNewFinalizedEvent(s.ctx, st)
|
|
|
|
require.Equal(t, 1, len(notifier.ReceivedEvents()))
|
|
e := notifier.ReceivedEvents()[0]
|
|
assert.Equal(t, statefeed.FinalizedCheckpoint, int(e.Type))
|
|
fc, ok := e.Data.(*ethpbv1.EventFinalizedCheckpoint)
|
|
require.Equal(t, true, ok, "event has wrong data type")
|
|
assert.Equal(t, primitives.Epoch(123), fc.Epoch)
|
|
assert.DeepEqual(t, sbbRoot[:], fc.Block)
|
|
assert.DeepEqual(t, finalizedStRoot[:], fc.State)
|
|
assert.Equal(t, false, fc.ExecutionOptimistic)
|
|
}
|
|
|
|
func Test_executePostFinalizationTasks(t *testing.T) {
|
|
logHook := logTest.NewGlobal()
|
|
|
|
headState, err := util.NewBeaconStateElectra()
|
|
require.NoError(t, err)
|
|
finalizedStRoot, err := headState.HashTreeRoot(t.Context())
|
|
require.NoError(t, err)
|
|
|
|
genesis := util.NewBeaconBlock()
|
|
genesisRoot, err := genesis.Block.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
finalizedSlot := params.BeaconConfig().SlotsPerEpoch*122 + 1
|
|
headBlock := util.NewBeaconBlock()
|
|
headBlock.Block.Slot = finalizedSlot
|
|
headBlock.Block.StateRoot = finalizedStRoot[:]
|
|
headBlock.Block.ParentRoot = bytesutil.PadTo(genesisRoot[:], 32)
|
|
headRoot, err := headBlock.Block.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
|
|
hexKey := "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a"
|
|
key, err := hexutil.Decode(hexKey)
|
|
require.NoError(t, err)
|
|
require.NoError(t, headState.SetValidators([]*ethpb.Validator{
|
|
{
|
|
PublicKey: key,
|
|
WithdrawalCredentials: make([]byte, fieldparams.RootLength),
|
|
},
|
|
}))
|
|
require.NoError(t, headState.SetSlot(finalizedSlot))
|
|
require.NoError(t, headState.SetFinalizedCheckpoint(ðpb.Checkpoint{
|
|
Epoch: 123,
|
|
Root: headRoot[:],
|
|
}))
|
|
require.NoError(t, headState.SetGenesisValidatorsRoot(params.BeaconConfig().ZeroHash[:]))
|
|
t.Run("pre deposit request", func(t *testing.T) {
|
|
require.NoError(t, headState.SetEth1DepositIndex(1))
|
|
s, tr := minimalTestService(t, WithFinalizedStateAtStartUp(headState))
|
|
ctx, beaconDB, stateGen := tr.ctx, tr.db, tr.sg
|
|
|
|
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, genesisRoot))
|
|
util.SaveBlock(t, ctx, beaconDB, genesis)
|
|
require.NoError(t, beaconDB.SaveState(ctx, headState, headRoot))
|
|
require.NoError(t, beaconDB.SaveState(ctx, headState, genesisRoot))
|
|
util.SaveBlock(t, ctx, beaconDB, headBlock)
|
|
require.NoError(t, beaconDB.SaveFinalizedCheckpoint(ctx, ðpb.Checkpoint{Epoch: slots.ToEpoch(finalizedSlot), Root: headRoot[:]}))
|
|
|
|
require.NoError(t, err)
|
|
require.NoError(t, stateGen.SaveState(ctx, headRoot, headState))
|
|
require.NoError(t, beaconDB.SaveLastValidatedCheckpoint(ctx, ðpb.Checkpoint{Epoch: slots.ToEpoch(finalizedSlot), Root: headRoot[:]}))
|
|
|
|
notifier := &blockchainTesting.MockStateNotifier{RecordEvents: true}
|
|
s.cfg.StateNotifier = notifier
|
|
s.executePostFinalizationTasks(s.ctx, headState)
|
|
|
|
time.Sleep(1 * time.Second) // sleep for a second because event is in a separate go routine
|
|
require.Equal(t, 1, len(notifier.ReceivedEvents()))
|
|
e := notifier.ReceivedEvents()[0]
|
|
assert.Equal(t, statefeed.FinalizedCheckpoint, int(e.Type))
|
|
fc, ok := e.Data.(*ethpbv1.EventFinalizedCheckpoint)
|
|
require.Equal(t, true, ok, "event has wrong data type")
|
|
assert.Equal(t, primitives.Epoch(123), fc.Epoch)
|
|
assert.DeepEqual(t, headRoot[:], fc.Block)
|
|
assert.DeepEqual(t, finalizedStRoot[:], fc.State)
|
|
assert.Equal(t, false, fc.ExecutionOptimistic)
|
|
|
|
// check the cache
|
|
index, ok := headState.ValidatorIndexByPubkey(bytesutil.ToBytes48(key))
|
|
require.Equal(t, true, ok)
|
|
require.Equal(t, primitives.ValidatorIndex(0), index) // first index
|
|
|
|
// check deposit
|
|
require.LogsContain(t, logHook, "Finalized deposit insertion completed at index")
|
|
})
|
|
t.Run("deposit requests started", func(t *testing.T) {
|
|
require.NoError(t, headState.SetEth1DepositIndex(1))
|
|
require.NoError(t, headState.SetDepositRequestsStartIndex(1))
|
|
s, tr := minimalTestService(t, WithFinalizedStateAtStartUp(headState))
|
|
ctx, beaconDB, stateGen := tr.ctx, tr.db, tr.sg
|
|
|
|
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, genesisRoot))
|
|
util.SaveBlock(t, ctx, beaconDB, genesis)
|
|
require.NoError(t, beaconDB.SaveState(ctx, headState, headRoot))
|
|
require.NoError(t, beaconDB.SaveState(ctx, headState, genesisRoot))
|
|
util.SaveBlock(t, ctx, beaconDB, headBlock)
|
|
require.NoError(t, beaconDB.SaveFinalizedCheckpoint(ctx, ðpb.Checkpoint{Epoch: slots.ToEpoch(finalizedSlot), Root: headRoot[:]}))
|
|
|
|
require.NoError(t, err)
|
|
require.NoError(t, stateGen.SaveState(ctx, headRoot, headState))
|
|
require.NoError(t, beaconDB.SaveLastValidatedCheckpoint(ctx, ðpb.Checkpoint{Epoch: slots.ToEpoch(finalizedSlot), Root: headRoot[:]}))
|
|
|
|
notifier := &blockchainTesting.MockStateNotifier{RecordEvents: true}
|
|
s.cfg.StateNotifier = notifier
|
|
s.executePostFinalizationTasks(s.ctx, headState)
|
|
|
|
time.Sleep(1 * time.Second) // sleep for a second because event is in a separate go routine
|
|
require.Equal(t, 1, len(notifier.ReceivedEvents()))
|
|
e := notifier.ReceivedEvents()[0]
|
|
assert.Equal(t, statefeed.FinalizedCheckpoint, int(e.Type))
|
|
fc, ok := e.Data.(*ethpbv1.EventFinalizedCheckpoint)
|
|
require.Equal(t, true, ok, "event has wrong data type")
|
|
assert.Equal(t, primitives.Epoch(123), fc.Epoch)
|
|
assert.DeepEqual(t, headRoot[:], fc.Block)
|
|
assert.DeepEqual(t, finalizedStRoot[:], fc.State)
|
|
assert.Equal(t, false, fc.ExecutionOptimistic)
|
|
|
|
// check the cache
|
|
index, ok := headState.ValidatorIndexByPubkey(bytesutil.ToBytes48(key))
|
|
require.Equal(t, true, ok)
|
|
require.Equal(t, primitives.ValidatorIndex(0), index) // first index
|
|
})
|
|
|
|
}
|
|
|
|
func TestProcessLightClientBootstrap(t *testing.T) {
|
|
featCfg := &features.Flags{}
|
|
featCfg.EnableLightClient = true
|
|
reset := features.InitWithReset(featCfg)
|
|
defer reset()
|
|
|
|
s, tr := minimalTestService(t, WithLCStore())
|
|
ctx := tr.ctx
|
|
|
|
for testVersion := version.Altair; testVersion <= version.Electra; testVersion++ {
|
|
t.Run(version.String(testVersion), func(t *testing.T) {
|
|
l := util.NewTestLightClient(t, testVersion)
|
|
|
|
require.NoError(t, s.cfg.BeaconDB.SaveBlock(ctx, l.FinalizedBlock))
|
|
finalizedBlockRoot, err := l.FinalizedBlock.Block().HashTreeRoot()
|
|
require.NoError(t, err)
|
|
require.NoError(t, s.cfg.BeaconDB.SaveState(ctx, l.FinalizedState, finalizedBlockRoot))
|
|
|
|
cp := l.AttestedState.FinalizedCheckpoint()
|
|
require.DeepSSZEqual(t, finalizedBlockRoot, [32]byte(cp.Root))
|
|
|
|
require.NoError(t, s.cfg.ForkChoiceStore.UpdateFinalizedCheckpoint(&forkchoicetypes.Checkpoint{Epoch: cp.Epoch, Root: [32]byte(cp.Root)}))
|
|
|
|
s.executePostFinalizationTasks(s.ctx, l.AttestedState)
|
|
|
|
// wait for the goroutine to finish processing
|
|
time.Sleep(1 * time.Second)
|
|
|
|
// Check that the light client bootstrap is saved
|
|
b, err := s.lcStore.LightClientBootstrap(ctx, [32]byte(cp.Root))
|
|
require.NoError(t, err)
|
|
require.NotNil(t, b)
|
|
|
|
btst, err := lightClient.NewLightClientBootstrapFromBeaconState(ctx, l.FinalizedState.Slot(), l.FinalizedState, l.FinalizedBlock)
|
|
require.NoError(t, err)
|
|
require.DeepEqual(t, btst, b)
|
|
require.Equal(t, b.Version(), testVersion)
|
|
})
|
|
}
|
|
}
|