Handle nil head block in cache (#4888)

* Nil check

* Fixed tests

* Covered rest of the codebase

* Race tests

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
This commit is contained in:
terence tsao
2020-02-17 11:21:42 -07:00
committed by GitHub
parent 9b2aa66667
commit 6b8ec26c56
7 changed files with 41 additions and 18 deletions

View File

@@ -32,7 +32,7 @@ type TimeFetcher interface {
type HeadFetcher interface { type HeadFetcher interface {
HeadSlot() uint64 HeadSlot() uint64
HeadRoot(ctx context.Context) ([]byte, error) HeadRoot(ctx context.Context) ([]byte, error)
HeadBlock() *ethpb.SignedBeaconBlock HeadBlock(ctx context.Context) (*ethpb.SignedBeaconBlock, error)
HeadState(ctx context.Context) (*state.BeaconState, error) HeadState(ctx context.Context) (*state.BeaconState, error)
HeadValidatorsIndices(epoch uint64) ([]uint64, error) HeadValidatorsIndices(epoch uint64) ([]uint64, error)
HeadSeed(epoch uint64) ([32]byte, error) HeadSeed(epoch uint64) ([32]byte, error)
@@ -135,23 +135,25 @@ func (s *Service) HeadRoot(ctx context.Context) ([]byte, error) {
} }
// HeadBlock returns the head block of the chain. // HeadBlock returns the head block of the chain.
func (s *Service) HeadBlock() *ethpb.SignedBeaconBlock { // If the head state is nil from service struct,
return s.headBlock() // it will attempt to get the head block from DB.
func (s *Service) HeadBlock(ctx context.Context) (*ethpb.SignedBeaconBlock, error) {
if s.hasHeadState() {
return s.headBlock(), nil
}
return s.beaconDB.HeadBlock(ctx)
} }
// HeadState returns the head state of the chain. // HeadState returns the head state of the chain.
// If the head state is nil from service struct, // If the head state is nil from service struct,
// it will attempt to get from DB and error if nil again. // it will attempt to get the head state from DB.
func (s *Service) HeadState(ctx context.Context) (*state.BeaconState, error) { func (s *Service) HeadState(ctx context.Context) (*state.BeaconState, error) {
if s.hasHeadState() { if s.hasHeadState() {
return s.headState(), nil return s.headState(), nil
} }
headState, err := s.beaconDB.HeadState(ctx) return s.beaconDB.HeadState(ctx)
if err != nil {
return nil, err
}
return headState, nil
} }
// HeadValidatorsIndices returns a list of active validator indices from the head view of a given epoch. // HeadValidatorsIndices returns a list of active validator indices from the head view of a given epoch.

View File

@@ -54,7 +54,7 @@ func TestHeadBlock_DataRace(t *testing.T) {
[32]byte{}, [32]byte{},
) )
}() }()
s.HeadBlock() s.HeadBlock(context.Background())
} }
func TestHeadState_DataRace(t *testing.T) { func TestHeadState_DataRace(t *testing.T) {

View File

@@ -145,10 +145,16 @@ func TestHeadRoot_CanRetrieve(t *testing.T) {
func TestHeadBlock_CanRetrieve(t *testing.T) { func TestHeadBlock_CanRetrieve(t *testing.T) {
b := &ethpb.SignedBeaconBlock{Block: &ethpb.BeaconBlock{Slot: 1}} b := &ethpb.SignedBeaconBlock{Block: &ethpb.BeaconBlock{Slot: 1}}
s, _ := state.InitializeFromProto(&pb.BeaconState{})
c := &Service{} c := &Service{}
c.head = &head{block: b} c.head = &head{block: b, state: s}
if !reflect.DeepEqual(b, c.HeadBlock()) { recevied, err := c.HeadBlock(context.Background())
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(b, recevied) {
t.Error("incorrect head block received") t.Error("incorrect head block received")
} }
} }

View File

@@ -305,7 +305,11 @@ func TestChainService_InitializeBeaconChain(t *testing.T) {
if _, err := bc.HeadState(ctx); err != nil { if _, err := bc.HeadState(ctx); err != nil {
t.Error(err) t.Error(err)
} }
if bc.HeadBlock() == nil { headBlk, err := bc.HeadBlock(ctx)
if err != nil {
t.Fatal(err)
}
if headBlk == nil {
t.Error("Head state can't be nil after initialize beacon chain") t.Error("Head state can't be nil after initialize beacon chain")
} }
if bc.headRoot() == params.BeaconConfig().ZeroHash { if bc.headRoot() == params.BeaconConfig().ZeroHash {
@@ -356,7 +360,11 @@ func TestChainService_InitializeChainInfo(t *testing.T) {
if err := c.initializeChainInfo(ctx); err != nil { if err := c.initializeChainInfo(ctx); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !reflect.DeepEqual(c.HeadBlock(), headBlock) { headBlk, err := c.HeadBlock(ctx)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(headBlk, headBlock) {
t.Error("head block incorrect") t.Error("head block incorrect")
} }
s, err := c.HeadState(ctx) s, err := c.HeadState(ctx)

View File

@@ -160,8 +160,8 @@ func (ms *ChainService) HeadRoot(ctx context.Context) ([]byte, error) {
} }
// HeadBlock mocks HeadBlock method in chain service. // HeadBlock mocks HeadBlock method in chain service.
func (ms *ChainService) HeadBlock() *ethpb.SignedBeaconBlock { func (ms *ChainService) HeadBlock(context.Context) (*ethpb.SignedBeaconBlock, error) {
return ms.Block return ms.Block, nil
} }
// HeadState mocks HeadState method in chain service. // HeadState mocks HeadState method in chain service.

View File

@@ -237,7 +237,10 @@ func (bs *Server) StreamChainHead(_ *ptypes.Empty, stream ethpb.BeaconChain_Stre
// Retrieve chain head information from the DB and the current beacon state. // Retrieve chain head information from the DB and the current beacon state.
func (bs *Server) chainHeadRetrieval(ctx context.Context) (*ethpb.ChainHead, error) { func (bs *Server) chainHeadRetrieval(ctx context.Context) (*ethpb.ChainHead, error) {
headBlock := bs.HeadFetcher.HeadBlock() headBlock, err := bs.HeadFetcher.HeadBlock(ctx)
if err != nil {
return nil, status.Error(codes.Internal, "Could not get head block")
}
if headBlock == nil { if headBlock == nil {
return nil, status.Error(codes.Internal, "Head block of chain was nil") return nil, status.Error(codes.Internal, "Head block of chain was nil")
} }

View File

@@ -158,7 +158,11 @@ func (vs *Server) DomainData(ctx context.Context, request *ethpb.DomainRequest)
// CanonicalHead of the current beacon chain. This method is requested on-demand // CanonicalHead of the current beacon chain. This method is requested on-demand
// by a validator when it is their time to propose or attest. // by a validator when it is their time to propose or attest.
func (vs *Server) CanonicalHead(ctx context.Context, req *ptypes.Empty) (*ethpb.SignedBeaconBlock, error) { func (vs *Server) CanonicalHead(ctx context.Context, req *ptypes.Empty) (*ethpb.SignedBeaconBlock, error) {
return vs.HeadFetcher.HeadBlock(), nil headBlk, err := vs.HeadFetcher.HeadBlock(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get head block: %v", err)
}
return headBlk, nil
} }
// WaitForChainStart queries the logs of the Deposit Contract in order to verify the beacon chain // WaitForChainStart queries the logs of the Deposit Contract in order to verify the beacon chain