From f0332e113148f0e51fddc5d7bd2f3246347ae636 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Thu, 29 Aug 2019 15:32:35 -0700 Subject: [PATCH] Save genesis state in DB (#3359) * Done * Test * Fixed test * emit state initialized --- beacon-chain/blockchain/service.go | 13 ++++++++- beacon-chain/blockchain/service_test.go | 3 ++ beacon-chain/db/block.go | 5 ++++ beacon-chain/db/db.go | 2 ++ beacon-chain/db/kv/blocks.go | 10 +++++++ beacon-chain/db/kv/schema.go | 1 + beacon-chain/db/kv/state.go | 24 ++++++++++++++++ beacon-chain/db/kv/state_test.go | 38 +++++++++++++++++++++++++ beacon-chain/db/state.go | 5 ++++ 9 files changed, 100 insertions(+), 1 deletion(-) diff --git a/beacon-chain/blockchain/service.go b/beacon-chain/blockchain/service.go index 67205fe92..c6970773a 100644 --- a/beacon-chain/blockchain/service.go +++ b/beacon-chain/blockchain/service.go @@ -99,6 +99,14 @@ func (c *ChainService) Start() { if beaconState != nil { log.Info("Beacon chain data already exists, starting service") c.genesisTime = time.Unix(int64(beaconState.GenesisTime), 0) + genesisState, err := c.beaconDB.GenesisState(c.ctx) + if err != nil { + log.Fatalf("Could not retrieve genesis state from db: %v", err) + } + if err := c.forkChoiceStore.GenesisStore(c.ctx, genesisState); err != nil { + log.Fatalf("Could not start fork choice service: %v", err) + } + c.stateInitializedFeed.Send(c.genesisTime) } else { log.Info("Waiting for ChainStart log from the Validator Deposit Contract to start the beacon chain...") if c.web3Service == nil { @@ -158,10 +166,13 @@ func (c *ChainService) initializeBeaconChain( return errors.Wrap(err, "could not start genesis store for fork choice") } + if err := c.beaconDB.SaveGenesisBlockRoot(ctx, bytesutil.ToBytes32(c.FinalizedCheckpt().Root)); err != nil { + return errors.Wrap(err, "could save genesis block root") + } + c.headBlock = genesisBlk c.headState = genesisState c.canonicalRoots[genesisState.Slot] = c.FinalizedCheckpt().Root - c.canonicalRoots[genesisState.Slot] = c.FinalizedCheckpt().Root return nil } diff --git a/beacon-chain/blockchain/service_test.go b/beacon-chain/blockchain/service_test.go index f32af4069..01ebeb6b4 100644 --- a/beacon-chain/blockchain/service_test.go +++ b/beacon-chain/blockchain/service_test.go @@ -281,6 +281,9 @@ func TestChainStartStop_Initialized(t *testing.T) { if err := db.SaveHeadBlockRoot(ctx, blkRoot); err != nil { t.Fatal(err) } + if err := db.SaveGenesisBlockRoot(ctx, blkRoot); err != nil { + t.Fatal(err) + } if err := db.SaveState(ctx, &pb.BeaconState{Slot: 1}, blkRoot); err != nil { t.Fatal(err) } diff --git a/beacon-chain/db/block.go b/beacon-chain/db/block.go index 001b28569..b6847a075 100644 --- a/beacon-chain/db/block.go +++ b/beacon-chain/db/block.go @@ -154,6 +154,11 @@ func (db *BeaconDB) SaveHeadBlockRoot(_ context.Context, root [32]byte) error { return errors.New("not implemented") } +// SaveGenesisBlockRoot is not implemented. +func (db *BeaconDB) SaveGenesisBlockRoot(_ context.Context, root [32]byte) error { + return errors.New("not implemented") +} + // SaveBlocks in db. func (db *BeaconDB) SaveBlocks(ctx context.Context, blocks []*ethpb.BeaconBlock) error { for _, blk := range blocks { diff --git a/beacon-chain/db/db.go b/beacon-chain/db/db.go index 22ffdaad9..573020ed6 100644 --- a/beacon-chain/db/db.go +++ b/beacon-chain/db/db.go @@ -43,6 +43,7 @@ type Database interface { SaveBlock(ctx context.Context, block *ethpb.BeaconBlock) error SaveBlocks(ctx context.Context, blocks []*ethpb.BeaconBlock) error SaveHeadBlockRoot(ctx context.Context, blockRoot [32]byte) error + SaveGenesisBlockRoot(ctx context.Context, blockRoot [32]byte) error // Validator related methods. ValidatorLatestVote(ctx context.Context, validatorIdx uint64) (*pb.ValidatorLatestVote, error) HasValidatorLatestVote(ctx context.Context, validatorIdx uint64) bool @@ -55,6 +56,7 @@ type Database interface { // State related methods. State(ctx context.Context, blockRoot [32]byte) (*pb.BeaconState, error) HeadState(ctx context.Context) (*pb.BeaconState, error) + GenesisState(ctx context.Context) (*pb.BeaconState, error) SaveState(ctx context.Context, state *pb.BeaconState, blockRoot [32]byte) error // Slashing operations. ProposerSlashing(ctx context.Context, slashingRoot [32]byte) (*ethpb.ProposerSlashing, error) diff --git a/beacon-chain/db/kv/blocks.go b/beacon-chain/db/kv/blocks.go index cc8febc8d..c45064848 100644 --- a/beacon-chain/db/kv/blocks.go +++ b/beacon-chain/db/kv/blocks.go @@ -259,6 +259,16 @@ func (k *Store) SaveHeadBlockRoot(ctx context.Context, blockRoot [32]byte) error }) } +// SaveGenesisBlockRoot to the db. +func (k *Store) SaveGenesisBlockRoot(ctx context.Context, blockRoot [32]byte) error { + ctx, span := trace.StartSpan(ctx, "BeaconDB.SaveGenesisBlockRoot") + defer span.End() + return k.db.Update(func(tx *bolt.Tx) error { + bucket := tx.Bucket(blocksBucket) + return bucket.Put(genesisBlockRootKey, blockRoot[:]) + }) +} + // fetchBlockRootsBySlotRange looks into a boltDB bucket and performs a binary search // range scan using sorted left-padded byte keys using a start slot and an end slot. // If both the start and end slot are the same, and are 0, the function returns nil. diff --git a/beacon-chain/db/kv/schema.go b/beacon-chain/db/kv/schema.go index 7a6824a50..70e898e66 100644 --- a/beacon-chain/db/kv/schema.go +++ b/beacon-chain/db/kv/schema.go @@ -26,5 +26,6 @@ var ( // Specific item keys. headBlockRootKey = []byte("head-root") + genesisBlockRootKey = []byte("genesis-root") depositContractAddressKey = []byte("deposit-contract") ) diff --git a/beacon-chain/db/kv/state.go b/beacon-chain/db/kv/state.go index 7a02099b2..6816475da 100644 --- a/beacon-chain/db/kv/state.go +++ b/beacon-chain/db/kv/state.go @@ -54,6 +54,30 @@ func (k *Store) HeadState(ctx context.Context) (*pb.BeaconState, error) { return s, err } +// GenesisState returns the genesis state in beacon chain. +func (k *Store) GenesisState(ctx context.Context) (*pb.BeaconState, error) { + ctx, span := trace.StartSpan(ctx, "BeaconDB.GenesisState") + defer span.End() + var s *pb.BeaconState + err := k.db.View(func(tx *bolt.Tx) error { + // Retrieve genesis block's signing root from blocks bucket, + // to look up what the genesis state is. + bucket := tx.Bucket(blocksBucket) + genesisBlockRoot := bucket.Get(genesisBlockRootKey) + + bucket = tx.Bucket(stateBucket) + enc := bucket.Get(genesisBlockRoot) + if enc == nil { + return nil + } + + var err error + s, err = createState(enc) + return err + }) + return s, err +} + // SaveState stores a state to the db using block's signing root which was used to generate the state. func (k *Store) SaveState(ctx context.Context, state *pb.BeaconState, blockRoot [32]byte) error { ctx, span := trace.StartSpan(ctx, "BeaconDB.SaveState") diff --git a/beacon-chain/db/kv/state_test.go b/beacon-chain/db/kv/state_test.go index 014ea3c03..80fa26b25 100644 --- a/beacon-chain/db/kv/state_test.go +++ b/beacon-chain/db/kv/state_test.go @@ -75,3 +75,41 @@ func TestHeadState_CanSaveRetrieve(t *testing.T) { t.Error("unsaved head state should've been nil") } } + +func TestGenesisState_CanSaveRetrieve(t *testing.T) { + db := setupDB(t) + defer teardownDB(t, db) + + s := &pb.BeaconState{Slot: 1} + headRoot := [32]byte{'B'} + + if err := db.SaveGenesisBlockRoot(context.Background(), headRoot); err != nil { + t.Fatal(err) + } + + if err := db.SaveState(context.Background(), s, headRoot); err != nil { + t.Fatal(err) + } + + savedGenesisS, err := db.GenesisState(context.Background()) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(s, savedGenesisS) { + t.Error("did not retrieve saved state") + } + + if err := db.SaveGenesisBlockRoot(context.Background(), [32]byte{'C'}); err != nil { + t.Fatal(err) + } + + savedGenesisS, err = db.HeadState(context.Background()) + if err != nil { + t.Fatal(err) + } + + if savedGenesisS != nil { + t.Error("unsaved genesis state should've been nil") + } +} diff --git a/beacon-chain/db/state.go b/beacon-chain/db/state.go index 114166605..3c05cbe62 100644 --- a/beacon-chain/db/state.go +++ b/beacon-chain/db/state.go @@ -150,6 +150,11 @@ func (db *BeaconDB) HeadState(ctx context.Context) (*pb.BeaconState, error) { return beaconState, err } +// GenesisState is not implemented. +func (db *BeaconDB) GenesisState(ctx context.Context) (*pb.BeaconState, error) { + return nil, errors.New("not implemented") +} + // HeadStateRoot returns the root of the current state from the db. func (db *BeaconDB) HeadStateRoot() [32]byte { return db.stateHash