mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-22 03:30:35 +00:00
5a66807989
* First take at updating everything to v5 * Patch gRPC gateway to use prysm v5 Fix patch * Update go ssz --------- Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>
856 lines
27 KiB
Go
856 lines
27 KiB
Go
package execution
|
|
|
|
import (
|
|
"context"
|
|
"math/big"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum"
|
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
gethTypes "github.com/ethereum/go-ethereum/core/types"
|
|
"github.com/ethereum/go-ethereum/rpc"
|
|
"github.com/pkg/errors"
|
|
"github.com/prysmaticlabs/prysm/v5/async/event"
|
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/cache/depositcache"
|
|
dbutil "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/testing"
|
|
mockExecution "github.com/prysmaticlabs/prysm/v5/beacon-chain/execution/testing"
|
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/execution/types"
|
|
doublylinkedtree "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/doubly-linked-tree"
|
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stategen"
|
|
"github.com/prysmaticlabs/prysm/v5/config/params"
|
|
"github.com/prysmaticlabs/prysm/v5/container/trie"
|
|
contracts "github.com/prysmaticlabs/prysm/v5/contracts/deposit"
|
|
"github.com/prysmaticlabs/prysm/v5/contracts/deposit/mock"
|
|
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
|
"github.com/prysmaticlabs/prysm/v5/monitoring/clientstats"
|
|
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
|
"github.com/prysmaticlabs/prysm/v5/testing/assert"
|
|
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
|
"github.com/prysmaticlabs/prysm/v5/testing/util"
|
|
"github.com/prysmaticlabs/prysm/v5/time/slots"
|
|
logTest "github.com/sirupsen/logrus/hooks/test"
|
|
)
|
|
|
|
var _ ChainStartFetcher = (*Service)(nil)
|
|
var _ ChainInfoFetcher = (*Service)(nil)
|
|
var _ POWBlockFetcher = (*Service)(nil)
|
|
var _ Chain = (*Service)(nil)
|
|
|
|
type goodLogger struct {
|
|
backend *backends.SimulatedBackend
|
|
}
|
|
|
|
func (_ *goodLogger) Close() {}
|
|
|
|
func (g *goodLogger) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- gethTypes.Log) (ethereum.Subscription, error) {
|
|
if g.backend == nil {
|
|
return new(event.Feed).Subscribe(ch), nil
|
|
}
|
|
return g.backend.SubscribeFilterLogs(ctx, q, ch)
|
|
}
|
|
|
|
func (g *goodLogger) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]gethTypes.Log, error) {
|
|
if g.backend == nil {
|
|
logs := make([]gethTypes.Log, 3)
|
|
for i := 0; i < len(logs); i++ {
|
|
logs[i].Address = common.Address{}
|
|
logs[i].Topics = make([]common.Hash, 5)
|
|
logs[i].Topics[0] = common.Hash{'a'}
|
|
logs[i].Topics[1] = common.Hash{'b'}
|
|
logs[i].Topics[2] = common.Hash{'c'}
|
|
|
|
}
|
|
return logs, nil
|
|
}
|
|
return g.backend.FilterLogs(ctx, q)
|
|
}
|
|
|
|
type goodNotifier struct {
|
|
MockStateFeed *event.Feed
|
|
}
|
|
|
|
func (g *goodNotifier) StateFeed() *event.Feed {
|
|
if g.MockStateFeed == nil {
|
|
g.MockStateFeed = new(event.Feed)
|
|
}
|
|
return g.MockStateFeed
|
|
}
|
|
|
|
var depositsReqForChainStart = 64
|
|
|
|
func TestStart_OK(t *testing.T) {
|
|
hook := logTest.NewGlobal()
|
|
beaconDB := dbutil.SetupDB(t)
|
|
testAcc, err := mock.Setup()
|
|
require.NoError(t, err, "Unable to set up simulated backend")
|
|
server, endpoint, err := mockExecution.SetupRPCServer()
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
server.Stop()
|
|
})
|
|
web3Service, err := NewService(context.Background(),
|
|
WithHttpEndpoint(endpoint),
|
|
WithDepositContractAddress(testAcc.ContractAddr),
|
|
WithDatabase(beaconDB),
|
|
)
|
|
require.NoError(t, err, "unable to setup execution service")
|
|
web3Service = setDefaultMocks(web3Service)
|
|
web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
|
|
web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend)
|
|
require.NoError(t, err)
|
|
testAcc.Backend.Commit()
|
|
|
|
web3Service.Start()
|
|
if len(hook.Entries) > 0 {
|
|
msg := hook.LastEntry().Message
|
|
want := "Could not connect to execution endpoint"
|
|
if strings.Contains(want, msg) {
|
|
t.Errorf("incorrect log, expected %s, got %s", want, msg)
|
|
}
|
|
}
|
|
hook.Reset()
|
|
web3Service.cancel()
|
|
}
|
|
|
|
func TestStart_NoHttpEndpointDefinedFails_WithoutChainStarted(t *testing.T) {
|
|
hook := logTest.NewGlobal()
|
|
beaconDB := dbutil.SetupDB(t)
|
|
testAcc, err := mock.Setup()
|
|
require.NoError(t, err, "Unable to set up simulated backend")
|
|
_, err = NewService(context.Background(),
|
|
WithHttpEndpoint(""),
|
|
WithDepositContractAddress(testAcc.ContractAddr),
|
|
WithDatabase(beaconDB),
|
|
)
|
|
require.NoError(t, err)
|
|
require.LogsDoNotContain(t, hook, "missing address")
|
|
}
|
|
|
|
func TestStop_OK(t *testing.T) {
|
|
hook := logTest.NewGlobal()
|
|
testAcc, err := mock.Setup()
|
|
require.NoError(t, err, "Unable to set up simulated backend")
|
|
beaconDB := dbutil.SetupDB(t)
|
|
server, endpoint, err := mockExecution.SetupRPCServer()
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
server.Stop()
|
|
})
|
|
web3Service, err := NewService(context.Background(),
|
|
WithHttpEndpoint(endpoint),
|
|
WithDepositContractAddress(testAcc.ContractAddr),
|
|
WithDatabase(beaconDB),
|
|
)
|
|
require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
|
|
web3Service = setDefaultMocks(web3Service)
|
|
web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend)
|
|
require.NoError(t, err)
|
|
|
|
testAcc.Backend.Commit()
|
|
|
|
err = web3Service.Stop()
|
|
require.NoError(t, err, "Unable to stop web3 ETH1.0 chain service")
|
|
|
|
// The context should have been canceled.
|
|
assert.NotNil(t, web3Service.ctx.Err(), "Context wasn't canceled")
|
|
|
|
hook.Reset()
|
|
}
|
|
|
|
func TestService_Eth1Synced(t *testing.T) {
|
|
testAcc, err := mock.Setup()
|
|
require.NoError(t, err, "Unable to set up simulated backend")
|
|
beaconDB := dbutil.SetupDB(t)
|
|
server, endpoint, err := mockExecution.SetupRPCServer()
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
server.Stop()
|
|
})
|
|
web3Service, err := NewService(context.Background(),
|
|
WithHttpEndpoint(endpoint),
|
|
WithDepositContractAddress(testAcc.ContractAddr),
|
|
WithDatabase(beaconDB),
|
|
)
|
|
require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
|
|
web3Service = setDefaultMocks(web3Service)
|
|
web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend)
|
|
require.NoError(t, err)
|
|
|
|
currTime := testAcc.Backend.Blockchain().CurrentHeader().Time
|
|
now := time.Now()
|
|
assert.NoError(t, testAcc.Backend.AdjustTime(now.Sub(time.Unix(int64(currTime), 0))))
|
|
testAcc.Backend.Commit()
|
|
}
|
|
|
|
func TestFollowBlock_OK(t *testing.T) {
|
|
testAcc, err := mock.Setup()
|
|
require.NoError(t, err, "Unable to set up simulated backend")
|
|
beaconDB := dbutil.SetupDB(t)
|
|
server, endpoint, err := mockExecution.SetupRPCServer()
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
server.Stop()
|
|
})
|
|
web3Service, err := NewService(context.Background(),
|
|
WithHttpEndpoint(endpoint),
|
|
WithDepositContractAddress(testAcc.ContractAddr),
|
|
WithDatabase(beaconDB),
|
|
)
|
|
require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
|
|
|
|
// simulated backend sets eth1 block
|
|
// time as 10 seconds
|
|
params.SetupTestConfigCleanup(t)
|
|
conf := params.BeaconConfig().Copy()
|
|
conf.SecondsPerETH1Block = 10
|
|
params.OverrideBeaconConfig(conf)
|
|
|
|
web3Service = setDefaultMocks(web3Service)
|
|
web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
|
|
baseHeight := testAcc.Backend.Blockchain().CurrentBlock().Number.Uint64()
|
|
// process follow_distance blocks
|
|
for i := 0; i < int(params.BeaconConfig().Eth1FollowDistance); i++ {
|
|
testAcc.Backend.Commit()
|
|
}
|
|
// set current height
|
|
web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().Number.Uint64()
|
|
web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time
|
|
|
|
h, err := web3Service.followedBlockHeight(context.Background())
|
|
require.NoError(t, err)
|
|
assert.Equal(t, baseHeight, h, "Unexpected block height")
|
|
numToForward := uint64(2)
|
|
expectedHeight := numToForward + baseHeight
|
|
// forward 2 blocks
|
|
for i := uint64(0); i < numToForward; i++ {
|
|
testAcc.Backend.Commit()
|
|
}
|
|
// set current height
|
|
web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().Number.Uint64()
|
|
web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time
|
|
|
|
h, err = web3Service.followedBlockHeight(context.Background())
|
|
require.NoError(t, err)
|
|
assert.Equal(t, expectedHeight, h, "Unexpected block height")
|
|
}
|
|
|
|
func TestStatus(t *testing.T) {
|
|
now := time.Now()
|
|
|
|
beforeFiveMinutesAgo := uint64(now.Add(-5*time.Minute - 30*time.Second).Unix())
|
|
afterFiveMinutesAgo := uint64(now.Add(-5*time.Minute + 30*time.Second).Unix())
|
|
|
|
testCases := map[*Service]string{
|
|
// "status is ok" cases
|
|
{}: "",
|
|
{isRunning: true, latestEth1Data: ðpb.LatestETH1Data{BlockTime: afterFiveMinutesAgo}}: "",
|
|
{isRunning: false, latestEth1Data: ðpb.LatestETH1Data{BlockTime: beforeFiveMinutesAgo}}: "",
|
|
{isRunning: false, runError: errors.New("test runError")}: "",
|
|
// "status is error" cases
|
|
{isRunning: true, runError: errors.New("test runError")}: "test runError",
|
|
}
|
|
|
|
for web3ServiceState, wantedErrorText := range testCases {
|
|
status := web3ServiceState.Status()
|
|
if status == nil {
|
|
assert.Equal(t, "", wantedErrorText)
|
|
|
|
} else {
|
|
assert.Equal(t, wantedErrorText, status.Error())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestHandlePanic_OK(t *testing.T) {
|
|
hook := logTest.NewGlobal()
|
|
beaconDB := dbutil.SetupDB(t)
|
|
server, endpoint, err := mockExecution.SetupRPCServer()
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
server.Stop()
|
|
})
|
|
web3Service, err := NewService(context.Background(),
|
|
WithHttpEndpoint(endpoint),
|
|
WithDatabase(beaconDB),
|
|
)
|
|
require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
|
|
// nil eth1DataFetcher would panic if cached value not used
|
|
web3Service.rpcClient = nil
|
|
web3Service.processBlockHeader(nil)
|
|
require.LogsContain(t, hook, "Panicked when handling data from ETH 1.0 Chain!")
|
|
}
|
|
|
|
func TestLogTillGenesis_OK(t *testing.T) {
|
|
// Reset the var at the end of the test.
|
|
currPeriod := logPeriod
|
|
logPeriod = 1 * time.Second
|
|
defer func() {
|
|
logPeriod = currPeriod
|
|
}()
|
|
|
|
params.SetupTestConfigCleanup(t)
|
|
cfg := params.BeaconConfig().Copy()
|
|
cfg.Eth1FollowDistance = 5
|
|
params.OverrideBeaconConfig(cfg)
|
|
|
|
nCfg := params.BeaconNetworkConfig()
|
|
nCfg.ContractDeploymentBlock = 0
|
|
params.OverrideBeaconNetworkConfig(nCfg)
|
|
|
|
hook := logTest.NewGlobal()
|
|
testAcc, err := mock.Setup()
|
|
require.NoError(t, err, "Unable to set up simulated backend")
|
|
beaconDB := dbutil.SetupDB(t)
|
|
server, endpoint, err := mockExecution.SetupRPCServer()
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
server.Stop()
|
|
})
|
|
web3Service, err := NewService(context.Background(),
|
|
WithHttpEndpoint(endpoint),
|
|
WithDepositContractAddress(testAcc.ContractAddr),
|
|
WithDatabase(beaconDB),
|
|
)
|
|
require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
|
|
web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend)
|
|
require.NoError(t, err)
|
|
|
|
web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
|
|
web3Service.httpLogger = testAcc.Backend
|
|
for i := 0; i < 30; i++ {
|
|
testAcc.Backend.Commit()
|
|
}
|
|
web3Service.latestEth1Data = ðpb.LatestETH1Data{LastRequestedBlock: 0}
|
|
// Spin off to a separate routine
|
|
go web3Service.run(web3Service.ctx.Done())
|
|
// Wait for 2 seconds so that the
|
|
// info is logged.
|
|
time.Sleep(2 * time.Second)
|
|
web3Service.cancel()
|
|
assert.LogsContain(t, hook, "Currently waiting for chainstart")
|
|
}
|
|
|
|
func TestInitDepositCache_OK(t *testing.T) {
|
|
ctrs := []*ethpb.DepositContainer{
|
|
{Index: 0, Eth1BlockHeight: 2, Deposit: ðpb.Deposit{Proof: [][]byte{[]byte("A")}, Data: ðpb.Deposit_Data{PublicKey: []byte{}}}},
|
|
{Index: 1, Eth1BlockHeight: 4, Deposit: ðpb.Deposit{Proof: [][]byte{[]byte("B")}, Data: ðpb.Deposit_Data{PublicKey: []byte{}}}},
|
|
{Index: 2, Eth1BlockHeight: 6, Deposit: ðpb.Deposit{Proof: [][]byte{[]byte("c")}, Data: ðpb.Deposit_Data{PublicKey: []byte{}}}},
|
|
}
|
|
gs, _ := util.DeterministicGenesisState(t, 1)
|
|
beaconDB := dbutil.SetupDB(t)
|
|
s := &Service{
|
|
chainStartData: ðpb.ChainStartData{Chainstarted: false},
|
|
preGenesisState: gs,
|
|
cfg: &config{beaconDB: beaconDB},
|
|
}
|
|
var err error
|
|
s.cfg.depositCache, err = depositcache.New()
|
|
require.NoError(t, err)
|
|
require.NoError(t, s.initDepositCaches(context.Background(), ctrs))
|
|
|
|
require.Equal(t, 0, len(s.cfg.depositCache.PendingContainers(context.Background(), nil)))
|
|
|
|
blockRootA := [32]byte{'a'}
|
|
|
|
emptyState, err := util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
require.NoError(t, s.cfg.beaconDB.SaveGenesisBlockRoot(context.Background(), blockRootA))
|
|
require.NoError(t, s.cfg.beaconDB.SaveState(context.Background(), emptyState, blockRootA))
|
|
s.chainStartData.Chainstarted = true
|
|
require.NoError(t, s.initDepositCaches(context.Background(), ctrs))
|
|
require.Equal(t, 3, len(s.cfg.depositCache.PendingContainers(context.Background(), nil)))
|
|
}
|
|
|
|
func TestInitDepositCacheWithFinalization_OK(t *testing.T) {
|
|
ctrs := []*ethpb.DepositContainer{
|
|
{
|
|
Index: 0,
|
|
Eth1BlockHeight: 2,
|
|
Deposit: ðpb.Deposit{
|
|
Data: ðpb.Deposit_Data{
|
|
PublicKey: bytesutil.PadTo([]byte{0}, 48),
|
|
WithdrawalCredentials: make([]byte, 32),
|
|
Signature: make([]byte, 96),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Index: 1,
|
|
Eth1BlockHeight: 4,
|
|
Deposit: ðpb.Deposit{
|
|
Data: ðpb.Deposit_Data{
|
|
PublicKey: bytesutil.PadTo([]byte{1}, 48),
|
|
WithdrawalCredentials: make([]byte, 32),
|
|
Signature: make([]byte, 96),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Index: 2,
|
|
Eth1BlockHeight: 6,
|
|
Deposit: ðpb.Deposit{
|
|
Data: ðpb.Deposit_Data{
|
|
PublicKey: bytesutil.PadTo([]byte{2}, 48),
|
|
WithdrawalCredentials: make([]byte, 32),
|
|
Signature: make([]byte, 96),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
gs, _ := util.DeterministicGenesisState(t, 1)
|
|
beaconDB := dbutil.SetupDB(t)
|
|
s := &Service{
|
|
chainStartData: ðpb.ChainStartData{Chainstarted: false},
|
|
preGenesisState: gs,
|
|
cfg: &config{beaconDB: beaconDB},
|
|
}
|
|
var err error
|
|
s.cfg.depositCache, err = depositcache.New()
|
|
require.NoError(t, err)
|
|
require.NoError(t, s.initDepositCaches(context.Background(), ctrs))
|
|
|
|
require.Equal(t, 0, len(s.cfg.depositCache.PendingContainers(context.Background(), nil)))
|
|
|
|
headBlock := util.NewBeaconBlock()
|
|
headRoot, err := headBlock.Block.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
stateGen := stategen.New(beaconDB, doublylinkedtree.New())
|
|
|
|
emptyState, err := util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
require.NoError(t, s.cfg.beaconDB.SaveGenesisBlockRoot(context.Background(), headRoot))
|
|
require.NoError(t, s.cfg.beaconDB.SaveState(context.Background(), emptyState, headRoot))
|
|
require.NoError(t, stateGen.SaveState(context.Background(), headRoot, emptyState))
|
|
s.cfg.stateGen = stateGen
|
|
require.NoError(t, emptyState.SetEth1DepositIndex(3))
|
|
|
|
ctx := context.Background()
|
|
require.NoError(t, beaconDB.SaveFinalizedCheckpoint(ctx, ðpb.Checkpoint{Epoch: slots.ToEpoch(0), Root: headRoot[:]}))
|
|
s.cfg.finalizedStateAtStartup = emptyState
|
|
|
|
s.chainStartData.Chainstarted = true
|
|
require.NoError(t, s.initDepositCaches(context.Background(), ctrs))
|
|
fDeposits, err := s.cfg.depositCache.FinalizedDeposits(ctx)
|
|
require.NoError(t, err)
|
|
deps := s.cfg.depositCache.NonFinalizedDeposits(context.Background(), fDeposits.MerkleTrieIndex(), nil)
|
|
assert.Equal(t, 0, len(deps))
|
|
}
|
|
|
|
func TestNewService_EarliestVotingBlock(t *testing.T) {
|
|
testAcc, err := mock.Setup()
|
|
require.NoError(t, err, "Unable to set up simulated backend")
|
|
beaconDB := dbutil.SetupDB(t)
|
|
server, endpoint, err := mockExecution.SetupRPCServer()
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
server.Stop()
|
|
})
|
|
web3Service, err := NewService(context.Background(),
|
|
WithHttpEndpoint(endpoint),
|
|
WithDepositContractAddress(testAcc.ContractAddr),
|
|
WithDatabase(beaconDB),
|
|
)
|
|
require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
|
|
// simulated backend sets eth1 block
|
|
// time as 10 seconds
|
|
params.SetupTestConfigCleanup(t)
|
|
conf := params.BeaconConfig().Copy()
|
|
conf.SecondsPerETH1Block = 10
|
|
conf.Eth1FollowDistance = 50
|
|
params.OverrideBeaconConfig(conf)
|
|
|
|
// Genesis not set
|
|
followBlock := uint64(2000)
|
|
blk, err := web3Service.determineEarliestVotingBlock(context.Background(), followBlock)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, followBlock-conf.Eth1FollowDistance, blk, "unexpected earliest voting block")
|
|
|
|
// Genesis is set.
|
|
|
|
numToForward := 1500
|
|
// forward 1500 blocks
|
|
for i := 0; i < numToForward; i++ {
|
|
testAcc.Backend.Commit()
|
|
}
|
|
currTime := testAcc.Backend.Blockchain().CurrentHeader().Time
|
|
now := time.Now()
|
|
err = testAcc.Backend.AdjustTime(now.Sub(time.Unix(int64(currTime), 0)))
|
|
require.NoError(t, err)
|
|
testAcc.Backend.Commit()
|
|
|
|
currTime = testAcc.Backend.Blockchain().CurrentHeader().Time
|
|
web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentHeader().Number.Uint64()
|
|
web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentHeader().Time
|
|
web3Service.chainStartData.GenesisTime = currTime
|
|
|
|
// With a current slot of zero, only request follow_blocks behind.
|
|
blk, err = web3Service.determineEarliestVotingBlock(context.Background(), followBlock)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, followBlock-conf.Eth1FollowDistance, blk, "unexpected earliest voting block")
|
|
|
|
}
|
|
|
|
func TestNewService_Eth1HeaderRequLimit(t *testing.T) {
|
|
testAcc, err := mock.Setup()
|
|
require.NoError(t, err, "Unable to set up simulated backend")
|
|
beaconDB := dbutil.SetupDB(t)
|
|
|
|
server, endpoint, err := mockExecution.SetupRPCServer()
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
server.Stop()
|
|
})
|
|
s1, err := NewService(context.Background(),
|
|
WithHttpEndpoint(endpoint),
|
|
WithDepositContractAddress(testAcc.ContractAddr),
|
|
WithDatabase(beaconDB),
|
|
)
|
|
require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
|
|
assert.Equal(t, defaultEth1HeaderReqLimit, s1.cfg.eth1HeaderReqLimit, "default eth1 header request limit not set")
|
|
s2, err := NewService(context.Background(),
|
|
WithHttpEndpoint(endpoint),
|
|
WithDepositContractAddress(testAcc.ContractAddr),
|
|
WithDatabase(beaconDB),
|
|
WithEth1HeaderRequestLimit(uint64(150)),
|
|
)
|
|
require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
|
|
assert.Equal(t, uint64(150), s2.cfg.eth1HeaderReqLimit, "unable to set eth1HeaderRequestLimit")
|
|
}
|
|
|
|
type mockBSUpdater struct {
|
|
lastBS clientstats.BeaconNodeStats
|
|
}
|
|
|
|
func (mbs *mockBSUpdater) Update(bs clientstats.BeaconNodeStats) {
|
|
mbs.lastBS = bs
|
|
}
|
|
|
|
var _ BeaconNodeStatsUpdater = &mockBSUpdater{}
|
|
|
|
func TestDedupEndpoints(t *testing.T) {
|
|
assert.DeepEqual(t, []string{"A"}, dedupEndpoints([]string{"A"}), "did not dedup correctly")
|
|
assert.DeepEqual(t, []string{"A", "B"}, dedupEndpoints([]string{"A", "B"}), "did not dedup correctly")
|
|
assert.DeepEqual(t, []string{"A", "B"}, dedupEndpoints([]string{"A", "A", "A", "B"}), "did not dedup correctly")
|
|
assert.DeepEqual(t, []string{"A", "B"}, dedupEndpoints([]string{"A", "A", "A", "B", "B"}), "did not dedup correctly")
|
|
}
|
|
|
|
func Test_batchRequestHeaders_UnderflowChecks(t *testing.T) {
|
|
srv := &Service{}
|
|
start := uint64(101)
|
|
end := uint64(100)
|
|
_, err := srv.batchRequestHeaders(start, end)
|
|
require.ErrorContains(t, "cannot be >", err)
|
|
|
|
start = uint64(200)
|
|
end = uint64(100)
|
|
_, err = srv.batchRequestHeaders(start, end)
|
|
require.ErrorContains(t, "cannot be >", err)
|
|
}
|
|
|
|
func TestService_EnsureConsistentPowchainData(t *testing.T) {
|
|
beaconDB := dbutil.SetupDB(t)
|
|
cache, err := depositcache.New()
|
|
require.NoError(t, err)
|
|
srv, endpoint, err := mockExecution.SetupRPCServer()
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
srv.Stop()
|
|
})
|
|
s1, err := NewService(context.Background(),
|
|
WithHttpEndpoint(endpoint),
|
|
WithDatabase(beaconDB),
|
|
WithDepositCache(cache),
|
|
)
|
|
require.NoError(t, err)
|
|
genState, err := util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
assert.NoError(t, genState.SetSlot(1000))
|
|
|
|
require.NoError(t, s1.cfg.beaconDB.SaveGenesisData(context.Background(), genState))
|
|
_, err = s1.validPowchainData(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
eth1Data, err := s1.cfg.beaconDB.ExecutionChainData(context.Background())
|
|
assert.NoError(t, err)
|
|
|
|
assert.NotNil(t, eth1Data)
|
|
assert.Equal(t, true, eth1Data.ChainstartData.Chainstarted)
|
|
}
|
|
|
|
func TestService_InitializeCorrectly(t *testing.T) {
|
|
beaconDB := dbutil.SetupDB(t)
|
|
cache, err := depositcache.New()
|
|
require.NoError(t, err)
|
|
|
|
srv, endpoint, err := mockExecution.SetupRPCServer()
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
srv.Stop()
|
|
})
|
|
s1, err := NewService(context.Background(),
|
|
WithHttpEndpoint(endpoint),
|
|
WithDatabase(beaconDB),
|
|
WithDepositCache(cache),
|
|
)
|
|
require.NoError(t, err)
|
|
genState, err := util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
assert.NoError(t, genState.SetSlot(1000))
|
|
|
|
require.NoError(t, s1.cfg.beaconDB.SaveGenesisData(context.Background(), genState))
|
|
_, err = s1.validPowchainData(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
eth1Data, err := s1.cfg.beaconDB.ExecutionChainData(context.Background())
|
|
assert.NoError(t, err)
|
|
|
|
assert.NoError(t, s1.initializeEth1Data(context.Background(), eth1Data))
|
|
assert.Equal(t, int64(-1), s1.lastReceivedMerkleIndex, "received incorrect last received merkle index")
|
|
}
|
|
|
|
func TestService_EnsureValidPowchainData(t *testing.T) {
|
|
beaconDB := dbutil.SetupDB(t)
|
|
cache, err := depositcache.New()
|
|
require.NoError(t, err)
|
|
srv, endpoint, err := mockExecution.SetupRPCServer()
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
srv.Stop()
|
|
})
|
|
s1, err := NewService(context.Background(),
|
|
WithHttpEndpoint(endpoint),
|
|
WithDatabase(beaconDB),
|
|
WithDepositCache(cache),
|
|
)
|
|
require.NoError(t, err)
|
|
genState, err := util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
assert.NoError(t, genState.SetSlot(1000))
|
|
|
|
require.NoError(t, s1.cfg.beaconDB.SaveGenesisData(context.Background(), genState))
|
|
|
|
err = s1.cfg.beaconDB.SaveExecutionChainData(context.Background(), ðpb.ETH1ChainData{
|
|
ChainstartData: ðpb.ChainStartData{Chainstarted: true},
|
|
DepositContainers: []*ethpb.DepositContainer{{Index: 1}},
|
|
})
|
|
require.NoError(t, err)
|
|
_, err = s1.validPowchainData(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
eth1Data, err := s1.cfg.beaconDB.ExecutionChainData(context.Background())
|
|
assert.NoError(t, err)
|
|
|
|
assert.NotNil(t, eth1Data)
|
|
assert.Equal(t, 0, len(eth1Data.DepositContainers))
|
|
}
|
|
|
|
func TestService_ValidateDepositContainers(t *testing.T) {
|
|
var tt = []struct {
|
|
name string
|
|
ctrsFunc func() []*ethpb.DepositContainer
|
|
expectedRes bool
|
|
}{
|
|
{
|
|
name: "zero containers",
|
|
ctrsFunc: func() []*ethpb.DepositContainer {
|
|
return make([]*ethpb.DepositContainer, 0)
|
|
},
|
|
expectedRes: true,
|
|
},
|
|
{
|
|
name: "ordered containers",
|
|
ctrsFunc: func() []*ethpb.DepositContainer {
|
|
ctrs := make([]*ethpb.DepositContainer, 0)
|
|
for i := 0; i < 10; i++ {
|
|
ctrs = append(ctrs, ðpb.DepositContainer{Index: int64(i), Eth1BlockHeight: uint64(i + 10)})
|
|
}
|
|
return ctrs
|
|
},
|
|
expectedRes: true,
|
|
},
|
|
{
|
|
name: "0th container missing",
|
|
ctrsFunc: func() []*ethpb.DepositContainer {
|
|
ctrs := make([]*ethpb.DepositContainer, 0)
|
|
for i := 1; i < 10; i++ {
|
|
ctrs = append(ctrs, ðpb.DepositContainer{Index: int64(i), Eth1BlockHeight: uint64(i + 10)})
|
|
}
|
|
return ctrs
|
|
},
|
|
expectedRes: false,
|
|
},
|
|
{
|
|
name: "skipped containers",
|
|
ctrsFunc: func() []*ethpb.DepositContainer {
|
|
ctrs := make([]*ethpb.DepositContainer, 0)
|
|
for i := 0; i < 10; i++ {
|
|
if i == 5 || i == 7 {
|
|
continue
|
|
}
|
|
ctrs = append(ctrs, ðpb.DepositContainer{Index: int64(i), Eth1BlockHeight: uint64(i + 10)})
|
|
}
|
|
return ctrs
|
|
},
|
|
expectedRes: false,
|
|
},
|
|
}
|
|
|
|
for _, test := range tt {
|
|
assert.Equal(t, test.expectedRes, validateDepositContainers(test.ctrsFunc()))
|
|
}
|
|
}
|
|
|
|
func TestETH1Endpoints(t *testing.T) {
|
|
server, firstEndpoint, err := mockExecution.SetupRPCServer()
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
server.Stop()
|
|
})
|
|
endpoints := []string{firstEndpoint}
|
|
|
|
testAcc, err := mock.Setup()
|
|
require.NoError(t, err, "Unable to set up simulated backend")
|
|
beaconDB := dbutil.SetupDB(t)
|
|
|
|
mbs := &mockBSUpdater{}
|
|
s1, err := NewService(context.Background(),
|
|
WithHttpEndpoint(endpoints[0]),
|
|
WithDepositContractAddress(testAcc.ContractAddr),
|
|
WithDatabase(beaconDB),
|
|
WithBeaconNodeStatsUpdater(mbs),
|
|
)
|
|
s1.cfg.beaconNodeStatsUpdater = mbs
|
|
require.NoError(t, err)
|
|
|
|
// Check default endpoint is set to current.
|
|
assert.Equal(t, firstEndpoint, s1.ExecutionClientEndpoint(), "Unexpected http endpoint")
|
|
}
|
|
|
|
func TestService_CacheBlockHeaders(t *testing.T) {
|
|
rClient := &slowRPCClient{limit: 1000}
|
|
s := &Service{
|
|
cfg: &config{eth1HeaderReqLimit: 1000},
|
|
rpcClient: rClient,
|
|
headerCache: newHeaderCache(),
|
|
}
|
|
assert.NoError(t, s.cacheBlockHeaders(1, 1000))
|
|
assert.Equal(t, 1, rClient.numOfCalls)
|
|
// Reset Num of Calls
|
|
rClient.numOfCalls = 0
|
|
// Increase header request limit to trigger the batch limiting
|
|
// code path.
|
|
s.cfg.eth1HeaderReqLimit = 1001
|
|
|
|
assert.NoError(t, s.cacheBlockHeaders(1000, 3000))
|
|
// 1000 - 2000 would be 1001 headers which is higher than our request limit, it
|
|
// is then reduced to 500 and tried again.
|
|
assert.Equal(t, 5, rClient.numOfCalls)
|
|
}
|
|
|
|
func TestService_FollowBlock(t *testing.T) {
|
|
followTime := params.BeaconConfig().Eth1FollowDistance * params.BeaconConfig().SecondsPerETH1Block
|
|
followTime += 10000
|
|
bMap := make(map[uint64]*types.HeaderInfo)
|
|
for i := uint64(3000); i > 0; i-- {
|
|
h := &gethTypes.Header{
|
|
Number: big.NewInt(int64(i)),
|
|
Time: followTime + (i * 40),
|
|
}
|
|
bMap[i] = &types.HeaderInfo{
|
|
Number: h.Number,
|
|
Hash: h.Hash(),
|
|
Time: h.Time,
|
|
}
|
|
}
|
|
s := &Service{
|
|
cfg: &config{eth1HeaderReqLimit: 1000},
|
|
rpcClient: &mockExecution.RPCClient{BlockNumMap: bMap},
|
|
headerCache: newHeaderCache(),
|
|
latestEth1Data: ðpb.LatestETH1Data{BlockTime: (3000 * 40) + followTime, BlockHeight: 3000},
|
|
}
|
|
h, err := s.followedBlockHeight(context.Background())
|
|
assert.NoError(t, err)
|
|
// With a much higher blocktime, the follow height is respectively shortened.
|
|
assert.Equal(t, uint64(2283), h)
|
|
}
|
|
|
|
type slowRPCClient struct {
|
|
limit int
|
|
numOfCalls int
|
|
}
|
|
|
|
func (s *slowRPCClient) Close() {
|
|
panic("implement me")
|
|
}
|
|
|
|
func (s *slowRPCClient) BatchCall(b []rpc.BatchElem) error {
|
|
s.numOfCalls++
|
|
if len(b) > s.limit {
|
|
return errTimedOut
|
|
}
|
|
for _, e := range b {
|
|
num, err := hexutil.DecodeBig(e.Args[0].(string))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
h := &gethTypes.Header{Number: num}
|
|
*e.Result.(*types.HeaderInfo) = types.HeaderInfo{Number: h.Number, Hash: h.Hash()}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *slowRPCClient) CallContext(_ context.Context, _ interface{}, _ string, _ ...interface{}) error {
|
|
panic("implement me")
|
|
}
|
|
|
|
func TestService_migrateOldDepositTree(t *testing.T) {
|
|
beaconDB := dbutil.SetupDB(t)
|
|
cache, err := depositcache.New()
|
|
require.NoError(t, err)
|
|
|
|
srv, endpoint, err := mockExecution.SetupRPCServer()
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
srv.Stop()
|
|
})
|
|
s, err := NewService(context.Background(),
|
|
WithHttpEndpoint(endpoint),
|
|
WithDatabase(beaconDB),
|
|
WithDepositCache(cache),
|
|
)
|
|
require.NoError(t, err)
|
|
eth1Data := ðpb.ETH1ChainData{
|
|
BeaconState: ðpb.BeaconState{
|
|
Eth1Data: ðpb.Eth1Data{
|
|
DepositCount: 800,
|
|
},
|
|
},
|
|
CurrentEth1Data: ðpb.LatestETH1Data{
|
|
BlockHeight: 100,
|
|
},
|
|
}
|
|
|
|
totalDeposits := 1000
|
|
input := bytesutil.ToBytes32([]byte("foo"))
|
|
dt, err := trie.NewTrie(32)
|
|
require.NoError(t, err)
|
|
|
|
for i := 0; i < totalDeposits; i++ {
|
|
err := dt.Insert(input[:], i)
|
|
require.NoError(t, err)
|
|
}
|
|
eth1Data.Trie = dt.ToProto()
|
|
|
|
err = s.migrateOldDepositTree(eth1Data)
|
|
require.NoError(t, err)
|
|
oldDepositTreeRoot, err := dt.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
newDepositTreeRoot, err := s.depositTrie.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
require.DeepEqual(t, oldDepositTreeRoot, newDepositTreeRoot)
|
|
}
|