From 5294caf5e8d431a4a51f832ef9d5e5998b855a18 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Fri, 23 Aug 2019 19:59:09 -0600 Subject: [PATCH] Save validators upon chainstart (#3295) --- beacon-chain/blockchain/service.go | 38 ++++++++++++++----- beacon-chain/blockchain/service_test.go | 29 ++++++++++++++ beacon-chain/sync/service_test.go | 2 +- .../sync/validate_beacon_attestation_test.go | 4 -- 4 files changed, 58 insertions(+), 15 deletions(-) diff --git a/beacon-chain/blockchain/service.go b/beacon-chain/blockchain/service.go index c9958ec66..c01d1551f 100644 --- a/beacon-chain/blockchain/service.go +++ b/beacon-chain/blockchain/service.go @@ -111,7 +111,7 @@ func (c *ChainService) Start() { subChainStart := c.web3Service.ChainStartFeed().Subscribe(c.chainStartChan) go func() { genesisTime := <-c.chainStartChan - c.processChainStartTime(genesisTime, subChainStart) + c.processChainStartTime(c.ctx, genesisTime, subChainStart) return }() } @@ -119,9 +119,9 @@ func (c *ChainService) Start() { // processChainStartTime initializes a series of deposits from the ChainStart deposits in the eth1 // deposit contract, initializes the beacon chain's state, and kicks off the beacon chain. -func (c *ChainService) processChainStartTime(genesisTime time.Time, chainStartSub event.Subscription) { +func (c *ChainService) processChainStartTime(ctx context.Context, genesisTime time.Time, chainStartSub event.Subscription) { initialDeposits := c.web3Service.ChainStartDeposits() - if err := c.initializeBeaconChain(genesisTime, initialDeposits, c.web3Service.ChainStartETH1Data()); err != nil { + if err := c.initializeBeaconChain(ctx, genesisTime, initialDeposits, c.web3Service.ChainStartETH1Data()); err != nil { log.Fatalf("Could not initialize beacon chain: %v", err) } c.stateInitializedFeed.Send(genesisTime) @@ -131,23 +131,31 @@ func (c *ChainService) processChainStartTime(genesisTime time.Time, chainStartSu // initializes the state and genesis block of the beacon chain to persistent storage // based on a genesis timestamp value obtained from the ChainStart event emitted // by the ETH1.0 Deposit Contract and the POWChain service of the node. -func (c *ChainService) initializeBeaconChain(genesisTime time.Time, deposits []*ethpb.Deposit, eth1data *ethpb.Eth1Data) error { +func (c *ChainService) initializeBeaconChain( + ctx context.Context, + genesisTime time.Time, + deposits []*ethpb.Deposit, + eth1data *ethpb.Eth1Data) error { _, span := trace.StartSpan(context.Background(), "beacon-chain.ChainService.initializeBeaconChain") defer span.End() log.Info("ChainStart time reached, starting the beacon chain!") c.genesisTime = genesisTime unixTime := uint64(genesisTime.Unix()) - beaconState, err := state.GenesisBeaconState(deposits, unixTime, eth1data) + genesisState, err := state.GenesisBeaconState(deposits, unixTime, eth1data) if err != nil { return errors.Wrap(err, "could not initialize genesis state") } - if err := c.forkChoiceStore.GenesisStore(c.ctx, beaconState); err != nil { - return errors.Wrap(err, "could not start gensis store for fork choice") + if err := c.saveGenesisValidators(ctx, genesisState); err != nil { + return errors.Wrap(err, "could not save genesis validators") } - c.canonicalRoots[beaconState.Slot] = c.FinalizedCheckpt().Root + if err := c.forkChoiceStore.GenesisStore(ctx, genesisState); err != nil { + return errors.Wrap(err, "could not start genesis store for fork choice") + } + + c.canonicalRoots[genesisState.Slot] = c.FinalizedCheckpt().Root return nil } @@ -244,6 +252,16 @@ func (c *ChainService) saveHead(ctx context.Context, b *ethpb.BeaconBlock, r [32 return nil } +// This gets called when beacon chain is first initialized to save validator indices and pubkeys in db +func (c *ChainService) saveGenesisValidators(ctx context.Context, s *pb.BeaconState) error { + for i, v := range s.Validators { + if err := c.beaconDB.SaveValidatorIndex(ctx, bytesutil.ToBytes48(v.PublicKey), uint64(i)); err != nil { + return errors.Wrapf(err, "could not save validator index: %d", i) + } + } + return nil +} + // This checks if the block is from a competing chain, emits warning and updates metrics. func isCompetingBlock(root []byte, slot uint64, headRoot []byte, headSlot uint64) { if !bytes.Equal(root[:], headRoot) { @@ -261,8 +279,8 @@ func isCompetingBlock(root []byte, slot uint64, headRoot []byte, headSlot uint64 func isCompetingAtts(root []byte, headRoot []byte) { if !bytes.Equal(root[:], headRoot) { log.WithFields(logrus.Fields{ - "attDataRoot": hex.EncodeToString(root[:]), - "headRoot": hex.EncodeToString(headRoot), + "attDataRoot": hex.EncodeToString(root[:]), + "headRoot": hex.EncodeToString(headRoot), }).Warn("Calculated head diffs from new attestation") competingAtts.Inc() } diff --git a/beacon-chain/blockchain/service_test.go b/beacon-chain/blockchain/service_test.go index 60479ef5d..86590c24d 100644 --- a/beacon-chain/blockchain/service_test.go +++ b/beacon-chain/blockchain/service_test.go @@ -2,6 +2,7 @@ package blockchain import ( "context" + "encoding/hex" "errors" "io/ioutil" "math/big" @@ -17,11 +18,13 @@ import ( b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/beacon-chain/db" + testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" "github.com/prysmaticlabs/prysm/beacon-chain/internal" "github.com/prysmaticlabs/prysm/beacon-chain/p2p" "github.com/prysmaticlabs/prysm/beacon-chain/powchain" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" + "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/event" "github.com/prysmaticlabs/prysm/shared/testutil" "github.com/sirupsen/logrus" @@ -286,3 +289,29 @@ func TestChainStartStop_Initialized(t *testing.T) { } testutil.AssertLogsContain(t, hook, "Beacon chain data already exists, starting service") } + +func TestChainService_initializeBeaconChain(t *testing.T) { + db := testDB.SetupDB(t) + defer testDB.TeardownDB(t, db) + ctx := context.Background() + + bc := setupBeaconChain(t, db) + + // Set up 10 deposits pre chain start for validators to register + count := uint64(10) + deposits, _ := testutil.SetupInitialDeposits(t, count) + if err := bc.initializeBeaconChain(ctx, time.Unix(0, 0), deposits, ðpb.Eth1Data{}); err != nil { + t.Fatal(err) + } + + s, err := bc.beaconDB.State(ctx, bytesutil.ToBytes32(bc.canonicalRoots[0])) + if err != nil { + t.Fatal(err) + } + + for _, v := range s.Validators { + if !db.HasValidatorIndex(ctx, bytesutil.ToBytes48(v.PublicKey)) { + t.Errorf("Validator %s missing from db", hex.EncodeToString(v.PublicKey)) + } + } +} diff --git a/beacon-chain/sync/service_test.go b/beacon-chain/sync/service_test.go index ddc12e958..1c550cc3b 100644 --- a/beacon-chain/sync/service_test.go +++ b/beacon-chain/sync/service_test.go @@ -51,4 +51,4 @@ func (ms *mockChainService) ReceiveAttestation(context.Context, *ethpb.Attestati func (ms *mockChainService) ReceiveAttestationNoPubsub(context.Context, *ethpb.Attestation) error { return nil -} \ No newline at end of file +} diff --git a/beacon-chain/sync/validate_beacon_attestation_test.go b/beacon-chain/sync/validate_beacon_attestation_test.go index 7959b57de..cad2c40ea 100644 --- a/beacon-chain/sync/validate_beacon_attestation_test.go +++ b/beacon-chain/sync/validate_beacon_attestation_test.go @@ -17,7 +17,6 @@ func TestValidateBeaconAttestation_ValidBlock(t *testing.T) { p := p2ptest.NewTestP2P(t) ctx := context.Background() - rs := &RegularSync{ db: db, } @@ -64,12 +63,10 @@ func TestValidateBeaconAttestation_InvalidBlock(t *testing.T) { p := p2ptest.NewTestP2P(t) ctx := context.Background() - rs := &RegularSync{ db: db, } - msg := ðpb.Attestation{ Data: ðpb.AttestationData{ BeaconBlockRoot: testutil.Random32Bytes(t), @@ -83,4 +80,3 @@ func TestValidateBeaconAttestation_InvalidBlock(t *testing.T) { t.Error("Invalid beacon attestation was broadcast") } } -