diff --git a/beacon-chain/chaintest/state-replay/BUILD.bazel b/beacon-chain/chaintest/state-replay/BUILD.bazel new file mode 100644 index 0000000000..e69de29bb2 diff --git a/beacon-chain/core/helpers/BUILD.bazel b/beacon-chain/core/helpers/BUILD.bazel index 3a6b5fc442..eb1e0b8b36 100644 --- a/beacon-chain/core/helpers/BUILD.bazel +++ b/beacon-chain/core/helpers/BUILD.bazel @@ -21,8 +21,6 @@ go_library( "//shared/mathutil:go_default_library", "//shared/params:go_default_library", "//shared/ssz:go_default_library", - "@org_golang_google_grpc//codes:go_default_library", - "@org_golang_google_grpc//status:go_default_library", ], ) @@ -41,7 +39,5 @@ go_test( "//proto/beacon/p2p/v1:go_default_library", "//shared/params:go_default_library", "@com_github_gogo_protobuf//proto:go_default_library", - "@org_golang_google_grpc//codes:go_default_library", - "@org_golang_google_grpc//status:go_default_library", ], ) diff --git a/beacon-chain/core/helpers/committee.go b/beacon-chain/core/helpers/committee.go index 870413bccf..3b0e419c9e 100644 --- a/beacon-chain/core/helpers/committee.go +++ b/beacon-chain/core/helpers/committee.go @@ -13,8 +13,6 @@ import ( "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/mathutil" "github.com/prysmaticlabs/prysm/shared/params" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) // CrosslinkCommittee defines the validator committee of slot and shard combinations. @@ -379,7 +377,6 @@ func CommitteeAssignment( slot uint64, validatorIndex uint64, registryChange bool) ([]uint64, uint64, uint64, bool, error) { - var selectedCommittees []*CrosslinkCommittee wantedEpoch := slot / params.BeaconConfig().SlotsPerEpoch prevEpoch := PrevEpoch(state) @@ -394,31 +391,36 @@ func CommitteeAssignment( ) } - startSlot := StartSlot(wantedEpoch) - for slot := startSlot; slot < startSlot+params.BeaconConfig().SlotsPerEpoch; slot++ { - crosslinkCommittees, err := CrosslinkCommitteesAtSlot( - state, slot, registryChange) - if err != nil { - return []uint64{}, 0, 0, false, fmt.Errorf("could not get crosslink committee: %v", err) - } - for _, committee := range crosslinkCommittees { - for _, idx := range committee.Committee { - if idx == validatorIndex { - selectedCommittees = append(selectedCommittees, committee) - } + crosslinkCommittees, err := CrosslinkCommitteesAtSlot( + state, slot, registryChange) + if err != nil { + return []uint64{}, 0, 0, false, fmt.Errorf("could not get crosslink committee: %v", err) + } + return ValidatorAssignment(validatorIndex, slot, crosslinkCommittees) +} - if len(selectedCommittees) > 0 { - validators := selectedCommittees[0].Committee - shard := selectedCommittees[0].Shard - firstCommitteeAtSlot := crosslinkCommittees[0].Committee - isProposer := firstCommitteeAtSlot[slot% - uint64(len(firstCommitteeAtSlot))] == validatorIndex - return validators, shard, slot, isProposer, nil - } +// ValidatorAssignment takes individual validator's index and returns its committee list, +// assigned shard, slot and role. +func ValidatorAssignment(vIndex uint64, slot uint64, committees []*CrosslinkCommittee) ([]uint64, uint64, uint64, bool, error) { + var selectedCommittees []*CrosslinkCommittee + for _, committee := range committees { + for _, idx := range committee.Committee { + if idx == vIndex { + selectedCommittees = append(selectedCommittees, committee) + } + + if len(selectedCommittees) > 0 { + validators := selectedCommittees[0].Committee + shard := selectedCommittees[0].Shard + firstCommitteeAtSlot := committees[0].Committee + isProposer := firstCommitteeAtSlot[slot% + uint64(len(firstCommitteeAtSlot))] == vIndex + return validators, shard, slot, isProposer, nil } } } - return []uint64{}, 0, 0, false, status.Error(codes.NotFound, "validator not found found in assignments") + return []uint64{}, 0, 0, false, fmt.Errorf("unable to find assignment for "+ + "validator %d at slot %d", vIndex, slot) } // prevEpochCommitteesAtSlot returns a list of crosslink committees of the previous epoch. diff --git a/beacon-chain/core/helpers/committee_test.go b/beacon-chain/core/helpers/committee_test.go index c45ba49b87..ad81e9cb37 100644 --- a/beacon-chain/core/helpers/committee_test.go +++ b/beacon-chain/core/helpers/committee_test.go @@ -8,8 +8,6 @@ import ( pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/params" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) var size = 1<<(params.BeaconConfig().RandBytes*8) - 1 @@ -498,12 +496,11 @@ func TestCommitteeAssignment_CantFindValidator(t *testing.T) { Slot: params.BeaconConfig().GenesisSlot + params.BeaconConfig().SlotsPerEpoch, } index := uint64(10000) + want := fmt.Sprintf( + "unable to find assignment for validator %d at slot %d", + index, params.BeaconConfig().GenesisSlot+params.BeaconConfig().SlotsPerEpoch) _, _, _, _, err := CommitteeAssignment(state, state.Slot, index, false) - statusErr, ok := status.FromError(err) - if !ok { - t.Fatal(err) - } - if statusErr.Code() != codes.NotFound { - t.Errorf("Unexpected error: %v", err) + if !strings.Contains(err.Error(), want) { + t.Errorf("Expected: %s, received: %v", want, err) } } diff --git a/beacon-chain/rpc/BUILD.bazel b/beacon-chain/rpc/BUILD.bazel index 925959a4a8..8f44d99379 100644 --- a/beacon-chain/rpc/BUILD.bazel +++ b/beacon-chain/rpc/BUILD.bazel @@ -5,6 +5,7 @@ go_library( srcs = [ "attester_server.go", "beacon_server.go", + "committees_cache.go", "proposer_server.go", "service.go", "validator_server.go", @@ -31,11 +32,17 @@ go_library( "@com_github_grpc_ecosystem_go_grpc_middleware//:go_default_library", "@com_github_grpc_ecosystem_go_grpc_middleware//recovery:go_default_library", "@com_github_grpc_ecosystem_go_grpc_prometheus//:go_default_library", + "@com_github_prometheus_client_golang//prometheus:go_default_library", + "@com_github_prometheus_client_golang//prometheus/promauto:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", + "@io_k8s_client_go//tools/cache:go_default_library", "@io_opencensus_go//plugin/ocgrpc:go_default_library", + "@io_opencensus_go//trace:go_default_library", "@org_golang_google_grpc//:go_default_library", + "@org_golang_google_grpc//codes:go_default_library", "@org_golang_google_grpc//credentials:go_default_library", "@org_golang_google_grpc//reflection:go_default_library", + "@org_golang_google_grpc//status:go_default_library", ], ) @@ -44,6 +51,7 @@ go_test( srcs = [ "attester_server_test.go", "beacon_server_test.go", + "committee_cache_test.go", "proposer_server_test.go", "service_test.go", "validator_server_test.go", diff --git a/beacon-chain/rpc/committee_cache_test.go b/beacon-chain/rpc/committee_cache_test.go new file mode 100644 index 0000000000..2eaaaa8337 --- /dev/null +++ b/beacon-chain/rpc/committee_cache_test.go @@ -0,0 +1,97 @@ +package rpc + +import ( + "reflect" + "strconv" + "testing" + + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" +) + +func TestSlotKeyFn_OK(t *testing.T) { + cInfo := &committeesInfo{ + slot: 999, + committees: []*helpers.CrosslinkCommittee{ + {Shard: 1, Committee: []uint64{1, 2, 3}}, + {Shard: 1, Committee: []uint64{4, 5, 6}}, + }, + } + + key, err := slotKeyFn(cInfo) + if err != nil { + t.Fatal(err) + } + if key != strconv.Itoa(cInfo.slot) { + t.Errorf("Incorrect hash key: %s, expected %s", key, strconv.Itoa(cInfo.slot)) + } +} + +func TestSlotKeyFn_InvalidObj(t *testing.T) { + _, err := slotKeyFn("bad") + if err != ErrNotACommitteeInfo { + t.Errorf("Expected error %v, got %v", ErrNotACommitteeInfo, err) + } +} + +func TestCommitteesCache_CommitteesInfoBySlot(t *testing.T) { + cache := newCommitteesCache() + + cInfo := &committeesInfo{ + slot: 123, + committees: []*helpers.CrosslinkCommittee{{Shard: 456}}, + } + + exists, _, err := cache.CommitteesInfoBySlot(cInfo.slot) + if err != nil { + t.Fatal(err) + } + if exists { + t.Error("Expected committees info not to exist in empty cache") + } + + if err := cache.AddCommittees(cInfo); err != nil { + t.Fatal(err) + } + exists, fetchedInfo, err := cache.CommitteesInfoBySlot(cInfo.slot) + if err != nil { + t.Fatal(err) + } + if !exists { + t.Error("Expected committee info to exist") + } + if fetchedInfo.slot != cInfo.slot { + t.Errorf( + "Expected fetched slot number to be %d, got %d", + cInfo.slot, + fetchedInfo.slot, + ) + } + if !reflect.DeepEqual(fetchedInfo.committees, cInfo.committees) { + t.Errorf( + "Expected fetched info committee to be %v, got %v", + cInfo.committees, + fetchedInfo.committees, + ) + } +} + +func TestBlockCache_maxSize(t *testing.T) { + cache := newCommitteesCache() + + for i := 0; i < maxCacheSize+10; i++ { + cInfo := &committeesInfo{ + slot: i, + } + if err := cache.AddCommittees(cInfo); err != nil { + t.Fatal(err) + } + } + + if len(cache.committeesCache.ListKeys()) != maxCacheSize { + t.Errorf( + "Expected hash cache key size to be %d, got %d", + maxCacheSize, + len(cache.committeesCache.ListKeys()), + ) + } +} diff --git a/beacon-chain/rpc/committees_cache.go b/beacon-chain/rpc/committees_cache.go new file mode 100644 index 0000000000..549e01d612 --- /dev/null +++ b/beacon-chain/rpc/committees_cache.go @@ -0,0 +1,122 @@ +package rpc + +import ( + "errors" + "strconv" + "sync" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" + "github.com/prysmaticlabs/prysm/shared/params" + "k8s.io/client-go/tools/cache" +) + +var ( + // ErrNotACommitteeInfo will be returned when a cache object is not a pointer to + // a committeeInfo struct. + ErrNotACommitteeInfo = errors.New("object is not an committee info") + + // maxCacheSize is 4x of the epoch length for additional cache padding. + // Requests should be only accessing committees within defined epoch length. + maxCacheSize = int(2 * params.BeaconConfig().SlotsPerEpoch) + + // Metrics + committeeCacheMiss = promauto.NewCounter(prometheus.CounterOpts{ + Name: "committee_cache_miss", + Help: "The number of committee requests that aren't present in the cache.", + }) + committeeCacheHit = promauto.NewCounter(prometheus.CounterOpts{ + Name: "committee_cache_hit", + Help: "The number of committee requests that are present in the cache.", + }) + committeeCacheSize = promauto.NewGauge(prometheus.GaugeOpts{ + Name: "committee_cache_size", + Help: "The number of committees in the committee cache", + }) +) + +// committeesInfo species the committee information of a given slot. +type committeesInfo struct { + slot int + committees []*helpers.CrosslinkCommittee +} + +// committeesCache struct with 1 queue for looking up crosslink committees by slot. +type committeesCache struct { + committeesCache *cache.FIFO + lock sync.RWMutex +} + +// slotKeyFn takes the string representation of the slot number as the key +// for a committeeInfo. +func slotKeyFn(obj interface{}) (string, error) { + cInfo, ok := obj.(*committeesInfo) + if !ok { + return "", ErrNotACommitteeInfo + } + + return strconv.Itoa(cInfo.slot), nil +} + +// newCommitteesCache creates a new committee cache for storing/accessing blockInfo from +// memory. +func newCommitteesCache() *committeesCache { + return &committeesCache{ + committeesCache: cache.NewFIFO(slotKeyFn), + } +} + +// CommitteesInfoBySlot fetches committeesInfo by slot. Returns true with a +// reference to the committees info, if exists. Otherwise returns false, nil. +func (c *committeesCache) CommitteesInfoBySlot(slot int) (bool, *committeesInfo, error) { + c.lock.RLock() + defer c.lock.RUnlock() + + obj, exists, err := c.committeesCache.GetByKey(strconv.Itoa(slot)) + if err != nil { + return false, nil, err + } + + if exists { + committeeCacheHit.Inc() + } else { + committeeCacheMiss.Inc() + return false, nil, nil + } + + cInfo, ok := obj.(*committeesInfo) + if !ok { + return false, nil, ErrNotACommitteeInfo + } + + return true, cInfo, nil +} + +// AddCommittees adds committeeInfo object to the cache. This method also trims the least +// recently added committeeInfo object if the cache size has ready the max cache size limit. +func (c *committeesCache) AddCommittees(committees *committeesInfo) error { + c.lock.Lock() + defer c.lock.Unlock() + + if err := c.committeesCache.AddIfNotPresent(committees); err != nil { + return err + } + + trim(c.committeesCache, maxCacheSize) + committeeCacheSize.Set(float64(len(c.committeesCache.ListKeys()))) + return nil +} + +// trim the FIFO queue to the maxSize. +func trim(queue *cache.FIFO, maxSize int) { + for s := len(queue.ListKeys()); s > maxSize; s-- { + // #nosec G104 popProcessNoopFunc never returns an error + _, _ = queue.Pop(popProcessNoopFunc) + } +} + +// popProcessNoopFunc is a no-op function that never returns an error. +func popProcessNoopFunc(obj interface{}) error { + return nil +} diff --git a/beacon-chain/rpc/service.go b/beacon-chain/rpc/service.go index 7d7b2a1334..7122e768db 100644 --- a/beacon-chain/rpc/service.go +++ b/beacon-chain/rpc/service.go @@ -71,6 +71,7 @@ type Service struct { canonicalStateChan chan *pbp2p.BeaconState incomingAttestation chan *pbp2p.Attestation credentialError error + committeesCache *committeesCache // cache to store committees info. } // Config options for the beacon node RPC server. @@ -100,6 +101,7 @@ func NewRPCService(ctx context.Context, cfg *Config) *Service { withKey: cfg.KeyFlag, canonicalStateChan: make(chan *pbp2p.BeaconState, params.BeaconConfig().DefaultBufferSize), incomingAttestation: make(chan *pbp2p.Attestation, params.BeaconConfig().DefaultBufferSize), + committeesCache: newCommitteesCache(), } } @@ -164,6 +166,7 @@ func (s *Service) Start() { beaconDB: s.beaconDB, chainService: s.chainService, canonicalStateChan: s.canonicalStateChan, + committeesCache: s.committeesCache, } pb.RegisterBeaconServiceServer(s.grpcServer, beaconServer) pb.RegisterProposerServiceServer(s.grpcServer, proposerServer) diff --git a/beacon-chain/rpc/validator_server.go b/beacon-chain/rpc/validator_server.go index d3f6708faa..51a2e1b3c8 100644 --- a/beacon-chain/rpc/validator_server.go +++ b/beacon-chain/rpc/validator_server.go @@ -11,8 +11,12 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/db" pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" + "github.com/prysmaticlabs/prysm/shared/featureconfig" "github.com/prysmaticlabs/prysm/shared/hashutil" "github.com/prysmaticlabs/prysm/shared/params" + "go.opencensus.io/trace" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) // ValidatorServer defines a server implementation of the gRPC Validator service, @@ -24,6 +28,7 @@ type ValidatorServer struct { beaconDB *db.BeaconDB chainService chainService canonicalStateChan chan *pbp2p.BeaconState + committeesCache *committeesCache } // WaitForActivation checks if a validator public key exists in the active validator registry of the current @@ -116,6 +121,7 @@ func (vs *ValidatorServer) ValidatorPerformance( func (vs *ValidatorServer) CommitteeAssignment( ctx context.Context, req *pb.CommitteeAssignmentsRequest) (*pb.CommitteeAssignmentResponse, error) { + beaconState, err := vs.beaconDB.HeadState(ctx) if err != nil { return nil, fmt.Errorf("could not fetch beacon state: %v", err) @@ -140,6 +146,9 @@ func (vs *ValidatorServer) assignment( epochStart uint64, ) (*pb.CommitteeAssignmentResponse_CommitteeAssignment, error) { + ctx, span := trace.StartSpan(ctx, "beacon-chain.rpcservice.CommitteeAssignment.assignment") + defer span.End() + if len(pubkey) != params.BeaconConfig().BLSPubkeyLength { return nil, fmt.Errorf( "expected public key to have length %d, received %d", @@ -150,8 +159,9 @@ func (vs *ValidatorServer) assignment( idx, err := vs.beaconDB.ValidatorIndex(pubkey) if err != nil { - return nil, fmt.Errorf("could not get active validator index: %v", err) + return nil, err } + chainHead, err := vs.beaconDB.ChainHead() if err != nil { return nil, fmt.Errorf("could not get chain head: %v", err) @@ -169,23 +179,64 @@ func (vs *ValidatorServer) assignment( } } - committee, shard, slot, isProposer, err := - helpers.CommitteeAssignment(beaconState, epochStart, uint64(idx), false) + vStatus, err := vs.validatorStatus(pubkey, beaconState) if err != nil { return nil, err } - status, err := vs.validatorStatus(pubkey, beaconState) - if err != nil { - return nil, err + + for slot := epochStart; slot < epochStart+params.BeaconConfig().SlotsPerEpoch; slot++ { + if featureconfig.FeatureConfig().EnableCommitteesCache { + if exists, committees, err := vs.committeesCache.CommitteesInfoBySlot(int(slot)); exists || err != nil { + if err != nil { + return nil, err + } + span.AddAttributes(trace.BoolAttribute("committeeCacheHit", true)) + committee, shard, slot, isProposer, err := + helpers.ValidatorAssignment(idx, slot, committees.committees) + if err == nil { + return &pb.CommitteeAssignmentResponse_CommitteeAssignment{ + Committee: committee, + Shard: shard, + Slot: slot, + IsProposer: isProposer, + PublicKey: pubkey, + Status: vStatus, + }, nil + } + } + } + committees, err := helpers.CrosslinkCommitteesAtSlot( + beaconState, + slot, + false /* registeryChange */) + if err != nil { + return nil, err + } + if featureconfig.FeatureConfig().EnableCommitteesCache { + committeesInfo := &committeesInfo{ + slot: int(slot), + committees: committees, + } + span.AddAttributes(trace.BoolAttribute("committeeCacheHit", false)) + if err := vs.committeesCache.AddCommittees(committeesInfo); err != nil { + return nil, err + } + } + committee, shard, slot, isProposer, err := + helpers.ValidatorAssignment(idx, slot, committees) + if err == nil { + return &pb.CommitteeAssignmentResponse_CommitteeAssignment{ + Committee: committee, + Shard: shard, + Slot: slot, + IsProposer: isProposer, + PublicKey: pubkey, + Status: vStatus, + }, nil + } } - return &pb.CommitteeAssignmentResponse_CommitteeAssignment{ - Committee: committee, - Shard: shard, - Slot: slot, - IsProposer: isProposer, - PublicKey: pubkey, - Status: status, - }, nil + + return nil, status.Error(codes.NotFound, "validator not found found in assignments") } // ValidatorStatus returns the validator status of the current epoch. diff --git a/beacon-chain/rpc/validator_server_test.go b/beacon-chain/rpc/validator_server_test.go index 38b434048c..d848f7224b 100644 --- a/beacon-chain/rpc/validator_server_test.go +++ b/beacon-chain/rpc/validator_server_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/binary" "fmt" + "reflect" "strconv" "strings" "sync" @@ -17,9 +18,16 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/internal" pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" + "github.com/prysmaticlabs/prysm/shared/featureconfig" "github.com/prysmaticlabs/prysm/shared/params" ) +func init() { + featureconfig.InitFeatureConfig(&featureconfig.FeatureFlagConfig{ + EnableCommitteesCache: true, + }) +} + func genesisState(validators uint64) (*pbp2p.BeaconState, error) { genesisTime := time.Unix(0, 0).Unix() deposits := make([]*pbp2p.Deposit, validators) @@ -103,7 +111,7 @@ func TestNextEpochCommitteeAssignment_CantFindValidatorIdx(t *testing.T) { } } -func TestCommitteeAssignment_OK(t *testing.T) { +func TestCommitteeAssignment_CacheMissOk(t *testing.T) { db := internal.SetupDB(t) defer internal.TeardownDB(t, db) ctx := context.Background() @@ -140,7 +148,8 @@ func TestCommitteeAssignment_OK(t *testing.T) { } vs := &ValidatorServer{ - beaconDB: db, + beaconDB: db, + committeesCache: newCommitteesCache(), } pubKeyBuf := make([]byte, params.BeaconConfig().BLSPubkeyLength) @@ -185,7 +194,7 @@ func TestCommitteeAssignment_OK(t *testing.T) { } } -func TestCommitteeAssignment_multipleKeys_OK(t *testing.T) { +func TestCommitteeAssignment_MultipleKeys_CacheHitOk(t *testing.T) { db := internal.SetupDB(t) defer internal.TeardownDB(t, db) ctx := context.Background() @@ -222,23 +231,51 @@ func TestCommitteeAssignment_multipleKeys_OK(t *testing.T) { } vs := &ValidatorServer{ - beaconDB: db, + + beaconDB: db, + committeesCache: newCommitteesCache(), } pubKeyBuf0 := make([]byte, params.BeaconConfig().BLSPubkeyLength) binary.PutUvarint(pubKeyBuf0, 0) pubKeyBuf1 := make([]byte, params.BeaconConfig().BLSPubkeyLength) - binary.PutUvarint(pubKeyBuf1, 0) + binary.PutUvarint(pubKeyBuf1, 1) // Test the first validator in registry. req := &pb.CommitteeAssignmentsRequest{ PublicKeys: [][]byte{pubKeyBuf0, pubKeyBuf1}, EpochStart: params.BeaconConfig().GenesisSlot, } + cInfo := &committeesInfo{ + slot: int(params.BeaconConfig().GenesisSlot), + committees: []*helpers.CrosslinkCommittee{ + {Shard: 123, Committee: []uint64{0, 10, 5}}, + {Shard: 234, Committee: []uint64{1, 2, 3}}, + }} + if err := vs.committeesCache.AddCommittees(cInfo); err != nil { + t.Fatal(err) + } res, err := vs.CommitteeAssignment(context.Background(), req) if err != nil { t.Fatalf("Could not call epoch committee assignment %v", err) } + if res.Assignment[0].Shard != cInfo.committees[0].Shard { + t.Errorf("Assigned shard %d is not equal to %d", + res.Assignment[0].Shard, cInfo.committees[0].Shard) + } + if !reflect.DeepEqual(res.Assignment[0].Committee, cInfo.committees[0].Committee) { + t.Errorf("Expected committee to be %v, got %v", + cInfo.committees[0].Committee, res.Assignment[0].Committee) + } + if res.Assignment[1].Shard != cInfo.committees[1].Shard { + t.Errorf("Assigned shard %d is not equal to %d", + res.Assignment[1].Shard, cInfo.committees[1].Shard) + } + if !reflect.DeepEqual(res.Assignment[1].Committee, cInfo.committees[1].Committee) { + t.Errorf("Expected committee to be %v, got %v", + cInfo.committees[1].Committee, res.Assignment[1].Committee) + } + if len(res.Assignment) != 2 { t.Fatalf("expected 2 assignments but got %d", len(res.Assignment)) } diff --git a/shared/featureconfig/config.go b/shared/featureconfig/config.go index 6621ec4e46..4c64d9b36b 100644 --- a/shared/featureconfig/config.go +++ b/shared/featureconfig/config.go @@ -30,6 +30,7 @@ type FeatureFlagConfig struct { EnableCrosslinks bool // EnableCrosslinks in epoch processing. EnableCheckBlockStateRoot bool // EnableCheckBlockStateRoot in block processing. EnableHistoricalStatePruning bool // EnableHistoricalStatePruning when updatifinalized states. + EnableCommitteesCache bool // EnableCommitteesCache for RPC server. } var featureConfig *FeatureFlagConfig @@ -60,6 +61,10 @@ func ConfigureBeaconFeatures(ctx *cli.Context) { log.Info("Enabled crosslink computations") cfg.EnableCrosslinks = true } + if ctx.GlobalBool(EnableCommitteesCacheFlag.Name) { + log.Info("Enabled committees cache") + cfg.EnableCommitteesCache = true + } if ctx.GlobalBool(EnableCheckBlockStateRootFlag.Name) { log.Info("Enabled check block state root") cfg.EnableCheckBlockStateRoot = true @@ -68,7 +73,6 @@ func ConfigureBeaconFeatures(ctx *cli.Context) { log.Info("Enabled historical state pruning") cfg.EnableHistoricalStatePruning = true } - InitFeatureConfig(cfg) } diff --git a/shared/featureconfig/flags.go b/shared/featureconfig/flags.go index dcd8218747..ea51710175 100644 --- a/shared/featureconfig/flags.go +++ b/shared/featureconfig/flags.go @@ -24,6 +24,11 @@ var ( Name: "enable-crosslinks", Usage: "Enable crosslinks in epoch processing, default is disabled.", } + // EnableCommitteesCacheFlag enables crosslink committees cache for rpc server. It is disabled by default. + EnableCommitteesCacheFlag = cli.BoolFlag{ + Name: "enable-committees-cache", + Usage: "Enable crosslink committees cache for rpc server, default is disabled.", + } // EnableCheckBlockStateRootFlag check block state root in block processing. It is disabled by default. EnableCheckBlockStateRootFlag = cli.BoolFlag{ Name: "enable-check-block-state-root", @@ -43,6 +48,7 @@ var ValidatorFlags = []cli.Flag{} var BeaconChainFlags = []cli.Flag{ EnableComputeStateRootFlag, EnableCrosslinksFlag, + EnableCommitteesCacheFlag, EnableCheckBlockStateRootFlag, EnableHistoricalStatePruningFlag, } diff --git a/validator/client/runner.go b/validator/client/runner.go index 0657bacf04..db2b85694f 100644 --- a/validator/client/runner.go +++ b/validator/client/runner.go @@ -97,9 +97,9 @@ func run(ctx context.Context, v Validator) { func handleAssignmentError(err error, slot uint64) { if errCode, ok := status.FromError(err); ok && errCode.Code() == codes.NotFound { - log.WithField( - "epoch", (slot/params.BeaconConfig().SlotsPerEpoch)-params.BeaconConfig().GenesisEpoch, - ).Warn("Validator not yet assigned to epoch") + log.Warnf("Validator not yet assigned between slots %d - %d", + slot - params.BeaconConfig().GenesisSlot, + slot + params.BeaconConfig().SlotsPerEpoch - params.BeaconConfig().GenesisSlot) } else { log.WithField("error", err).Error("Failed to update assignments") }