Add checkpoint to state caching (#3333)

This commit is contained in:
terence tsao
2019-08-27 15:01:27 -07:00
committed by GitHub
parent 3a138b9e77
commit 323bbe10ed
12 changed files with 381 additions and 75 deletions

View File

@@ -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",

View File

@@ -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++ {

View File

@@ -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 {

View File

@@ -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.

View File

@@ -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] = &ethpb.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: &ethpb.BeaconBlockHeader{},
JustificationBits: []byte{0},
CurrentJustifiedCheckpoint: &ethpb.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 := &ethpb.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 := &ethpb.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)
}
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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.

View File

@@ -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
View 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()
}

View 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 := &ethpb.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 := &ethpb.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 := &ethpb.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: &ethpb.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()),
)
}
}

View File

@@ -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