Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor head info for better usages for lock #7432

Merged
merged 9 commits into from
Oct 6, 2020
32 changes: 26 additions & 6 deletions beacon-chain/blockchain/chain_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,10 @@ func (s *Service) PreviousJustifiedCheckpt() *ethpb.Checkpoint {

// HeadSlot returns the slot of the head of the chain.
func (s *Service) HeadSlot() uint64 {
if !s.HasHeadState() {
s.headLock.RLock()
defer s.headLock.RUnlock()

if !s.hasHeadState() {
return 0
}

Expand All @@ -106,6 +109,9 @@ func (s *Service) HeadSlot() uint64 {

// HeadRoot returns the root of the head of the chain.
func (s *Service) HeadRoot(ctx context.Context) ([]byte, error) {
s.headLock.RLock()
defer s.headLock.RUnlock()

if s.headRoot() != params.BeaconConfig().ZeroHash {
r := s.headRoot()
return r[:], nil
Expand All @@ -131,7 +137,10 @@ func (s *Service) HeadRoot(ctx context.Context) ([]byte, error) {
// If the head is nil from service struct,
// it will attempt to get the head block from DB.
func (s *Service) HeadBlock(ctx context.Context) (*ethpb.SignedBeaconBlock, error) {
if s.HasHeadState() {
s.headLock.RLock()
defer s.headLock.RUnlock()

if s.hasHeadState() {
return s.headBlock(), nil
}

Expand All @@ -144,8 +153,10 @@ func (s *Service) HeadBlock(ctx context.Context) (*ethpb.SignedBeaconBlock, erro
func (s *Service) HeadState(ctx context.Context) (*state.BeaconState, error) {
ctx, span := trace.StartSpan(ctx, "blockChain.HeadState")
defer span.End()
s.headLock.RLock()
defer s.headLock.RUnlock()

ok := s.HasHeadState()
ok := s.hasHeadState()
span.AddAttributes(trace.BoolAttribute("cache_hit", ok))

if ok {
Expand All @@ -157,15 +168,21 @@ func (s *Service) HeadState(ctx context.Context) (*state.BeaconState, error) {

// HeadValidatorsIndices returns a list of active validator indices from the head view of a given epoch.
func (s *Service) HeadValidatorsIndices(ctx context.Context, epoch uint64) ([]uint64, error) {
if !s.HasHeadState() {
s.headLock.RLock()
defer s.headLock.RUnlock()

if !s.hasHeadState() {
return []uint64{}, nil
}
return helpers.ActiveValidatorIndices(s.headState(ctx), epoch)
}

// HeadSeed returns the seed from the head view of a given epoch.
func (s *Service) HeadSeed(ctx context.Context, epoch uint64) ([32]byte, error) {
if !s.HasHeadState() {
s.headLock.RLock()
defer s.headLock.RUnlock()

if !s.hasHeadState() {
return [32]byte{}, nil
}

Expand All @@ -174,7 +191,10 @@ func (s *Service) HeadSeed(ctx context.Context, epoch uint64) ([32]byte, error)

// HeadGenesisValidatorRoot returns genesis validator root of the head state.
func (s *Service) HeadGenesisValidatorRoot() [32]byte {
if !s.HasHeadState() {
s.headLock.RLock()
defer s.headLock.RUnlock()

if !s.hasHeadState() {
return [32]byte{}
}

Expand Down
6 changes: 4 additions & 2 deletions beacon-chain/blockchain/chain_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,15 @@ func TestHeadSlot_CanRetrieve(t *testing.T) {
s, err := state.InitializeFromProto(&pb.BeaconState{})
require.NoError(t, err)
c.head = &head{slot: 100, state: s}
assert.Equal(t, uint64(100), c.headSlot())
assert.Equal(t, uint64(100), c.HeadSlot())
}

func TestHeadRoot_CanRetrieve(t *testing.T) {
c := &Service{}
c.head = &head{root: [32]byte{'A'}}
assert.Equal(t, [32]byte{'A'}, c.headRoot())
r, err := c.HeadRoot(context.Background())
require.NoError(t, err)
assert.Equal(t, [32]byte{'A'}, bytesutil.ToBytes32(r))
}

func TestHeadBlock_CanRetrieve(t *testing.T) {
Expand Down
33 changes: 14 additions & 19 deletions beacon-chain/blockchain/head.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,11 @@ func (s *Service) saveHead(ctx context.Context, headRoot [32]byte) error {
defer span.End()

// Do nothing if head hasn't changed.
if headRoot == s.headRoot() {
r, err := s.HeadRoot(ctx)
if err != nil {
return err
}
if headRoot == bytesutil.ToBytes32(r) {
return nil
}

Expand Down Expand Up @@ -101,16 +105,17 @@ func (s *Service) saveHead(ctx context.Context, headRoot [32]byte) error {
}

// A chain re-org occurred, so we fire an event notifying the rest of the services.
if bytesutil.ToBytes32(newHeadBlock.Block.ParentRoot) != s.headRoot() {
headSlot := s.HeadSlot()
if bytesutil.ToBytes32(newHeadBlock.Block.ParentRoot) != bytesutil.ToBytes32(r) {
log.WithFields(logrus.Fields{
"newSlot": fmt.Sprintf("%d", newHeadBlock.Block.Slot),
"oldSlot": fmt.Sprintf("%d", s.headSlot()),
"oldSlot": fmt.Sprintf("%d", headSlot),
}).Debug("Chain reorg occurred")
s.stateNotifier.StateFeed().Send(&feed.Event{
Type: statefeed.Reorg,
Data: &statefeed.ReorgData{
NewSlot: newHeadBlock.Block.Slot,
OldSlot: s.headSlot(),
OldSlot: headSlot,
},
})

Expand Down Expand Up @@ -179,52 +184,42 @@ func (s *Service) setHeadInitialSync(root [32]byte, block *ethpb.SignedBeaconBlo
}

// This returns the head slot.
// This is a lock free version.
func (s *Service) headSlot() uint64 {
s.headLock.RLock()
defer s.headLock.RUnlock()

return s.head.slot
}

// This returns the head root.
// It does a full copy on head root for immutability.
// This is a lock free version.
func (s *Service) headRoot() [32]byte {
if s.head == nil {
return params.BeaconConfig().ZeroHash
}

s.headLock.RLock()
defer s.headLock.RUnlock()

return s.head.root
}

// This returns the head block.
// It does a full copy on head block for immutability.
// This is a lock free version.
func (s *Service) headBlock() *ethpb.SignedBeaconBlock {
s.headLock.RLock()
defer s.headLock.RUnlock()

return stateTrie.CopySignedBeaconBlock(s.head.block)
}

// This returns the head state.
// It does a full copy on head state for immutability.
// This is a lock free version.
func (s *Service) headState(ctx context.Context) *stateTrie.BeaconState {
ctx, span := trace.StartSpan(ctx, "blockChain.headState")
defer span.End()

s.headLock.RLock()
defer s.headLock.RUnlock()

return s.head.state.Copy()
}

// This returns the genesis validator root of the head state.
// This is a lock free version.
func (s *Service) headGenesisValidatorRoot() [32]byte {
s.headLock.RLock()
defer s.headLock.RUnlock()

return bytesutil.ToBytes32(s.head.state.GenesisValidatorRoot())
}

Expand Down
11 changes: 8 additions & 3 deletions beacon-chain/blockchain/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ const template = `<html>

// TreeHandler is a handler to serve /tree page in metrics.
func (s *Service) TreeHandler(w http.ResponseWriter, r *http.Request) {
if s.headState(r.Context()) == nil {
headState, err := s.HeadState(r.Context())
if err != nil {
log.WithError(err).Error("Could not get head state")
return
}
if headState == nil {
if _, err := w.Write([]byte("Unavailable during initial syncing")); err != nil {
log.WithError(err).Error("Failed to render p2p info page")
}
Expand All @@ -47,7 +52,7 @@ func (s *Service) TreeHandler(w http.ResponseWriter, r *http.Request) {
graph.Attr("labeljust", "l")

dotNodes := make([]*dot.Node, len(nodes))
avgBalance := uint64(averageBalance(s.headState(r.Context()).Balances()))
avgBalance := uint64(averageBalance(headState.Balances()))

for i := len(nodes) - 1; i >= 0; i-- {
// Construct label for each node.
Expand All @@ -63,7 +68,7 @@ func (s *Service) TreeHandler(w http.ResponseWriter, r *http.Request) {
dotN = graph.Node(index).Box().Attr("label", label)
}

if nodes[i].Slot() == s.headSlot() &&
if nodes[i].Slot() == s.HeadSlot() &&
nodes[i].BestDescendant() == ^uint64(0) &&
nodes[i].Parent() != ^uint64(0) {
dotN = dotN.Attr("color", "green")
Expand Down
6 changes: 3 additions & 3 deletions beacon-chain/blockchain/receive_block.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func (s *Service) ReceiveBlock(ctx context.Context, block *ethpb.SignedBeaconBlo
}

// Reports on block and fork choice metrics.
reportSlotMetrics(blockCopy.Block.Slot, s.headSlot(), s.CurrentSlot(), s.finalizedCheckpt)
reportSlotMetrics(blockCopy.Block.Slot, s.HeadSlot(), s.CurrentSlot(), s.finalizedCheckpt)

// Log block sync status.
logBlockSyncStatus(blockCopy.Block, blockRoot, s.finalizedCheckpt)
Expand Down Expand Up @@ -95,7 +95,7 @@ func (s *Service) ReceiveBlockInitialSync(ctx context.Context, block *ethpb.Sign
})

// Reports on blockCopy and fork choice metrics.
reportSlotMetrics(blockCopy.Block.Slot, s.headSlot(), s.CurrentSlot(), s.finalizedCheckpt)
reportSlotMetrics(blockCopy.Block.Slot, s.HeadSlot(), s.CurrentSlot(), s.finalizedCheckpt)

// Log state transition data.
log.WithFields(logrus.Fields{
Expand Down Expand Up @@ -139,7 +139,7 @@ func (s *Service) ReceiveBlockBatch(ctx context.Context, blocks []*ethpb.SignedB
})

// Reports on blockCopy and fork choice metrics.
reportSlotMetrics(blockCopy.Block.Slot, s.headSlot(), s.CurrentSlot(), s.finalizedCheckpt)
reportSlotMetrics(blockCopy.Block.Slot, s.HeadSlot(), s.CurrentSlot(), s.finalizedCheckpt)
}

if err := s.VerifyWeakSubjectivityRoot(s.ctx); err != nil {
Expand Down
4 changes: 3 additions & 1 deletion beacon-chain/blockchain/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,9 @@ func TestChainService_InitializeBeaconChain(t *testing.T) {
if headBlk == nil {
t.Error("Head state can't be nil after initialize beacon chain")
}
if bc.headRoot() == params.BeaconConfig().ZeroHash {
r, err := bc.HeadRoot(ctx)
require.NoError(t, err)
if bytesutil.ToBytes32(r) == params.BeaconConfig().ZeroHash {
t.Error("Canonical root for slot 0 can't be zeros after initialize beacon chain")
}
}
Expand Down
2 changes: 1 addition & 1 deletion beacon-chain/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import (
_ "github.com/prysmaticlabs/prysm/shared/maxprocs"
"github.com/prysmaticlabs/prysm/shared/version"
"github.com/sirupsen/logrus"
"github.com/wercker/journalhook"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v2/altsrc"
"github.com/wercker/journalhook"
prefixed "github.com/x-cray/logrus-prefixed-formatter"
)

Expand Down
2 changes: 1 addition & 1 deletion slasher/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ import (
"github.com/prysmaticlabs/prysm/slasher/flags"
"github.com/prysmaticlabs/prysm/slasher/node"
"github.com/sirupsen/logrus"
"github.com/wercker/journalhook"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v2/altsrc"
"github.com/wercker/journalhook"
prefixed "github.com/x-cray/logrus-prefixed-formatter"
)

Expand Down
2 changes: 1 addition & 1 deletion validator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ import (
"github.com/prysmaticlabs/prysm/validator/flags"
"github.com/prysmaticlabs/prysm/validator/node"
"github.com/sirupsen/logrus"
"github.com/wercker/journalhook"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v2/altsrc"
"github.com/wercker/journalhook"
prefixed "github.com/x-cray/logrus-prefixed-formatter"
"google.golang.org/grpc"
)
Expand Down