mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 13:28:01 -05:00
Add checkpoint to state caching (#3333)
This commit is contained in:
@@ -12,6 +12,7 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/forkchoice",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/core/state:go_default_library",
|
||||
@@ -20,8 +21,8 @@ go_library(
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
@@ -42,6 +43,7 @@ go_test(
|
||||
data = ["lmd_ghost_test.yaml"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
@@ -50,7 +52,6 @@ go_test(
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@in_gopkg_yaml_v2//:go_default_library",
|
||||
|
||||
@@ -4,11 +4,11 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
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/hashutil"
|
||||
)
|
||||
|
||||
func BenchmarkForkChoiceTree1(b *testing.B) {
|
||||
@@ -38,11 +38,12 @@ func BenchmarkForkChoiceTree1(b *testing.B) {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
h, err := hashutil.HashProto(store.justifiedCheckpt)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
if err := store.checkpointState.AddCheckpointState(&cache.CheckpointState{
|
||||
Checkpoint: store.justifiedCheckpt,
|
||||
State: s,
|
||||
}); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
store.checkptBlkRoot[h] = bytesutil.ToBytes32(roots[0])
|
||||
|
||||
// Spread out the votes evenly for all 3 leaf nodes
|
||||
for i := 0; i < len(validators); i++ {
|
||||
@@ -98,11 +99,12 @@ func BenchmarkForkChoiceTree2(b *testing.B) {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
h, err := hashutil.HashProto(store.justifiedCheckpt)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
if err := store.checkpointState.AddCheckpointState(&cache.CheckpointState{
|
||||
Checkpoint: store.justifiedCheckpt,
|
||||
State: s,
|
||||
}); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
store.checkptBlkRoot[h] = bytesutil.ToBytes32(roots[0])
|
||||
|
||||
// Spread out the votes evenly for all the leaf nodes. 8 to 15
|
||||
nodeIndex := 8
|
||||
@@ -151,11 +153,12 @@ func BenchmarkForkChoiceTree3(b *testing.B) {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
h, err := hashutil.HashProto(store.justifiedCheckpt)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
if err := store.checkpointState.AddCheckpointState(&cache.CheckpointState{
|
||||
Checkpoint: store.justifiedCheckpt,
|
||||
State: s,
|
||||
}); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
store.checkptBlkRoot[h] = bytesutil.ToBytes32(roots[0])
|
||||
|
||||
// All validators vote on the same head
|
||||
for i := 0; i < len(validators); i++ {
|
||||
|
||||
@@ -9,12 +9,12 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
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/hashutil"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
@@ -107,14 +107,16 @@ func TestGetHeadFromYaml(t *testing.T) {
|
||||
}
|
||||
|
||||
store.justifiedCheckpt.Root = blksRoot[0]
|
||||
h, err := hashutil.HashProto(store.justifiedCheckpt)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := store.db.SaveState(ctx, s, bytesutil.ToBytes32(blksRoot[0])); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
store.checkptBlkRoot[h] = bytesutil.ToBytes32(blksRoot[0])
|
||||
|
||||
if err := store.checkpointState.AddCheckpointState(&cache.CheckpointState{
|
||||
Checkpoint: store.justifiedCheckpt,
|
||||
State: s,
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
head, err := store.Head(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -5,14 +5,15 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
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/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
@@ -76,7 +77,7 @@ func (s *Store) OnAttestation(ctx context.Context, a *ethpb.Attestation) error {
|
||||
}
|
||||
|
||||
// Store target checkpoint state if not yet seen.
|
||||
baseState, err = s.saveChkptState(ctx, baseState, tgt)
|
||||
baseState, err = s.saveCheckpointState(ctx, baseState, tgt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -111,25 +112,30 @@ func (s *Store) verifyAttPreState(ctx context.Context, c *ethpb.Checkpoint) (*pb
|
||||
return baseState, nil
|
||||
}
|
||||
|
||||
// saveChkptState saves the block root with check point to avoid excessive slot processing down the line.
|
||||
func (s *Store) saveChkptState(ctx context.Context, baseState *pb.BeaconState, c *ethpb.Checkpoint) (*pb.BeaconState, error) {
|
||||
h, err := hashutil.HashProto(c)
|
||||
// saveCheckpointState saves and returns the processed state with the associated check point.
|
||||
func (s *Store) saveCheckpointState(ctx context.Context, baseState *pb.BeaconState, c *ethpb.Checkpoint) (*pb.BeaconState, error) {
|
||||
targetState, err := s.checkpointState.StateByCheckpoint(c)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not hash justified checkpoint")
|
||||
return nil, errors.Wrap(err, "could not get cached checkpoint state")
|
||||
}
|
||||
s.lock.RLock()
|
||||
_, exists := s.checkptBlkRoot[h]
|
||||
s.lock.RUnlock()
|
||||
if !exists {
|
||||
baseState, err = state.ProcessSlots(ctx, baseState, helpers.StartSlot(c.Epoch))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not process slots up to %d", helpers.StartSlot(c.Epoch))
|
||||
}
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
s.checkptBlkRoot[h] = bytesutil.ToBytes32(c.Root)
|
||||
if targetState != nil {
|
||||
return targetState, nil
|
||||
}
|
||||
return baseState, nil
|
||||
|
||||
stateCopy := proto.Clone(baseState).(*pb.BeaconState)
|
||||
targetState, err = state.ProcessSlots(ctx, stateCopy, helpers.StartSlot(c.Epoch))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not process slots up to %d", helpers.StartSlot(c.Epoch))
|
||||
}
|
||||
|
||||
if err := s.checkpointState.AddCheckpointState(&cache.CheckpointState{
|
||||
Checkpoint: c,
|
||||
State: targetState,
|
||||
}); err != nil {
|
||||
return nil, errors.Wrap(err, "could not saved checkpoint state to cache")
|
||||
}
|
||||
|
||||
return targetState, nil
|
||||
}
|
||||
|
||||
// verifyAttSlotTime validates input attestation is not from the future.
|
||||
|
||||
@@ -120,3 +120,82 @@ func TestStore_OnAttestation(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStore_SaveCheckpointState(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
db := testDB.SetupDB(t)
|
||||
defer testDB.TeardownDB(t, db)
|
||||
params.UseDemoBeaconConfig()
|
||||
|
||||
store := NewForkChoiceService(ctx, db)
|
||||
|
||||
crosslinks := make([]*ethpb.Crosslink, params.BeaconConfig().ShardCount)
|
||||
for i := 0; i < len(crosslinks); i++ {
|
||||
crosslinks[i] = ðpb.Crosslink{
|
||||
ParentRoot: make([]byte, 32),
|
||||
DataRoot: make([]byte, 32),
|
||||
}
|
||||
}
|
||||
s := &pb.BeaconState{
|
||||
Fork: &pb.Fork{
|
||||
Epoch: 0,
|
||||
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
|
||||
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
|
||||
},
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
StateRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
BlockRoots: make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot),
|
||||
LatestBlockHeader: ðpb.BeaconBlockHeader{},
|
||||
JustificationBits: []byte{0},
|
||||
CurrentJustifiedCheckpoint: ðpb.Checkpoint{},
|
||||
CurrentCrosslinks: crosslinks,
|
||||
CompactCommitteesRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
|
||||
}
|
||||
if err := store.GenesisStore(ctx, s); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cp1 := ðpb.Checkpoint{Epoch: 1, Root: []byte{'A'}}
|
||||
s1, err := store.saveCheckpointState(ctx, s, cp1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s1.Slot != 1*params.BeaconConfig().SlotsPerEpoch {
|
||||
t.Errorf("Wanted state slot: %d, got: %d", 1*params.BeaconConfig().SlotsPerEpoch, s1.Slot)
|
||||
}
|
||||
|
||||
cp2 := ðpb.Checkpoint{Epoch: 2, Root: []byte{'B'}}
|
||||
s2, err := store.saveCheckpointState(ctx, s, cp2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s2.Slot != 2*params.BeaconConfig().SlotsPerEpoch {
|
||||
t.Errorf("Wanted state slot: %d, got: %d", 2*params.BeaconConfig().SlotsPerEpoch, s2.Slot)
|
||||
}
|
||||
|
||||
s1, err = store.saveCheckpointState(ctx, nil, cp1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s1.Slot != 1*params.BeaconConfig().SlotsPerEpoch {
|
||||
t.Errorf("Wanted state slot: %d, got: %d", 1*params.BeaconConfig().SlotsPerEpoch, s1.Slot)
|
||||
}
|
||||
|
||||
s1, err = store.checkpointState.StateByCheckpoint(cp1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s1.Slot != 1*params.BeaconConfig().SlotsPerEpoch {
|
||||
t.Errorf("Wanted state slot: %d, got: %d", 1*params.BeaconConfig().SlotsPerEpoch, s1.Slot)
|
||||
}
|
||||
|
||||
s2, err = store.checkpointState.StateByCheckpoint(cp2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s2.Slot != 2*params.BeaconConfig().SlotsPerEpoch {
|
||||
t.Errorf("Wanted state slot: %d, got: %d", 2*params.BeaconConfig().SlotsPerEpoch, s2.Slot)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@ package forkchoice
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
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/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
@@ -37,8 +36,7 @@ type Store struct {
|
||||
db db.Database
|
||||
justifiedCheckpt *ethpb.Checkpoint
|
||||
finalizedCheckpt *ethpb.Checkpoint
|
||||
lock sync.RWMutex
|
||||
checkptBlkRoot map[[32]byte][32]byte
|
||||
checkpointState *cache.CheckpointStateCache
|
||||
}
|
||||
|
||||
// NewForkChoiceService instantiates a new service instance that will
|
||||
@@ -46,10 +44,10 @@ type Store struct {
|
||||
func NewForkChoiceService(ctx context.Context, db db.Database) *Store {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
return &Store{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
db: db,
|
||||
checkptBlkRoot: make(map[[32]byte][32]byte),
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
db: db,
|
||||
checkpointState: cache.NewCheckpointStateCache(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,13 +94,12 @@ func (s *Store) GenesisStore(ctx context.Context, genesisState *pb.BeaconState)
|
||||
return errors.Wrap(err, "could not save genesis state")
|
||||
}
|
||||
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
h, err := hashutil.HashProto(s.justifiedCheckpt)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not hash proto justified checkpoint")
|
||||
if err := s.checkpointState.AddCheckpointState(&cache.CheckpointState{
|
||||
Checkpoint: s.justifiedCheckpt,
|
||||
State: genesisState,
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "could not save genesis state in check point cache")
|
||||
}
|
||||
s.checkptBlkRoot[h] = blkRoot
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -151,17 +148,9 @@ func (s *Store) latestAttestingBalance(ctx context.Context, root []byte) (uint64
|
||||
ctx, span := trace.StartSpan(ctx, "forkchoice.latestAttestingBalance")
|
||||
defer span.End()
|
||||
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
h, err := hashutil.HashProto(s.justifiedCheckpt)
|
||||
lastJustifiedState, err := s.checkpointState.StateByCheckpoint(s.justifiedCheckpt)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not hash proto justified checkpoint")
|
||||
}
|
||||
lastJustifiedBlkRoot := s.checkptBlkRoot[h]
|
||||
|
||||
lastJustifiedState, err := s.db.State(ctx, lastJustifiedBlkRoot)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not get checkpoint state")
|
||||
return 0, errors.Wrap(err, "could not retrieve cached state via last justified check point")
|
||||
}
|
||||
if lastJustifiedState == nil {
|
||||
return 0, errors.Wrapf(err, "could not get justified state at epoch %d", s.justifiedCheckpt.Epoch)
|
||||
|
||||
@@ -8,13 +8,13 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db/filters"
|
||||
testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
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/hashutil"
|
||||
)
|
||||
|
||||
func TestStore_GenesisStoreOk(t *testing.T) {
|
||||
@@ -54,15 +54,15 @@ func TestStore_GenesisStoreOk(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(b, genesisBlk) {
|
||||
t.Error("Incorrect genesis block saved from store")
|
||||
t.Error("Incorrect genesis block saved in store")
|
||||
}
|
||||
|
||||
h, err := hashutil.HashProto(genesisCheckpt)
|
||||
cachedState, err := store.checkpointState.StateByCheckpoint(genesisCheckpt)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if store.checkptBlkRoot[h] != genesisBlkRoot {
|
||||
t.Error("Incorrect genesis check point to block root saved from store")
|
||||
if !reflect.DeepEqual(cachedState, genesisState) {
|
||||
t.Error("Incorrect genesis state cached")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,11 +258,12 @@ func TestStore_GetHead(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
store.justifiedCheckpt.Root = roots[0]
|
||||
h, err := hashutil.HashProto(store.justifiedCheckpt)
|
||||
if err != nil {
|
||||
if err := store.checkpointState.AddCheckpointState(&cache.CheckpointState{
|
||||
Checkpoint: store.justifiedCheckpt,
|
||||
State: s,
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
store.checkptBlkRoot[h] = bytesutil.ToBytes32(roots[0])
|
||||
|
||||
// /- B1 (33 votes)
|
||||
// B0 /- B5 - B7 (33 votes)
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
@@ -190,7 +189,6 @@ func (c *ChainService) saveValidatorIdx(ctx context.Context, state *pb.BeaconSta
|
||||
nextEpoch := helpers.CurrentEpoch(state) + 1
|
||||
activatedValidators := validators.ActivatedValFromEpoch(nextEpoch)
|
||||
var idxNotInState []uint64
|
||||
fmt.Println(activatedValidators)
|
||||
for _, idx := range activatedValidators {
|
||||
// If for some reason the activated validator indices is not in state,
|
||||
// we skip them and save them to process for next epoch.
|
||||
|
||||
4
beacon-chain/cache/BUILD.bazel
vendored
4
beacon-chain/cache/BUILD.bazel
vendored
@@ -8,6 +8,7 @@ go_library(
|
||||
"active_indices.go",
|
||||
"attestation_data.go",
|
||||
"block.go",
|
||||
"checkpoint_state.go",
|
||||
"common.go",
|
||||
"eth1_data.go",
|
||||
"seed.go",
|
||||
@@ -22,6 +23,7 @@ go_library(
|
||||
"//proto/beacon/rpc/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
|
||||
@@ -39,6 +41,7 @@ go_test(
|
||||
"attestation_data_test.go",
|
||||
"benchmarks_test.go",
|
||||
"block_test.go",
|
||||
"checkpoint_state_test.go",
|
||||
"eth1_data_test.go",
|
||||
"feature_flag_test.go",
|
||||
"seed_test.go",
|
||||
@@ -53,6 +56,7 @@ go_test(
|
||||
"//proto/beacon/rpc/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
],
|
||||
|
||||
113
beacon-chain/cache/checkpoint_state.go
vendored
Normal file
113
beacon-chain/cache/checkpoint_state.go
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotCheckpointState will be returned when a cache object is not a pointer to
|
||||
// a CheckpointState struct.
|
||||
ErrNotCheckpointState = errors.New("object is not a state by check point struct")
|
||||
|
||||
// maxCheckpointStateSize defines the max number of entries check point to state cache can contain.
|
||||
maxCheckpointStateSize = 4
|
||||
|
||||
// Metrics.
|
||||
checkpointStateMiss = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "check_point_statecache_miss",
|
||||
Help: "The number of check point state requests that aren't present in the cache.",
|
||||
})
|
||||
checkpointStateHit = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "check_point_state_cache_hit",
|
||||
Help: "The number of check point state requests that are present in the cache.",
|
||||
})
|
||||
)
|
||||
|
||||
// CheckpointState defines the active validator indices per epoch.
|
||||
type CheckpointState struct {
|
||||
Checkpoint *ethpb.Checkpoint
|
||||
State *pb.BeaconState
|
||||
}
|
||||
|
||||
// CheckpointStateCache is a struct with 1 queue for looking up state by checkpoint.
|
||||
type CheckpointStateCache struct {
|
||||
cache *cache.FIFO
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// checkpointState takes the checkpoint as the key of the resulting state.
|
||||
func checkpointState(obj interface{}) (string, error) {
|
||||
info, ok := obj.(*CheckpointState)
|
||||
if !ok {
|
||||
return "", ErrNotCheckpointState
|
||||
}
|
||||
|
||||
h, err := hashutil.HashProto(info.Checkpoint)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(h[:]), nil
|
||||
}
|
||||
|
||||
// NewCheckpointStateCache creates a new checkpoint state cache for storing/accessing processed state.
|
||||
func NewCheckpointStateCache() *CheckpointStateCache {
|
||||
return &CheckpointStateCache{
|
||||
cache: cache.NewFIFO(checkpointState),
|
||||
}
|
||||
}
|
||||
|
||||
// StateByCheckpoint fetches state by checkpoint. Returns true with a
|
||||
// reference to the CheckpointState info, if exists. Otherwise returns false, nil.
|
||||
func (c *CheckpointStateCache) StateByCheckpoint(cp *ethpb.Checkpoint) (*pb.BeaconState, error) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
h, err := hashutil.HashProto(cp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
obj, exists, err := c.cache.GetByKey(string(h[:]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if exists {
|
||||
checkpointStateHit.Inc()
|
||||
} else {
|
||||
checkpointStateMiss.Inc()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
info, ok := obj.(*CheckpointState)
|
||||
if !ok {
|
||||
return nil, ErrNotCheckpointState
|
||||
}
|
||||
|
||||
return info.State, nil
|
||||
}
|
||||
|
||||
// AddCheckpointState adds CheckpointState object to the cache. This method also trims the least
|
||||
// recently added CheckpointState object if the cache size has ready the max cache size limit.
|
||||
func (c *CheckpointStateCache) AddCheckpointState(cp *CheckpointState) error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if err := c.cache.AddIfNotPresent(cp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
trim(c.cache, maxCheckpointStateSize)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckpointStateKeys returns the keys of the state in cache.
|
||||
func (c *CheckpointStateCache) CheckpointStateKeys() []string {
|
||||
return c.cache.ListKeys()
|
||||
}
|
||||
110
beacon-chain/cache/checkpoint_state_test.go
vendored
Normal file
110
beacon-chain/cache/checkpoint_state_test.go
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
)
|
||||
|
||||
func TestCheckpointStateCacheKeyFn_OK(t *testing.T) {
|
||||
cp := ðpb.Checkpoint{Epoch: 1, Root: []byte{'A'}}
|
||||
info := &CheckpointState{
|
||||
Checkpoint: cp,
|
||||
State: &pb.BeaconState{Slot: 64},
|
||||
}
|
||||
key, err := checkpointState(info)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wantedKey, err := hashutil.HashProto(cp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if key != string(wantedKey[:]) {
|
||||
t.Errorf("Incorrect hash key: %s, expected %s", key, string(wantedKey[:]))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckpointStateCacheKeyFn_InvalidObj(t *testing.T) {
|
||||
_, err := checkpointState("bad")
|
||||
if err != ErrNotCheckpointState {
|
||||
t.Errorf("Expected error %v, got %v", ErrNotCheckpointState, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckpointStateCache_StateByCheckpoint(t *testing.T) {
|
||||
cache := NewCheckpointStateCache()
|
||||
|
||||
cp1 := ðpb.Checkpoint{Epoch: 1, Root: []byte{'A'}}
|
||||
info1 := &CheckpointState{
|
||||
Checkpoint: cp1,
|
||||
State: &pb.BeaconState{Slot: 64},
|
||||
}
|
||||
state, err := cache.StateByCheckpoint(cp1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if state != nil {
|
||||
t.Error("Expected state not to exist in empty cache")
|
||||
}
|
||||
|
||||
if err := cache.AddCheckpointState(info1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
state, err = cache.StateByCheckpoint(cp1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(state, info1.State) {
|
||||
t.Error("incorrectly cached state")
|
||||
}
|
||||
|
||||
cp2 := ðpb.Checkpoint{Epoch: 2, Root: []byte{'B'}}
|
||||
info2 := &CheckpointState{
|
||||
Checkpoint: cp2,
|
||||
State: &pb.BeaconState{Slot: 128},
|
||||
}
|
||||
if err := cache.AddCheckpointState(info2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
state, err = cache.StateByCheckpoint(cp2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(state, info2.State) {
|
||||
t.Error("incorrectly cached state")
|
||||
}
|
||||
|
||||
state, err = cache.StateByCheckpoint(cp1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(state, info1.State) {
|
||||
t.Error("incorrectly cached state")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckpointStateCache__MaxSize(t *testing.T) {
|
||||
c := NewCheckpointStateCache()
|
||||
|
||||
for i := 0; i < maxCheckpointStateSize+100; i++ {
|
||||
info := &CheckpointState{
|
||||
Checkpoint: ðpb.Checkpoint{Epoch: uint64(i)},
|
||||
State: &pb.BeaconState{Slot: uint64(i)},
|
||||
}
|
||||
if err := c.AddCheckpointState(info); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(c.cache.ListKeys()) != maxCheckpointStateSize {
|
||||
t.Errorf(
|
||||
"Expected hash cache key size to be %d, got %d",
|
||||
maxCheckpointStateSize,
|
||||
len(c.cache.ListKeys()),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -74,9 +74,9 @@ func (s *Service) Start() {
|
||||
|
||||
// TODO(3147): Add gossip sub options
|
||||
// Gossipsub registration is done before we add in any new peers
|
||||
// due to libp2p's gossipsub implementation not taking into
|
||||
// due to libp2p's gossipsub implementation not taking into
|
||||
// account previously added peers when creating the gossipsub
|
||||
// object.
|
||||
// object.
|
||||
gs, err := pubsub.NewGossipSub(s.ctx, s.host)
|
||||
if err != nil {
|
||||
s.startupErr = err
|
||||
|
||||
Reference in New Issue
Block a user