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

     1  // Package powchain defines a runtime service which is tasked with
     2  // communicating with an eth1 endpoint, processing logs from a deposit
     3  // contract, and the latest eth1 data headers for usage in the beacon node.
     4  package powchain
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"math/big"
    10  	"reflect"
    11  	"runtime/debug"
    12  	"sort"
    13  	"sync"
    14  	"time"
    15  
    16  	"github.com/ethereum/go-ethereum"
    17  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    18  	"github.com/ethereum/go-ethereum/common"
    19  	"github.com/ethereum/go-ethereum/common/hexutil"
    20  	gethTypes "github.com/ethereum/go-ethereum/core/types"
    21  	"github.com/ethereum/go-ethereum/ethclient"
    22  	gethRPC "github.com/ethereum/go-ethereum/rpc"
    23  	"github.com/pkg/errors"
    24  	"github.com/prometheus/client_golang/prometheus"
    25  	"github.com/prometheus/client_golang/prometheus/promauto"
    26  	"github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache"
    27  	statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state"
    28  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    29  	"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
    30  	"github.com/prysmaticlabs/prysm/beacon-chain/db"
    31  	"github.com/prysmaticlabs/prysm/beacon-chain/powchain/types"
    32  	iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface"
    33  	"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
    34  	"github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
    35  	contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
    36  	protodb "github.com/prysmaticlabs/prysm/proto/beacon/db"
    37  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    38  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    39  	"github.com/prysmaticlabs/prysm/shared/clientstats"
    40  	"github.com/prysmaticlabs/prysm/shared/httputils"
    41  	"github.com/prysmaticlabs/prysm/shared/httputils/authorizationmethod"
    42  	"github.com/prysmaticlabs/prysm/shared/logutil"
    43  	"github.com/prysmaticlabs/prysm/shared/params"
    44  	"github.com/prysmaticlabs/prysm/shared/timeutils"
    45  	"github.com/prysmaticlabs/prysm/shared/trieutil"
    46  	"github.com/sirupsen/logrus"
    47  )
    48  
    49  var (
    50  	validDepositsCount = promauto.NewCounter(prometheus.CounterOpts{
    51  		Name: "powchain_valid_deposits_received",
    52  		Help: "The number of valid deposits received in the deposit contract",
    53  	})
    54  	blockNumberGauge = promauto.NewGauge(prometheus.GaugeOpts{
    55  		Name: "powchain_block_number",
    56  		Help: "The current block number in the proof-of-work chain",
    57  	})
    58  	missedDepositLogsCount = promauto.NewCounter(prometheus.CounterOpts{
    59  		Name: "powchain_missed_deposit_logs",
    60  		Help: "The number of times a missed deposit log is detected",
    61  	})
    62  )
    63  
    64  var (
    65  	// time to wait before trying to reconnect with the eth1 node.
    66  	backOffPeriod = 15 * time.Second
    67  	// amount of times before we log the status of the eth1 dial attempt.
    68  	logThreshold = 8
    69  	// period to log chainstart related information
    70  	logPeriod = 1 * time.Minute
    71  	// threshold of how old we will accept an eth1 node's head to be.
    72  	eth1Threshold = 20 * time.Minute
    73  	// error when eth1 node is not synced.
    74  	errNotSynced = errors.New("eth1 node is still syncing")
    75  	// error when eth1 node is too far behind.
    76  	errFarBehind = errors.Errorf("eth1 head is more than %s behind from current wall clock time", eth1Threshold.String())
    77  )
    78  
    79  // ChainStartFetcher retrieves information pertaining to the chain start event
    80  // of the beacon chain for usage across various services.
    81  type ChainStartFetcher interface {
    82  	ChainStartDeposits() []*ethpb.Deposit
    83  	ChainStartEth1Data() *ethpb.Eth1Data
    84  	PreGenesisState() iface.BeaconState
    85  	ClearPreGenesisData()
    86  }
    87  
    88  // ChainInfoFetcher retrieves information about eth1 metadata at the Ethereum consensus genesis time.
    89  type ChainInfoFetcher interface {
    90  	Eth2GenesisPowchainInfo() (uint64, *big.Int)
    91  	IsConnectedToETH1() bool
    92  }
    93  
    94  // POWBlockFetcher defines a struct that can retrieve mainchain blocks.
    95  type POWBlockFetcher interface {
    96  	BlockTimeByHeight(ctx context.Context, height *big.Int) (uint64, error)
    97  	BlockByTimestamp(ctx context.Context, time uint64) (*types.HeaderInfo, error)
    98  	BlockHashByHeight(ctx context.Context, height *big.Int) (common.Hash, error)
    99  	BlockExists(ctx context.Context, hash common.Hash) (bool, *big.Int, error)
   100  	BlockExistsWithCache(ctx context.Context, hash common.Hash) (bool, *big.Int, error)
   101  }
   102  
   103  // Chain defines a standard interface for the powchain service in Prysm.
   104  type Chain interface {
   105  	ChainStartFetcher
   106  	ChainInfoFetcher
   107  	POWBlockFetcher
   108  }
   109  
   110  // RPCDataFetcher defines a subset of methods conformed to by ETH1.0 RPC clients for
   111  // fetching eth1 data from the clients.
   112  type RPCDataFetcher interface {
   113  	HeaderByNumber(ctx context.Context, number *big.Int) (*gethTypes.Header, error)
   114  	HeaderByHash(ctx context.Context, hash common.Hash) (*gethTypes.Header, error)
   115  	SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error)
   116  }
   117  
   118  // RPCClient defines the rpc methods required to interact with the eth1 node.
   119  type RPCClient interface {
   120  	BatchCall(b []gethRPC.BatchElem) error
   121  }
   122  
   123  // Service fetches important information about the canonical
   124  // Ethereum ETH1.0 chain via a web3 endpoint using an ethclient. The Random
   125  // Beacon Chain requires synchronization with the ETH1.0 chain's current
   126  // blockhash, block number, and access to logs within the
   127  // Validator Registration Contract on the ETH1.0 chain to kick off the beacon
   128  // chain's validator registration process.
   129  type Service struct {
   130  	connectedETH1           bool
   131  	isRunning               bool
   132  	processingLock          sync.RWMutex
   133  	cfg                     *Web3ServiceConfig
   134  	ctx                     context.Context
   135  	cancel                  context.CancelFunc
   136  	headTicker              *time.Ticker
   137  	httpEndpoints           []httputils.Endpoint
   138  	currHttpEndpoint        httputils.Endpoint
   139  	httpLogger              bind.ContractFilterer
   140  	eth1DataFetcher         RPCDataFetcher
   141  	rpcClient               RPCClient
   142  	headerCache             *headerCache // cache to store block hash/block height.
   143  	latestEth1Data          *protodb.LatestETH1Data
   144  	depositContractCaller   *contracts.DepositContractCaller
   145  	depositTrie             *trieutil.SparseMerkleTrie
   146  	chainStartData          *protodb.ChainStartData
   147  	lastReceivedMerkleIndex int64 // Keeps track of the last received index to prevent log spam.
   148  	runError                error
   149  	preGenesisState         iface.BeaconState
   150  	bsUpdater               BeaconNodeStatsUpdater
   151  }
   152  
   153  // Web3ServiceConfig defines a config struct for web3 service to use through its life cycle.
   154  type Web3ServiceConfig struct {
   155  	HttpEndpoints          []string
   156  	DepositContract        common.Address
   157  	BeaconDB               db.HeadAccessDatabase
   158  	DepositCache           *depositcache.DepositCache
   159  	StateNotifier          statefeed.Notifier
   160  	StateGen               *stategen.State
   161  	Eth1HeaderReqLimit     uint64
   162  	BeaconNodeStatsUpdater BeaconNodeStatsUpdater
   163  }
   164  
   165  // NewService sets up a new instance with an ethclient when
   166  // given a web3 endpoint as a string in the config.
   167  func NewService(ctx context.Context, config *Web3ServiceConfig) (*Service, error) {
   168  	ctx, cancel := context.WithCancel(ctx)
   169  	_ = cancel // govet fix for lost cancel. Cancel is handled in service.Stop()
   170  	depositTrie, err := trieutil.NewTrie(params.BeaconConfig().DepositContractTreeDepth)
   171  	if err != nil {
   172  		cancel()
   173  		return nil, errors.Wrap(err, "could not setup deposit trie")
   174  	}
   175  	genState, err := state.EmptyGenesisState()
   176  	if err != nil {
   177  		return nil, errors.Wrap(err, "could not setup genesis state")
   178  	}
   179  
   180  	if config.Eth1HeaderReqLimit == 0 {
   181  		config.Eth1HeaderReqLimit = defaultEth1HeaderReqLimit
   182  	}
   183  
   184  	stringEndpoints := dedupEndpoints(config.HttpEndpoints)
   185  	endpoints := make([]httputils.Endpoint, len(stringEndpoints))
   186  	for i, e := range stringEndpoints {
   187  		endpoints[i] = HttpEndpoint(e)
   188  	}
   189  
   190  	// Select first http endpoint in the provided list.
   191  	var currEndpoint httputils.Endpoint
   192  	if len(config.HttpEndpoints) > 0 {
   193  		currEndpoint = endpoints[0]
   194  	}
   195  	s := &Service{
   196  		ctx:              ctx,
   197  		cancel:           cancel,
   198  		cfg:              config,
   199  		httpEndpoints:    endpoints,
   200  		currHttpEndpoint: currEndpoint,
   201  		latestEth1Data: &protodb.LatestETH1Data{
   202  			BlockHeight:        0,
   203  			BlockTime:          0,
   204  			BlockHash:          []byte{},
   205  			LastRequestedBlock: 0,
   206  		},
   207  		headerCache: newHeaderCache(),
   208  		depositTrie: depositTrie,
   209  		chainStartData: &protodb.ChainStartData{
   210  			Eth1Data:           &ethpb.Eth1Data{},
   211  			ChainstartDeposits: make([]*ethpb.Deposit, 0),
   212  		},
   213  		lastReceivedMerkleIndex: -1,
   214  		preGenesisState:         genState,
   215  		headTicker:              time.NewTicker(time.Duration(params.BeaconConfig().SecondsPerETH1Block) * time.Second),
   216  		// use the nop updater by default, rely on upstream set up to pass in an appropriate impl
   217  		bsUpdater: config.BeaconNodeStatsUpdater,
   218  	}
   219  
   220  	if config.BeaconNodeStatsUpdater == nil {
   221  		s.bsUpdater = &NopBeaconNodeStatsUpdater{}
   222  	}
   223  
   224  	if err := s.ensureValidPowchainData(ctx); err != nil {
   225  		return nil, errors.Wrap(err, "unable to validate powchain data")
   226  	}
   227  
   228  	eth1Data, err := config.BeaconDB.PowchainData(ctx)
   229  	if err != nil {
   230  		return nil, errors.Wrap(err, "unable to retrieve eth1 data")
   231  	}
   232  	if err := s.initializeEth1Data(ctx, eth1Data); err != nil {
   233  		return nil, err
   234  	}
   235  
   236  	return s, nil
   237  }
   238  
   239  // Start a web3 service's main event loop.
   240  func (s *Service) Start() {
   241  	// If the chain has not started already and we don't have access to eth1 nodes, we will not be
   242  	// able to generate the genesis state.
   243  	if !s.chainStartData.Chainstarted && s.currHttpEndpoint.Url == "" {
   244  		// check for genesis state before shutting down the node,
   245  		// if a genesis state exists, we can continue on.
   246  		genState, err := s.cfg.BeaconDB.GenesisState(s.ctx)
   247  		if err != nil {
   248  			log.Fatal(err)
   249  		}
   250  		if genState == nil || genState.IsNil() {
   251  			log.Fatal("cannot create genesis state: no eth1 http endpoint defined")
   252  		}
   253  	}
   254  
   255  	// Exit early if eth1 endpoint is not set.
   256  	if s.currHttpEndpoint.Url == "" {
   257  		return
   258  	}
   259  	go func() {
   260  		s.isRunning = true
   261  		s.waitForConnection()
   262  		if s.ctx.Err() != nil {
   263  			log.Info("Context closed, exiting pow goroutine")
   264  			return
   265  		}
   266  		s.run(s.ctx.Done())
   267  	}()
   268  }
   269  
   270  // Stop the web3 service's main event loop and associated goroutines.
   271  func (s *Service) Stop() error {
   272  	if s.cancel != nil {
   273  		defer s.cancel()
   274  	}
   275  	s.closeClients()
   276  	return nil
   277  }
   278  
   279  // ChainStartDeposits returns a slice of validator deposit data processed
   280  // by the deposit contract and cached in the powchain service.
   281  func (s *Service) ChainStartDeposits() []*ethpb.Deposit {
   282  	return s.chainStartData.ChainstartDeposits
   283  }
   284  
   285  // ClearPreGenesisData clears out the stored chainstart deposits and beacon state.
   286  func (s *Service) ClearPreGenesisData() {
   287  	s.chainStartData.ChainstartDeposits = []*ethpb.Deposit{}
   288  	s.preGenesisState = &v1.BeaconState{}
   289  }
   290  
   291  // ChainStartEth1Data returns the eth1 data at chainstart.
   292  func (s *Service) ChainStartEth1Data() *ethpb.Eth1Data {
   293  	return s.chainStartData.Eth1Data
   294  }
   295  
   296  // PreGenesisState returns a state that contains
   297  // pre-chainstart deposits.
   298  func (s *Service) PreGenesisState() iface.BeaconState {
   299  	return s.preGenesisState
   300  }
   301  
   302  // Status is service health checks. Return nil or error.
   303  func (s *Service) Status() error {
   304  	// Service don't start
   305  	if !s.isRunning {
   306  		return nil
   307  	}
   308  	// get error from run function
   309  	if s.runError != nil {
   310  		return s.runError
   311  	}
   312  	return nil
   313  }
   314  
   315  func (s *Service) updateBeaconNodeStats() {
   316  	bs := clientstats.BeaconNodeStats{}
   317  	if len(s.httpEndpoints) > 1 {
   318  		bs.SyncEth1FallbackConfigured = true
   319  	}
   320  	if s.IsConnectedToETH1() {
   321  		if s.primaryConnected() {
   322  			bs.SyncEth1Connected = true
   323  		} else {
   324  			bs.SyncEth1FallbackConnected = true
   325  		}
   326  	}
   327  	s.bsUpdater.Update(bs)
   328  }
   329  
   330  func (s *Service) updateCurrHttpEndpoint(endpoint httputils.Endpoint) {
   331  	s.currHttpEndpoint = endpoint
   332  	s.updateBeaconNodeStats()
   333  }
   334  
   335  func (s *Service) updateConnectedETH1(state bool) {
   336  	s.connectedETH1 = state
   337  	s.updateBeaconNodeStats()
   338  }
   339  
   340  // IsConnectedToETH1 checks if the beacon node is connected to a ETH1 Node.
   341  func (s *Service) IsConnectedToETH1() bool {
   342  	return s.connectedETH1
   343  }
   344  
   345  // DepositRoot returns the Merkle root of the latest deposit trie
   346  // from the ETH1.0 deposit contract.
   347  func (s *Service) DepositRoot() [32]byte {
   348  	return s.depositTrie.Root()
   349  }
   350  
   351  // DepositTrie returns the sparse Merkle trie used for storing
   352  // deposits from the ETH1.0 deposit contract.
   353  func (s *Service) DepositTrie() *trieutil.SparseMerkleTrie {
   354  	return s.depositTrie
   355  }
   356  
   357  // LatestBlockHeight in the ETH1.0 chain.
   358  func (s *Service) LatestBlockHeight() *big.Int {
   359  	return big.NewInt(int64(s.latestEth1Data.BlockHeight))
   360  }
   361  
   362  // LatestBlockHash in the ETH1.0 chain.
   363  func (s *Service) LatestBlockHash() common.Hash {
   364  	return bytesutil.ToBytes32(s.latestEth1Data.BlockHash)
   365  }
   366  
   367  // AreAllDepositsProcessed determines if all the logs from the deposit contract
   368  // are processed.
   369  func (s *Service) AreAllDepositsProcessed() (bool, error) {
   370  	s.processingLock.RLock()
   371  	defer s.processingLock.RUnlock()
   372  	countByte, err := s.depositContractCaller.GetDepositCount(&bind.CallOpts{})
   373  	if err != nil {
   374  		return false, errors.Wrap(err, "could not get deposit count")
   375  	}
   376  	count := bytesutil.FromBytes8(countByte)
   377  	deposits := s.cfg.DepositCache.AllDeposits(s.ctx, nil)
   378  	if count != uint64(len(deposits)) {
   379  		return false, nil
   380  	}
   381  	return true, nil
   382  }
   383  
   384  // refers to the latest eth1 block which follows the condition: eth1_timestamp +
   385  // SECONDS_PER_ETH1_BLOCK * ETH1_FOLLOW_DISTANCE <= current_unix_time
   386  func (s *Service) followBlockHeight(ctx context.Context) (uint64, error) {
   387  	latestValidBlock := uint64(0)
   388  	if s.latestEth1Data.BlockHeight > params.BeaconConfig().Eth1FollowDistance {
   389  		latestValidBlock = s.latestEth1Data.BlockHeight - params.BeaconConfig().Eth1FollowDistance
   390  	}
   391  	return latestValidBlock, nil
   392  }
   393  
   394  func (s *Service) connectToPowChain() error {
   395  	httpClient, rpcClient, err := s.dialETH1Nodes(s.currHttpEndpoint)
   396  	if err != nil {
   397  		return errors.Wrap(err, "could not dial eth1 nodes")
   398  	}
   399  
   400  	depositContractCaller, err := contracts.NewDepositContractCaller(s.cfg.DepositContract, httpClient)
   401  	if err != nil {
   402  		return errors.Wrap(err, "could not create deposit contract caller")
   403  	}
   404  
   405  	if httpClient == nil || rpcClient == nil || depositContractCaller == nil {
   406  		return errors.New("eth1 client is nil")
   407  	}
   408  
   409  	s.initializeConnection(httpClient, rpcClient, depositContractCaller)
   410  	return nil
   411  }
   412  
   413  func (s *Service) dialETH1Nodes(endpoint httputils.Endpoint) (*ethclient.Client, *gethRPC.Client, error) {
   414  	httpRPCClient, err := gethRPC.Dial(endpoint.Url)
   415  	if err != nil {
   416  		return nil, nil, err
   417  	}
   418  	if endpoint.Auth.Method != authorizationmethod.None {
   419  		header, err := endpoint.Auth.ToHeaderValue()
   420  		if err != nil {
   421  			return nil, nil, err
   422  		}
   423  		httpRPCClient.SetHeader("Authorization", header)
   424  	}
   425  	httpClient := ethclient.NewClient(httpRPCClient)
   426  	// Add a method to clean-up and close clients in the event
   427  	// of any connection failure.
   428  	closeClients := func() {
   429  		httpRPCClient.Close()
   430  		httpClient.Close()
   431  	}
   432  	syncProg, err := httpClient.SyncProgress(s.ctx)
   433  	if err != nil {
   434  		closeClients()
   435  		return nil, nil, err
   436  	}
   437  	if syncProg != nil {
   438  		closeClients()
   439  		return nil, nil, errors.New("eth1 node has not finished syncing yet")
   440  	}
   441  	// Make a simple call to ensure we are actually connected to a working node.
   442  	cID, err := httpClient.ChainID(s.ctx)
   443  	if err != nil {
   444  		closeClients()
   445  		return nil, nil, err
   446  	}
   447  	nID, err := httpClient.NetworkID(s.ctx)
   448  	if err != nil {
   449  		closeClients()
   450  		return nil, nil, err
   451  	}
   452  	if cID.Uint64() != params.BeaconConfig().DepositChainID {
   453  		closeClients()
   454  		return nil, nil, fmt.Errorf("eth1 node using incorrect chain id, %d != %d", cID.Uint64(), params.BeaconConfig().DepositChainID)
   455  	}
   456  	if nID.Uint64() != params.BeaconConfig().DepositNetworkID {
   457  		closeClients()
   458  		return nil, nil, fmt.Errorf("eth1 node using incorrect network id, %d != %d", nID.Uint64(), params.BeaconConfig().DepositNetworkID)
   459  	}
   460  
   461  	return httpClient, httpRPCClient, nil
   462  }
   463  
   464  func (s *Service) initializeConnection(
   465  	httpClient *ethclient.Client,
   466  	rpcClient *gethRPC.Client,
   467  	contractCaller *contracts.DepositContractCaller,
   468  ) {
   469  	s.httpLogger = httpClient
   470  	s.eth1DataFetcher = httpClient
   471  	s.depositContractCaller = contractCaller
   472  	s.rpcClient = rpcClient
   473  }
   474  
   475  // closes down our active eth1 clients.
   476  func (s *Service) closeClients() {
   477  	gethClient, ok := s.rpcClient.(*gethRPC.Client)
   478  	if ok {
   479  		gethClient.Close()
   480  	}
   481  	httpClient, ok := s.eth1DataFetcher.(*ethclient.Client)
   482  	if ok {
   483  		httpClient.Close()
   484  	}
   485  }
   486  
   487  func (s *Service) waitForConnection() {
   488  	errConnect := s.connectToPowChain()
   489  	if errConnect == nil {
   490  		synced, errSynced := s.isEth1NodeSynced()
   491  		// Resume if eth1 node is synced.
   492  		if synced {
   493  			s.updateConnectedETH1(true)
   494  			s.runError = nil
   495  			log.WithFields(logrus.Fields{
   496  				"endpoint": logutil.MaskCredentialsLogging(s.currHttpEndpoint.Url),
   497  			}).Info("Connected to eth1 proof-of-work chain")
   498  			return
   499  		}
   500  		if errSynced != nil {
   501  			s.runError = errSynced
   502  			log.WithError(errSynced).Error("Could not check sync status of eth1 chain")
   503  		}
   504  	}
   505  	if errConnect != nil {
   506  		s.runError = errConnect
   507  		log.WithError(errConnect).Error("Could not connect to powchain endpoint")
   508  	}
   509  	// Use a custom logger to only log errors
   510  	// once in  a while.
   511  	logCounter := 0
   512  	errorLogger := func(err error, msg string) {
   513  		if logCounter > logThreshold {
   514  			log.Errorf("%s: %v", msg, err)
   515  			logCounter = 0
   516  		}
   517  		logCounter++
   518  	}
   519  
   520  	ticker := time.NewTicker(backOffPeriod)
   521  	defer ticker.Stop()
   522  	for {
   523  		select {
   524  		case <-ticker.C:
   525  			log.Debugf("Trying to dial endpoint: %s", logutil.MaskCredentialsLogging(s.currHttpEndpoint.Url))
   526  			errConnect := s.connectToPowChain()
   527  			if errConnect != nil {
   528  				errorLogger(errConnect, "Could not connect to powchain endpoint")
   529  				s.runError = errConnect
   530  				s.fallbackToNextEndpoint()
   531  				continue
   532  			}
   533  			synced, errSynced := s.isEth1NodeSynced()
   534  			if errSynced != nil {
   535  				errorLogger(errSynced, "Could not check sync status of eth1 chain")
   536  				s.runError = errSynced
   537  				s.fallbackToNextEndpoint()
   538  				continue
   539  			}
   540  			if synced {
   541  				s.updateConnectedETH1(true)
   542  				s.runError = nil
   543  				log.WithFields(logrus.Fields{
   544  					"endpoint": logutil.MaskCredentialsLogging(s.currHttpEndpoint.Url),
   545  				}).Info("Connected to eth1 proof-of-work chain")
   546  				return
   547  			}
   548  			s.runError = errNotSynced
   549  			log.Debug("Eth1 node is currently syncing")
   550  		case <-s.ctx.Done():
   551  			log.Debug("Received cancelled context,closing existing powchain service")
   552  			return
   553  		}
   554  	}
   555  }
   556  
   557  // checks if the eth1 node is healthy and ready to serve before
   558  // fetching data from  it.
   559  func (s *Service) isEth1NodeSynced() (bool, error) {
   560  	syncProg, err := s.eth1DataFetcher.SyncProgress(s.ctx)
   561  	if err != nil {
   562  		return false, err
   563  	}
   564  	if syncProg != nil {
   565  		return false, nil
   566  	}
   567  	head, err := s.eth1DataFetcher.HeaderByNumber(s.ctx, nil)
   568  	if err != nil {
   569  		return false, err
   570  	}
   571  	return !eth1HeadIsBehind(head.Time), nil
   572  }
   573  
   574  // Reconnect to eth1 node in case of any failure.
   575  func (s *Service) retryETH1Node(err error) {
   576  	s.runError = err
   577  	s.updateConnectedETH1(false)
   578  	// Back off for a while before
   579  	// resuming dialing the eth1 node.
   580  	time.Sleep(backOffPeriod)
   581  	s.waitForConnection()
   582  	// Reset run error in the event of a successful connection.
   583  	s.runError = nil
   584  }
   585  
   586  func (s *Service) initDepositCaches(ctx context.Context, ctrs []*protodb.DepositContainer) error {
   587  	if len(ctrs) == 0 {
   588  		return nil
   589  	}
   590  	s.cfg.DepositCache.InsertDepositContainers(ctx, ctrs)
   591  	if !s.chainStartData.Chainstarted {
   592  		// do not add to pending cache
   593  		// if no genesis state exists.
   594  		validDepositsCount.Add(float64(s.preGenesisState.Eth1DepositIndex()))
   595  		return nil
   596  	}
   597  	genesisState, err := s.cfg.BeaconDB.GenesisState(ctx)
   598  	if err != nil {
   599  		return err
   600  	}
   601  	// Default to all deposits post-genesis deposits in
   602  	// the event we cannot find a finalized state.
   603  	currIndex := genesisState.Eth1DepositIndex()
   604  	chkPt, err := s.cfg.BeaconDB.FinalizedCheckpoint(ctx)
   605  	if err != nil {
   606  		return err
   607  	}
   608  	rt := bytesutil.ToBytes32(chkPt.Root)
   609  	if rt != [32]byte{} {
   610  		fState, err := s.cfg.StateGen.StateByRoot(ctx, rt)
   611  		if err != nil {
   612  			return errors.Wrap(err, "could not get finalized state")
   613  		}
   614  		if fState == nil || fState.IsNil() {
   615  			return errors.Errorf("finalized state with root %#x does not exist in the db", rt)
   616  		}
   617  		// Set deposit index to the one in the current archived state.
   618  		currIndex = fState.Eth1DepositIndex()
   619  	}
   620  	validDepositsCount.Add(float64(currIndex))
   621  	// Only add pending deposits if the container slice length
   622  	// is more than the current index in state.
   623  	if uint64(len(ctrs)) > currIndex {
   624  		for _, c := range ctrs[currIndex:] {
   625  			s.cfg.DepositCache.InsertPendingDeposit(ctx, c.Deposit, c.Eth1BlockHeight, c.Index, bytesutil.ToBytes32(c.DepositRoot))
   626  		}
   627  	}
   628  	return nil
   629  }
   630  
   631  // processBlockHeader adds a newly observed eth1 block to the block cache and
   632  // updates the latest blockHeight, blockHash, and blockTime properties of the service.
   633  func (s *Service) processBlockHeader(header *gethTypes.Header) {
   634  	defer safelyHandlePanic()
   635  	blockNumberGauge.Set(float64(header.Number.Int64()))
   636  	s.latestEth1Data.BlockHeight = header.Number.Uint64()
   637  	s.latestEth1Data.BlockHash = header.Hash().Bytes()
   638  	s.latestEth1Data.BlockTime = header.Time
   639  	log.WithFields(logrus.Fields{
   640  		"blockNumber": s.latestEth1Data.BlockHeight,
   641  		"blockHash":   hexutil.Encode(s.latestEth1Data.BlockHash),
   642  	}).Debug("Latest eth1 chain event")
   643  }
   644  
   645  // batchRequestHeaders requests the block range specified in the arguments. Instead of requesting
   646  // each block in one call, it batches all requests into a single rpc call.
   647  func (s *Service) batchRequestHeaders(startBlock, endBlock uint64) ([]*gethTypes.Header, error) {
   648  	if startBlock > endBlock {
   649  		return nil, fmt.Errorf("start block height %d cannot be > end block height %d", startBlock, endBlock)
   650  	}
   651  	requestRange := (endBlock - startBlock) + 1
   652  	elems := make([]gethRPC.BatchElem, 0, requestRange)
   653  	headers := make([]*gethTypes.Header, 0, requestRange)
   654  	errs := make([]error, 0, requestRange)
   655  	if requestRange == 0 {
   656  		return headers, nil
   657  	}
   658  	for i := startBlock; i <= endBlock; i++ {
   659  		header := &gethTypes.Header{}
   660  		err := error(nil)
   661  		elems = append(elems, gethRPC.BatchElem{
   662  			Method: "eth_getBlockByNumber",
   663  			Args:   []interface{}{hexutil.EncodeBig(big.NewInt(int64(i))), false},
   664  			Result: header,
   665  			Error:  err,
   666  		})
   667  		headers = append(headers, header)
   668  		errs = append(errs, err)
   669  	}
   670  	ioErr := s.rpcClient.BatchCall(elems)
   671  	if ioErr != nil {
   672  		return nil, ioErr
   673  	}
   674  	for _, e := range errs {
   675  		if e != nil {
   676  			return nil, e
   677  		}
   678  	}
   679  	for _, h := range headers {
   680  		if h != nil {
   681  			if err := s.headerCache.AddHeader(h); err != nil {
   682  				return nil, err
   683  			}
   684  		}
   685  	}
   686  	return headers, nil
   687  }
   688  
   689  // safelyHandleHeader will recover and log any panic that occurs from the
   690  // block
   691  func safelyHandlePanic() {
   692  	if r := recover(); r != nil {
   693  		log.WithFields(logrus.Fields{
   694  			"r": r,
   695  		}).Error("Panicked when handling data from ETH 1.0 Chain! Recovering...")
   696  
   697  		debug.PrintStack()
   698  	}
   699  }
   700  
   701  func (s *Service) handleETH1FollowDistance() {
   702  	defer safelyHandlePanic()
   703  	ctx := s.ctx
   704  
   705  	// use a 5 minutes timeout for block time, because the max mining time is 278 sec (block 7208027)
   706  	// (analyzed the time of the block from 2018-09-01 to 2019-02-13)
   707  	fiveMinutesTimeout := timeutils.Now().Add(-5 * time.Minute)
   708  	// check that web3 client is syncing
   709  	if time.Unix(int64(s.latestEth1Data.BlockTime), 0).Before(fiveMinutesTimeout) {
   710  		log.Warn("eth1 client is not syncing")
   711  	}
   712  	if !s.chainStartData.Chainstarted {
   713  		if err := s.checkBlockNumberForChainStart(ctx, big.NewInt(int64(s.latestEth1Data.LastRequestedBlock))); err != nil {
   714  			s.runError = err
   715  			log.Error(err)
   716  			return
   717  		}
   718  	}
   719  	// If the last requested block has not changed,
   720  	// we do not request batched logs as this means there are no new
   721  	// logs for the powchain service to process. Also is a potential
   722  	// failure condition as would mean we have not respected the protocol
   723  	// threshold.
   724  	if s.latestEth1Data.LastRequestedBlock == s.latestEth1Data.BlockHeight {
   725  		log.Error("Beacon node is not respecting the follow distance")
   726  		return
   727  	}
   728  	if err := s.requestBatchedHeadersAndLogs(ctx); err != nil {
   729  		s.runError = err
   730  		log.Error(err)
   731  		return
   732  	}
   733  	// Reset the Status.
   734  	if s.runError != nil {
   735  		s.runError = nil
   736  	}
   737  }
   738  
   739  func (s *Service) initPOWService() {
   740  
   741  	// Run in a select loop to retry in the event of any failures.
   742  	for {
   743  		select {
   744  		case <-s.ctx.Done():
   745  			return
   746  		default:
   747  			ctx := s.ctx
   748  			header, err := s.eth1DataFetcher.HeaderByNumber(ctx, nil)
   749  			if err != nil {
   750  				log.Errorf("Unable to retrieve latest ETH1.0 chain header: %v", err)
   751  				s.retryETH1Node(err)
   752  				continue
   753  			}
   754  
   755  			s.latestEth1Data.BlockHeight = header.Number.Uint64()
   756  			s.latestEth1Data.BlockHash = header.Hash().Bytes()
   757  			s.latestEth1Data.BlockTime = header.Time
   758  
   759  			if err := s.processPastLogs(ctx); err != nil {
   760  				log.Errorf("Unable to process past logs %v", err)
   761  				s.retryETH1Node(err)
   762  				continue
   763  			}
   764  			// Cache eth1 headers from our voting period.
   765  			if err := s.cacheHeadersForEth1DataVote(ctx); err != nil {
   766  				log.Errorf("Unable to process past headers %v", err)
   767  				s.retryETH1Node(err)
   768  				continue
   769  			}
   770  			// Handle edge case with embedded genesis state by fetching genesis header to determine
   771  			// its height.
   772  			if s.chainStartData.Chainstarted && s.chainStartData.GenesisBlock == 0 {
   773  				genHeader, err := s.eth1DataFetcher.HeaderByHash(ctx, common.BytesToHash(s.chainStartData.Eth1Data.BlockHash))
   774  				if err != nil {
   775  					log.Errorf("Unable to retrieve genesis ETH1.0 chain header: %v", err)
   776  					s.retryETH1Node(err)
   777  					continue
   778  				}
   779  				s.chainStartData.GenesisBlock = genHeader.Number.Uint64()
   780  				if err := s.savePowchainData(ctx); err != nil {
   781  					log.Errorf("Unable to save powchain data: %v", err)
   782  				}
   783  			}
   784  			return
   785  		}
   786  	}
   787  }
   788  
   789  // run subscribes to all the services for the ETH1.0 chain.
   790  func (s *Service) run(done <-chan struct{}) {
   791  	s.runError = nil
   792  
   793  	s.initPOWService()
   794  
   795  	chainstartTicker := time.NewTicker(logPeriod)
   796  	defer chainstartTicker.Stop()
   797  
   798  	for {
   799  		select {
   800  		case <-done:
   801  			s.isRunning = false
   802  			s.runError = nil
   803  			s.updateConnectedETH1(false)
   804  			log.Debug("Context closed, exiting goroutine")
   805  			return
   806  		case <-s.headTicker.C:
   807  			head, err := s.eth1DataFetcher.HeaderByNumber(s.ctx, nil)
   808  			if err != nil {
   809  				log.WithError(err).Debug("Could not fetch latest eth1 header")
   810  				s.retryETH1Node(err)
   811  				continue
   812  			}
   813  			if eth1HeadIsBehind(head.Time) {
   814  				log.WithError(errFarBehind).Debug("Could not get an up to date eth1 header")
   815  				s.retryETH1Node(errFarBehind)
   816  				continue
   817  			}
   818  			s.processBlockHeader(head)
   819  			s.handleETH1FollowDistance()
   820  			s.checkDefaultEndpoint()
   821  		case <-chainstartTicker.C:
   822  			if s.chainStartData.Chainstarted {
   823  				chainstartTicker.Stop()
   824  				continue
   825  			}
   826  			s.logTillChainStart()
   827  		}
   828  	}
   829  }
   830  
   831  // logs the current thresholds required to hit chainstart every minute.
   832  func (s *Service) logTillChainStart() {
   833  	if s.chainStartData.Chainstarted {
   834  		return
   835  	}
   836  	_, blockTime, err := s.retrieveBlockHashAndTime(s.ctx, big.NewInt(int64(s.latestEth1Data.LastRequestedBlock)))
   837  	if err != nil {
   838  		log.Error(err)
   839  		return
   840  	}
   841  	valCount, genesisTime := s.currentCountAndTime(blockTime)
   842  	valNeeded := uint64(0)
   843  	if valCount < params.BeaconConfig().MinGenesisActiveValidatorCount {
   844  		valNeeded = params.BeaconConfig().MinGenesisActiveValidatorCount - valCount
   845  	}
   846  	secondsLeft := uint64(0)
   847  	if genesisTime < params.BeaconConfig().MinGenesisTime {
   848  		secondsLeft = params.BeaconConfig().MinGenesisTime - genesisTime
   849  	}
   850  
   851  	fields := logrus.Fields{
   852  		"Additional validators needed": valNeeded,
   853  	}
   854  	if secondsLeft > 0 {
   855  		fields["Generating genesis state in"] = time.Duration(secondsLeft) * time.Second
   856  	}
   857  
   858  	log.WithFields(fields).Info("Currently waiting for chainstart")
   859  }
   860  
   861  // cacheHeadersForEth1DataVote makes sure that voting for eth1data after startup utilizes cached headers
   862  // instead of making multiple RPC requests to the ETH1 endpoint.
   863  func (s *Service) cacheHeadersForEth1DataVote(ctx context.Context) error {
   864  	// Find the end block to request from.
   865  	end, err := s.followBlockHeight(ctx)
   866  	if err != nil {
   867  		return err
   868  	}
   869  	start, err := s.determineEarliestVotingBlock(ctx, end)
   870  	if err != nil {
   871  		return err
   872  	}
   873  	// We call batchRequestHeaders for its header caching side-effect, so we don't need the return value.
   874  	_, err = s.batchRequestHeaders(start, end)
   875  	if err != nil {
   876  		return err
   877  	}
   878  	return nil
   879  }
   880  
   881  // determines the earliest voting block from which to start caching all our previous headers from.
   882  func (s *Service) determineEarliestVotingBlock(ctx context.Context, followBlock uint64) (uint64, error) {
   883  	genesisTime := s.chainStartData.GenesisTime
   884  	currSlot := helpers.CurrentSlot(genesisTime)
   885  
   886  	// In the event genesis has not occurred yet, we just request go back follow_distance blocks.
   887  	if genesisTime == 0 || currSlot == 0 {
   888  		earliestBlk := uint64(0)
   889  		if followBlock > params.BeaconConfig().Eth1FollowDistance {
   890  			earliestBlk = followBlock - params.BeaconConfig().Eth1FollowDistance
   891  		}
   892  		return earliestBlk, nil
   893  	}
   894  	votingTime := helpers.VotingPeriodStartTime(genesisTime, currSlot)
   895  	followBackDist := 2 * params.BeaconConfig().SecondsPerETH1Block * params.BeaconConfig().Eth1FollowDistance
   896  	if followBackDist > votingTime {
   897  		return 0, errors.Errorf("invalid genesis time provided. %d > %d", followBackDist, votingTime)
   898  	}
   899  	earliestValidTime := votingTime - followBackDist
   900  	hdr, err := s.BlockByTimestamp(ctx, earliestValidTime)
   901  	if err != nil {
   902  		return 0, err
   903  	}
   904  	return hdr.Number.Uint64(), nil
   905  }
   906  
   907  // This performs a health check on our primary endpoint, and if it
   908  // is ready to serve we connect to it again. This method is only
   909  // relevant if we are on our backup endpoint.
   910  func (s *Service) checkDefaultEndpoint() {
   911  	primaryEndpoint := s.httpEndpoints[0]
   912  	// Return early if we are running on our primary
   913  	// endpoint.
   914  	if s.currHttpEndpoint.Equals(primaryEndpoint) {
   915  		return
   916  	}
   917  
   918  	httpClient, rpcClient, err := s.dialETH1Nodes(primaryEndpoint)
   919  	if err != nil {
   920  		log.Debugf("Primary endpoint not ready: %v", err)
   921  		return
   922  	}
   923  	log.Info("Primary endpoint ready again, switching back to it")
   924  	// Close the clients and let our main connection routine
   925  	// properly connect with it.
   926  	httpClient.Close()
   927  	rpcClient.Close()
   928  	// Close current active clients.
   929  	s.closeClients()
   930  
   931  	// Switch back to primary endpoint and try connecting
   932  	// to it again.
   933  	s.updateCurrHttpEndpoint(primaryEndpoint)
   934  	s.retryETH1Node(nil)
   935  }
   936  
   937  // This is an inefficient way to search for the next endpoint, but given N is expected to be
   938  // small ( < 25), it is fine to search this way.
   939  func (s *Service) fallbackToNextEndpoint() {
   940  	currEndpoint := s.currHttpEndpoint
   941  	currIndex := 0
   942  	totalEndpoints := len(s.httpEndpoints)
   943  
   944  	for i, endpoint := range s.httpEndpoints {
   945  		if endpoint.Equals(currEndpoint) {
   946  			currIndex = i
   947  			break
   948  		}
   949  	}
   950  	nextIndex := currIndex + 1
   951  	if nextIndex >= totalEndpoints {
   952  		nextIndex = 0
   953  	}
   954  	if nextIndex != currIndex {
   955  		log.Infof("Falling back to alternative endpoint: %s", logutil.MaskCredentialsLogging(s.currHttpEndpoint.Url))
   956  	}
   957  	s.updateCurrHttpEndpoint(s.httpEndpoints[nextIndex])
   958  }
   959  
   960  // initializes our service from the provided eth1data object by initializing all the relevant
   961  // fields and data.
   962  func (s *Service) initializeEth1Data(ctx context.Context, eth1DataInDB *protodb.ETH1ChainData) error {
   963  	// The node has no eth1data persisted on disk, so we exit and instead
   964  	// request from contract logs.
   965  	if eth1DataInDB == nil {
   966  		return nil
   967  	}
   968  	s.depositTrie = trieutil.CreateTrieFromProto(eth1DataInDB.Trie)
   969  	s.chainStartData = eth1DataInDB.ChainstartData
   970  	var err error
   971  	if !reflect.ValueOf(eth1DataInDB.BeaconState).IsZero() {
   972  		s.preGenesisState, err = v1.InitializeFromProto(eth1DataInDB.BeaconState)
   973  		if err != nil {
   974  			return errors.Wrap(err, "Could not initialize state trie")
   975  		}
   976  	}
   977  	s.latestEth1Data = eth1DataInDB.CurrentEth1Data
   978  	numOfItems := s.depositTrie.NumOfItems()
   979  	s.lastReceivedMerkleIndex = int64(numOfItems - 1)
   980  	if err := s.initDepositCaches(ctx, eth1DataInDB.DepositContainers); err != nil {
   981  		return errors.Wrap(err, "could not initialize caches")
   982  	}
   983  	return nil
   984  }
   985  
   986  // validates that all deposit containers are valid and have their relevant indices
   987  // in order.
   988  func (s *Service) validateDepositContainers(ctrs []*protodb.DepositContainer) bool {
   989  	ctrLen := len(ctrs)
   990  	// Exit for empty containers.
   991  	if ctrLen == 0 {
   992  		return true
   993  	}
   994  	// Sort deposits in ascending order.
   995  	sort.Slice(ctrs, func(i, j int) bool {
   996  		return ctrs[i].Index < ctrs[j].Index
   997  	})
   998  	startIndex := int64(0)
   999  	for _, c := range ctrs {
  1000  		if c.Index != startIndex {
  1001  			log.Info("Recovering missing deposit containers, node is re-requesting missing deposit data")
  1002  			return false
  1003  		}
  1004  		startIndex++
  1005  	}
  1006  	return true
  1007  }
  1008  
  1009  // validates the current powchain data saved and makes sure that any
  1010  // embedded genesis state is correctly accounted for.
  1011  func (s *Service) ensureValidPowchainData(ctx context.Context) error {
  1012  	genState, err := s.cfg.BeaconDB.GenesisState(ctx)
  1013  	if err != nil {
  1014  		return err
  1015  	}
  1016  	// Exit early if no genesis state is saved.
  1017  	if genState == nil || genState.IsNil() {
  1018  		return nil
  1019  	}
  1020  	eth1Data, err := s.cfg.BeaconDB.PowchainData(ctx)
  1021  	if err != nil {
  1022  		return errors.Wrap(err, "unable to retrieve eth1 data")
  1023  	}
  1024  	if eth1Data == nil || !eth1Data.ChainstartData.Chainstarted || !s.validateDepositContainers(eth1Data.DepositContainers) {
  1025  		pbState, err := v1.ProtobufBeaconState(s.preGenesisState.InnerStateUnsafe())
  1026  		if err != nil {
  1027  			return err
  1028  		}
  1029  		s.chainStartData = &protodb.ChainStartData{
  1030  			Chainstarted:       true,
  1031  			GenesisTime:        genState.GenesisTime(),
  1032  			GenesisBlock:       0,
  1033  			Eth1Data:           genState.Eth1Data(),
  1034  			ChainstartDeposits: make([]*ethpb.Deposit, 0),
  1035  		}
  1036  		eth1Data = &protodb.ETH1ChainData{
  1037  			CurrentEth1Data:   s.latestEth1Data,
  1038  			ChainstartData:    s.chainStartData,
  1039  			BeaconState:       pbState,
  1040  			Trie:              s.depositTrie.ToProto(),
  1041  			DepositContainers: s.cfg.DepositCache.AllDepositContainers(ctx),
  1042  		}
  1043  		return s.cfg.BeaconDB.SavePowchainData(ctx, eth1Data)
  1044  	}
  1045  	return nil
  1046  }
  1047  
  1048  func dedupEndpoints(endpoints []string) []string {
  1049  	selectionMap := make(map[string]bool)
  1050  	newEndpoints := make([]string, 0, len(endpoints))
  1051  	for _, point := range endpoints {
  1052  		if selectionMap[point] {
  1053  			continue
  1054  		}
  1055  		newEndpoints = append(newEndpoints, point)
  1056  		selectionMap[point] = true
  1057  	}
  1058  	return newEndpoints
  1059  }
  1060  
  1061  // Checks if the provided timestamp is beyond the prescribed bound from
  1062  // the current wall clock time.
  1063  func eth1HeadIsBehind(timestamp uint64) bool {
  1064  	timeout := timeutils.Now().Add(-eth1Threshold)
  1065  	// check that web3 client is syncing
  1066  	return time.Unix(int64(timestamp), 0).Before(timeout)
  1067  }
  1068  
  1069  func (s *Service) primaryConnected() bool {
  1070  	return s.currHttpEndpoint.Equals(s.httpEndpoints[0])
  1071  }