From ef9f6c5b4d9588048c36adb06930bdb2af872b54 Mon Sep 17 00:00:00 2001 From: Nishant Das Date: Sun, 25 Apr 2021 13:11:15 +0800 Subject: [PATCH] Initialize Data Correctly For Powchain Service (#8812) * initialize index correctly * comments * review comments * Update beacon-chain/powchain/service.go Co-authored-by: Raul Jordan --- beacon-chain/powchain/service.go | 66 +++++++++++++++++------ beacon-chain/powchain/service_test.go | 77 +++++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 15 deletions(-) diff --git a/beacon-chain/powchain/service.go b/beacon-chain/powchain/service.go index ab21f235741d..e37b3700d9d2 100644 --- a/beacon-chain/powchain/service.go +++ b/beacon-chain/powchain/service.go @@ -4,6 +4,7 @@ package powchain import ( + "bytes" "context" "fmt" "math/big" @@ -220,20 +221,8 @@ func NewService(ctx context.Context, config *Web3ServiceConfig) (*Service, error if err != nil { return nil, errors.Wrap(err, "unable to retrieve eth1 data") } - if eth1Data != nil { - s.depositTrie = trieutil.CreateTrieFromProto(eth1Data.Trie) - s.chainStartData = eth1Data.ChainstartData - if !reflect.ValueOf(eth1Data.BeaconState).IsZero() { - s.preGenesisState, err = stateV0.InitializeFromProto(eth1Data.BeaconState) - if err != nil { - return nil, errors.Wrap(err, "Could not initialize state trie") - } - } - s.latestEth1Data = eth1Data.CurrentEth1Data - s.lastReceivedMerkleIndex = int64(len(s.depositTrie.Items()) - 1) - if err := s.initDepositCaches(ctx, eth1Data.DepositContainers); err != nil { - return nil, errors.Wrap(err, "could not initialize caches") - } + if err := s.initializeEth1Data(ctx, eth1Data); err != nil { + return nil, err } return s, nil } @@ -936,6 +925,53 @@ func (s *Service) fallbackToNextEndpoint() { log.Infof("Falling back to alternative endpoint: %s", logutil.MaskCredentialsLogging(s.currHttpEndpoint.Url)) } +// initializes our service from the provided eth1data object by initializing all the relevant +// fields and data. +func (s *Service) initializeEth1Data(ctx context.Context, eth1DataInDB *protodb.ETH1ChainData) error { + // The node has no eth1data persisted on disk, so we exit and instead + // request from contract logs. + if eth1DataInDB == nil { + return nil + } + s.depositTrie = trieutil.CreateTrieFromProto(eth1DataInDB.Trie) + s.chainStartData = eth1DataInDB.ChainstartData + var err error + if !reflect.ValueOf(eth1DataInDB.BeaconState).IsZero() { + s.preGenesisState, err = stateV0.InitializeFromProto(eth1DataInDB.BeaconState) + if err != nil { + return errors.Wrap(err, "Could not initialize state trie") + } + } + s.latestEth1Data = eth1DataInDB.CurrentEth1Data + items := s.depositTrie.Items() + s.lastReceivedMerkleIndex = int64(len(items) - 1) + // Account for 0 elements existing in the deposit trie. + if len(items) == 1 && bytes.Equal(items[0], params.BeaconConfig().ZeroHash[:]) { + s.lastReceivedMerkleIndex = -1 + } + if err := s.initDepositCaches(ctx, eth1DataInDB.DepositContainers); err != nil { + return errors.Wrap(err, "could not initialize caches") + } + return nil +} + +// validates that all deposit containers are valid and have their relevant indices +// in order. +func (s *Service) validateDepositContainers(ctrs []*protodb.DepositContainer) bool { + ctrLen := len(ctrs) + // Exit for empty containers. + if ctrLen == 0 { + return true + } + for _, c := range ctrs { + if c.Index == 0 { + return true + } + } + log.Info("Recovering missing deposit containers, node is re-requesting missing deposit data") + return false +} + // validates the current powchain data saved and makes sure that any // embedded genesis state is correctly accounted for. func (s *Service) ensureValidPowchainData(ctx context.Context) error { @@ -951,7 +987,7 @@ func (s *Service) ensureValidPowchainData(ctx context.Context) error { if err != nil { return errors.Wrap(err, "unable to retrieve eth1 data") } - if eth1Data == nil || !eth1Data.ChainstartData.Chainstarted { + if eth1Data == nil || !eth1Data.ChainstartData.Chainstarted || !s.validateDepositContainers(eth1Data.DepositContainers) { pbState, err := stateV0.ProtobufBeaconState(s.preGenesisState.InnerStateUnsafe()) if err != nil { return err diff --git a/beacon-chain/powchain/service_test.go b/beacon-chain/powchain/service_test.go index b304dafc676f..b4020ecd4d1c 100644 --- a/beacon-chain/powchain/service_test.go +++ b/beacon-chain/powchain/service_test.go @@ -620,6 +620,83 @@ func TestService_EnsureConsistentPowchainData(t *testing.T) { 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) + + s1, err := NewService(context.Background(), &Web3ServiceConfig{ + BeaconDB: beaconDB, + DepositCache: cache, + }) + require.NoError(t, err) + genState, err := testutil.NewBeaconState() + require.NoError(t, err) + assert.NoError(t, genState.SetSlot(1000)) + + require.NoError(t, s1.cfg.BeaconDB.SaveGenesisData(context.Background(), genState)) + require.NoError(t, s1.ensureValidPowchainData(context.Background())) + + eth1Data, err := s1.cfg.BeaconDB.PowchainData(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) + + s1, err := NewService(context.Background(), &Web3ServiceConfig{ + BeaconDB: beaconDB, + DepositCache: cache, + }) + require.NoError(t, err) + genState, err := testutil.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.SavePowchainData(context.Background(), &protodb.ETH1ChainData{ + ChainstartData: &protodb.ChainStartData{Chainstarted: true}, + DepositContainers: []*protodb.DepositContainer{{Index: 1}}, + }) + require.NoError(t, err) + require.NoError(t, s1.ensureValidPowchainData(context.Background())) + + eth1Data, err := s1.cfg.BeaconDB.PowchainData(context.Background()) + assert.NoError(t, err) + + assert.NotNil(t, eth1Data) + assert.Equal(t, 0, len(eth1Data.DepositContainers)) +} + +func TestService_ValidateDepositContainers(t *testing.T) { + beaconDB := dbutil.SetupDB(t) + cache, err := depositcache.New() + require.NoError(t, err) + + s1, err := NewService(context.Background(), &Web3ServiceConfig{ + BeaconDB: beaconDB, + DepositCache: cache, + }) + require.NoError(t, err) + ctrs := make([]*protodb.DepositContainer, 0) + assert.Equal(t, true, s1.validateDepositContainers(ctrs)) + for i := 0; i < 10; i++ { + ctrs = append(ctrs, &protodb.DepositContainer{Index: int64(i), Eth1BlockHeight: uint64(i + 10)}) + } + assert.Equal(t, true, s1.validateDepositContainers(ctrs)) + ctrs = make([]*protodb.DepositContainer, 0) + for i := 1; i < 10; i++ { + ctrs = append(ctrs, &protodb.DepositContainer{Index: int64(i), Eth1BlockHeight: uint64(i + 10)}) + } + assert.Equal(t, false, s1.validateDepositContainers(ctrs)) +} + func TestTimestampIsChecked(t *testing.T) { timestamp := uint64(time.Now().Unix()) assert.Equal(t, false, eth1HeadIsBehind(timestamp))