github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/powchain/service_test.go (about)

     1  package powchain
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  	"fmt"
     8  	"math/big"
     9  	"strings"
    10  	"sync"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/ethereum/go-ethereum"
    15  	"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
    16  	"github.com/ethereum/go-ethereum/common"
    17  	gethTypes "github.com/ethereum/go-ethereum/core/types"
    18  	"github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache"
    19  	dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
    20  	mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing"
    21  	contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
    22  	protodb "github.com/prysmaticlabs/prysm/proto/beacon/db"
    23  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    24  	"github.com/prysmaticlabs/prysm/shared/clientstats"
    25  	"github.com/prysmaticlabs/prysm/shared/event"
    26  	"github.com/prysmaticlabs/prysm/shared/httputils"
    27  	"github.com/prysmaticlabs/prysm/shared/params"
    28  	"github.com/prysmaticlabs/prysm/shared/testutil"
    29  	"github.com/prysmaticlabs/prysm/shared/testutil/assert"
    30  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    31  	logTest "github.com/sirupsen/logrus/hooks/test"
    32  )
    33  
    34  var _ ChainStartFetcher = (*Service)(nil)
    35  var _ ChainInfoFetcher = (*Service)(nil)
    36  var _ POWBlockFetcher = (*Service)(nil)
    37  var _ Chain = (*Service)(nil)
    38  
    39  type goodLogger struct {
    40  	backend *backends.SimulatedBackend
    41  }
    42  
    43  func (g *goodLogger) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- gethTypes.Log) (ethereum.Subscription, error) {
    44  	if g.backend == nil {
    45  		return new(event.Feed).Subscribe(ch), nil
    46  	}
    47  	return g.backend.SubscribeFilterLogs(ctx, q, ch)
    48  }
    49  
    50  func (g *goodLogger) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]gethTypes.Log, error) {
    51  	if g.backend == nil {
    52  		logs := make([]gethTypes.Log, 3)
    53  		for i := 0; i < len(logs); i++ {
    54  			logs[i].Address = common.Address{}
    55  			logs[i].Topics = make([]common.Hash, 5)
    56  			logs[i].Topics[0] = common.Hash{'a'}
    57  			logs[i].Topics[1] = common.Hash{'b'}
    58  			logs[i].Topics[2] = common.Hash{'c'}
    59  
    60  		}
    61  		return logs, nil
    62  	}
    63  	return g.backend.FilterLogs(ctx, q)
    64  }
    65  
    66  type goodNotifier struct {
    67  	MockStateFeed *event.Feed
    68  }
    69  
    70  func (g *goodNotifier) StateFeed() *event.Feed {
    71  	if g.MockStateFeed == nil {
    72  		g.MockStateFeed = new(event.Feed)
    73  	}
    74  	return g.MockStateFeed
    75  }
    76  
    77  type goodFetcher struct {
    78  	backend *backends.SimulatedBackend
    79  }
    80  
    81  func (g *goodFetcher) HeaderByHash(_ context.Context, hash common.Hash) (*gethTypes.Header, error) {
    82  	if bytes.Equal(hash.Bytes(), common.BytesToHash([]byte{0}).Bytes()) {
    83  		return nil, fmt.Errorf("expected block hash to be nonzero %v", hash)
    84  	}
    85  	if g.backend == nil {
    86  		return &gethTypes.Header{
    87  			Number: big.NewInt(0),
    88  		}, nil
    89  	}
    90  	header := g.backend.Blockchain().GetHeaderByHash(hash)
    91  	if header == nil {
    92  		return nil, errors.New("nil header returned")
    93  	}
    94  	return header, nil
    95  
    96  }
    97  
    98  func (g *goodFetcher) HeaderByNumber(_ context.Context, number *big.Int) (*gethTypes.Header, error) {
    99  	if g.backend == nil {
   100  		return &gethTypes.Header{
   101  			Number: big.NewInt(15),
   102  			Time:   150,
   103  		}, nil
   104  	}
   105  	var header *gethTypes.Header
   106  	if number == nil {
   107  		header = g.backend.Blockchain().CurrentHeader()
   108  	} else {
   109  		header = g.backend.Blockchain().GetHeaderByNumber(number.Uint64())
   110  	}
   111  	if header == nil {
   112  		return nil, errors.New("nil header returned")
   113  	}
   114  	return header, nil
   115  }
   116  
   117  func (g *goodFetcher) SyncProgress(_ context.Context) (*ethereum.SyncProgress, error) {
   118  	return nil, nil
   119  }
   120  
   121  var depositsReqForChainStart = 64
   122  
   123  func TestStart_OK(t *testing.T) {
   124  	hook := logTest.NewGlobal()
   125  	beaconDB := dbutil.SetupDB(t)
   126  	testAcc, err := contracts.Setup()
   127  	require.NoError(t, err, "Unable to set up simulated backend")
   128  	web3Service, err := NewService(context.Background(), &Web3ServiceConfig{
   129  		HttpEndpoints:   []string{endpoint},
   130  		DepositContract: testAcc.ContractAddr,
   131  		BeaconDB:        beaconDB,
   132  	})
   133  	require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
   134  	web3Service = setDefaultMocks(web3Service)
   135  	web3Service.rpcClient = &mockPOW.RPCClient{Backend: testAcc.Backend}
   136  	web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend)
   137  	require.NoError(t, err)
   138  	testAcc.Backend.Commit()
   139  
   140  	web3Service.Start()
   141  	if len(hook.Entries) > 0 {
   142  		msg := hook.LastEntry().Message
   143  		want := "Could not connect to ETH1.0 chain RPC client"
   144  		if strings.Contains(want, msg) {
   145  			t.Errorf("incorrect log, expected %s, got %s", want, msg)
   146  		}
   147  	}
   148  	hook.Reset()
   149  	web3Service.cancel()
   150  }
   151  
   152  func TestStart_NoHttpEndpointDefinedFails_WithoutChainStarted(t *testing.T) {
   153  	hook := logTest.NewGlobal()
   154  	beaconDB := dbutil.SetupDB(t)
   155  	testAcc, err := contracts.Setup()
   156  	require.NoError(t, err, "Unable to set up simulated backend")
   157  	s, err := NewService(context.Background(), &Web3ServiceConfig{
   158  		HttpEndpoints:   []string{""}, // No endpoint defined!
   159  		DepositContract: testAcc.ContractAddr,
   160  		BeaconDB:        beaconDB,
   161  	})
   162  	require.NoError(t, err)
   163  	// Set custom exit func so test can proceed
   164  	log.Logger.ExitFunc = func(i int) {
   165  		panic(i)
   166  	}
   167  	defer func() {
   168  		log.Logger.ExitFunc = nil
   169  	}()
   170  	wg := new(sync.WaitGroup)
   171  	wg.Add(1)
   172  	// Expect Start function to fail from a fatal call due
   173  	// to no state existing.
   174  	go func() {
   175  		defer func() {
   176  			if r := recover(); r != nil {
   177  				wg.Done()
   178  			}
   179  		}()
   180  		s.Start()
   181  	}()
   182  	testutil.WaitTimeout(wg, time.Second)
   183  	require.LogsContain(t, hook, "cannot create genesis state: no eth1 http endpoint defined")
   184  	hook.Reset()
   185  }
   186  
   187  func TestStart_NoHttpEndpointDefinedSucceeds_WithGenesisState(t *testing.T) {
   188  	hook := logTest.NewGlobal()
   189  	beaconDB := dbutil.SetupDB(t)
   190  	testAcc, err := contracts.Setup()
   191  	require.NoError(t, err, "Unable to set up simulated backend")
   192  	st, _ := testutil.DeterministicGenesisState(t, 10)
   193  	b := testutil.NewBeaconBlock()
   194  	genRoot, err := b.HashTreeRoot()
   195  	require.NoError(t, err)
   196  	require.NoError(t, beaconDB.SaveState(context.Background(), st, genRoot))
   197  	require.NoError(t, beaconDB.SaveGenesisBlockRoot(context.Background(), genRoot))
   198  	depositCache, err := depositcache.New()
   199  	require.NoError(t, err)
   200  	s, err := NewService(context.Background(), &Web3ServiceConfig{
   201  		HttpEndpoints:   []string{""}, // No endpoint defined!
   202  		DepositContract: testAcc.ContractAddr,
   203  		BeaconDB:        beaconDB,
   204  		DepositCache:    depositCache,
   205  	})
   206  	require.NoError(t, err)
   207  
   208  	wg := new(sync.WaitGroup)
   209  	wg.Add(1)
   210  
   211  	go func() {
   212  		s.Start()
   213  		wg.Done()
   214  	}()
   215  	s.cancel()
   216  	testutil.WaitTimeout(wg, time.Second)
   217  	require.LogsDoNotContain(t, hook, "cannot create genesis state: no eth1 http endpoint defined")
   218  	hook.Reset()
   219  }
   220  
   221  func TestStart_NoHttpEndpointDefinedSucceeds_WithChainStarted(t *testing.T) {
   222  	hook := logTest.NewGlobal()
   223  	beaconDB := dbutil.SetupDB(t)
   224  	testAcc, err := contracts.Setup()
   225  	require.NoError(t, err, "Unable to set up simulated backend")
   226  
   227  	require.NoError(t, beaconDB.SavePowchainData(context.Background(), &protodb.ETH1ChainData{
   228  		ChainstartData: &protodb.ChainStartData{Chainstarted: true},
   229  		Trie:           &protodb.SparseMerkleTrie{},
   230  	}))
   231  	s, err := NewService(context.Background(), &Web3ServiceConfig{
   232  		HttpEndpoints:   []string{""}, // No endpoint defined!
   233  		DepositContract: testAcc.ContractAddr,
   234  		BeaconDB:        beaconDB,
   235  	})
   236  	require.NoError(t, err)
   237  
   238  	s.Start()
   239  	require.LogsDoNotContain(t, hook, "cannot create genesis state: no eth1 http endpoint defined")
   240  	hook.Reset()
   241  }
   242  
   243  func TestStop_OK(t *testing.T) {
   244  	hook := logTest.NewGlobal()
   245  	testAcc, err := contracts.Setup()
   246  	require.NoError(t, err, "Unable to set up simulated backend")
   247  	beaconDB := dbutil.SetupDB(t)
   248  	web3Service, err := NewService(context.Background(), &Web3ServiceConfig{
   249  		HttpEndpoints:   []string{endpoint},
   250  		DepositContract: testAcc.ContractAddr,
   251  		BeaconDB:        beaconDB,
   252  	})
   253  	require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
   254  	web3Service = setDefaultMocks(web3Service)
   255  	web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend)
   256  	require.NoError(t, err)
   257  
   258  	testAcc.Backend.Commit()
   259  
   260  	err = web3Service.Stop()
   261  	require.NoError(t, err, "Unable to stop web3 ETH1.0 chain service")
   262  
   263  	// The context should have been canceled.
   264  	assert.NotNil(t, web3Service.ctx.Err(), "Context wasnt canceled")
   265  
   266  	hook.Reset()
   267  }
   268  
   269  func TestService_Eth1Synced(t *testing.T) {
   270  	testAcc, err := contracts.Setup()
   271  	require.NoError(t, err, "Unable to set up simulated backend")
   272  	beaconDB := dbutil.SetupDB(t)
   273  	web3Service, err := NewService(context.Background(), &Web3ServiceConfig{
   274  		HttpEndpoints:   []string{endpoint},
   275  		DepositContract: testAcc.ContractAddr,
   276  		BeaconDB:        beaconDB,
   277  	})
   278  	require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
   279  	web3Service = setDefaultMocks(web3Service)
   280  	web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend)
   281  	require.NoError(t, err)
   282  	web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
   283  
   284  	currTime := testAcc.Backend.Blockchain().CurrentHeader().Time
   285  	now := time.Now()
   286  	assert.NoError(t, testAcc.Backend.AdjustTime(now.Sub(time.Unix(int64(currTime), 0))))
   287  	testAcc.Backend.Commit()
   288  
   289  	synced, err := web3Service.isEth1NodeSynced()
   290  	require.NoError(t, err)
   291  	assert.Equal(t, true, synced, "Expected eth1 nodes to be synced")
   292  }
   293  
   294  func TestFollowBlock_OK(t *testing.T) {
   295  	testAcc, err := contracts.Setup()
   296  	require.NoError(t, err, "Unable to set up simulated backend")
   297  	beaconDB := dbutil.SetupDB(t)
   298  	web3Service, err := NewService(context.Background(), &Web3ServiceConfig{
   299  		HttpEndpoints:   []string{endpoint},
   300  		DepositContract: testAcc.ContractAddr,
   301  		BeaconDB:        beaconDB,
   302  	})
   303  	require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
   304  
   305  	// simulated backend sets eth1 block
   306  	// time as 10 seconds
   307  	conf := params.BeaconConfig()
   308  	conf.SecondsPerETH1Block = 10
   309  	params.OverrideBeaconConfig(conf)
   310  	defer params.UseMainnetConfig()
   311  
   312  	web3Service = setDefaultMocks(web3Service)
   313  	web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
   314  	baseHeight := testAcc.Backend.Blockchain().CurrentBlock().NumberU64()
   315  	// process follow_distance blocks
   316  	for i := 0; i < int(params.BeaconConfig().Eth1FollowDistance); i++ {
   317  		testAcc.Backend.Commit()
   318  	}
   319  	// set current height
   320  	web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().NumberU64()
   321  	web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time()
   322  
   323  	h, err := web3Service.followBlockHeight(context.Background())
   324  	require.NoError(t, err)
   325  	assert.Equal(t, baseHeight, h, "Unexpected block height")
   326  	numToForward := uint64(2)
   327  	expectedHeight := numToForward + baseHeight
   328  	// forward 2 blocks
   329  	for i := uint64(0); i < numToForward; i++ {
   330  		testAcc.Backend.Commit()
   331  	}
   332  	// set current height
   333  	web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().NumberU64()
   334  	web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time()
   335  
   336  	h, err = web3Service.followBlockHeight(context.Background())
   337  	require.NoError(t, err)
   338  	assert.Equal(t, expectedHeight, h, "Unexpected block height")
   339  }
   340  
   341  func TestStatus(t *testing.T) {
   342  	now := time.Now()
   343  
   344  	beforeFiveMinutesAgo := uint64(now.Add(-5*time.Minute - 30*time.Second).Unix())
   345  	afterFiveMinutesAgo := uint64(now.Add(-5*time.Minute + 30*time.Second).Unix())
   346  
   347  	testCases := map[*Service]string{
   348  		// "status is ok" cases
   349  		{}: "",
   350  		{isRunning: true, latestEth1Data: &protodb.LatestETH1Data{BlockTime: afterFiveMinutesAgo}}:   "",
   351  		{isRunning: false, latestEth1Data: &protodb.LatestETH1Data{BlockTime: beforeFiveMinutesAgo}}: "",
   352  		{isRunning: false, runError: errors.New("test runError")}:                                    "",
   353  		// "status is error" cases
   354  		{isRunning: true, runError: errors.New("test runError")}: "test runError",
   355  	}
   356  
   357  	for web3ServiceState, wantedErrorText := range testCases {
   358  		status := web3ServiceState.Status()
   359  		if status == nil {
   360  			assert.Equal(t, "", wantedErrorText)
   361  
   362  		} else {
   363  			assert.Equal(t, wantedErrorText, status.Error())
   364  		}
   365  	}
   366  }
   367  
   368  func TestHandlePanic_OK(t *testing.T) {
   369  	hook := logTest.NewGlobal()
   370  	beaconDB := dbutil.SetupDB(t)
   371  	web3Service, err := NewService(context.Background(), &Web3ServiceConfig{
   372  		HttpEndpoints: []string{endpoint},
   373  		BeaconDB:      beaconDB,
   374  	})
   375  	require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
   376  	// nil eth1DataFetcher would panic if cached value not used
   377  	web3Service.eth1DataFetcher = nil
   378  	web3Service.processBlockHeader(nil)
   379  	require.LogsContain(t, hook, "Panicked when handling data from ETH 1.0 Chain!")
   380  }
   381  
   382  func TestLogTillGenesis_OK(t *testing.T) {
   383  	// Reset the var at the end of the test.
   384  	currPeriod := logPeriod
   385  	logPeriod = 1 * time.Second
   386  	defer func() {
   387  		logPeriod = currPeriod
   388  	}()
   389  
   390  	orgConfig := params.BeaconConfig().Copy()
   391  	cfg := params.BeaconConfig()
   392  	cfg.Eth1FollowDistance = 5
   393  	params.OverrideBeaconConfig(cfg)
   394  	defer func() {
   395  		params.OverrideBeaconConfig(orgConfig)
   396  	}()
   397  
   398  	orgNetworkConfig := params.BeaconNetworkConfig().Copy()
   399  	nCfg := params.BeaconNetworkConfig()
   400  	nCfg.ContractDeploymentBlock = 0
   401  	params.OverrideBeaconNetworkConfig(nCfg)
   402  	defer func() {
   403  		params.OverrideBeaconNetworkConfig(orgNetworkConfig)
   404  	}()
   405  
   406  	hook := logTest.NewGlobal()
   407  	testAcc, err := contracts.Setup()
   408  	require.NoError(t, err, "Unable to set up simulated backend")
   409  	beaconDB := dbutil.SetupDB(t)
   410  	web3Service, err := NewService(context.Background(), &Web3ServiceConfig{
   411  		HttpEndpoints:   []string{endpoint},
   412  		DepositContract: testAcc.ContractAddr,
   413  		BeaconDB:        beaconDB,
   414  	})
   415  	require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
   416  	web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend)
   417  	require.NoError(t, err)
   418  
   419  	web3Service.rpcClient = &mockPOW.RPCClient{Backend: testAcc.Backend}
   420  	web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
   421  	web3Service.httpLogger = testAcc.Backend
   422  	for i := 0; i < 30; i++ {
   423  		testAcc.Backend.Commit()
   424  	}
   425  	web3Service.latestEth1Data = &protodb.LatestETH1Data{LastRequestedBlock: 0}
   426  	// Spin off to a separate routine
   427  	go web3Service.run(web3Service.ctx.Done())
   428  	// Wait for 2 seconds so that the
   429  	// info is logged.
   430  	time.Sleep(2 * time.Second)
   431  	web3Service.cancel()
   432  	assert.LogsContain(t, hook, "Currently waiting for chainstart")
   433  }
   434  
   435  func TestInitDepositCache_OK(t *testing.T) {
   436  	ctrs := []*protodb.DepositContainer{
   437  		{Index: 0, Eth1BlockHeight: 2, Deposit: &ethpb.Deposit{Proof: [][]byte{[]byte("A")}}},
   438  		{Index: 1, Eth1BlockHeight: 4, Deposit: &ethpb.Deposit{Proof: [][]byte{[]byte("B")}}},
   439  		{Index: 2, Eth1BlockHeight: 6, Deposit: &ethpb.Deposit{Proof: [][]byte{[]byte("c")}}},
   440  	}
   441  	gs, _ := testutil.DeterministicGenesisState(t, 1)
   442  	beaconDB := dbutil.SetupDB(t)
   443  	s := &Service{
   444  		chainStartData:  &protodb.ChainStartData{Chainstarted: false},
   445  		preGenesisState: gs,
   446  		cfg:             &Web3ServiceConfig{BeaconDB: beaconDB},
   447  	}
   448  	var err error
   449  	s.cfg.DepositCache, err = depositcache.New()
   450  	require.NoError(t, err)
   451  	require.NoError(t, s.initDepositCaches(context.Background(), ctrs))
   452  
   453  	require.Equal(t, 0, len(s.cfg.DepositCache.PendingContainers(context.Background(), nil)))
   454  
   455  	blockRootA := [32]byte{'a'}
   456  
   457  	emptyState, err := testutil.NewBeaconState()
   458  	require.NoError(t, err)
   459  	require.NoError(t, s.cfg.BeaconDB.SaveGenesisBlockRoot(context.Background(), blockRootA))
   460  	require.NoError(t, s.cfg.BeaconDB.SaveState(context.Background(), emptyState, blockRootA))
   461  	s.chainStartData.Chainstarted = true
   462  	require.NoError(t, s.initDepositCaches(context.Background(), ctrs))
   463  	require.Equal(t, 3, len(s.cfg.DepositCache.PendingContainers(context.Background(), nil)))
   464  }
   465  
   466  func TestNewService_EarliestVotingBlock(t *testing.T) {
   467  	testAcc, err := contracts.Setup()
   468  	require.NoError(t, err, "Unable to set up simulated backend")
   469  	beaconDB := dbutil.SetupDB(t)
   470  	web3Service, err := NewService(context.Background(), &Web3ServiceConfig{
   471  		HttpEndpoints:   []string{endpoint},
   472  		DepositContract: testAcc.ContractAddr,
   473  		BeaconDB:        beaconDB,
   474  	})
   475  	require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
   476  	web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
   477  	// simulated backend sets eth1 block
   478  	// time as 10 seconds
   479  	conf := params.BeaconConfig()
   480  	conf.SecondsPerETH1Block = 10
   481  	conf.Eth1FollowDistance = 50
   482  	params.OverrideBeaconConfig(conf)
   483  	defer params.UseMainnetConfig()
   484  
   485  	// Genesis not set
   486  	followBlock := uint64(2000)
   487  	blk, err := web3Service.determineEarliestVotingBlock(context.Background(), followBlock)
   488  	require.NoError(t, err)
   489  	assert.Equal(t, followBlock-conf.Eth1FollowDistance, blk, "unexpected earliest voting block")
   490  
   491  	// Genesis is set.
   492  
   493  	numToForward := 1500
   494  	// forward 1500 blocks
   495  	for i := 0; i < numToForward; i++ {
   496  		testAcc.Backend.Commit()
   497  	}
   498  	currTime := testAcc.Backend.Blockchain().CurrentHeader().Time
   499  	now := time.Now()
   500  	err = testAcc.Backend.AdjustTime(now.Sub(time.Unix(int64(currTime), 0)))
   501  	require.NoError(t, err)
   502  	testAcc.Backend.Commit()
   503  
   504  	currTime = testAcc.Backend.Blockchain().CurrentHeader().Time
   505  	web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentHeader().Number.Uint64()
   506  	web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentHeader().Time
   507  	web3Service.chainStartData.GenesisTime = currTime
   508  
   509  	// With a current slot of zero, only request follow_blocks behind.
   510  	blk, err = web3Service.determineEarliestVotingBlock(context.Background(), followBlock)
   511  	require.NoError(t, err)
   512  	assert.Equal(t, followBlock-conf.Eth1FollowDistance, blk, "unexpected earliest voting block")
   513  
   514  }
   515  
   516  func TestNewService_Eth1HeaderRequLimit(t *testing.T) {
   517  	testAcc, err := contracts.Setup()
   518  	require.NoError(t, err, "Unable to set up simulated backend")
   519  	beaconDB := dbutil.SetupDB(t)
   520  
   521  	s1, err := NewService(context.Background(), &Web3ServiceConfig{
   522  		HttpEndpoints:   []string{endpoint},
   523  		DepositContract: testAcc.ContractAddr,
   524  		BeaconDB:        beaconDB,
   525  	})
   526  	require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
   527  	assert.Equal(t, defaultEth1HeaderReqLimit, s1.cfg.Eth1HeaderReqLimit, "default eth1 header request limit not set")
   528  
   529  	s2, err := NewService(context.Background(), &Web3ServiceConfig{
   530  		HttpEndpoints:      []string{endpoint},
   531  		DepositContract:    testAcc.ContractAddr,
   532  		BeaconDB:           beaconDB,
   533  		Eth1HeaderReqLimit: uint64(150),
   534  	})
   535  	require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
   536  	assert.Equal(t, uint64(150), s2.cfg.Eth1HeaderReqLimit, "unable to set eth1HeaderRequestLimit")
   537  }
   538  
   539  type mockBSUpdater struct {
   540  	lastBS clientstats.BeaconNodeStats
   541  }
   542  
   543  func (mbs *mockBSUpdater) Update(bs clientstats.BeaconNodeStats) {
   544  	mbs.lastBS = bs
   545  }
   546  
   547  var _ BeaconNodeStatsUpdater = &mockBSUpdater{}
   548  
   549  func TestServiceFallbackCorrectly(t *testing.T) {
   550  	firstEndpoint := "A"
   551  	secondEndpoint := "B"
   552  
   553  	testAcc, err := contracts.Setup()
   554  	require.NoError(t, err, "Unable to set up simulated backend")
   555  	beaconDB := dbutil.SetupDB(t)
   556  
   557  	mbs := &mockBSUpdater{}
   558  	s1, err := NewService(context.Background(), &Web3ServiceConfig{
   559  		HttpEndpoints:          []string{firstEndpoint},
   560  		DepositContract:        testAcc.ContractAddr,
   561  		BeaconDB:               beaconDB,
   562  		BeaconNodeStatsUpdater: mbs,
   563  	})
   564  	s1.bsUpdater = mbs
   565  	require.NoError(t, err)
   566  
   567  	assert.Equal(t, firstEndpoint, s1.currHttpEndpoint.Url, "Unexpected http endpoint")
   568  	// Stay at the first endpoint.
   569  	s1.fallbackToNextEndpoint()
   570  	assert.Equal(t, firstEndpoint, s1.currHttpEndpoint.Url, "Unexpected http endpoint")
   571  	assert.Equal(t, false, mbs.lastBS.SyncEth1FallbackConfigured, "SyncEth1FallbackConfigured in clientstats update should be false when only 1 endpoint is configured")
   572  
   573  	s1.httpEndpoints = append(s1.httpEndpoints, httputils.Endpoint{Url: secondEndpoint})
   574  
   575  	s1.fallbackToNextEndpoint()
   576  	assert.Equal(t, secondEndpoint, s1.currHttpEndpoint.Url, "Unexpected http endpoint")
   577  	assert.Equal(t, true, mbs.lastBS.SyncEth1FallbackConfigured, "SyncEth1FallbackConfigured in clientstats update should be true when > 1 endpoint is configured")
   578  
   579  	thirdEndpoint := "C"
   580  	fourthEndpoint := "D"
   581  
   582  	s1.httpEndpoints = append(s1.httpEndpoints, httputils.Endpoint{Url: thirdEndpoint}, httputils.Endpoint{Url: fourthEndpoint})
   583  
   584  	s1.fallbackToNextEndpoint()
   585  	assert.Equal(t, thirdEndpoint, s1.currHttpEndpoint.Url, "Unexpected http endpoint")
   586  
   587  	s1.fallbackToNextEndpoint()
   588  	assert.Equal(t, fourthEndpoint, s1.currHttpEndpoint.Url, "Unexpected http endpoint")
   589  
   590  	// Rollover correctly back to the first endpoint
   591  	s1.fallbackToNextEndpoint()
   592  	assert.Equal(t, firstEndpoint, s1.currHttpEndpoint.Url, "Unexpected http endpoint")
   593  }
   594  
   595  func TestDedupEndpoints(t *testing.T) {
   596  	assert.DeepEqual(t, []string{"A"}, dedupEndpoints([]string{"A"}), "did not dedup correctly")
   597  	assert.DeepEqual(t, []string{"A", "B"}, dedupEndpoints([]string{"A", "B"}), "did not dedup correctly")
   598  	assert.DeepEqual(t, []string{"A", "B"}, dedupEndpoints([]string{"A", "A", "A", "B"}), "did not dedup correctly")
   599  	assert.DeepEqual(t, []string{"A", "B"}, dedupEndpoints([]string{"A", "A", "A", "B", "B"}), "did not dedup correctly")
   600  }
   601  
   602  func Test_batchRequestHeaders_UnderflowChecks(t *testing.T) {
   603  	srv := &Service{}
   604  	start := uint64(101)
   605  	end := uint64(100)
   606  	_, err := srv.batchRequestHeaders(start, end)
   607  	require.ErrorContains(t, "cannot be >", err)
   608  
   609  	start = uint64(200)
   610  	end = uint64(100)
   611  	_, err = srv.batchRequestHeaders(start, end)
   612  	require.ErrorContains(t, "cannot be >", err)
   613  }
   614  
   615  func TestService_EnsureConsistentPowchainData(t *testing.T) {
   616  	beaconDB := dbutil.SetupDB(t)
   617  	cache, err := depositcache.New()
   618  	require.NoError(t, err)
   619  
   620  	s1, err := NewService(context.Background(), &Web3ServiceConfig{
   621  		BeaconDB:     beaconDB,
   622  		DepositCache: cache,
   623  	})
   624  	require.NoError(t, err)
   625  	genState, err := testutil.NewBeaconState()
   626  	require.NoError(t, err)
   627  	assert.NoError(t, genState.SetSlot(1000))
   628  
   629  	require.NoError(t, s1.cfg.BeaconDB.SaveGenesisData(context.Background(), genState))
   630  	require.NoError(t, s1.ensureValidPowchainData(context.Background()))
   631  
   632  	eth1Data, err := s1.cfg.BeaconDB.PowchainData(context.Background())
   633  	assert.NoError(t, err)
   634  
   635  	assert.NotNil(t, eth1Data)
   636  	assert.Equal(t, true, eth1Data.ChainstartData.Chainstarted)
   637  }
   638  
   639  func TestService_InitializeCorrectly(t *testing.T) {
   640  	beaconDB := dbutil.SetupDB(t)
   641  	cache, err := depositcache.New()
   642  	require.NoError(t, err)
   643  
   644  	s1, err := NewService(context.Background(), &Web3ServiceConfig{
   645  		BeaconDB:     beaconDB,
   646  		DepositCache: cache,
   647  	})
   648  	require.NoError(t, err)
   649  	genState, err := testutil.NewBeaconState()
   650  	require.NoError(t, err)
   651  	assert.NoError(t, genState.SetSlot(1000))
   652  
   653  	require.NoError(t, s1.cfg.BeaconDB.SaveGenesisData(context.Background(), genState))
   654  	require.NoError(t, s1.ensureValidPowchainData(context.Background()))
   655  
   656  	eth1Data, err := s1.cfg.BeaconDB.PowchainData(context.Background())
   657  	assert.NoError(t, err)
   658  
   659  	assert.NoError(t, s1.initializeEth1Data(context.Background(), eth1Data))
   660  	assert.Equal(t, int64(-1), s1.lastReceivedMerkleIndex, "received incorrect last received merkle index")
   661  }
   662  
   663  func TestService_EnsureValidPowchainData(t *testing.T) {
   664  	beaconDB := dbutil.SetupDB(t)
   665  	cache, err := depositcache.New()
   666  	require.NoError(t, err)
   667  
   668  	s1, err := NewService(context.Background(), &Web3ServiceConfig{
   669  		BeaconDB:     beaconDB,
   670  		DepositCache: cache,
   671  	})
   672  	require.NoError(t, err)
   673  	genState, err := testutil.NewBeaconState()
   674  	require.NoError(t, err)
   675  	assert.NoError(t, genState.SetSlot(1000))
   676  
   677  	require.NoError(t, s1.cfg.BeaconDB.SaveGenesisData(context.Background(), genState))
   678  
   679  	err = s1.cfg.BeaconDB.SavePowchainData(context.Background(), &protodb.ETH1ChainData{
   680  		ChainstartData:    &protodb.ChainStartData{Chainstarted: true},
   681  		DepositContainers: []*protodb.DepositContainer{{Index: 1}},
   682  	})
   683  	require.NoError(t, err)
   684  	require.NoError(t, s1.ensureValidPowchainData(context.Background()))
   685  
   686  	eth1Data, err := s1.cfg.BeaconDB.PowchainData(context.Background())
   687  	assert.NoError(t, err)
   688  
   689  	assert.NotNil(t, eth1Data)
   690  	assert.Equal(t, 0, len(eth1Data.DepositContainers))
   691  }
   692  
   693  func TestService_ValidateDepositContainers(t *testing.T) {
   694  	beaconDB := dbutil.SetupDB(t)
   695  	cache, err := depositcache.New()
   696  	require.NoError(t, err)
   697  
   698  	s1, err := NewService(context.Background(), &Web3ServiceConfig{
   699  		BeaconDB:     beaconDB,
   700  		DepositCache: cache,
   701  	})
   702  	require.NoError(t, err)
   703  
   704  	var tt = []struct {
   705  		name        string
   706  		ctrsFunc    func() []*protodb.DepositContainer
   707  		expectedRes bool
   708  	}{
   709  		{
   710  			name: "zero containers",
   711  			ctrsFunc: func() []*protodb.DepositContainer {
   712  				return make([]*protodb.DepositContainer, 0)
   713  			},
   714  			expectedRes: true,
   715  		},
   716  		{
   717  			name: "ordered containers",
   718  			ctrsFunc: func() []*protodb.DepositContainer {
   719  				ctrs := make([]*protodb.DepositContainer, 0)
   720  				for i := 0; i < 10; i++ {
   721  					ctrs = append(ctrs, &protodb.DepositContainer{Index: int64(i), Eth1BlockHeight: uint64(i + 10)})
   722  				}
   723  				return ctrs
   724  			},
   725  			expectedRes: true,
   726  		},
   727  		{
   728  			name: "0th container missing",
   729  			ctrsFunc: func() []*protodb.DepositContainer {
   730  				ctrs := make([]*protodb.DepositContainer, 0)
   731  				for i := 1; i < 10; i++ {
   732  					ctrs = append(ctrs, &protodb.DepositContainer{Index: int64(i), Eth1BlockHeight: uint64(i + 10)})
   733  				}
   734  				return ctrs
   735  			},
   736  			expectedRes: false,
   737  		},
   738  		{
   739  			name: "skipped containers",
   740  			ctrsFunc: func() []*protodb.DepositContainer {
   741  				ctrs := make([]*protodb.DepositContainer, 0)
   742  				for i := 0; i < 10; i++ {
   743  					if i == 5 || i == 7 {
   744  						continue
   745  					}
   746  					ctrs = append(ctrs, &protodb.DepositContainer{Index: int64(i), Eth1BlockHeight: uint64(i + 10)})
   747  				}
   748  				return ctrs
   749  			},
   750  			expectedRes: false,
   751  		},
   752  	}
   753  
   754  	for _, test := range tt {
   755  		assert.Equal(t, test.expectedRes, s1.validateDepositContainers(test.ctrsFunc()))
   756  	}
   757  }
   758  
   759  func TestTimestampIsChecked(t *testing.T) {
   760  	timestamp := uint64(time.Now().Unix())
   761  	assert.Equal(t, false, eth1HeadIsBehind(timestamp))
   762  
   763  	// Give an older timestmap beyond threshold.
   764  	timestamp = uint64(time.Now().Add(-eth1Threshold).Add(-1 * time.Minute).Unix())
   765  	assert.Equal(t, true, eth1HeadIsBehind(timestamp))
   766  }