mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 13:28:01 -05:00
Remove deprecated services and --next (#3371)
* Save new validators in DB * Use info * Add total validator count * Fixed tests * Add new test * Revert light client config * Add state metrics back * Gaz * Mark old ones as deprecated * Deprecate not --next services * Fixed all operation tests * Fixed node test * All tests passing locally * Add deprecated-p2p back, blocked by boostrap-query * Revert message proto * delete deprecated DB items * delete all other instances of old db * gaz * cycle rem * clear db
This commit is contained in:
committed by
Raul Jordan
parent
75bce9b7e1
commit
14c59b2ff9
@@ -1,44 +0,0 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"service.go",
|
||||
"vote_metrics.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/attestation",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/event:go_default_library",
|
||||
"//shared/messagehandler:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto: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",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
srcs = ["service_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/internal:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -1,283 +0,0 @@
|
||||
// Package attestation defines the life-cycle and status of single and aggregated attestation.
|
||||
package attestation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
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/event"
|
||||
handler "github.com/prysmaticlabs/prysm/shared/messagehandler"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var log = logrus.WithField("prefix", "attestation")
|
||||
|
||||
// TargetHandler provides an interface for fetching latest attestation targets
|
||||
// and updating attestations in batches.
|
||||
type TargetHandler interface {
|
||||
LatestAttestationTarget(state *pb.BeaconState, validatorIndex uint64) (*pb.AttestationTarget, error)
|
||||
BatchUpdateLatestAttestations(ctx context.Context, atts []*ethpb.Attestation) error
|
||||
}
|
||||
|
||||
type attestationStore struct {
|
||||
sync.RWMutex
|
||||
m map[[48]byte]*ethpb.Attestation
|
||||
}
|
||||
|
||||
// Service represents a service that handles the internal
|
||||
// logic of managing single and aggregated attestation.
|
||||
type Service struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
beaconDB db.Database
|
||||
incomingFeed *event.Feed
|
||||
incomingChan chan *ethpb.Attestation
|
||||
// store is the mapping of individual
|
||||
// validator's public key to it's latest attestation.
|
||||
store attestationStore
|
||||
pooledAttestations []*ethpb.Attestation
|
||||
poolLimit int
|
||||
}
|
||||
|
||||
// Config options for the service.
|
||||
type Config struct {
|
||||
BeaconDB db.Database
|
||||
}
|
||||
|
||||
// NewAttestationService instantiates a new service instance that will
|
||||
// be registered into a running beacon node.
|
||||
func NewAttestationService(ctx context.Context, cfg *Config) *Service {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
return &Service{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
beaconDB: cfg.BeaconDB,
|
||||
incomingFeed: new(event.Feed),
|
||||
incomingChan: make(chan *ethpb.Attestation, params.BeaconConfig().DefaultBufferSize),
|
||||
store: attestationStore{m: make(map[[48]byte]*ethpb.Attestation)},
|
||||
pooledAttestations: make([]*ethpb.Attestation, 0, 1),
|
||||
poolLimit: 1,
|
||||
}
|
||||
}
|
||||
|
||||
// Start an attestation service's main event loop.
|
||||
func (a *Service) Start() {
|
||||
log.Info("Starting service")
|
||||
go a.attestationPool()
|
||||
}
|
||||
|
||||
// Stop the Attestation service's main event loop and associated goroutines.
|
||||
func (a *Service) Stop() error {
|
||||
defer a.cancel()
|
||||
log.Info("Stopping service")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Status always returns nil.
|
||||
// TODO(#1201): Add service health checks.
|
||||
func (a *Service) Status() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IncomingAttestationFeed returns a feed that any service can send incoming p2p attestations into.
|
||||
// The attestation service will subscribe to this feed in order to relay incoming attestations.
|
||||
func (a *Service) IncomingAttestationFeed() *event.Feed {
|
||||
return a.incomingFeed
|
||||
}
|
||||
|
||||
// LatestAttestationTarget returns the target block that the validator index attested to,
|
||||
// the highest slotNumber attestation in attestation pool gets returned.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// Let `get_latest_attestation_target(store: Store, validator_index: ValidatorIndex) ->
|
||||
// BeaconBlock` be the target block in the attestation
|
||||
// `get_latest_attestation(store, validator_index)`.
|
||||
func (a *Service) LatestAttestationTarget(beaconState *pb.BeaconState, index uint64) (*pb.AttestationTarget, error) {
|
||||
if index >= uint64(len(beaconState.Validators)) {
|
||||
return nil, fmt.Errorf("invalid validator index %d", index)
|
||||
}
|
||||
validator := beaconState.Validators[index]
|
||||
|
||||
pubKey := bytesutil.ToBytes48(validator.PublicKey)
|
||||
a.store.RLock()
|
||||
defer a.store.RUnlock()
|
||||
if _, exists := a.store.m[pubKey]; !exists {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
attestation := a.store.m[pubKey]
|
||||
if attestation == nil {
|
||||
return nil, nil
|
||||
}
|
||||
targetRoot := bytesutil.ToBytes32(attestation.Data.BeaconBlockRoot)
|
||||
if !a.beaconDB.HasBlock(context.TODO(), targetRoot) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// TODO(3219): remove after fork choice service changes.
|
||||
return a.beaconDB.(*db.BeaconDB).AttestationTarget(targetRoot)
|
||||
}
|
||||
|
||||
// attestationPool takes an newly received attestation from sync service
|
||||
// and updates attestation pool.
|
||||
func (a *Service) attestationPool() {
|
||||
incomingSub := a.incomingFeed.Subscribe(a.incomingChan)
|
||||
defer incomingSub.Unsubscribe()
|
||||
for {
|
||||
select {
|
||||
case <-a.ctx.Done():
|
||||
log.Debug("AttestationDeprecated pool closed, exiting goroutine")
|
||||
return
|
||||
// Listen for a newly received incoming attestation from the sync service.
|
||||
case attestations := <-a.incomingChan:
|
||||
handler.SafelyHandleMessage(a.ctx, a.handleAttestation, attestations)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Service) handleAttestation(ctx context.Context, msg proto.Message) error {
|
||||
attestation := msg.(*ethpb.Attestation)
|
||||
a.pooledAttestations = append(a.pooledAttestations, attestation)
|
||||
if len(a.pooledAttestations) > a.poolLimit {
|
||||
if err := a.BatchUpdateLatestAttestations(ctx, a.pooledAttestations); err != nil {
|
||||
return err
|
||||
}
|
||||
state, err := a.beaconDB.HeadState(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// This sets the pool limit, once the old pool is cleared out. It does by using the number of active
|
||||
// validators per slot as an estimate. The active indices here are not used in the actual processing
|
||||
// of attestations.
|
||||
count, err := helpers.ActiveValidatorCount(state, helpers.CurrentEpoch(state))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
attPerSlot := count / params.BeaconConfig().SlotsPerEpoch
|
||||
// we only set the limit at 70% of the calculated amount to be safe so that relevant attestations
|
||||
// arent carried over to the next batch.
|
||||
a.poolLimit = int(attPerSlot) * 7 / 10
|
||||
if a.poolLimit == 0 {
|
||||
a.poolLimit++
|
||||
}
|
||||
attestationPoolLimit.Set(float64(a.poolLimit))
|
||||
a.pooledAttestations = make([]*ethpb.Attestation, 0, a.poolLimit)
|
||||
}
|
||||
attestationPoolSize.Set(float64(len(a.pooledAttestations)))
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateLatestAttestation inputs an new attestation and checks whether
|
||||
// the attesters who submitted this attestation with the higher slot number
|
||||
// have been noted in the attestation pool. If not, it updates the
|
||||
// attestation pool with attester's public key to attestation.
|
||||
func (a *Service) UpdateLatestAttestation(ctx context.Context, attestation *ethpb.Attestation) error {
|
||||
totalAttestationSeen.Inc()
|
||||
|
||||
// Potential improvement, instead of getting the state,
|
||||
// we could get a mapping of validator index to public key.
|
||||
beaconState, err := a.beaconDB.HeadState(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return a.updateAttestation(beaconState, attestation)
|
||||
}
|
||||
|
||||
// BatchUpdateLatestAttestations updates multiple attestations and adds them into the attestation store
|
||||
// if they are valid.
|
||||
func (a *Service) BatchUpdateLatestAttestations(ctx context.Context, attestations []*ethpb.Attestation) error {
|
||||
|
||||
if attestations == nil {
|
||||
return nil
|
||||
}
|
||||
// Potential improvement, instead of getting the state,
|
||||
// we could get a mapping of validator index to public key.
|
||||
beaconState, err := a.beaconDB.HeadState(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, attestation := range attestations {
|
||||
if err := a.updateAttestation(beaconState, attestation); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InsertAttestationIntoStore locks the store, inserts the attestation, then
|
||||
// unlocks the store again. This method may be used by external services
|
||||
// in testing to populate the attestation store.
|
||||
func (a *Service) InsertAttestationIntoStore(pubkey [48]byte, att *ethpb.Attestation) {
|
||||
a.store.Lock()
|
||||
defer a.store.Unlock()
|
||||
a.store.m[pubkey] = att
|
||||
}
|
||||
|
||||
func (a *Service) updateAttestation(beaconState *pb.BeaconState, attestation *ethpb.Attestation) error {
|
||||
totalAttestationSeen.Inc()
|
||||
|
||||
committee, err := helpers.CrosslinkCommittee(beaconState, helpers.CurrentEpoch(beaconState), attestation.Data.Crosslink.Shard)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"attestationTargetEpoch": attestation.Data.Target.Epoch,
|
||||
"attestationShard": attestation.Data.Crosslink.Shard,
|
||||
"committeesList": committee,
|
||||
"lengthOfCommittees": len(committee),
|
||||
}).Debug("Updating latest attestation")
|
||||
|
||||
// Check each bit of participation bitfield to find out which
|
||||
// attester has submitted new attestation.
|
||||
// This is has O(n) run time and could be optimized down the line.
|
||||
for i := uint64(0); i < attestation.AggregationBits.Len(); i++ {
|
||||
if !attestation.AggregationBits.BitAt(i) {
|
||||
continue
|
||||
}
|
||||
if i >= uint64(len(committee)) {
|
||||
// This should never happen.
|
||||
log.Warnf("bitfield points to an invalid index in the committee: bitfield %08b", attestation.AggregationBits)
|
||||
return nil
|
||||
}
|
||||
|
||||
if int(committee[i]) >= len(beaconState.Validators) {
|
||||
// This should never happen.
|
||||
log.Warnf("index doesn't exist in validator registry: index %d", committee[i])
|
||||
return nil
|
||||
}
|
||||
|
||||
// If the attestation came from this attester. We use the slot committee to find the
|
||||
// validator's actual index.
|
||||
pubkey := bytesutil.ToBytes48(beaconState.Validators[committee[i]].PublicKey)
|
||||
attTargetBoundarySlot := attestation.Data.Target.Epoch * params.BeaconConfig().SlotsPerEpoch
|
||||
currentAttestationSlot := uint64(0)
|
||||
a.store.Lock()
|
||||
if _, exists := a.store.m[pubkey]; exists {
|
||||
currentAttestationSlot = attTargetBoundarySlot
|
||||
}
|
||||
// If the attestation is newer than this attester's one in pool.
|
||||
if attTargetBoundarySlot > currentAttestationSlot {
|
||||
a.store.m[pubkey] = attestation
|
||||
|
||||
log.WithFields(
|
||||
logrus.Fields{
|
||||
"attTargetBoundarySlot": attTargetBoundarySlot,
|
||||
"sourceEpoch": attestation.Data.Source.Epoch,
|
||||
},
|
||||
).Debug("Attestation store updated")
|
||||
}
|
||||
a.store.Unlock()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,370 +0,0 @@
|
||||
package attestation
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/internal"
|
||||
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/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/sirupsen/logrus"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
)
|
||||
|
||||
func init() {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
|
||||
var _ = TargetHandler(&Service{})
|
||||
|
||||
func TestUpdateLatestAttestation_UpdatesLatest(t *testing.T) {
|
||||
beaconDB := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, beaconDB)
|
||||
ctx := context.Background()
|
||||
|
||||
var validators []*ethpb.Validator
|
||||
for i := 0; i < 64; i++ {
|
||||
validators = append(validators, ðpb.Validator{
|
||||
PublicKey: []byte{byte(i)},
|
||||
ActivationEpoch: 0,
|
||||
ExitEpoch: 10,
|
||||
})
|
||||
}
|
||||
|
||||
beaconState := &pb.BeaconState{
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
}
|
||||
block := ðpb.BeaconBlock{
|
||||
Slot: 1,
|
||||
}
|
||||
if err := beaconDB.SaveBlockDeprecated(block); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
service := NewAttestationService(context.Background(), &Config{BeaconDB: beaconDB})
|
||||
|
||||
attestation := ðpb.Attestation{
|
||||
AggregationBits: bitfield.Bitlist{0x03},
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 1,
|
||||
},
|
||||
Target: ðpb.Checkpoint{Epoch: 1},
|
||||
Source: ðpb.Checkpoint{},
|
||||
},
|
||||
}
|
||||
|
||||
if err := service.UpdateLatestAttestation(ctx, attestation); err != nil {
|
||||
t.Fatalf("could not update latest attestation: %v", err)
|
||||
}
|
||||
pubkey := bytesutil.ToBytes48(beaconState.Validators[10].PublicKey)
|
||||
if service.store.m[pubkey].Data.Crosslink.Shard !=
|
||||
attestation.Data.Crosslink.Shard {
|
||||
t.Errorf("Incorrect shard stored, wanted: %d, got: %d",
|
||||
attestation.Data.Crosslink.Shard, service.store.m[pubkey].Data.Crosslink.Shard)
|
||||
}
|
||||
|
||||
beaconState = &pb.BeaconState{
|
||||
Slot: 36,
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
}
|
||||
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
|
||||
t.Fatalf("could not save state: %v", err)
|
||||
}
|
||||
|
||||
attestation.Data.Crosslink.Shard = 36
|
||||
if err := service.UpdateLatestAttestation(ctx, attestation); err != nil {
|
||||
t.Fatalf("could not update latest attestation: %v", err)
|
||||
}
|
||||
if service.store.m[pubkey].Data.Crosslink.Shard !=
|
||||
attestation.Data.Crosslink.Shard {
|
||||
t.Errorf("Incorrect shard stored, wanted: %d, got: %d",
|
||||
attestation.Data.Crosslink.Shard, service.store.m[pubkey].Data.Crosslink.Shard)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttestationPool_UpdatesAttestationPool(t *testing.T) {
|
||||
beaconDB := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, beaconDB)
|
||||
ctx := context.Background()
|
||||
|
||||
var validators []*ethpb.Validator
|
||||
for i := 0; i < 64; i++ {
|
||||
validators = append(validators, ðpb.Validator{
|
||||
PublicKey: []byte{byte(i)},
|
||||
ActivationEpoch: 0,
|
||||
ExitEpoch: 10,
|
||||
})
|
||||
}
|
||||
beaconState := &pb.BeaconState{
|
||||
Slot: 1,
|
||||
Validators: validators,
|
||||
}
|
||||
block := ðpb.BeaconBlock{
|
||||
Slot: 1,
|
||||
}
|
||||
if err := beaconDB.SaveBlockDeprecated(block); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
service := NewAttestationService(context.Background(), &Config{BeaconDB: beaconDB})
|
||||
attestation := ðpb.Attestation{
|
||||
AggregationBits: bitfield.Bitlist{0x80, 0x01},
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := service.handleAttestation(context.Background(), attestation); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLatestAttestationTarget_CantGetAttestation(t *testing.T) {
|
||||
beaconDB := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, beaconDB)
|
||||
ctx := context.Background()
|
||||
|
||||
if err := beaconDB.SaveStateDeprecated(ctx, &pb.BeaconState{
|
||||
Validators: []*ethpb.Validator{{}},
|
||||
}); err != nil {
|
||||
t.Fatalf("could not save state: %v", err)
|
||||
}
|
||||
service := NewAttestationService(context.Background(), &Config{BeaconDB: beaconDB})
|
||||
headState, err := beaconDB.HeadState(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
index := uint64(100)
|
||||
want := fmt.Sprintf("invalid validator index %d", index)
|
||||
if _, err := service.LatestAttestationTarget(headState, index); !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("Wanted error to contain %s, received %v", want, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLatestAttestationTarget_ReturnsLatestAttestedBlock(t *testing.T) {
|
||||
beaconDB := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, beaconDB)
|
||||
ctx := context.Background()
|
||||
|
||||
pubKey := []byte{'A'}
|
||||
if err := beaconDB.SaveStateDeprecated(ctx, &pb.BeaconState{
|
||||
Validators: []*ethpb.Validator{{PublicKey: pubKey}},
|
||||
}); err != nil {
|
||||
t.Fatalf("could not save state: %v", err)
|
||||
}
|
||||
|
||||
block := ðpb.BeaconBlock{Slot: 999}
|
||||
if err := beaconDB.SaveBlockDeprecated(block); err != nil {
|
||||
t.Fatalf("could not save block: %v", err)
|
||||
}
|
||||
blockRoot, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
log.Fatalf("could not hash block: %v", err)
|
||||
}
|
||||
if err := beaconDB.SaveAttestationTarget(ctx, &pb.AttestationTarget{
|
||||
Slot: block.Slot,
|
||||
BeaconBlockRoot: blockRoot[:],
|
||||
ParentRoot: []byte{},
|
||||
}); err != nil {
|
||||
log.Fatalf("could not save att target: %v", err)
|
||||
}
|
||||
|
||||
service := NewAttestationService(context.Background(), &Config{BeaconDB: beaconDB})
|
||||
|
||||
attestation := ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
BeaconBlockRoot: blockRoot[:],
|
||||
}}
|
||||
pubKey48 := bytesutil.ToBytes48(pubKey)
|
||||
service.store.m[pubKey48] = attestation
|
||||
|
||||
headState, err := beaconDB.HeadState(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
latestAttestedTarget, err := service.LatestAttestationTarget(headState, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not get latest attestation: %v", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(blockRoot[:], latestAttestedTarget.BeaconBlockRoot) {
|
||||
t.Errorf("Wanted: %v, got: %v", blockRoot[:], latestAttestedTarget.BeaconBlockRoot)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateLatestAttestation_InvalidIndex(t *testing.T) {
|
||||
beaconDB := internal.SetupDBDeprecated(t)
|
||||
hook := logTest.NewGlobal()
|
||||
defer internal.TeardownDBDeprecated(t, beaconDB)
|
||||
ctx := context.Background()
|
||||
|
||||
var validators []*ethpb.Validator
|
||||
for i := 0; i < 64; i++ {
|
||||
validators = append(validators, ðpb.Validator{
|
||||
PublicKey: []byte{byte(i)},
|
||||
ActivationEpoch: 0,
|
||||
ExitEpoch: 10,
|
||||
})
|
||||
}
|
||||
|
||||
beaconState := &pb.BeaconState{
|
||||
Slot: 1,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
Validators: validators,
|
||||
}
|
||||
block := ðpb.BeaconBlock{
|
||||
Slot: 1,
|
||||
}
|
||||
if err := beaconDB.SaveBlockDeprecated(block); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
service := NewAttestationService(context.Background(), &Config{BeaconDB: beaconDB})
|
||||
attestation := ðpb.Attestation{
|
||||
AggregationBits: bitfield.Bitlist{0xC0, 0x01},
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 1,
|
||||
},
|
||||
Target: ðpb.Checkpoint{},
|
||||
Source: ðpb.Checkpoint{},
|
||||
},
|
||||
}
|
||||
|
||||
wanted := "bitfield points to an invalid index in the committee"
|
||||
|
||||
if err := service.UpdateLatestAttestation(ctx, attestation); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
testutil.AssertLogsContain(t, hook, wanted)
|
||||
}
|
||||
|
||||
func TestBatchUpdate_FromSync(t *testing.T) {
|
||||
beaconDB := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, beaconDB)
|
||||
ctx := context.Background()
|
||||
|
||||
var validators []*ethpb.Validator
|
||||
var latestRandaoMixes [][]byte
|
||||
var latestActiveIndexRoots [][]byte
|
||||
for i := 0; i < 64; i++ {
|
||||
validators = append(validators, ðpb.Validator{
|
||||
PublicKey: []byte{byte(i)},
|
||||
ActivationEpoch: 0,
|
||||
ExitEpoch: 10,
|
||||
})
|
||||
latestRandaoMixes = append(latestRandaoMixes, []byte{'A'})
|
||||
latestActiveIndexRoots = append(latestActiveIndexRoots, []byte{'B'})
|
||||
}
|
||||
|
||||
beaconState := &pb.BeaconState{
|
||||
Slot: 1,
|
||||
Validators: validators,
|
||||
RandaoMixes: latestRandaoMixes,
|
||||
ActiveIndexRoots: latestActiveIndexRoots,
|
||||
}
|
||||
block := ðpb.BeaconBlock{
|
||||
Slot: 1,
|
||||
}
|
||||
if err := beaconDB.SaveBlockDeprecated(block); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
service := NewAttestationService(context.Background(), &Config{BeaconDB: beaconDB})
|
||||
service.poolLimit = 9
|
||||
for i := 0; i < 10; i++ {
|
||||
attestation := ðpb.Attestation{
|
||||
AggregationBits: bitfield.Bitlist{0x80},
|
||||
Data: ðpb.AttestationData{
|
||||
Target: ðpb.Checkpoint{Epoch: 2},
|
||||
Source: ðpb.Checkpoint{},
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := service.handleAttestation(ctx, attestation); err != nil {
|
||||
t.Fatalf("could not update latest attestation: %v", err)
|
||||
}
|
||||
}
|
||||
if len(service.pooledAttestations) != 0 {
|
||||
t.Errorf("pooled attestations were not cleared out, still %d attestations in pool", len(service.pooledAttestations))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateLatestAttestation_BatchUpdate(t *testing.T) {
|
||||
beaconDB := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, beaconDB)
|
||||
ctx := context.Background()
|
||||
|
||||
var validators []*ethpb.Validator
|
||||
for i := 0; i < 64; i++ {
|
||||
validators = append(validators, ðpb.Validator{
|
||||
PublicKey: []byte{byte(i)},
|
||||
ActivationEpoch: 0,
|
||||
ExitEpoch: 10,
|
||||
})
|
||||
}
|
||||
|
||||
beaconState := &pb.BeaconState{
|
||||
Slot: 1,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
Validators: validators,
|
||||
}
|
||||
block := ðpb.BeaconBlock{
|
||||
Slot: 1,
|
||||
}
|
||||
if err := beaconDB.SaveBlockDeprecated(block); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
service := NewAttestationService(context.Background(), &Config{BeaconDB: beaconDB})
|
||||
attestations := make([]*ethpb.Attestation, 0)
|
||||
for i := 0; i < 10; i++ {
|
||||
attestations = append(attestations, ðpb.Attestation{
|
||||
AggregationBits: bitfield.Bitlist{0x80, 0x01},
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 1,
|
||||
},
|
||||
Target: ðpb.Checkpoint{},
|
||||
Source: ðpb.Checkpoint{},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if err := service.BatchUpdateLatestAttestations(ctx, attestations); err != nil {
|
||||
t.Fatalf("could not update latest attestation: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package attestation
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
var (
|
||||
totalAttestationSeen = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "total_seen_attestations",
|
||||
Help: "Total number of attestations seen by the validators",
|
||||
})
|
||||
|
||||
attestationPoolLimit = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "attestation_pool_limit",
|
||||
Help: "The limit of the attestation pool",
|
||||
})
|
||||
attestationPoolSize = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "attestation_pool_size",
|
||||
Help: "The current size of the attestation pool",
|
||||
})
|
||||
)
|
||||
@@ -52,7 +52,6 @@ go_test(
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/core/state:go_default_library",
|
||||
"//beacon-chain/core/validators:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/db/testing:go_default_library",
|
||||
"//beacon-chain/p2p:go_default_library",
|
||||
|
||||
@@ -10,12 +10,8 @@ import (
|
||||
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
v "github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
|
||||
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/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
)
|
||||
@@ -245,59 +241,3 @@ func TestReceiveBlockNoPubsubForkchoice_ProcessCorrectly(t *testing.T) {
|
||||
testutil.AssertLogsContain(t, hook, "Finished state transition and updated fork choice store for block")
|
||||
testutil.AssertLogsDoNotContain(t, hook, "Finished fork choice")
|
||||
}
|
||||
|
||||
func TestReceiveBlockNoPubsubForkchoice_CanUpdateValidatorDB(t *testing.T) {
|
||||
db := testDB.SetupDB(t)
|
||||
defer testDB.TeardownDB(t, db)
|
||||
ctx := context.Background()
|
||||
|
||||
chainService := setupBeaconChain(t, db)
|
||||
|
||||
b := ðpb.BeaconBlock{
|
||||
Slot: params.BeaconConfig().SlotsPerEpoch,
|
||||
Body: ðpb.BeaconBlockBody{}}
|
||||
bRoot, err := ssz.SigningRoot(b)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.SaveState(ctx, &pb.BeaconState{
|
||||
Validators: []*ethpb.Validator{
|
||||
{PublicKey: []byte{'X'}},
|
||||
{PublicKey: []byte{'Y'}},
|
||||
{PublicKey: []byte{'Z'}},
|
||||
},
|
||||
}, bRoot); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
headBlk := ðpb.BeaconBlock{Slot: 100}
|
||||
if err := db.SaveBlock(ctx, headBlk); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
r, err := ssz.SigningRoot(headBlk)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
chainService.forkChoiceStore = &store{headRoot: r[:]}
|
||||
|
||||
v.DeleteActivatedVal(1)
|
||||
v.InsertActivatedIndices(1, []uint64{0})
|
||||
|
||||
if err := chainService.ReceiveBlockNoPubsub(ctx, b); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
index, _, _ := db.ValidatorIndex(ctx, bytesutil.ToBytes48([]byte{'X'}))
|
||||
if index != 0 {
|
||||
t.Errorf("Wanted: %d, got: %d", 1, index)
|
||||
}
|
||||
_, e, _ := db.ValidatorIndex(ctx, bytesutil.ToBytes48([]byte{'Y'}))
|
||||
if e == true {
|
||||
t.Error("Index should not exist in DB")
|
||||
}
|
||||
_, e, _ = db.ValidatorIndex(ctx, bytesutil.ToBytes48([]byte{'Z'}))
|
||||
if e == true {
|
||||
t.Error("Index should not exist in DB")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,18 +56,18 @@ type ChainService struct {
|
||||
headState *pb.BeaconState
|
||||
canonicalRoots map[uint64][]byte
|
||||
canonicalRootsLock sync.RWMutex
|
||||
preloadStatePath string
|
||||
preloadStatePath string
|
||||
}
|
||||
|
||||
// Config options for the service.
|
||||
type Config struct {
|
||||
BeaconBlockBuf int
|
||||
Web3Service *powchain.Web3Service
|
||||
BeaconDB db.Database
|
||||
DepositCache *depositcache.DepositCache
|
||||
OpsPoolService operations.OperationFeeds
|
||||
P2p p2p.Broadcaster
|
||||
MaxRoutines int64
|
||||
BeaconBlockBuf int
|
||||
Web3Service *powchain.Web3Service
|
||||
BeaconDB db.Database
|
||||
DepositCache *depositcache.DepositCache
|
||||
OpsPoolService operations.OperationFeeds
|
||||
P2p p2p.Broadcaster
|
||||
MaxRoutines int64
|
||||
PreloadStatePath string
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ func NewChainService(ctx context.Context, cfg *Config) (*ChainService, error) {
|
||||
p2p: cfg.P2p,
|
||||
canonicalRoots: make(map[uint64][]byte),
|
||||
maxRoutines: cfg.MaxRoutines,
|
||||
preloadStatePath: cfg.PreloadStatePath,
|
||||
preloadStatePath: cfg.PreloadStatePath,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -9,5 +9,6 @@ go_library(
|
||||
deps = [
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/event:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
)
|
||||
|
||||
// ChainService defines the mock interface for testing
|
||||
@@ -65,3 +66,8 @@ func (ms *ChainService) ReceiveAttestation(context.Context, *ethpb.Attestation)
|
||||
func (ms *ChainService) ReceiveAttestationNoPubsub(context.Context, *ethpb.Attestation) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// StateInitializedFeed mocks StateInitializedFeed method in chain service.
|
||||
func (ms *ChainService) StateInitializedFeed() *event.Feed {
|
||||
return new(event.Feed)
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ go_test(
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/internal:go_default_library",
|
||||
"//beacon-chain/core/state:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package helpers_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/internal"
|
||||
"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/params"
|
||||
@@ -13,13 +12,8 @@ import (
|
||||
)
|
||||
|
||||
func TestAttestationDataSlot_OK(t *testing.T) {
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
deposits, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
if err := db.InitializeState(context.Background(), uint64(0), deposits, ðpb.Eth1Data{}); err != nil {
|
||||
t.Fatalf("Could not initialize beacon state to disk: %v", err)
|
||||
}
|
||||
beaconState, err := db.HeadState(context.Background())
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -62,13 +56,8 @@ func TestAttestationDataSlot_ReturnsErrorWithNilData(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAttestationDataSlot_ReturnsErrorWithErroneousTargetEpoch(t *testing.T) {
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
deposits, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
if err := db.InitializeState(context.Background(), uint64(0), deposits, ðpb.Eth1Data{}); err != nil {
|
||||
t.Fatalf("Could not initialize beacon state to disk: %v", err)
|
||||
}
|
||||
beaconState, err := db.HeadState(context.Background())
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -82,13 +71,8 @@ func TestAttestationDataSlot_ReturnsErrorWithErroneousTargetEpoch(t *testing.T)
|
||||
}
|
||||
|
||||
func TestAttestationDataSlot_ReturnsErrorWhenTargetEpochLessThanCurrentEpoch(t *testing.T) {
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
deposits, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
if err := db.InitializeState(context.Background(), uint64(0), deposits, ðpb.Eth1Data{}); err != nil {
|
||||
t.Fatalf("Could not initialize beacon state to disk: %v", err)
|
||||
}
|
||||
beaconState, err := db.HeadState(context.Background())
|
||||
beaconState, err := state.GenesisBeaconState(deposits, uint64(0), ðpb.Eth1Data{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
package validators
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
@@ -14,20 +12,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
type validatorStore struct {
|
||||
sync.RWMutex
|
||||
// activatedValidators is a mapping that tracks validator activation epoch to validators index.
|
||||
activatedValidators map[uint64][]uint64
|
||||
// exitedValidators is a mapping that tracks validator exit epoch to validators index.
|
||||
exitedValidators map[uint64][]uint64
|
||||
}
|
||||
|
||||
//VStore validator map for quick
|
||||
var VStore = validatorStore{
|
||||
activatedValidators: make(map[uint64][]uint64),
|
||||
exitedValidators: make(map[uint64][]uint64),
|
||||
}
|
||||
|
||||
// InitiateValidatorExit takes in validator index and updates
|
||||
// validator with correct voluntary exit parameters.
|
||||
//
|
||||
@@ -170,82 +154,3 @@ func SlashValidator(state *pb.BeaconState, slashedIdx uint64, whistleBlowerIdx u
|
||||
state = helpers.IncreaseBalance(state, whistleBlowerIdx, whistleblowerReward-proposerReward)
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// InitializeValidatorStore sets the current active validators from the current
|
||||
// state.
|
||||
func InitializeValidatorStore(bState *pb.BeaconState) error {
|
||||
VStore.Lock()
|
||||
defer VStore.Unlock()
|
||||
|
||||
currentEpoch := helpers.CurrentEpoch(bState)
|
||||
activeValidatorIndices, err := helpers.ActiveValidatorIndices(bState, currentEpoch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
VStore.activatedValidators[currentEpoch] = activeValidatorIndices
|
||||
return nil
|
||||
}
|
||||
|
||||
// InsertActivatedVal locks the validator store, inserts the activated validator
|
||||
// indices, then unlocks the store again. This method may be used by
|
||||
// external services in testing to populate the validator store.
|
||||
func InsertActivatedVal(epoch uint64, validators []uint64) {
|
||||
VStore.Lock()
|
||||
defer VStore.Unlock()
|
||||
VStore.activatedValidators[epoch] = validators
|
||||
}
|
||||
|
||||
// InsertActivatedIndices locks the validator store, inserts the activated validator
|
||||
// indices corresponding to their activation epochs.
|
||||
func InsertActivatedIndices(epoch uint64, indices []uint64) {
|
||||
VStore.Lock()
|
||||
defer VStore.Unlock()
|
||||
VStore.activatedValidators[epoch] = append(VStore.activatedValidators[epoch], indices...)
|
||||
}
|
||||
|
||||
// InsertExitedVal locks the validator store, inserts the exited validator
|
||||
// indices, then unlocks the store again. This method may be used by
|
||||
// external services in testing to remove the validator store.
|
||||
func InsertExitedVal(epoch uint64, validators []uint64) {
|
||||
VStore.Lock()
|
||||
defer VStore.Unlock()
|
||||
VStore.exitedValidators[epoch] = validators
|
||||
}
|
||||
|
||||
// ActivatedValFromEpoch locks the validator store, retrieves the activated validator
|
||||
// indices of a given epoch, then unlocks the store again.
|
||||
func ActivatedValFromEpoch(epoch uint64) []uint64 {
|
||||
VStore.RLock()
|
||||
defer VStore.RUnlock()
|
||||
if _, exists := VStore.activatedValidators[epoch]; !exists {
|
||||
return nil
|
||||
}
|
||||
return VStore.activatedValidators[epoch]
|
||||
}
|
||||
|
||||
// ExitedValFromEpoch locks the validator store, retrieves the exited validator
|
||||
// indices of a given epoch, then unlocks the store again.
|
||||
func ExitedValFromEpoch(epoch uint64) []uint64 {
|
||||
VStore.RLock()
|
||||
defer VStore.RUnlock()
|
||||
if _, exists := VStore.exitedValidators[epoch]; !exists {
|
||||
return nil
|
||||
}
|
||||
return VStore.exitedValidators[epoch]
|
||||
}
|
||||
|
||||
// DeleteActivatedVal locks the validator store, delete the activated validator
|
||||
// indices of a given epoch, then unlocks the store again.
|
||||
func DeleteActivatedVal(epoch uint64) {
|
||||
VStore.Lock()
|
||||
defer VStore.Unlock()
|
||||
delete(VStore.activatedValidators, epoch)
|
||||
}
|
||||
|
||||
// DeleteExitedVal locks the validator store, delete the exited validator
|
||||
// indices of a given epoch, then unlocks the store again.
|
||||
func DeleteExitedVal(epoch uint64) {
|
||||
VStore.Lock()
|
||||
defer VStore.Unlock()
|
||||
delete(VStore.exitedValidators, epoch)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package validators
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
@@ -196,44 +195,3 @@ func TestSlashValidator_OK(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestInitializeValidatoreStore(t *testing.T) {
|
||||
registry := make([]*ethpb.Validator, 0)
|
||||
indices := make([]uint64, 0)
|
||||
validatorsLimit := 100
|
||||
for i := 0; i < validatorsLimit; i++ {
|
||||
registry = append(registry, ðpb.Validator{
|
||||
PublicKey: []byte(strconv.Itoa(i)),
|
||||
ActivationEpoch: 0,
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
})
|
||||
indices = append(indices, uint64(i))
|
||||
}
|
||||
|
||||
bState := &pb.BeaconState{
|
||||
Validators: registry,
|
||||
Slot: 0,
|
||||
}
|
||||
|
||||
if _, ok := VStore.activatedValidators[helpers.CurrentEpoch(bState)]; ok {
|
||||
t.Fatalf("Validator store already has indices saved in this epoch")
|
||||
}
|
||||
|
||||
InitializeValidatorStore(bState)
|
||||
retrievedIndices := VStore.activatedValidators[helpers.CurrentEpoch(bState)]
|
||||
|
||||
if !reflect.DeepEqual(retrievedIndices, indices) {
|
||||
t.Errorf("Saved active indices are not the same as the one in the validator store, got %v but expected %v", retrievedIndices, indices)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInsertActivatedIndices_Works(t *testing.T) {
|
||||
InsertActivatedIndices(100, []uint64{1, 2, 3})
|
||||
if !reflect.DeepEqual(VStore.activatedValidators[100], []uint64{1, 2, 3}) {
|
||||
t.Error("Activated validators aren't the same")
|
||||
}
|
||||
InsertActivatedIndices(100, []uint64{100})
|
||||
if !reflect.DeepEqual(VStore.activatedValidators[100], []uint64{1, 2, 3, 100}) {
|
||||
t.Error("Activated validators aren't the same")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,69 +1,15 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"attestation.go",
|
||||
"block.go",
|
||||
"block_operations.go",
|
||||
"db.go",
|
||||
"deposit_contract.go",
|
||||
"schema.go",
|
||||
"setup_db.go",
|
||||
"state.go",
|
||||
"state_metrics.go",
|
||||
"validator.go",
|
||||
],
|
||||
srcs = ["db.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/db",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/state:go_default_library",
|
||||
"//beacon-chain/db/filters:go_default_library",
|
||||
"//beacon-chain/db/kv:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_boltdb_bolt//:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@io_opencensus_go//trace:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "medium",
|
||||
srcs = [
|
||||
"attestation_test.go",
|
||||
"block_operations_test.go",
|
||||
"block_test.go",
|
||||
"db_test.go",
|
||||
"deposit_contract_test.go",
|
||||
"state_test.go",
|
||||
"validator_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
race = "on",
|
||||
deps = [
|
||||
"//beacon-chain/db/kv:go_default_library",
|
||||
"//proto/beacon/p2p/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",
|
||||
"//shared/testutil:go_default_library",
|
||||
"@com_github_boltdb_bolt//:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1,191 +0,0 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db/filters"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
// SaveAttestations in the db.
|
||||
func (db *BeaconDB) SaveAttestations(ctx context.Context, atts []*ethpb.Attestation) error {
|
||||
for _, a := range atts {
|
||||
if err := db.SaveAttestation(ctx, a); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SaveAttestation puts the attestation record into the beacon chain db.
|
||||
func (db *BeaconDB) SaveAttestation(ctx context.Context, attestation *ethpb.Attestation) error {
|
||||
ctx, span := trace.StartSpan(ctx, "beaconDB.SaveAttestation")
|
||||
defer span.End()
|
||||
|
||||
encodedAtt, err := proto.Marshal(attestation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hash, err := hashutil.HashProto(attestation.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return db.batch(func(tx *bolt.Tx) error {
|
||||
a := tx.Bucket(attestationBucket)
|
||||
|
||||
return a.Put(hash[:], encodedAtt)
|
||||
})
|
||||
}
|
||||
|
||||
// SaveAttestationTarget puts the attestation target record into the beacon chain db.
|
||||
func (db *BeaconDB) SaveAttestationTarget(ctx context.Context, attTarget *pb.AttestationTarget) error {
|
||||
ctx, span := trace.StartSpan(ctx, "beaconDB.SaveAttestationTarget")
|
||||
defer span.End()
|
||||
|
||||
encodedAttTgt, err := proto.Marshal(attTarget)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return db.update(func(tx *bolt.Tx) error {
|
||||
a := tx.Bucket(attestationTargetBucket)
|
||||
|
||||
return a.Put(attTarget.BeaconBlockRoot, encodedAttTgt)
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteAttestation deletes the attestation record into the beacon chain db.
|
||||
func (db *BeaconDB) DeleteAttestation(_ context.Context, hash [32]byte) error {
|
||||
return db.batch(func(tx *bolt.Tx) error {
|
||||
a := tx.Bucket(attestationBucket)
|
||||
return a.Delete(hash[:])
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteAttestationDeprecated deletes the attestation record into the beacon chain db.
|
||||
// DEPRECATED: Use DeleteAttestation.
|
||||
func (db *BeaconDB) DeleteAttestationDeprecated(attestation *ethpb.Attestation) error {
|
||||
hash, err := hashutil.HashProto(attestation.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return db.DeleteAttestation(context.Background(), hash)
|
||||
}
|
||||
|
||||
// Attestation retrieves an attestation record from the db using the hash of attestation.data.
|
||||
func (db *BeaconDB) Attestation(_ context.Context, hash [32]byte) (*ethpb.Attestation, error) {
|
||||
return db.AttestationDeprecated(hash)
|
||||
}
|
||||
|
||||
// AttestationDeprecated retrieves an attestation record from the db using the hash of attestation.data.
|
||||
// DEPRECATED: Use Attestation.
|
||||
func (db *BeaconDB) AttestationDeprecated(hash [32]byte) (*ethpb.Attestation, error) {
|
||||
var attestation *ethpb.Attestation
|
||||
err := db.view(func(tx *bolt.Tx) error {
|
||||
a := tx.Bucket(attestationBucket)
|
||||
|
||||
enc := a.Get(hash[:])
|
||||
if enc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
attestation, err = createAttestation(enc)
|
||||
return err
|
||||
})
|
||||
|
||||
return attestation, err
|
||||
}
|
||||
|
||||
// Attestations retrieves all the attestation records from the db.
|
||||
// These are the attestations that have not been seen on the beacon chain.
|
||||
func (db *BeaconDB) Attestations(_ context.Context, _ *filters.QueryFilter) ([]*ethpb.Attestation, error) {
|
||||
return db.AttestationsDeprecated()
|
||||
}
|
||||
|
||||
// AttestationsDeprecated retrieves all the attestation records from the db.
|
||||
// These are the attestations that have not been seen on the beacon chain.
|
||||
// DEPRECATED: Use Attestations.
|
||||
func (db *BeaconDB) AttestationsDeprecated() ([]*ethpb.Attestation, error) {
|
||||
var attestations []*ethpb.Attestation
|
||||
err := db.view(func(tx *bolt.Tx) error {
|
||||
a := tx.Bucket(attestationBucket)
|
||||
|
||||
if err := a.ForEach(func(k, v []byte) error {
|
||||
attestation, err := createAttestation(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
attestations = append(attestations, attestation)
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return attestations, err
|
||||
}
|
||||
|
||||
// AttestationTarget retrieves an attestation target record from the db using the hash of attestation.data.
|
||||
func (db *BeaconDB) AttestationTarget(hash [32]byte) (*pb.AttestationTarget, error) {
|
||||
var attTgt *pb.AttestationTarget
|
||||
err := db.view(func(tx *bolt.Tx) error {
|
||||
a := tx.Bucket(attestationTargetBucket)
|
||||
|
||||
enc := a.Get(hash[:])
|
||||
if enc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
attTgt, err = createAttestationTarget(enc)
|
||||
return err
|
||||
})
|
||||
|
||||
return attTgt, err
|
||||
}
|
||||
|
||||
// HasAttestation checks if the attestaiton exists.
|
||||
func (db *BeaconDB) HasAttestation(_ context.Context, hash [32]byte) bool {
|
||||
exists := false
|
||||
// #nosec G104
|
||||
db.view(func(tx *bolt.Tx) error {
|
||||
a := tx.Bucket(attestationBucket)
|
||||
|
||||
exists = a.Get(hash[:]) != nil
|
||||
return nil
|
||||
})
|
||||
return exists
|
||||
}
|
||||
|
||||
// HasAttestationDeprecated checks if the attestation exists.
|
||||
// DEPRECATED: Use HasAttestation.
|
||||
func (db *BeaconDB) HasAttestationDeprecated(hash [32]byte) bool {
|
||||
return db.HasAttestation(context.Background(), hash)
|
||||
}
|
||||
|
||||
func createAttestation(enc []byte) (*ethpb.Attestation, error) {
|
||||
protoAttestation := ðpb.Attestation{}
|
||||
if err := proto.Unmarshal(enc, protoAttestation); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal encoding")
|
||||
}
|
||||
return protoAttestation, nil
|
||||
}
|
||||
|
||||
func createAttestationTarget(enc []byte) (*pb.AttestationTarget, error) {
|
||||
protoAttTgt := &pb.AttestationTarget{}
|
||||
if err := proto.Unmarshal(enc, protoAttTgt); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal encoding")
|
||||
}
|
||||
return protoAttTgt, nil
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
)
|
||||
|
||||
func TestSaveAndRetrieveAttestation_OK(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
a := ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := db.SaveAttestation(context.Background(), a); err != nil {
|
||||
t.Fatalf("Failed to save attestation: %v", err)
|
||||
}
|
||||
|
||||
aDataHash, err := hashutil.HashProto(a.Data)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to hash AttestationDeprecated: %v", err)
|
||||
}
|
||||
aPrime, err := db.AttestationDeprecated(aDataHash)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to call AttestationDeprecated: %v", err)
|
||||
}
|
||||
|
||||
aEnc, err := proto.Marshal(a)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to encode: %v", err)
|
||||
}
|
||||
aPrimeEnc, err := proto.Marshal(aPrime)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to encode: %v", err)
|
||||
}
|
||||
if !bytes.Equal(aEnc, aPrimeEnc) {
|
||||
t.Fatalf("Saved attestation and retrieved attestation are not equal: %#x and %#x", aEnc, aPrimeEnc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetrieveAttestations_OK(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
// Generate 100 unique attestations to save in DB.
|
||||
attestations := make([]*ethpb.Attestation, 100)
|
||||
for i := 0; i < len(attestations); i++ {
|
||||
attestations[i] = ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: uint64(i),
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := db.SaveAttestation(context.Background(), attestations[i]); err != nil {
|
||||
t.Fatalf("Failed to save attestation: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
retrievedAttestations, err := db.AttestationsDeprecated()
|
||||
if err != nil {
|
||||
t.Fatalf("Could not retrieve attestations: %v", err)
|
||||
}
|
||||
|
||||
sort.Slice(retrievedAttestations, func(i, j int) bool {
|
||||
return retrievedAttestations[i].Data.Crosslink.Shard < retrievedAttestations[j].Data.Crosslink.Shard
|
||||
})
|
||||
|
||||
if !reflect.DeepEqual(retrievedAttestations, attestations) {
|
||||
t.Fatal("Retrieved attestations did not match generated attestations")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteAttestation_OK(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
a := ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := db.SaveAttestation(context.Background(), a); err != nil {
|
||||
t.Fatalf("Could not save attestation: %v", err)
|
||||
}
|
||||
|
||||
aDataHash, err := hashutil.HashProto(a.Data)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to hash AttestationDeprecated: %v", err)
|
||||
}
|
||||
aPrime, err := db.AttestationDeprecated(aDataHash)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not call AttestationDeprecated: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(aPrime, a) {
|
||||
t.Errorf("Saved attestation and retrieved attestation are not equal")
|
||||
}
|
||||
|
||||
if err := db.DeleteAttestationDeprecated(a); err != nil {
|
||||
t.Fatalf("Could not delete attestation: %v", err)
|
||||
}
|
||||
|
||||
if db.HasAttestationDeprecated(aDataHash) {
|
||||
t.Error("Deleted attestation still there")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNilAttestation_OK(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
nilHash := [32]byte{}
|
||||
a, err := db.AttestationDeprecated(nilHash)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to retrieve nilHash: %v", err)
|
||||
}
|
||||
if a != nil {
|
||||
t.Fatal("Expected nilHash to return no attestation")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasAttestation_OK(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
a := ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
aDataHash, err := hashutil.HashProto(a.Data)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to hash AttestationDeprecated: %v", err)
|
||||
}
|
||||
|
||||
if db.HasAttestationDeprecated(aDataHash) {
|
||||
t.Fatal("Expected HasAttestationDeprecated to return false")
|
||||
}
|
||||
|
||||
if err := db.SaveAttestation(context.Background(), a); err != nil {
|
||||
t.Fatalf("Failed to save attestation: %v", err)
|
||||
}
|
||||
if !db.HasAttestationDeprecated(aDataHash) {
|
||||
t.Fatal("Expected HasAttestationDeprecated to return true")
|
||||
}
|
||||
}
|
||||
@@ -1,464 +0,0 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db/filters"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
var (
|
||||
badBlockCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "bad_blocks",
|
||||
Help: "Number of bad, blacklisted blocks received",
|
||||
})
|
||||
blockCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "beacon_block_cache_miss",
|
||||
Help: "The number of block requests that aren't present in the cache.",
|
||||
})
|
||||
blockCacheHit = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "beacon_block_cache_hit",
|
||||
Help: "The number of block requests that are present in the cache.",
|
||||
})
|
||||
blockCacheSize = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "beacon_block_cache_size",
|
||||
Help: "The number of beacon blocks in the block cache",
|
||||
})
|
||||
)
|
||||
|
||||
func createBlock(enc []byte) (*ethpb.BeaconBlock, error) {
|
||||
protoBlock := ðpb.BeaconBlock{}
|
||||
err := proto.Unmarshal(enc, protoBlock)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal encoding")
|
||||
}
|
||||
return protoBlock, nil
|
||||
}
|
||||
|
||||
// Block accepts a block root and returns the corresponding block.
|
||||
// Returns nil if the block does not exist.
|
||||
func (db *BeaconDB) Block(_ context.Context, root [32]byte) (*ethpb.BeaconBlock, error) {
|
||||
return db.BlockDeprecated(root)
|
||||
}
|
||||
|
||||
// BlockDeprecated accepts a block root and returns the corresponding block.
|
||||
// Returns nil if the block does not exist.
|
||||
// DEPRECATED: Use Block.
|
||||
func (db *BeaconDB) BlockDeprecated(root [32]byte) (*ethpb.BeaconBlock, error) {
|
||||
db.blocksLock.RLock()
|
||||
|
||||
// Return block from cache if it exists
|
||||
if blk, exists := db.blocks[root]; exists && blk != nil {
|
||||
defer db.blocksLock.RUnlock()
|
||||
blockCacheHit.Inc()
|
||||
return db.blocks[root], nil
|
||||
}
|
||||
|
||||
var block *ethpb.BeaconBlock
|
||||
err := db.view(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(blockBucket)
|
||||
|
||||
enc := bucket.Get(root[:])
|
||||
if enc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
block, err = createBlock(enc)
|
||||
return err
|
||||
})
|
||||
|
||||
db.blocksLock.RUnlock()
|
||||
db.blocksLock.Lock()
|
||||
defer db.blocksLock.Unlock()
|
||||
// Save block to the cache since it wasn't there before.
|
||||
if block != nil {
|
||||
db.blocks[root] = block
|
||||
blockCacheMiss.Inc()
|
||||
blockCacheSize.Set(float64(len(db.blocks)))
|
||||
}
|
||||
|
||||
return block, err
|
||||
}
|
||||
|
||||
// HasBlock accepts a block root and returns true if the block does not exist.
|
||||
func (db *BeaconDB) HasBlock(_ context.Context, root [32]byte) bool {
|
||||
db.blocksLock.RLock()
|
||||
defer db.blocksLock.RUnlock()
|
||||
|
||||
// Check the cache first to see if block exists.
|
||||
if _, exists := db.blocks[root]; exists {
|
||||
return true
|
||||
}
|
||||
|
||||
hasBlock := false
|
||||
// #nosec G104
|
||||
_ = db.view(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(blockBucket)
|
||||
|
||||
hasBlock = bucket.Get(root[:]) != nil
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return hasBlock
|
||||
}
|
||||
|
||||
// HasBlockDeprecated accepts a block root and returns true if the block does not exist.
|
||||
func (db *BeaconDB) HasBlockDeprecated(root [32]byte) bool {
|
||||
return db.HasBlock(context.Background(), root)
|
||||
}
|
||||
|
||||
// HeadBlock is not implemented.
|
||||
// DEPRECATED: Do not use. Not implemented.
|
||||
func (db *BeaconDB) HeadBlock(_ context.Context) (*ethpb.BeaconBlock, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
// IsEvilBlockHash determines if a certain block root has been blacklisted
|
||||
// due to failing to process core state transitions.
|
||||
func (db *BeaconDB) IsEvilBlockHash(root [32]byte) bool {
|
||||
db.badBlocksLock.Lock()
|
||||
defer db.badBlocksLock.Unlock()
|
||||
if db.badBlockHashes != nil {
|
||||
return db.badBlockHashes[root]
|
||||
}
|
||||
db.badBlockHashes = make(map[[32]byte]bool)
|
||||
return false
|
||||
}
|
||||
|
||||
// MarkEvilBlockHash makes a block hash as tainted because it corresponds
|
||||
// to a block which fails core state transition processing.
|
||||
func (db *BeaconDB) MarkEvilBlockHash(root [32]byte) {
|
||||
db.badBlocksLock.Lock()
|
||||
defer db.badBlocksLock.Unlock()
|
||||
if db.badBlockHashes == nil {
|
||||
db.badBlockHashes = make(map[[32]byte]bool)
|
||||
}
|
||||
db.badBlockHashes[root] = true
|
||||
badBlockCount.Inc()
|
||||
}
|
||||
|
||||
// SaveHeadBlockRoot is not implemented.
|
||||
func (db *BeaconDB) SaveHeadBlockRoot(_ context.Context, root [32]byte) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
// SaveGenesisBlockRoot is not implemented.
|
||||
func (db *BeaconDB) SaveGenesisBlockRoot(_ context.Context, root [32]byte) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
// SaveBlocks in db.
|
||||
func (db *BeaconDB) SaveBlocks(ctx context.Context, blocks []*ethpb.BeaconBlock) error {
|
||||
for _, blk := range blocks {
|
||||
if err := db.SaveBlock(ctx, blk); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SaveBlock in db.
|
||||
func (db *BeaconDB) SaveBlock(_ context.Context, block *ethpb.BeaconBlock) error {
|
||||
return db.SaveBlockDeprecated(block)
|
||||
}
|
||||
|
||||
// SaveBlockDeprecated accepts a block and writes it to disk.
|
||||
// DEPRECATED: Use SaveBlock.
|
||||
func (db *BeaconDB) SaveBlockDeprecated(block *ethpb.BeaconBlock) error {
|
||||
db.blocksLock.Lock()
|
||||
defer db.blocksLock.Unlock()
|
||||
|
||||
signingRoot, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to tree hash header")
|
||||
}
|
||||
|
||||
// Skip saving block to DB if it exists in the cache.
|
||||
if blk, exists := db.blocks[signingRoot]; exists && blk != nil {
|
||||
return nil
|
||||
}
|
||||
// Save it to the cache if it's not in the cache.
|
||||
db.blocks[signingRoot] = block
|
||||
blockCacheSize.Set(float64(len(db.blocks)))
|
||||
|
||||
enc, err := proto.Marshal(block)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to encode block")
|
||||
}
|
||||
slotRootBinary := encodeSlotNumberRoot(block.Slot, signingRoot)
|
||||
|
||||
if block.Slot > db.highestBlockSlot {
|
||||
db.highestBlockSlot = block.Slot
|
||||
}
|
||||
|
||||
return db.update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(blockBucket)
|
||||
if err := bucket.Put(slotRootBinary, enc); err != nil {
|
||||
return errors.Wrap(err, "failed to include the block in the main chain bucket")
|
||||
}
|
||||
return bucket.Put(signingRoot[:], enc)
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteBlock deletes a block using the slot and its root as keys in their respective buckets.
|
||||
func (db *BeaconDB) DeleteBlock(_ context.Context, hash [32]byte) error {
|
||||
return db.update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(blockBucket)
|
||||
return bucket.Delete(hash[:])
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteBlockDeprecated deletes a block using the slot and its root as keys in their respective buckets.
|
||||
func (db *BeaconDB) DeleteBlockDeprecated(block *ethpb.BeaconBlock) error {
|
||||
db.blocksLock.Lock()
|
||||
defer db.blocksLock.Unlock()
|
||||
|
||||
signingRoot, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to tree hash block")
|
||||
}
|
||||
|
||||
// Delete the block from the cache.
|
||||
delete(db.blocks, signingRoot)
|
||||
blockCacheSize.Set(float64(len(db.blocks)))
|
||||
|
||||
slotRootBinary := encodeSlotNumberRoot(block.Slot, signingRoot)
|
||||
|
||||
return db.update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(blockBucket)
|
||||
if err := bucket.Delete(slotRootBinary); err != nil {
|
||||
return errors.Wrap(err, "failed to include the block in the main chain bucket")
|
||||
}
|
||||
return bucket.Delete(signingRoot[:])
|
||||
})
|
||||
}
|
||||
|
||||
// SaveJustifiedBlock saves the last justified block from canonical chain to DB.
|
||||
func (db *BeaconDB) SaveJustifiedBlock(block *ethpb.BeaconBlock) error {
|
||||
return db.update(func(tx *bolt.Tx) error {
|
||||
enc, err := proto.Marshal(block)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to encode block")
|
||||
}
|
||||
chainInfo := tx.Bucket(chainInfoBucket)
|
||||
return chainInfo.Put(justifiedBlockLookupKey, enc)
|
||||
})
|
||||
}
|
||||
|
||||
// SaveFinalizedBlock saves the last finalized block from canonical chain to DB.
|
||||
func (db *BeaconDB) SaveFinalizedBlock(block *ethpb.BeaconBlock) error {
|
||||
return db.update(func(tx *bolt.Tx) error {
|
||||
enc, err := proto.Marshal(block)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to encode block")
|
||||
}
|
||||
chainInfo := tx.Bucket(chainInfoBucket)
|
||||
return chainInfo.Put(finalizedBlockLookupKey, enc)
|
||||
})
|
||||
}
|
||||
|
||||
// JustifiedBlock retrieves the justified block from the db.
|
||||
func (db *BeaconDB) JustifiedBlock() (*ethpb.BeaconBlock, error) {
|
||||
var block *ethpb.BeaconBlock
|
||||
err := db.view(func(tx *bolt.Tx) error {
|
||||
chainInfo := tx.Bucket(chainInfoBucket)
|
||||
encBlock := chainInfo.Get(justifiedBlockLookupKey)
|
||||
if encBlock == nil {
|
||||
return errors.New("no justified block saved")
|
||||
}
|
||||
|
||||
var err error
|
||||
block, err = createBlock(encBlock)
|
||||
return err
|
||||
})
|
||||
return block, err
|
||||
}
|
||||
|
||||
// FinalizedBlock retrieves the finalized block from the db.
|
||||
func (db *BeaconDB) FinalizedBlock() (*ethpb.BeaconBlock, error) {
|
||||
var block *ethpb.BeaconBlock
|
||||
err := db.view(func(tx *bolt.Tx) error {
|
||||
chainInfo := tx.Bucket(chainInfoBucket)
|
||||
encBlock := chainInfo.Get(finalizedBlockLookupKey)
|
||||
if encBlock == nil {
|
||||
return errors.New("no finalized block saved")
|
||||
}
|
||||
|
||||
var err error
|
||||
block, err = createBlock(encBlock)
|
||||
return err
|
||||
})
|
||||
return block, err
|
||||
}
|
||||
|
||||
// ChainHead returns the head of the main chain.
|
||||
func (db *BeaconDB) ChainHead() (*ethpb.BeaconBlock, error) {
|
||||
var block *ethpb.BeaconBlock
|
||||
err := db.view(func(tx *bolt.Tx) error {
|
||||
chainInfo := tx.Bucket(chainInfoBucket)
|
||||
blockBkt := tx.Bucket(blockBucket)
|
||||
|
||||
height := chainInfo.Get(mainChainHeightKey)
|
||||
if height == nil {
|
||||
return errors.New("unable to determine chain height")
|
||||
}
|
||||
|
||||
blockRoot := chainInfo.Get(canonicalHeadKey)
|
||||
if blockRoot == nil {
|
||||
return fmt.Errorf("root at the current height not found: %d", height)
|
||||
}
|
||||
|
||||
enc := blockBkt.Get(blockRoot)
|
||||
if enc == nil {
|
||||
return fmt.Errorf("block not found: %x", blockRoot)
|
||||
}
|
||||
|
||||
var err error
|
||||
block, err = createBlock(enc)
|
||||
return err
|
||||
})
|
||||
|
||||
return block, err
|
||||
}
|
||||
|
||||
// UpdateChainHead atomically updates the head of the chain as well as the corresponding state changes
|
||||
// Including a new state is optional.
|
||||
func (db *BeaconDB) UpdateChainHead(ctx context.Context, block *ethpb.BeaconBlock, beaconState *pb.BeaconState) error {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon-chain.db.UpdateChainHead")
|
||||
defer span.End()
|
||||
|
||||
blockRoot, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to determine block signing root")
|
||||
}
|
||||
|
||||
slotBinary := encodeSlotNumber(block.Slot)
|
||||
if block.Slot > db.highestBlockSlot {
|
||||
db.highestBlockSlot = block.Slot
|
||||
}
|
||||
|
||||
if err := db.SaveStateDeprecated(ctx, beaconState); err != nil {
|
||||
return errors.Wrap(err, "failed to save beacon state as canonical")
|
||||
}
|
||||
|
||||
blockEnc, err := proto.Marshal(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return db.update(func(tx *bolt.Tx) error {
|
||||
blockBucket := tx.Bucket(blockBucket)
|
||||
chainInfo := tx.Bucket(chainInfoBucket)
|
||||
mainChainBucket := tx.Bucket(mainChainBucket)
|
||||
|
||||
if blockBucket.Get(blockRoot[:]) == nil {
|
||||
return fmt.Errorf("expected block %#x to have already been saved before updating head", blockRoot)
|
||||
}
|
||||
|
||||
if err := chainInfo.Put(mainChainHeightKey, slotBinary); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := mainChainBucket.Put(slotBinary, blockEnc); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := chainInfo.Put(canonicalHeadKey, blockRoot[:]); err != nil {
|
||||
return errors.Wrap(err, "failed to record the block as the head of the main chain")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// CanonicalBlockBySlot accepts a slot number and returns the corresponding canonical block.
|
||||
func (db *BeaconDB) CanonicalBlockBySlot(ctx context.Context, slot uint64) (*ethpb.BeaconBlock, error) {
|
||||
_, span := trace.StartSpan(ctx, "BeaconDB.CanonicalBlockBySlot")
|
||||
defer span.End()
|
||||
span.AddAttributes(trace.Int64Attribute("slot", int64(slot)))
|
||||
|
||||
var block *ethpb.BeaconBlock
|
||||
slotEnc := encodeSlotNumber(slot)
|
||||
|
||||
err := db.view(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(mainChainBucket)
|
||||
blockEnc := bkt.Get(slotEnc)
|
||||
var err error
|
||||
if blockEnc != nil {
|
||||
block, err = createBlock(blockEnc)
|
||||
}
|
||||
return err
|
||||
})
|
||||
|
||||
return block, err
|
||||
}
|
||||
|
||||
// BlocksBySlot accepts a slot number and returns the corresponding blocks in the db.
|
||||
// Returns empty list if no blocks were recorded for the given slot.
|
||||
func (db *BeaconDB) BlocksBySlot(ctx context.Context, slot uint64) ([]*ethpb.BeaconBlock, error) {
|
||||
if ctx.Err() != nil {
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
_, span := trace.StartSpan(ctx, "BeaconDB.BlocksBySlot")
|
||||
defer span.End()
|
||||
span.AddAttributes(trace.Int64Attribute("slot", int64(slot)))
|
||||
|
||||
blocks := []*ethpb.BeaconBlock{}
|
||||
slotEnc := encodeSlotNumber(slot)
|
||||
|
||||
err := db.view(func(tx *bolt.Tx) error {
|
||||
c := tx.Bucket(blockBucket).Cursor()
|
||||
|
||||
var err error
|
||||
prefix := slotEnc
|
||||
for k, v := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, v = c.Next() {
|
||||
block, err := createBlock(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
blocks = append(blocks, block)
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
|
||||
return blocks, err
|
||||
}
|
||||
|
||||
// HighestBlockSlot returns the in-memory value for the highest block we've
|
||||
// seen in the database.
|
||||
func (db *BeaconDB) HighestBlockSlot() uint64 {
|
||||
return db.highestBlockSlot
|
||||
}
|
||||
|
||||
// ClearBlockCache prunes the block cache. This is used on every new finalized epoch.
|
||||
func (db *BeaconDB) ClearBlockCache() {
|
||||
db.blocksLock.Lock()
|
||||
defer db.blocksLock.Unlock()
|
||||
db.blocks = make(map[[32]byte]*ethpb.BeaconBlock)
|
||||
blockCacheSize.Set(float64(len(db.blocks)))
|
||||
}
|
||||
|
||||
// BlockRoots retrieves a list of beacon block roots by filter criteria.
|
||||
// DEPRECATED: Not implemented at all. Use github.com/prysmaticlabs/prysm/db/kv
|
||||
func (db *BeaconDB) BlockRoots(ctx context.Context, f *filters.QueryFilter) ([][]byte, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
// Blocks retrieves a list of beacon blocks by filter criteria.
|
||||
// DEPRECATED: Not implemented at all. Use github.com/prysmaticlabs/prysm/db/kv
|
||||
func (db *BeaconDB) Blocks(ctx context.Context, f *filters.QueryFilter) ([]*ethpb.BeaconBlock, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
// VoluntaryExit by root.
|
||||
// DEPRECATED: Use the kv store in beacon-chain/db/kv instead.
|
||||
func (db *BeaconDB) VoluntaryExit(ctx context.Context, exitRoot [32]byte) (*ethpb.VoluntaryExit, error) {
|
||||
return nil, errors.New("unimplemented")
|
||||
}
|
||||
|
||||
// SaveVoluntaryExit by root.
|
||||
// DEPRECATED: Use the kv store in beacon-chain/db/kv instead.
|
||||
func (db *BeaconDB) SaveVoluntaryExit(ctx context.Context, exit *ethpb.VoluntaryExit) error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
|
||||
// HasVoluntaryExit by root.
|
||||
// DEPRECATED: Use the kv store in beacon-chain/db/kv instead.
|
||||
func (db *BeaconDB) HasVoluntaryExit(ctx context.Context, exitRoot [32]byte) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// DeleteVoluntaryExit by root.
|
||||
// DEPRECATED: Use the kv store in beacon-chain/db/kv instead.
|
||||
func (db *BeaconDB) DeleteVoluntaryExit(ctx context.Context, exitRoot [32]byte) error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
|
||||
// ProposerSlashing retrieval from the db.
|
||||
// DEPRECATED: Use the kv store in beacon-chain/db/kv instead.
|
||||
func (db *BeaconDB) ProposerSlashing(ctx context.Context, slashingRoot [32]byte) (*ethpb.ProposerSlashing, error) {
|
||||
return nil, errors.New("unimplemented")
|
||||
}
|
||||
|
||||
// AttesterSlashing retrieval from the db.
|
||||
// DEPRECATED: Use the kv store in beacon-chain/db/kv instead.
|
||||
func (db *BeaconDB) AttesterSlashing(ctx context.Context, slashingRoot [32]byte) (*ethpb.AttesterSlashing, error) {
|
||||
return nil, errors.New("unimplemented")
|
||||
}
|
||||
|
||||
// SaveProposerSlashing to the db.
|
||||
// DEPRECATED: Use the kv store in beacon-chain/db/kv instead.
|
||||
func (db *BeaconDB) SaveProposerSlashing(ctx context.Context, slashing *ethpb.ProposerSlashing) error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
|
||||
// SaveAttesterSlashing to the db.
|
||||
// DEPRECATED: Use the kv store in beacon-chain/db/kv instead.
|
||||
func (db *BeaconDB) SaveAttesterSlashing(ctx context.Context, slashing *ethpb.AttesterSlashing) error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
|
||||
// HasProposerSlashing by root.
|
||||
// DEPRECATED: Use the kv store in beacon-chain/db/kv instead.
|
||||
func (db *BeaconDB) HasProposerSlashing(ctx context.Context, slashingRoot [32]byte) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// HasAttesterSlashing by root.
|
||||
// DEPRECATED: Use the kv store in beacon-chain/db/kv instead.
|
||||
func (db *BeaconDB) HasAttesterSlashing(ctx context.Context, slashingRoot [32]byte) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// DeleteProposerSlashing by root.
|
||||
// DEPRECATED: Use the kv store in beacon-chain/db/kv instead.
|
||||
func (db *BeaconDB) DeleteProposerSlashing(ctx context.Context, slashingRoot [32]byte) error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
|
||||
// DeleteAttesterSlashing by root.
|
||||
// DEPRECATED: Use the kv store in beacon-chain/db/kv instead.
|
||||
func (db *BeaconDB) DeleteAttesterSlashing(ctx context.Context, slashingRoot [32]byte) error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
|
||||
// SaveExit puts the exit request into the beacon chain db.
|
||||
func (db *BeaconDB) SaveExit(ctx context.Context, exit *ethpb.VoluntaryExit) error {
|
||||
ctx, span := trace.StartSpan(ctx, "beaconDB.SaveExit")
|
||||
defer span.End()
|
||||
|
||||
hash, err := hashutil.HashProto(exit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
encodedExit, err := proto.Marshal(exit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return db.update(func(tx *bolt.Tx) error {
|
||||
a := tx.Bucket(blockOperationsBucket)
|
||||
return a.Put(hash[:], encodedExit)
|
||||
})
|
||||
}
|
||||
|
||||
// HasExit checks if the exit request exists.
|
||||
func (db *BeaconDB) HasExit(hash [32]byte) bool {
|
||||
exists := false
|
||||
if err := db.view(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket(blockOperationsBucket)
|
||||
exists = b.Get(hash[:]) != nil
|
||||
return nil
|
||||
}); err != nil {
|
||||
return false
|
||||
}
|
||||
return exists
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
)
|
||||
|
||||
func TestBeaconDB_HasExit(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
d := ðpb.VoluntaryExit{
|
||||
Epoch: 100,
|
||||
}
|
||||
hash, err := hashutil.HashProto(d)
|
||||
if err != nil {
|
||||
t.Fatalf("could not hash exit request: %v", err)
|
||||
}
|
||||
|
||||
if db.HasExit(hash) {
|
||||
t.Fatal("Expected HasExit to return false")
|
||||
}
|
||||
|
||||
if err := db.SaveExit(context.Background(), d); err != nil {
|
||||
t.Fatalf("Failed to save exit request: %v", err)
|
||||
}
|
||||
if !db.HasExit(hash) {
|
||||
t.Fatal("Expected HasExit to return true")
|
||||
}
|
||||
}
|
||||
@@ -1,520 +0,0 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
)
|
||||
|
||||
func TestNilDB_OK(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
block := ðpb.BeaconBlock{}
|
||||
h, _ := ssz.SigningRoot(block)
|
||||
|
||||
hasBlock := db.HasBlockDeprecated(h)
|
||||
if hasBlock {
|
||||
t.Fatal("HashBlock should return false")
|
||||
}
|
||||
|
||||
bPrime, err := db.BlockDeprecated(h)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get block: %v", err)
|
||||
}
|
||||
if bPrime != nil {
|
||||
t.Fatalf("get should return nil for a non existent key")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveBlock_OK(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
block1 := ðpb.BeaconBlock{}
|
||||
h1, _ := ssz.SigningRoot(block1)
|
||||
|
||||
err := db.SaveBlockDeprecated(block1)
|
||||
if err != nil {
|
||||
t.Fatalf("save block failed: %v", err)
|
||||
}
|
||||
|
||||
b1Prime, err := db.BlockDeprecated(h1)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get block: %v", err)
|
||||
}
|
||||
h1Prime, _ := ssz.SigningRoot(b1Prime)
|
||||
|
||||
if b1Prime == nil || h1 != h1Prime {
|
||||
t.Fatalf("get should return b1: %x", h1)
|
||||
}
|
||||
|
||||
block2 := ðpb.BeaconBlock{
|
||||
Slot: 0,
|
||||
}
|
||||
h2, _ := ssz.SigningRoot(block2)
|
||||
|
||||
err = db.SaveBlockDeprecated(block2)
|
||||
if err != nil {
|
||||
t.Fatalf("save block failed: %v", err)
|
||||
}
|
||||
|
||||
b2Prime, err := db.BlockDeprecated(h2)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get block: %v", err)
|
||||
}
|
||||
h2Prime, _ := ssz.SigningRoot(b2Prime)
|
||||
if b2Prime == nil || h2 != h2Prime {
|
||||
t.Fatalf("get should return b2: %x", h2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveBlock_NilBlkInCache(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
block := ðpb.BeaconBlock{Slot: 999}
|
||||
h1, _ := ssz.SigningRoot(block)
|
||||
|
||||
// Save a nil block to with block root.
|
||||
db.blocks[h1] = nil
|
||||
|
||||
if err := db.SaveBlockDeprecated(block); err != nil {
|
||||
t.Fatalf("save block failed: %v", err)
|
||||
}
|
||||
|
||||
savedBlock, err := db.BlockDeprecated(h1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !proto.Equal(block, savedBlock) {
|
||||
t.Error("Could not save block in DB")
|
||||
}
|
||||
|
||||
// Verify we have the correct cached block
|
||||
if !proto.Equal(db.blocks[h1], savedBlock) {
|
||||
t.Error("Could not save block in cache")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveBlockInCache_OK(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
block := ðpb.BeaconBlock{Slot: 999}
|
||||
h, _ := ssz.SigningRoot(block)
|
||||
|
||||
err := db.SaveBlockDeprecated(block)
|
||||
if err != nil {
|
||||
t.Fatalf("save block failed: %v", err)
|
||||
}
|
||||
|
||||
if !proto.Equal(block, db.blocks[h]) {
|
||||
t.Error("Could not save block in cache")
|
||||
}
|
||||
|
||||
savedBlock, err := db.BlockDeprecated(h)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !proto.Equal(block, savedBlock) {
|
||||
t.Error("Could not save block in cache")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteBlock_OK(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
block := ðpb.BeaconBlock{Slot: 0}
|
||||
h, _ := ssz.SigningRoot(block)
|
||||
|
||||
err := db.SaveBlockDeprecated(block)
|
||||
if err != nil {
|
||||
t.Fatalf("save block failed: %v", err)
|
||||
}
|
||||
|
||||
savedBlock, err := db.BlockDeprecated(h)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !proto.Equal(block, savedBlock) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.DeleteBlockDeprecated(block); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
savedBlock, err = db.BlockDeprecated(h)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if savedBlock != nil {
|
||||
t.Errorf("Expected block to have been deleted, received: %v", savedBlock)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteBlockInCache_OK(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
block := ðpb.BeaconBlock{Slot: 0}
|
||||
h, _ := ssz.SigningRoot(block)
|
||||
|
||||
err := db.SaveBlockDeprecated(block)
|
||||
if err != nil {
|
||||
t.Fatalf("save block failed: %v", err)
|
||||
}
|
||||
|
||||
if err := db.DeleteBlockDeprecated(block); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, exists := db.blocks[h]; exists {
|
||||
t.Error("Expected block to have been deleted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlocksBySlotEmptyChain_OK(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
ctx := context.Background()
|
||||
|
||||
blocks, _ := db.BlocksBySlot(ctx, 0)
|
||||
if len(blocks) > 0 {
|
||||
t.Error("BlockBySlot should return nil for an empty chain")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlocksBySlot_MultipleBlocks(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
ctx := context.Background()
|
||||
|
||||
slotNum := uint64(3)
|
||||
b1 := ðpb.BeaconBlock{
|
||||
Slot: slotNum,
|
||||
ParentRoot: []byte("A"),
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
RandaoReveal: []byte("A"),
|
||||
},
|
||||
}
|
||||
b2 := ðpb.BeaconBlock{
|
||||
Slot: slotNum,
|
||||
ParentRoot: []byte("B"),
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
RandaoReveal: []byte("B"),
|
||||
}}
|
||||
b3 := ðpb.BeaconBlock{
|
||||
Slot: slotNum,
|
||||
ParentRoot: []byte("C"),
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
RandaoReveal: []byte("C"),
|
||||
}}
|
||||
if err := db.SaveBlockDeprecated(b1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.SaveBlockDeprecated(b2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.SaveBlockDeprecated(b3); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
blocks, _ := db.BlocksBySlot(ctx, 3)
|
||||
if len(blocks) != 3 {
|
||||
t.Errorf("Wanted %d blocks, received %d", 3, len(blocks))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateChainHead_NoBlock(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
ctx := context.Background()
|
||||
|
||||
genesisTime := uint64(time.Now().Unix())
|
||||
deposits, _ := testutil.SetupInitialDeposits(t, 10)
|
||||
err := db.InitializeState(context.Background(), genesisTime, deposits, ðpb.Eth1Data{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to initialize state: %v", err)
|
||||
}
|
||||
beaconState, err := db.HeadState(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get beacon state: %v", err)
|
||||
}
|
||||
|
||||
block := ðpb.BeaconBlock{Slot: 1}
|
||||
if err := db.UpdateChainHead(ctx, block, beaconState); err == nil {
|
||||
t.Fatalf("expected UpdateChainHead to fail if the block does not exist: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateChainHead_OK(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
ctx := context.Background()
|
||||
|
||||
genesisTime := uint64(time.Now().Unix())
|
||||
deposits, _ := testutil.SetupInitialDeposits(t, 10)
|
||||
err := db.InitializeState(context.Background(), genesisTime, deposits, ðpb.Eth1Data{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to initialize state: %v", err)
|
||||
}
|
||||
|
||||
block, err := db.ChainHead()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get genesis block: %v", err)
|
||||
}
|
||||
bHash, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get hash of b: %v", err)
|
||||
}
|
||||
|
||||
beaconState, err := db.HeadState(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get beacon state: %v", err)
|
||||
}
|
||||
|
||||
block2 := ðpb.BeaconBlock{
|
||||
Slot: 1,
|
||||
ParentRoot: bHash[:],
|
||||
}
|
||||
b2Hash, err := ssz.SigningRoot(block2)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to hash b2: %v", err)
|
||||
}
|
||||
if err := db.SaveBlockDeprecated(block2); err != nil {
|
||||
t.Fatalf("failed to save block: %v", err)
|
||||
}
|
||||
if err := db.UpdateChainHead(ctx, block2, beaconState); err != nil {
|
||||
t.Fatalf("failed to record the new head of the main chain: %v", err)
|
||||
}
|
||||
|
||||
b2Prime, err := db.CanonicalBlockBySlot(ctx, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve slot 1: %v", err)
|
||||
}
|
||||
b2Sigma, err := db.ChainHead()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve head: %v", err)
|
||||
}
|
||||
|
||||
b2PrimeHash, err := ssz.SigningRoot(b2Prime)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to hash b2Prime: %v", err)
|
||||
}
|
||||
b2SigmaHash, err := ssz.SigningRoot(b2Sigma)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to hash b2Sigma: %v", err)
|
||||
}
|
||||
|
||||
if b2Hash != b2PrimeHash {
|
||||
t.Fatalf("expected %x and %x to be equal", b2Hash, b2PrimeHash)
|
||||
}
|
||||
if b2Hash != b2SigmaHash {
|
||||
t.Fatalf("expected %x and %x to be equal", b2Hash, b2SigmaHash)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChainProgress_OK(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
ctx := context.Background()
|
||||
|
||||
genesisTime := uint64(time.Now().Unix())
|
||||
deposits, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
err := db.InitializeState(context.Background(), genesisTime, deposits, ðpb.Eth1Data{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to initialize state: %v", err)
|
||||
}
|
||||
|
||||
beaconState, err := db.HeadState(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get beacon state: %v", err)
|
||||
}
|
||||
cycleLength := params.BeaconConfig().SlotsPerEpoch
|
||||
|
||||
block1 := ðpb.BeaconBlock{Slot: 1}
|
||||
if err := db.SaveBlockDeprecated(block1); err != nil {
|
||||
t.Fatalf("failed to save block: %v", err)
|
||||
}
|
||||
if err := db.UpdateChainHead(ctx, block1, beaconState); err != nil {
|
||||
t.Fatalf("failed to record the new head: %v", err)
|
||||
}
|
||||
heighestBlock, err := db.ChainHead()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get chain head: %v", err)
|
||||
}
|
||||
if heighestBlock.Slot != block1.Slot {
|
||||
t.Fatalf("expected height to equal %d, got %d", block1.Slot, heighestBlock.Slot)
|
||||
}
|
||||
|
||||
block2 := ðpb.BeaconBlock{Slot: cycleLength}
|
||||
if err := db.SaveBlockDeprecated(block2); err != nil {
|
||||
t.Fatalf("failed to save block: %v", err)
|
||||
}
|
||||
if err := db.UpdateChainHead(ctx, block2, beaconState); err != nil {
|
||||
t.Fatalf("failed to record the new head: %v", err)
|
||||
}
|
||||
heighestBlock, err = db.ChainHead()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get block: %v", err)
|
||||
}
|
||||
if heighestBlock.Slot != block2.Slot {
|
||||
t.Fatalf("expected height to equal %d, got %d", block2.Slot, heighestBlock.Slot)
|
||||
}
|
||||
|
||||
block3 := ðpb.BeaconBlock{Slot: 3}
|
||||
if err := db.SaveBlockDeprecated(block3); err != nil {
|
||||
t.Fatalf("failed to save block: %v", err)
|
||||
}
|
||||
if err := db.UpdateChainHead(ctx, block3, beaconState); err != nil {
|
||||
t.Fatalf("failed to update head: %v", err)
|
||||
}
|
||||
heighestBlock, err = db.ChainHead()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get chain head: %v", err)
|
||||
}
|
||||
if heighestBlock.Slot != block3.Slot {
|
||||
t.Fatalf("expected height to equal %d, got %d", block3.Slot, heighestBlock.Slot)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJustifiedBlock_NoneExists(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
wanted := "no justified block saved"
|
||||
_, err := db.JustifiedBlock()
|
||||
if !strings.Contains(err.Error(), wanted) {
|
||||
t.Errorf("Expected: %s, received: %s", wanted, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestJustifiedBlock_CanSaveRetrieve(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
blkSlot := uint64(10)
|
||||
block1 := ðpb.BeaconBlock{
|
||||
Slot: blkSlot,
|
||||
}
|
||||
|
||||
if err := db.SaveJustifiedBlock(block1); err != nil {
|
||||
t.Fatalf("could not save justified block: %v", err)
|
||||
}
|
||||
|
||||
justifiedBlk, err := db.JustifiedBlock()
|
||||
if err != nil {
|
||||
t.Fatalf("could not get justified block: %v", err)
|
||||
}
|
||||
if justifiedBlk.Slot != blkSlot {
|
||||
t.Errorf("Saved block does not have the slot from which it was requested, wanted: %d, got: %d",
|
||||
blkSlot, justifiedBlk.Slot)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFinalizedBlock_NoneExists(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
wanted := "no finalized block saved"
|
||||
_, err := db.FinalizedBlock()
|
||||
if !strings.Contains(err.Error(), wanted) {
|
||||
t.Errorf("Expected: %s, received: %s", wanted, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestFinalizedBlock_CanSaveRetrieve(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
blkSlot := uint64(22)
|
||||
block1 := ðpb.BeaconBlock{
|
||||
Slot: blkSlot,
|
||||
}
|
||||
|
||||
if err := db.SaveFinalizedBlock(block1); err != nil {
|
||||
t.Fatalf("could not save finalized block: %v", err)
|
||||
}
|
||||
|
||||
finalizedblk, err := db.FinalizedBlock()
|
||||
if err != nil {
|
||||
t.Fatalf("could not get finalized block: %v", err)
|
||||
}
|
||||
if finalizedblk.Slot != blkSlot {
|
||||
t.Errorf("Saved block does not have the slot from which it was requested, wanted: %d, got: %d",
|
||||
blkSlot, finalizedblk.Slot)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasBlock_returnsTrue(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
block := ðpb.BeaconBlock{
|
||||
Slot: uint64(44),
|
||||
}
|
||||
|
||||
root, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := db.SaveBlockDeprecated(block); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !db.HasBlockDeprecated(root) {
|
||||
t.Fatal("db.HasBlockDeprecated returned false for block just saved")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHighestBlockSlot_UpdatedOnSaveBlock(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
block := ðpb.BeaconBlock{
|
||||
Slot: 23,
|
||||
}
|
||||
|
||||
if err := db.SaveBlockDeprecated(block); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if db.HighestBlockSlot() != block.Slot {
|
||||
t.Errorf("Unexpected highest slot %d, wanted %d", db.HighestBlockSlot(), block.Slot)
|
||||
}
|
||||
|
||||
block.Slot = 55
|
||||
if err := db.SaveBlockDeprecated(block); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if db.HighestBlockSlot() != block.Slot {
|
||||
t.Errorf("Unexpected highest slot %d, wanted %d", db.HighestBlockSlot(), block.Slot)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClearBlockCache_OK(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
block := ðpb.BeaconBlock{Slot: 0}
|
||||
|
||||
err := db.SaveBlockDeprecated(block)
|
||||
if err != nil {
|
||||
t.Fatalf("save block failed: %v", err)
|
||||
}
|
||||
if len(db.blocks) != 1 {
|
||||
t.Error("incorrect block cache length")
|
||||
}
|
||||
db.ClearBlockCache()
|
||||
if len(db.blocks) != 0 {
|
||||
t.Error("incorrect block cache length")
|
||||
}
|
||||
}
|
||||
@@ -3,23 +3,14 @@ package db
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db/filters"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db/kv"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var log = logrus.WithField("prefix", "beacondb")
|
||||
|
||||
// Database defines the necessary methods for Prysm's eth2 backend which may
|
||||
// be implemented by any key-value or relational database in practice.
|
||||
type Database interface {
|
||||
@@ -82,105 +73,7 @@ type Database interface {
|
||||
SaveDepositContractAddress(ctx context.Context, addr common.Address) error
|
||||
}
|
||||
|
||||
var _ = Database(&BeaconDB{})
|
||||
|
||||
// BeaconDB manages the data layer of the beacon chain implementation.
|
||||
// The exposed methods do not have an opinion of the underlying data engine,
|
||||
// but instead reflect the beacon chain logic.
|
||||
// For example, instead of defining get, put, remove
|
||||
// This defines methods such as getBlock, saveBlocksAndAttestations, etc.
|
||||
// DEPRECATED: Use github.com/prysmaticlabs/prysm/db/kv instead.
|
||||
type BeaconDB struct {
|
||||
// state objects and caches
|
||||
stateLock sync.RWMutex
|
||||
serializedState []byte
|
||||
stateHash [32]byte
|
||||
validatorRegistry []*ethpb.Validator
|
||||
validatorBalances []uint64
|
||||
db *bolt.DB
|
||||
databasePath string
|
||||
|
||||
// Beacon block info in memory.
|
||||
highestBlockSlot uint64
|
||||
// We keep a map of hashes of blocks which failed processing for blacklisting.
|
||||
badBlockHashes map[[32]byte]bool
|
||||
badBlocksLock sync.RWMutex
|
||||
blocks map[[32]byte]*ethpb.BeaconBlock
|
||||
blocksLock sync.RWMutex
|
||||
}
|
||||
|
||||
// Close closes the underlying boltdb database.
|
||||
func (db *BeaconDB) Close() error {
|
||||
return db.db.Close()
|
||||
}
|
||||
|
||||
func (db *BeaconDB) update(fn func(*bolt.Tx) error) error {
|
||||
return db.db.Update(fn)
|
||||
}
|
||||
func (db *BeaconDB) batch(fn func(*bolt.Tx) error) error {
|
||||
return db.db.Batch(fn)
|
||||
}
|
||||
func (db *BeaconDB) view(fn func(*bolt.Tx) error) error {
|
||||
return db.db.View(fn)
|
||||
}
|
||||
|
||||
func createBuckets(tx *bolt.Tx, buckets ...[]byte) error {
|
||||
for _, bucket := range buckets {
|
||||
if _, err := tx.CreateBucketIfNotExists(bucket); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewDBDeprecated initializes a new DB. If the genesis block and states do not exist, this method creates it.
|
||||
// DEPRECATED: Use github.com/prysmaticlabs/prysm/db.NewDB instead.
|
||||
func NewDBDeprecated(dirPath string) (*BeaconDB, error) {
|
||||
if err := os.MkdirAll(dirPath, 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
datafile := path.Join(dirPath, "beaconchain.db")
|
||||
boltDB, err := bolt.Open(datafile, 0600, &bolt.Options{Timeout: 1 * time.Second})
|
||||
if err != nil {
|
||||
if err == bolt.ErrTimeout {
|
||||
return nil, errors.New("cannot obtain database lock, database may be in use by another process")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db := &BeaconDB{db: boltDB, databasePath: dirPath}
|
||||
db.blocks = make(map[[32]byte]*ethpb.BeaconBlock)
|
||||
|
||||
if err := db.update(func(tx *bolt.Tx) error {
|
||||
return createBuckets(tx, blockBucket, attestationBucket, attestationTargetBucket, mainChainBucket,
|
||||
histStateBucket, chainInfoBucket, cleanupHistoryBucket, blockOperationsBucket, validatorBucket)
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return db, err
|
||||
}
|
||||
|
||||
// NewDB initializes a new DB.
|
||||
func NewDB(dirPath string) (Database, error) {
|
||||
return kv.NewKVStore(dirPath)
|
||||
}
|
||||
|
||||
// ClearDB removes the previously stored directory at the data directory.
|
||||
func ClearDB(dirPath string) error {
|
||||
if _, err := os.Stat(dirPath); os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return os.RemoveAll(dirPath)
|
||||
}
|
||||
|
||||
// DatabasePath returns the filepath to the database directory.
|
||||
func (db *BeaconDB) DatabasePath() string {
|
||||
return db.databasePath
|
||||
}
|
||||
|
||||
// ClearDB removes the previously stored directory at the data directory.
|
||||
func (db *BeaconDB) ClearDB() error {
|
||||
return ClearDB(db.databasePath)
|
||||
}
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db/kv"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
)
|
||||
|
||||
var _ = Database(&kv.Store{})
|
||||
|
||||
// setupDB instantiates and returns a BeaconDB instance.
|
||||
func setupDB(t testing.TB) *BeaconDB {
|
||||
randPath, err := rand.Int(rand.Reader, big.NewInt(1000000))
|
||||
if err != nil {
|
||||
t.Fatalf("Could not generate random file path: %v", err)
|
||||
}
|
||||
path := path.Join(testutil.TempDir(), fmt.Sprintf("/%d", randPath))
|
||||
if err := os.RemoveAll(path); err != nil {
|
||||
t.Fatalf("Failed to remove directory: %v", err)
|
||||
}
|
||||
db, err := NewDBDeprecated(path)
|
||||
db.blocks = make(map[[32]byte]*ethpb.BeaconBlock)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to instantiate DB: %v", err)
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
// teardownDB cleans up a test BeaconDB instance.
|
||||
func teardownDB(t testing.TB, db *BeaconDB) {
|
||||
if err := db.Close(); err != nil {
|
||||
t.Fatalf("Failed to close database: %v", err)
|
||||
}
|
||||
if err := os.RemoveAll(db.DatabasePath()); err != nil {
|
||||
t.Fatalf("Failed to remove directory: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClearDB(t *testing.T) {
|
||||
beaconDB := setupDB(t)
|
||||
path := strings.TrimSuffix(beaconDB.DatabasePath(), "beaconchain.db")
|
||||
if err := ClearDB(path); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(beaconDB.DatabasePath()); !os.IsNotExist(err) {
|
||||
t.Fatalf("db wasnt cleared %v", err)
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/pkg/errors"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
var depositContractAddressKey = []byte("deposit-contract")
|
||||
|
||||
// DepositContractAddress returns contract address is the address of
|
||||
// the deposit contract on the proof of work chain.
|
||||
// DEPRECATED: Use the kv store in beacon-chain/db/kv instead.
|
||||
func (db *BeaconDB) DepositContractAddress(ctx context.Context) ([]byte, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.DepositContractAddress")
|
||||
defer span.End()
|
||||
|
||||
var addr []byte
|
||||
err := db.view(func(tx *bolt.Tx) error {
|
||||
chainInfo := tx.Bucket(chainInfoBucket)
|
||||
addr = chainInfo.Get(depositContractAddressKey)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
// SaveDepositContractAddress to the db.
|
||||
// DEPRECATED: Use the kv store in beacon-chain/db/kv instead.
|
||||
func (db *BeaconDB) SaveDepositContractAddress(ctx context.Context, addr common.Address) error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
|
||||
// VerifyContractAddress that represents the data in this database. The
|
||||
// contract address is the address of the deposit contract on the proof of work
|
||||
// Ethereum chain. This value will never change or all of the data in the
|
||||
// database would be made invalid.
|
||||
// DEPRECATED: Use the kv store in beacon-chain/db/kv instead.
|
||||
func (db *BeaconDB) VerifyContractAddress(ctx context.Context, addr common.Address) error {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.VerifyContractAddress")
|
||||
defer span.End()
|
||||
|
||||
return db.update(func(tx *bolt.Tx) error {
|
||||
chainInfo := tx.Bucket(chainInfoBucket)
|
||||
|
||||
expectedAddress := chainInfo.Get(depositContractAddressKey)
|
||||
if expectedAddress == nil {
|
||||
return chainInfo.Put(depositContractAddressKey, addr.Bytes())
|
||||
}
|
||||
|
||||
if !bytes.Equal(expectedAddress, addr.Bytes()) {
|
||||
return fmt.Errorf("invalid deposit contract address, expected %#x - try running with --clear-db", expectedAddress)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
func TestVerifyContractAddress(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
address := common.HexToAddress("0x0cd549b4abcbc0cb63012ea7de6fd34ebdccfd45")
|
||||
// There should be no error the first time.
|
||||
if err := db.VerifyContractAddress(ctx, address); err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
// There should be no error the second time.
|
||||
if err := db.VerifyContractAddress(ctx, address); err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
// But there should be an error with a different address.
|
||||
otherAddr := common.HexToAddress("0x247b06d9890ab9b032ec318ca436aef262d0f08a")
|
||||
if err := db.VerifyContractAddress(ctx, otherAddr); err == nil {
|
||||
t.Fatal("Expected error, but didn't receive one")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
)
|
||||
|
||||
// The Schema will define how to store and retrieve data from the db.
|
||||
// Currently we store blocks by prefixing `block` to their hash and
|
||||
// using that as the key to store blocks.
|
||||
// `block` + hash -> block
|
||||
//
|
||||
// We store the state using the state lookup key, and
|
||||
// also the genesis block using the genesis lookup key.
|
||||
// The canonical head is stored using the canonical head lookup key.
|
||||
|
||||
// The fields below define the suffix of keys in the db.
|
||||
var (
|
||||
attestationBucket = []byte("attestation-bucket")
|
||||
attestationTargetBucket = []byte("attestation-target-bucket")
|
||||
blockOperationsBucket = []byte("block-operations-bucket")
|
||||
blockBucket = []byte("block-bucket")
|
||||
mainChainBucket = []byte("main-chain-bucket")
|
||||
histStateBucket = []byte("historical-state-bucket")
|
||||
chainInfoBucket = []byte("chain-info")
|
||||
validatorBucket = []byte("validator")
|
||||
|
||||
mainChainHeightKey = []byte("chain-height")
|
||||
canonicalHeadKey = []byte("canonical-head")
|
||||
stateLookupKey = []byte("state")
|
||||
finalizedStateLookupKey = []byte("finalized-state")
|
||||
justifiedStateLookupKey = []byte("justified-state")
|
||||
finalizedBlockLookupKey = []byte("finalized-block")
|
||||
justifiedBlockLookupKey = []byte("justified-block")
|
||||
|
||||
// DB internal use
|
||||
cleanupHistoryBucket = []byte("cleanup-history-bucket")
|
||||
)
|
||||
|
||||
func encodeSlotNumberRoot(number uint64, root [32]byte) []byte {
|
||||
return append(bytesutil.Bytes8(number), root[:]...)
|
||||
}
|
||||
|
||||
// encodeSlotNumber encodes a slot number as little-endian uint32.
|
||||
func encodeSlotNumber(number uint64) []byte {
|
||||
return bytesutil.Bytes8(number)
|
||||
}
|
||||
|
||||
// decodeSlotNumber returns a slot number which has been
|
||||
// encoded as a little-endian uint32 in the byte array.
|
||||
func decodeToSlotNumber(bytearray []byte) uint64 {
|
||||
return bytesutil.FromBytes8(bytearray)
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// SetupDB instantiates and returns a simulated backend BeaconDB instance.
|
||||
// DEPRECATED: Use beacon-chain/db/testing.SetupDB
|
||||
func SetupDB() (*BeaconDB, error) {
|
||||
randPath, err := rand.Int(rand.Reader, big.NewInt(1000000))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not generate random file path")
|
||||
}
|
||||
path := path.Join(os.TempDir(), fmt.Sprintf("/%d", randPath))
|
||||
if err := os.RemoveAll(path); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to remove directory")
|
||||
}
|
||||
return NewDBDeprecated(path)
|
||||
}
|
||||
@@ -1,535 +0,0 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"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/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
var (
|
||||
stateBytes = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "beacondb_state_size_bytes",
|
||||
Help: "The protobuf encoded size of the last saved state in the beaconDB",
|
||||
})
|
||||
)
|
||||
|
||||
// InitializeState creates an initial genesis state for the beacon
|
||||
// node using a set of genesis validators.
|
||||
func (db *BeaconDB) InitializeState(ctx context.Context, genesisTime uint64, deposits []*ethpb.Deposit, eth1Data *ethpb.Eth1Data) error {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.InitializeState")
|
||||
defer span.End()
|
||||
|
||||
beaconState, err := state.GenesisBeaconState(deposits, genesisTime, eth1Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// #nosec G104
|
||||
stateEnc, _ := proto.Marshal(beaconState)
|
||||
stateHash := hashutil.Hash(stateEnc)
|
||||
genesisBlock := b.NewGenesisBlock(stateHash[:])
|
||||
// #nosec G104
|
||||
blockRoot, _ := ssz.SigningRoot(genesisBlock)
|
||||
// #nosec G104
|
||||
blockEnc, _ := proto.Marshal(genesisBlock)
|
||||
zeroBinary := encodeSlotNumberRoot(0, blockRoot)
|
||||
|
||||
db.serializedState = stateEnc
|
||||
db.stateHash = stateHash
|
||||
|
||||
if err := db.SaveStateDeprecated(ctx, beaconState); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return db.update(func(tx *bolt.Tx) error {
|
||||
blockBkt := tx.Bucket(blockBucket)
|
||||
validatorBkt := tx.Bucket(validatorBucket)
|
||||
mainChain := tx.Bucket(mainChainBucket)
|
||||
chainInfo := tx.Bucket(chainInfoBucket)
|
||||
|
||||
if err := chainInfo.Put(mainChainHeightKey, zeroBinary); err != nil {
|
||||
return errors.Wrap(err, "failed to record block height")
|
||||
}
|
||||
|
||||
if err := mainChain.Put(zeroBinary, blockEnc); err != nil {
|
||||
return errors.Wrap(err, "failed to record block hash")
|
||||
}
|
||||
|
||||
if err := chainInfo.Put(canonicalHeadKey, blockRoot[:]); err != nil {
|
||||
return errors.Wrap(err, "failed to record block as canonical")
|
||||
}
|
||||
|
||||
if err := blockBkt.Put(blockRoot[:], blockEnc); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, validator := range beaconState.Validators {
|
||||
h := hashutil.Hash(validator.PublicKey)
|
||||
buf := make([]byte, binary.MaxVarintLen64)
|
||||
n := binary.PutUvarint(buf, uint64(i))
|
||||
if err := validatorBkt.Put(h[:], buf[:n]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Putting in finalized state.
|
||||
if err := chainInfo.Put(finalizedStateLookupKey, stateEnc); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return chainInfo.Put(stateLookupKey, stateEnc)
|
||||
})
|
||||
}
|
||||
|
||||
// State is not implemented.
|
||||
func (db *BeaconDB) State(ctx context.Context, blockRoot [32]byte) (*pb.BeaconState, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
// HeadState fetches the canonical beacon chain's head state from the DB.
|
||||
func (db *BeaconDB) HeadState(ctx context.Context) (*pb.BeaconState, error) {
|
||||
if ctx.Err() != nil {
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.HeadState")
|
||||
defer span.End()
|
||||
|
||||
ctx, lockSpan := trace.StartSpan(ctx, "BeaconDB.stateLock.Lock")
|
||||
db.stateLock.RLock()
|
||||
defer db.stateLock.RUnlock()
|
||||
lockSpan.End()
|
||||
|
||||
// Return in-memory cached state, if available.
|
||||
if db.serializedState != nil {
|
||||
_, span := trace.StartSpan(ctx, "proto.Marshal")
|
||||
defer span.End()
|
||||
newState := &pb.BeaconState{}
|
||||
// For each READ we unmarshal the serialized state into a new state struct and return that.
|
||||
if err := proto.Unmarshal(db.serializedState, newState); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newState, nil
|
||||
}
|
||||
|
||||
var beaconState *pb.BeaconState
|
||||
err := db.view(func(tx *bolt.Tx) error {
|
||||
chainInfo := tx.Bucket(chainInfoBucket)
|
||||
enc := chainInfo.Get(stateLookupKey)
|
||||
if enc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
beaconState, err = createState(enc)
|
||||
|
||||
if beaconState != nil && beaconState.Slot > db.highestBlockSlot {
|
||||
db.highestBlockSlot = beaconState.Slot
|
||||
}
|
||||
db.serializedState = enc
|
||||
db.stateHash = hashutil.Hash(enc)
|
||||
|
||||
return err
|
||||
})
|
||||
|
||||
return beaconState, err
|
||||
}
|
||||
|
||||
// GenesisState is not implemented.
|
||||
func (db *BeaconDB) GenesisState(ctx context.Context) (*pb.BeaconState, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
// HeadStateRoot returns the root of the current state from the db.
|
||||
func (db *BeaconDB) HeadStateRoot() [32]byte {
|
||||
return db.stateHash
|
||||
}
|
||||
|
||||
// SaveState in db.
|
||||
func (db *BeaconDB) SaveState(ctx context.Context, state *pb.BeaconState, _ [32]byte) error {
|
||||
return db.SaveStateDeprecated(ctx, state)
|
||||
}
|
||||
|
||||
// SaveStateDeprecated updates the beacon chain state.
|
||||
func (db *BeaconDB) SaveStateDeprecated(ctx context.Context, beaconState *pb.BeaconState) error {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.SaveStateDeprecated")
|
||||
defer span.End()
|
||||
|
||||
ctx, lockSpan := trace.StartSpan(ctx, "BeaconDB.stateLock.Lock")
|
||||
db.stateLock.Lock()
|
||||
defer db.stateLock.Unlock()
|
||||
lockSpan.End()
|
||||
|
||||
// For each WRITE of the state, we serialize the inputted state and save it in memory,
|
||||
// and then the state is saved to disk.
|
||||
enc, err := proto.Marshal(beaconState)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stateHash := hashutil.Hash(enc)
|
||||
tempState := &pb.BeaconState{}
|
||||
tempState.Validators = beaconState.Validators
|
||||
|
||||
copy(db.validatorBalances, beaconState.Balances)
|
||||
db.validatorRegistry = proto.Clone(tempState).(*pb.BeaconState).Validators
|
||||
db.serializedState = enc
|
||||
db.stateHash = stateHash
|
||||
|
||||
if beaconState.LatestBlockHeader != nil {
|
||||
blockRoot, err := ssz.SigningRoot(beaconState.LatestBlockHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := db.SaveHistoricalState(ctx, beaconState, blockRoot); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return db.update(func(tx *bolt.Tx) error {
|
||||
chainInfo := tx.Bucket(chainInfoBucket)
|
||||
|
||||
stateBytes.Set(float64(len(enc)))
|
||||
reportStateMetrics(beaconState)
|
||||
return chainInfo.Put(stateLookupKey, enc)
|
||||
})
|
||||
}
|
||||
|
||||
// SaveJustifiedState saves the last justified state in the db.
|
||||
func (db *BeaconDB) SaveJustifiedState(beaconState *pb.BeaconState) error {
|
||||
return db.update(func(tx *bolt.Tx) error {
|
||||
chainInfo := tx.Bucket(chainInfoBucket)
|
||||
beaconStateEnc, err := proto.Marshal(beaconState)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return chainInfo.Put(justifiedStateLookupKey, beaconStateEnc)
|
||||
})
|
||||
}
|
||||
|
||||
// SaveFinalizedState saves the last finalized state in the db.
|
||||
func (db *BeaconDB) SaveFinalizedState(beaconState *pb.BeaconState) error {
|
||||
|
||||
// Delete historical states if we are saving a new finalized state.
|
||||
if err := db.deleteHistoricalStates(beaconState.Slot); err != nil {
|
||||
return err
|
||||
}
|
||||
return db.update(func(tx *bolt.Tx) error {
|
||||
chainInfo := tx.Bucket(chainInfoBucket)
|
||||
beaconStateEnc, err := proto.Marshal(beaconState)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return chainInfo.Put(finalizedStateLookupKey, beaconStateEnc)
|
||||
})
|
||||
}
|
||||
|
||||
// SaveHistoricalState saves the last finalized state in the db.
|
||||
func (db *BeaconDB) SaveHistoricalState(ctx context.Context, beaconState *pb.BeaconState, blockRoot [32]byte) error {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon-chain.db.SaveHistoricalState")
|
||||
defer span.End()
|
||||
|
||||
slotRootBinary := encodeSlotNumberRoot(beaconState.Slot, blockRoot)
|
||||
stateHash, err := hashutil.HashProto(beaconState)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return db.update(func(tx *bolt.Tx) error {
|
||||
histState := tx.Bucket(histStateBucket)
|
||||
chainInfo := tx.Bucket(chainInfoBucket)
|
||||
if err := histState.Put(slotRootBinary, stateHash[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
beaconStateEnc, err := proto.Marshal(beaconState)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return chainInfo.Put(stateHash[:], beaconStateEnc)
|
||||
})
|
||||
}
|
||||
|
||||
// JustifiedState retrieves the justified state from the db.
|
||||
func (db *BeaconDB) JustifiedState() (*pb.BeaconState, error) {
|
||||
var beaconState *pb.BeaconState
|
||||
err := db.view(func(tx *bolt.Tx) error {
|
||||
chainInfo := tx.Bucket(chainInfoBucket)
|
||||
encState := chainInfo.Get(justifiedStateLookupKey)
|
||||
if encState == nil {
|
||||
return errors.New("no justified state saved")
|
||||
}
|
||||
|
||||
var err error
|
||||
beaconState, err = createState(encState)
|
||||
return err
|
||||
})
|
||||
return beaconState, err
|
||||
}
|
||||
|
||||
// FinalizedState retrieves the finalized state from the db.
|
||||
func (db *BeaconDB) FinalizedState() (*pb.BeaconState, error) {
|
||||
var beaconState *pb.BeaconState
|
||||
err := db.view(func(tx *bolt.Tx) error {
|
||||
chainInfo := tx.Bucket(chainInfoBucket)
|
||||
encState := chainInfo.Get(finalizedStateLookupKey)
|
||||
if encState == nil {
|
||||
return errors.New("no finalized state saved")
|
||||
}
|
||||
|
||||
var err error
|
||||
beaconState, err = createState(encState)
|
||||
return err
|
||||
})
|
||||
return beaconState, err
|
||||
}
|
||||
|
||||
// HistoricalStateFromSlot retrieves the state that is closest to the input slot,
|
||||
// while being smaller than or equal to the input slot.
|
||||
func (db *BeaconDB) HistoricalStateFromSlot(ctx context.Context, slot uint64, blockRoot [32]byte) (*pb.BeaconState, error) {
|
||||
if ctx.Err() != nil {
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
_, span := trace.StartSpan(ctx, "BeaconDB.HistoricalStateFromSlot")
|
||||
defer span.End()
|
||||
span.AddAttributes(trace.Int64Attribute("slot", int64(slot)))
|
||||
var beaconState *pb.BeaconState
|
||||
err := db.view(func(tx *bolt.Tx) error {
|
||||
var err error
|
||||
var highestStateSlot uint64
|
||||
var stateExists bool
|
||||
histStateKey := make([]byte, 32)
|
||||
|
||||
chainInfo := tx.Bucket(chainInfoBucket)
|
||||
histState := tx.Bucket(histStateBucket)
|
||||
hsCursor := histState.Cursor()
|
||||
|
||||
for k, v := hsCursor.First(); k != nil; k, v = hsCursor.Next() {
|
||||
slotBinary := k[:8]
|
||||
blockRootBinary := k[8:]
|
||||
slotNumber := decodeToSlotNumber(slotBinary)
|
||||
|
||||
if slotNumber == slot && bytes.Equal(blockRootBinary, blockRoot[:]) {
|
||||
stateExists = true
|
||||
highestStateSlot = slotNumber
|
||||
histStateKey = v
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If no historical state exists, retrieve and decode the finalized state.
|
||||
if !stateExists {
|
||||
for k, v := hsCursor.First(); k != nil; k, v = hsCursor.Next() {
|
||||
slotBinary := k[:8]
|
||||
slotNumber := decodeToSlotNumber(slotBinary)
|
||||
// find the state with slot closest to the requested slot
|
||||
if slotNumber >= highestStateSlot && slotNumber <= slot {
|
||||
stateExists = true
|
||||
highestStateSlot = slotNumber
|
||||
histStateKey = v
|
||||
}
|
||||
}
|
||||
|
||||
if !stateExists {
|
||||
return errors.New("no historical states saved in db")
|
||||
}
|
||||
}
|
||||
|
||||
// If historical state exists, retrieve and decode it.
|
||||
encState := chainInfo.Get(histStateKey)
|
||||
if encState == nil {
|
||||
return errors.New("no historical state saved")
|
||||
}
|
||||
beaconState, err = createState(encState)
|
||||
return err
|
||||
})
|
||||
return beaconState, err
|
||||
}
|
||||
|
||||
// Validators fetches the current validator registry stored in state.
|
||||
func (db *BeaconDB) Validators(ctx context.Context) ([]*ethpb.Validator, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.Validators")
|
||||
defer span.End()
|
||||
|
||||
db.stateLock.RLock()
|
||||
defer db.stateLock.RUnlock()
|
||||
|
||||
// Return in-memory cached state, if available.
|
||||
if db.validatorRegistry != nil {
|
||||
_, span := trace.StartSpan(ctx, "proto.Clone.Validators")
|
||||
defer span.End()
|
||||
tempState := &pb.BeaconState{
|
||||
Validators: db.validatorRegistry,
|
||||
}
|
||||
newState := proto.Clone(tempState).(*pb.BeaconState)
|
||||
return newState.Validators, nil
|
||||
}
|
||||
|
||||
var beaconState *pb.BeaconState
|
||||
err := db.view(func(tx *bolt.Tx) error {
|
||||
chainInfo := tx.Bucket(chainInfoBucket)
|
||||
enc := chainInfo.Get(stateLookupKey)
|
||||
if enc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
beaconState, err = createState(enc)
|
||||
if beaconState != nil && beaconState.Slot > db.highestBlockSlot {
|
||||
db.highestBlockSlot = beaconState.Slot
|
||||
}
|
||||
return err
|
||||
})
|
||||
|
||||
return beaconState.Validators, err
|
||||
}
|
||||
|
||||
// ValidatorLatestVote is not implemented.
|
||||
func (db *BeaconDB) ValidatorLatestVote(_ context.Context, _ uint64) (*pb.ValidatorLatestVote, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
// ValidatorFromState fetches the validator with the desired index from the cached registry.
|
||||
func (db *BeaconDB) ValidatorFromState(ctx context.Context, index uint64) (*ethpb.Validator, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.ValidatorFromState")
|
||||
defer span.End()
|
||||
|
||||
db.stateLock.RLock()
|
||||
defer db.stateLock.RUnlock()
|
||||
|
||||
if db.validatorRegistry != nil {
|
||||
// return error if it's an invalid validator index.
|
||||
if index >= uint64(len(db.validatorRegistry)) {
|
||||
return nil, fmt.Errorf("invalid validator index %d", index)
|
||||
}
|
||||
validator := proto.Clone(db.validatorRegistry[index]).(*ethpb.Validator)
|
||||
return validator, nil
|
||||
}
|
||||
|
||||
var beaconState *pb.BeaconState
|
||||
err := db.view(func(tx *bolt.Tx) error {
|
||||
chainInfo := tx.Bucket(chainInfoBucket)
|
||||
enc := chainInfo.Get(stateLookupKey)
|
||||
if enc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
beaconState, err = createState(enc)
|
||||
if beaconState != nil && beaconState.Slot > db.highestBlockSlot {
|
||||
db.highestBlockSlot = beaconState.Slot
|
||||
}
|
||||
return err
|
||||
})
|
||||
|
||||
// return error if it's an invalid validator index.
|
||||
if index >= uint64(len(db.validatorRegistry)) {
|
||||
return nil, fmt.Errorf("invalid validator index %d", index)
|
||||
}
|
||||
|
||||
return beaconState.Validators[index], err
|
||||
}
|
||||
|
||||
// Balances fetches the current validator balances stored in state.
|
||||
func (db *BeaconDB) Balances(ctx context.Context) ([]uint64, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.Balances")
|
||||
defer span.End()
|
||||
|
||||
db.stateLock.RLock()
|
||||
defer db.stateLock.RUnlock()
|
||||
|
||||
// Return in-memory cached state, if available.
|
||||
if db.validatorBalances != nil {
|
||||
_, span := trace.StartSpan(ctx, "BeaconDB.Copy.Balances")
|
||||
defer span.End()
|
||||
newBalances := make([]uint64, len(db.validatorBalances))
|
||||
copy(newBalances, db.validatorBalances)
|
||||
return newBalances, nil
|
||||
}
|
||||
|
||||
var beaconState *pb.BeaconState
|
||||
err := db.view(func(tx *bolt.Tx) error {
|
||||
chainInfo := tx.Bucket(chainInfoBucket)
|
||||
enc := chainInfo.Get(stateLookupKey)
|
||||
if enc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
beaconState, err = createState(enc)
|
||||
if beaconState != nil && beaconState.Slot > db.highestBlockSlot {
|
||||
db.highestBlockSlot = beaconState.Slot
|
||||
}
|
||||
return err
|
||||
})
|
||||
|
||||
return beaconState.Balances, err
|
||||
}
|
||||
|
||||
// JustifiedCheckpoint is not implemented.
|
||||
func (db *BeaconDB) JustifiedCheckpoint(ctx context.Context) (*ethpb.Checkpoint, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
// FinalizedCheckpoint is not implemented.
|
||||
func (db *BeaconDB) FinalizedCheckpoint(ctx context.Context) (*ethpb.Checkpoint, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
// SaveJustifiedCheckpoint is not implemented.
|
||||
func (db *BeaconDB) SaveJustifiedCheckpoint(ctx context.Context, checkpoint *ethpb.Checkpoint) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
// SaveFinalizedCheckpoint is not implemented.
|
||||
func (db *BeaconDB) SaveFinalizedCheckpoint(ctx context.Context, checkpoint *ethpb.Checkpoint) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
func createState(enc []byte) (*pb.BeaconState, error) {
|
||||
protoState := &pb.BeaconState{}
|
||||
err := proto.Unmarshal(enc, protoState)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal encoding")
|
||||
}
|
||||
return protoState, nil
|
||||
}
|
||||
|
||||
func (db *BeaconDB) deleteHistoricalStates(slot uint64) error {
|
||||
if featureconfig.FeatureConfig().DisableHistoricalStatePruning {
|
||||
return nil
|
||||
}
|
||||
return db.update(func(tx *bolt.Tx) error {
|
||||
histState := tx.Bucket(histStateBucket)
|
||||
chainInfo := tx.Bucket(chainInfoBucket)
|
||||
hsCursor := histState.Cursor()
|
||||
|
||||
for k, v := hsCursor.First(); k != nil; k, v = hsCursor.Next() {
|
||||
slotBinary := k[:8]
|
||||
keySlotNumber := decodeToSlotNumber(slotBinary)
|
||||
if keySlotNumber < slot {
|
||||
if err := histState.Delete(k); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := chainInfo.Delete(v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
var (
|
||||
lastSlotGauge = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "deprecated_state_last_slot",
|
||||
Help: "Last slot number of the processed state",
|
||||
})
|
||||
lastJustifiedEpochGauge = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "deprecated_state_last_justified_epoch",
|
||||
Help: "Last justified epoch of the processed state",
|
||||
})
|
||||
lastPrevJustifiedEpochGauge = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "deprecated_state_last_prev_justified_epoch",
|
||||
Help: "Last prev justified epoch of the processed state",
|
||||
})
|
||||
lastFinalizedEpochGauge = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "deprecated_state_last_finalized_epoch",
|
||||
Help: "Last finalized epoch of the processed state",
|
||||
})
|
||||
activeValidatorsGauge = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "deprecated_state_active_validators",
|
||||
Help: "Total number of active validators",
|
||||
})
|
||||
slashedValidatorsGauge = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "deprecated_state_slashed_validators",
|
||||
Help: "Total slashed validators",
|
||||
})
|
||||
withdrawnValidatorsGauge = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "deprecated_state_withdrawn_validators",
|
||||
Help: "Total withdrawn validators",
|
||||
})
|
||||
totalValidatorsGauge = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "deprecated_state_total_validators",
|
||||
Help: "All time total validators",
|
||||
})
|
||||
)
|
||||
|
||||
func reportStateMetrics(state *pb.BeaconState) {
|
||||
currentEpoch := state.Slot / params.BeaconConfig().SlotsPerEpoch
|
||||
|
||||
// Validator counts
|
||||
var active float64
|
||||
var slashed float64
|
||||
var withdrawn float64
|
||||
for _, v := range state.Validators {
|
||||
if v.ActivationEpoch <= currentEpoch && currentEpoch < v.ExitEpoch {
|
||||
active++
|
||||
}
|
||||
if v.Slashed {
|
||||
slashed++
|
||||
}
|
||||
if currentEpoch >= v.ExitEpoch {
|
||||
withdrawn++
|
||||
}
|
||||
}
|
||||
activeValidatorsGauge.Set(active)
|
||||
slashedValidatorsGauge.Set(slashed)
|
||||
withdrawnValidatorsGauge.Set(withdrawn)
|
||||
totalValidatorsGauge.Set(float64(len(state.Validators)))
|
||||
|
||||
// Slot number
|
||||
lastSlotGauge.Set(float64(state.Slot))
|
||||
|
||||
// Last justified slot
|
||||
if state.CurrentJustifiedCheckpoint != nil {
|
||||
lastJustifiedEpochGauge.Set(float64(state.CurrentJustifiedCheckpoint.Epoch))
|
||||
}
|
||||
// Last previous justified slot
|
||||
if state.PreviousJustifiedCheckpoint != nil {
|
||||
lastPrevJustifiedEpochGauge.Set(float64(state.PreviousJustifiedCheckpoint.Epoch))
|
||||
}
|
||||
// Last finalized slot
|
||||
if state.FinalizedCheckpoint != nil {
|
||||
lastFinalizedEpochGauge.Set(float64(state.FinalizedCheckpoint.Epoch))
|
||||
}
|
||||
}
|
||||
@@ -1,360 +0,0 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
)
|
||||
|
||||
func init() {
|
||||
featureconfig.InitFeatureConfig(&featureconfig.FeatureFlagConfig{
|
||||
DisableHistoricalStatePruning: false,
|
||||
})
|
||||
}
|
||||
|
||||
func TestInitializeState_OK(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
ctx := context.Background()
|
||||
|
||||
genesisTime := uint64(time.Now().Unix())
|
||||
deposits, _ := testutil.SetupInitialDeposits(t, 10)
|
||||
if err := db.InitializeState(context.Background(), genesisTime, deposits, ðpb.Eth1Data{}); err != nil {
|
||||
t.Fatalf("Failed to initialize state: %v", err)
|
||||
}
|
||||
b, err := db.ChainHead()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get chain head: %v", err)
|
||||
}
|
||||
if b.GetSlot() != 0 {
|
||||
t.Fatalf("Expected block height to equal 1. Got %d", b.GetSlot())
|
||||
}
|
||||
|
||||
beaconState, err := db.HeadState(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get state: %v", err)
|
||||
}
|
||||
if beaconState == nil {
|
||||
t.Fatalf("Failed to retrieve state: %v", beaconState)
|
||||
}
|
||||
beaconStateEnc, err := proto.Marshal(beaconState)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to encode state: %v", err)
|
||||
}
|
||||
|
||||
statePrime, err := db.HeadState(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get state: %v", err)
|
||||
}
|
||||
statePrimeEnc, err := proto.Marshal(statePrime)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to encode state: %v", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(beaconStateEnc, statePrimeEnc) {
|
||||
t.Fatalf("Expected %#x and %#x to be equal", beaconStateEnc, statePrimeEnc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFinalizeState_OK(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
genesisTime := uint64(time.Now().Unix())
|
||||
deposits, _ := testutil.SetupInitialDeposits(t, 20)
|
||||
if err := db.InitializeState(context.Background(), genesisTime, deposits, ðpb.Eth1Data{}); err != nil {
|
||||
t.Fatalf("Failed to initialize state: %v", err)
|
||||
}
|
||||
|
||||
state, err := db.HeadState(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to retrieve state: %v", err)
|
||||
}
|
||||
|
||||
if err := db.SaveFinalizedState(state); err != nil {
|
||||
t.Fatalf("Unable to save finalized state")
|
||||
}
|
||||
|
||||
fState, err := db.FinalizedState()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to retrieve finalized state")
|
||||
}
|
||||
|
||||
if !proto.Equal(fState, state) {
|
||||
t.Error("Retrieved and saved finalized are unequal")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkState_ReadingFromCache(b *testing.B) {
|
||||
db := setupDB(b)
|
||||
defer teardownDB(b, db)
|
||||
ctx := context.Background()
|
||||
|
||||
genesisTime := uint64(time.Now().Unix())
|
||||
deposits, _ := testutil.SetupInitialDeposits(b, 10)
|
||||
if err := db.InitializeState(context.Background(), genesisTime, deposits, ðpb.Eth1Data{}); err != nil {
|
||||
b.Fatalf("Failed to initialize state: %v", err)
|
||||
}
|
||||
|
||||
state, err := db.HeadState(ctx)
|
||||
if err != nil {
|
||||
b.Fatalf("Could not read DV beacon state from DB: %v", err)
|
||||
}
|
||||
state.Slot++
|
||||
err = db.SaveStateDeprecated(ctx, state)
|
||||
if err != nil {
|
||||
b.Fatalf("Could not save beacon state to cache from DB: %v", err)
|
||||
}
|
||||
|
||||
savedState := &pb.BeaconState{}
|
||||
savedState.Unmarshal(db.serializedState)
|
||||
|
||||
if savedState.Slot != 1 {
|
||||
b.Fatal("cache should be prepared on state after saving to DB")
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := db.HeadState(ctx)
|
||||
if err != nil {
|
||||
b.Fatalf("Could not read beacon state from cache: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFinalizedState_NoneExists(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
wanted := "no finalized state saved"
|
||||
_, err := db.FinalizedState()
|
||||
if !strings.Contains(err.Error(), wanted) {
|
||||
t.Errorf("Expected: %s, received: %s", wanted, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestJustifiedState_CanSaveRetrieve(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
stateSlot := uint64(10)
|
||||
state := &pb.BeaconState{
|
||||
Slot: stateSlot,
|
||||
}
|
||||
|
||||
if err := db.SaveJustifiedState(state); err != nil {
|
||||
t.Fatalf("could not save justified state: %v", err)
|
||||
}
|
||||
|
||||
justifiedState, err := db.JustifiedState()
|
||||
if err != nil {
|
||||
t.Fatalf("could not get justified state: %v", err)
|
||||
}
|
||||
if justifiedState.Slot != stateSlot {
|
||||
t.Errorf("Saved state does not have the slot from which it was requested, wanted: %d, got: %d",
|
||||
stateSlot, justifiedState.Slot)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJustifiedState_NoneExists(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
wanted := "no justified state saved"
|
||||
_, err := db.JustifiedState()
|
||||
if !strings.Contains(err.Error(), wanted) {
|
||||
t.Errorf("Expected: %s, received: %s", wanted, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestFinalizedState_CanSaveRetrieve(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
stateSlot := uint64(10)
|
||||
state := &pb.BeaconState{
|
||||
Slot: stateSlot,
|
||||
}
|
||||
|
||||
if err := db.SaveFinalizedState(state); err != nil {
|
||||
t.Fatalf("could not save finalized state: %v", err)
|
||||
}
|
||||
|
||||
finalizedState, err := db.FinalizedState()
|
||||
if err != nil {
|
||||
t.Fatalf("could not get finalized state: %v", err)
|
||||
}
|
||||
if finalizedState.Slot != stateSlot {
|
||||
t.Errorf("Saved state does not have the slot from which it was requested, wanted: %d, got: %d",
|
||||
stateSlot, finalizedState.Slot)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHistoricalState_CanSaveRetrieve(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
ctx := context.Background()
|
||||
|
||||
tests := []struct {
|
||||
state *pb.BeaconState
|
||||
}{
|
||||
{
|
||||
state: &pb.BeaconState{
|
||||
Slot: 66,
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
state: &pb.BeaconState{
|
||||
Slot: 72,
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
state: &pb.BeaconState{
|
||||
Slot: 96,
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
state: &pb.BeaconState{
|
||||
Slot: 130,
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 2},
|
||||
},
|
||||
},
|
||||
{
|
||||
state: &pb.BeaconState{
|
||||
Slot: 300,
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 4},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
if err := db.SaveFinalizedState(tt.state); err != nil {
|
||||
t.Fatalf("could not save finalized state: %v", err)
|
||||
}
|
||||
if err := db.SaveHistoricalState(context.Background(), tt.state, [32]byte{}); err != nil {
|
||||
t.Fatalf("could not save historical state: %v", err)
|
||||
}
|
||||
|
||||
retState, err := db.HistoricalStateFromSlot(ctx, tt.state.Slot, [32]byte{})
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to retrieve state %v", err)
|
||||
}
|
||||
|
||||
if !proto.Equal(tt.state, retState) {
|
||||
t.Errorf("Saved and retrieved states are not equal got\n %v but wanted\n %v", proto.MarshalTextString(retState), proto.MarshalTextString(tt.state))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHistoricalState_Pruning(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
ctx := context.Background()
|
||||
|
||||
epochSize := params.BeaconConfig().SlotsPerEpoch
|
||||
|
||||
tests := []struct {
|
||||
histState1 *pb.BeaconState
|
||||
histState2 *pb.BeaconState
|
||||
}{
|
||||
{
|
||||
histState1: &pb.BeaconState{
|
||||
Slot: 2 * epochSize,
|
||||
},
|
||||
histState2: &pb.BeaconState{
|
||||
Slot: 3 * epochSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
histState1: &pb.BeaconState{
|
||||
Slot: 1 * epochSize,
|
||||
},
|
||||
histState2: &pb.BeaconState{
|
||||
Slot: 4 * epochSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
histState1: &pb.BeaconState{
|
||||
Slot: 2 * epochSize,
|
||||
},
|
||||
histState2: &pb.BeaconState{
|
||||
Slot: 5 * epochSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
histState1: &pb.BeaconState{
|
||||
Slot: 6 * epochSize,
|
||||
},
|
||||
histState2: &pb.BeaconState{
|
||||
Slot: 14 * epochSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
histState1: &pb.BeaconState{
|
||||
Slot: 12 * epochSize,
|
||||
},
|
||||
histState2: &pb.BeaconState{
|
||||
Slot: 103 * epochSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
histState1: &pb.BeaconState{
|
||||
Slot: 100 * epochSize,
|
||||
},
|
||||
histState2: &pb.BeaconState{
|
||||
Slot: 600 * epochSize,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
root := [32]byte{}
|
||||
if err := db.SaveHistoricalState(context.Background(), tt.histState1, root); err != nil {
|
||||
t.Fatalf("could not save historical state: %v", err)
|
||||
}
|
||||
if err := db.SaveHistoricalState(context.Background(), tt.histState2, root); err != nil {
|
||||
t.Fatalf("could not save historical state: %v", err)
|
||||
}
|
||||
|
||||
// Delete up to and including historical state 1.
|
||||
if err := db.deleteHistoricalStates(tt.histState1.Slot + 1); err != nil {
|
||||
t.Fatalf("Could not delete historical states %v", err)
|
||||
}
|
||||
|
||||
// Save a dummy genesis state so that db doesnt return an error.
|
||||
if err := db.SaveHistoricalState(context.Background(), &pb.BeaconState{Slot: 0, FinalizedCheckpoint: ðpb.Checkpoint{}}, root); err != nil {
|
||||
t.Fatalf("could not save historical state: %v", err)
|
||||
}
|
||||
|
||||
retState, err := db.HistoricalStateFromSlot(ctx, tt.histState1.Slot, root)
|
||||
if err != nil {
|
||||
t.Errorf("Unable to retrieve state %v", err)
|
||||
}
|
||||
|
||||
if proto.Equal(tt.histState1, retState) {
|
||||
t.Errorf("Saved and retrieved states are equal when they supposed to be different %d", tt.histState1.Slot)
|
||||
}
|
||||
|
||||
retState, err = db.HistoricalStateFromSlot(ctx, tt.histState2.Slot, root)
|
||||
if err != nil {
|
||||
t.Errorf("Unable to retrieve state %v", err)
|
||||
}
|
||||
|
||||
if !proto.Equal(tt.histState2, retState) {
|
||||
t.Errorf("Saved and retrieved states are not equal when they supposed to be for slot %d", tt.histState2.Slot)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
)
|
||||
|
||||
// SaveValidatorIndex in db.
|
||||
func (db *BeaconDB) SaveValidatorIndex(ctx context.Context, pubkey [48]byte, idx uint64) error {
|
||||
return db.SaveValidatorIndexDeprecated(pubkey[:], int(idx))
|
||||
}
|
||||
|
||||
// SaveValidatorIndexDeprecated accepts a public key and validator index and writes them to disk.
|
||||
func (db *BeaconDB) SaveValidatorIndexDeprecated(pubKey []byte, index int) error {
|
||||
h := hashutil.Hash(pubKey)
|
||||
|
||||
return db.update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(validatorBucket)
|
||||
|
||||
buf := make([]byte, binary.MaxVarintLen64)
|
||||
n := binary.PutUvarint(buf, uint64(index))
|
||||
|
||||
return bucket.Put(h[:], buf[:n])
|
||||
})
|
||||
}
|
||||
|
||||
// SaveValidatorLatestVote not implemented.
|
||||
func (db *BeaconDB) SaveValidatorLatestVote(_ context.Context, _ uint64, _ *pb.ValidatorLatestVote) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
// SaveValidatorIndexBatch accepts a public key and validator index and writes them to disk.
|
||||
func (db *BeaconDB) SaveValidatorIndexBatch(pubKey []byte, index int) error {
|
||||
h := hashutil.Hash(pubKey)
|
||||
|
||||
return db.batch(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(validatorBucket)
|
||||
buf := make([]byte, binary.MaxVarintLen64)
|
||||
n := binary.PutUvarint(buf, uint64(index))
|
||||
return bucket.Put(h[:], buf[:n])
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// ValidatorIndex returns validator index from database.
|
||||
func (db *BeaconDB) ValidatorIndex(_ context.Context, pubkey [48]byte) (uint64, bool, error) {
|
||||
idx, err := db.ValidatorIndexDeprecated(pubkey[:])
|
||||
return idx, true, err
|
||||
}
|
||||
|
||||
// ValidatorIndexDeprecated accepts a public key and returns the corresponding validator index.
|
||||
// If the validator index is not found in DB, as a fail over, it searches the state and
|
||||
// saves it to the DB when found.
|
||||
func (db *BeaconDB) ValidatorIndexDeprecated(pubKey []byte) (uint64, error) {
|
||||
if !db.HasValidator(pubKey) {
|
||||
state, err := db.HeadState(context.Background())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for i := 0; i < len(state.Validators); i++ {
|
||||
v := state.Validators[i]
|
||||
if bytes.Equal(v.PublicKey, pubKey) {
|
||||
if err := db.SaveValidatorIndexDeprecated(pubKey, i); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint64(i), nil
|
||||
}
|
||||
}
|
||||
return 0, fmt.Errorf("validator %#x does not exist", pubKey)
|
||||
}
|
||||
|
||||
var index uint64
|
||||
h := hashutil.Hash(pubKey)
|
||||
err := db.view(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(validatorBucket)
|
||||
|
||||
enc := bucket.Get(h[:])
|
||||
if enc == nil {
|
||||
return nil
|
||||
}
|
||||
var err error
|
||||
buf := bytes.NewBuffer(enc)
|
||||
index, err = binary.ReadUvarint(buf)
|
||||
return err
|
||||
})
|
||||
|
||||
return index, err
|
||||
}
|
||||
|
||||
// DeleteValidatorIndex deletes the validator index map record.
|
||||
func (db *BeaconDB) DeleteValidatorIndex(_ context.Context, pubkey [48]byte) error {
|
||||
return db.DeleteValidatorIndexDeprecated(pubkey[:])
|
||||
}
|
||||
|
||||
// DeleteValidatorIndexDeprecated deletes the validator index map record.
|
||||
// DEPRECATED: Do not use.
|
||||
func (db *BeaconDB) DeleteValidatorIndexDeprecated(pubKey []byte) error {
|
||||
h := hashutil.Hash(pubKey)
|
||||
|
||||
return db.update(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(validatorBucket)
|
||||
|
||||
return bkt.Delete(h[:])
|
||||
})
|
||||
}
|
||||
|
||||
// HasValidatorIndex returns hasValidator(pubkey).
|
||||
func (db *BeaconDB) HasValidatorIndex(_ context.Context, pubkey [48]byte) bool {
|
||||
return db.HasValidator(pubkey[:])
|
||||
}
|
||||
|
||||
// HasValidatorLatestVote always returns false. Don't use this.
|
||||
// DEPRECATED: Do not use.
|
||||
func (db *BeaconDB) HasValidatorLatestVote(_ context.Context, _ uint64) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// DeleteValidatorLatestVote by validator index.
|
||||
// DEPRECATED: Do not use.
|
||||
func (db *BeaconDB) DeleteValidatorLatestVote(_ context.Context, _ uint64) error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
|
||||
// HasValidator checks if a validator index map exists.
|
||||
func (db *BeaconDB) HasValidator(pubKey []byte) bool {
|
||||
exists := false
|
||||
h := hashutil.Hash(pubKey)
|
||||
// #nosec G104, similar to HasBlockDeprecated, HasAttestationDeprecated... etc
|
||||
db.view(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(validatorBucket)
|
||||
|
||||
exists = bkt.Get(h[:]) != nil
|
||||
return nil
|
||||
})
|
||||
return exists
|
||||
}
|
||||
|
||||
// HasAnyValidators returns true if any validator in a list of public keys
|
||||
// are in the bucket.
|
||||
func (db *BeaconDB) HasAnyValidators(state *pb.BeaconState, pubKeys [][]byte) (bool, error) {
|
||||
exists := false
|
||||
// #nosec G104, similar to HasBlockDeprecated, HasAttestationDeprecated... etc
|
||||
db.view(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(validatorBucket)
|
||||
for _, pk := range pubKeys {
|
||||
h := hashutil.Hash(pk)
|
||||
exists = bkt.Get(h[:]) != nil
|
||||
break
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if !exists {
|
||||
for _, pubKey := range pubKeys {
|
||||
for i := 0; i < len(state.Validators); i++ {
|
||||
v := state.Validators[i]
|
||||
if bytes.Equal(v.PublicKey, pubKey) {
|
||||
if err := db.SaveValidatorIndexDeprecated(pubKey, i); err != nil {
|
||||
return false, err
|
||||
}
|
||||
exists = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return exists, nil
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
)
|
||||
|
||||
func TestSaveAndRetrieveValidatorIndex_OK(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
p1 := []byte{'A', 'B', 'C'}
|
||||
p2 := []byte{'D', 'E', 'F'}
|
||||
|
||||
if err := db.SaveValidatorIndexDeprecated(p1, 1); err != nil {
|
||||
t.Fatalf("Failed to save vallidator index: %v", err)
|
||||
}
|
||||
if err := db.SaveValidatorIndexDeprecated(p2, 2); err != nil {
|
||||
t.Fatalf("Failed to save vallidator index: %v", err)
|
||||
}
|
||||
|
||||
index1, err := db.ValidatorIndexDeprecated(p1)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to call AttestationDeprecated: %v", err)
|
||||
}
|
||||
if index1 != 1 {
|
||||
t.Fatalf("Saved index and retrieved index are not equal: %#x and %#x", 1, index1)
|
||||
}
|
||||
|
||||
index2, err := db.ValidatorIndexDeprecated(p2)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to call AttestationDeprecated: %v", err)
|
||||
}
|
||||
if index2 != 2 {
|
||||
t.Fatalf("Saved index and retrieved index are not equal: %#x and %#x", 2, index2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveAndDeleteValidatorIndex_OK(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
p1 := []byte{'1', '2', '3'}
|
||||
|
||||
if err := db.SaveValidatorIndexDeprecated(p1, 3); err != nil {
|
||||
t.Fatalf("Failed to save validator index: %v", err)
|
||||
}
|
||||
if err := db.SaveStateDeprecated(context.Background(), &pb.BeaconState{}); err != nil {
|
||||
t.Fatalf("Failed to save state: %v", err)
|
||||
}
|
||||
index, err := db.ValidatorIndexDeprecated(p1)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to call validator Index: %v", err)
|
||||
}
|
||||
if index != 3 {
|
||||
t.Fatalf("Saved index and retrieved index are not equal: %#x and %#x", 3, index)
|
||||
}
|
||||
|
||||
if err := db.DeleteValidatorIndexDeprecated(p1); err != nil {
|
||||
t.Fatalf("Could not delete attestation: %v", err)
|
||||
}
|
||||
_, err = db.ValidatorIndexDeprecated(p1)
|
||||
want := fmt.Sprintf("validator %#x does not exist", p1)
|
||||
if !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("Want: %v, got: %v", want, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasValidator(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
pk := []byte("pk")
|
||||
|
||||
// Populate the db with some public key
|
||||
if err := db.db.Update(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(validatorBucket)
|
||||
h := hashutil.Hash(pk)
|
||||
return bkt.Put(h[:], []byte("data"))
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !db.HasValidator(pk) {
|
||||
t.Error("Database did not have expected validator")
|
||||
}
|
||||
|
||||
if db.HasValidator([]byte("bogus")) {
|
||||
t.Error("Database returned true for validator that did not exist")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasAnyValidator(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
defer teardownDB(t, db)
|
||||
|
||||
knownPubKeys := [][]byte{
|
||||
[]byte("pk1"),
|
||||
[]byte("pk2"),
|
||||
}
|
||||
unknownPubKeys := [][]byte{
|
||||
[]byte("pk3"),
|
||||
[]byte("pk4"),
|
||||
}
|
||||
|
||||
// Populate the db with some public key
|
||||
if err := db.db.Update(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(validatorBucket)
|
||||
for _, pk := range knownPubKeys {
|
||||
h := hashutil.Hash(pk)
|
||||
if err := bkt.Put(h[:], []byte("data")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
beaconState := &pb.BeaconState{
|
||||
Validators: []*ethpb.Validator{},
|
||||
}
|
||||
|
||||
has, err := db.HasAnyValidators(beaconState, append(knownPubKeys, unknownPubKeys...))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !has {
|
||||
t.Error("Database did not have expected validators")
|
||||
}
|
||||
|
||||
has, err = db.HasAnyValidators(beaconState, unknownPubKeys)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if has {
|
||||
t.Error("Database returned true when there are only pubkeys that did not exist")
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"fork_choice_deprecated.go",
|
||||
"receive_block.go",
|
||||
"service.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/deprecated-blockchain",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
"//beacon-chain/attestation:go_default_library",
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/cache/depositcache:go_default_library",
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/core/state:go_default_library",
|
||||
"//beacon-chain/core/validators:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/operations:go_default_library",
|
||||
"//beacon-chain/p2p:go_default_library",
|
||||
"//beacon-chain/powchain:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/event:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@io_opencensus_go//trace:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "medium",
|
||||
srcs = [
|
||||
"fork_choice_deprecated_test.go",
|
||||
"fork_choice_reorg_deprecated_test.go",
|
||||
"receive_block_test.go",
|
||||
"service_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/attestation:go_default_library",
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/cache/depositcache:go_default_library",
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/core/state:go_default_library",
|
||||
"//beacon-chain/core/validators:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/internal:go_default_library",
|
||||
"//beacon-chain/p2p:go_default_library",
|
||||
"//beacon-chain/powchain:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/bls:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/event:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -1,503 +0,0 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
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/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
var (
|
||||
reorgCount = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "reorg_counter",
|
||||
Help: "The number of chain reorganization events that have happened in the fork choice rule",
|
||||
})
|
||||
)
|
||||
var blkAncestorCache = cache.NewBlockAncestorCache()
|
||||
|
||||
// ForkChoice interface defines the methods for applying fork choice rule
|
||||
// operations to the blockchain.
|
||||
type ForkChoice interface {
|
||||
ApplyForkChoiceRuleDeprecated(ctx context.Context, block *ethpb.BeaconBlock, computedState *pb.BeaconState) error
|
||||
}
|
||||
|
||||
// TargetsFetcher defines a struct which can retrieve latest attestation targets
|
||||
// from a given justified state.
|
||||
type TargetsFetcher interface {
|
||||
AttestationTargets(justifiedState *pb.BeaconState) (map[uint64]*pb.AttestationTarget, error)
|
||||
}
|
||||
|
||||
// updateFFGCheckPts checks whether the existing FFG check points saved in DB
|
||||
// are not older than the ones just processed in state. If it's older, we update
|
||||
// the db with the latest FFG check points, both justification and finalization.
|
||||
func (c *ChainService) updateFFGCheckPts(ctx context.Context, state *pb.BeaconState) error {
|
||||
lastJustifiedSlot := helpers.StartSlot(state.CurrentJustifiedCheckpoint.Epoch)
|
||||
savedJustifiedBlock, err := c.beaconDB.(*db.BeaconDB).JustifiedBlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If the last processed justification slot in state is greater than
|
||||
// the slot of justified block saved in DB.
|
||||
if lastJustifiedSlot > savedJustifiedBlock.Slot {
|
||||
// Retrieve the new justified block from DB using the new justified slot and save it.
|
||||
newJustifiedBlock, err := c.beaconDB.(*db.BeaconDB).CanonicalBlockBySlot(ctx, lastJustifiedSlot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If the new justified slot is a skip slot in db then we keep getting it's ancestors
|
||||
// until we can get a block.
|
||||
lastAvailBlkSlot := lastJustifiedSlot
|
||||
for newJustifiedBlock == nil {
|
||||
log.WithField("slot", lastAvailBlkSlot).Debug("Missing block in DB, looking one slot back")
|
||||
lastAvailBlkSlot--
|
||||
newJustifiedBlock, err = c.beaconDB.(*db.BeaconDB).CanonicalBlockBySlot(ctx, lastAvailBlkSlot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
newJustifiedRoot, err := ssz.SigningRoot(newJustifiedBlock)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Fetch justified state from historical states db.
|
||||
newJustifiedState, err := c.beaconDB.(*db.BeaconDB).HistoricalStateFromSlot(ctx, newJustifiedBlock.Slot, newJustifiedRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.beaconDB.(*db.BeaconDB).SaveJustifiedBlock(newJustifiedBlock); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.beaconDB.(*db.BeaconDB).SaveJustifiedState(newJustifiedState); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
lastFinalizedSlot := helpers.StartSlot(state.FinalizedCheckpoint.Epoch)
|
||||
savedFinalizedBlock, err := c.beaconDB.(*db.BeaconDB).FinalizedBlock()
|
||||
// If the last processed finalized slot in state is greater than
|
||||
// the slot of finalized block saved in DB.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if lastFinalizedSlot > savedFinalizedBlock.Slot {
|
||||
// Retrieve the new finalized block from DB using the new finalized slot and save it.
|
||||
newFinalizedBlock, err := c.beaconDB.(*db.BeaconDB).CanonicalBlockBySlot(ctx, lastFinalizedSlot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If the new finalized slot is a skip slot in db then we keep getting it's ancestors
|
||||
// until we can get a block.
|
||||
lastAvailBlkSlot := lastFinalizedSlot
|
||||
for newFinalizedBlock == nil {
|
||||
log.WithField("slot", lastAvailBlkSlot).Debug("Missing block in DB, looking one slot back")
|
||||
lastAvailBlkSlot--
|
||||
newFinalizedBlock, err = c.beaconDB.(*db.BeaconDB).CanonicalBlockBySlot(ctx, lastAvailBlkSlot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
newFinalizedRoot, err := ssz.SigningRoot(newFinalizedBlock)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Generate the new finalized state with using new finalized block and
|
||||
// save it.
|
||||
newFinalizedState, err := c.beaconDB.(*db.BeaconDB).HistoricalStateFromSlot(ctx, lastFinalizedSlot, newFinalizedRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.beaconDB.(*db.BeaconDB).SaveFinalizedBlock(newFinalizedBlock); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.beaconDB.(*db.BeaconDB).SaveFinalizedState(newFinalizedState); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ApplyForkChoiceRuleDeprecated determines the current beacon chain head using LMD
|
||||
// GHOST as a block-vote weighted function to select a canonical head in
|
||||
// Ethereum Serenity. The inputs are the the recently processed block and its
|
||||
// associated state.
|
||||
func (c *ChainService) ApplyForkChoiceRuleDeprecated(
|
||||
ctx context.Context,
|
||||
block *ethpb.BeaconBlock,
|
||||
postState *pb.BeaconState,
|
||||
) error {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.ApplyForkChoiceRule")
|
||||
defer span.End()
|
||||
log.Info("Applying LMD-GHOST Fork Choice Rule")
|
||||
|
||||
justifiedState, err := c.beaconDB.(*db.BeaconDB).JustifiedState()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not retrieve justified state")
|
||||
}
|
||||
attestationTargets, err := c.AttestationTargets(justifiedState)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not retrieve attestation target")
|
||||
}
|
||||
justifiedHead, err := c.beaconDB.(*db.BeaconDB).JustifiedBlock()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not retrieve justified head")
|
||||
}
|
||||
|
||||
newHead, err := c.lmdGhost(ctx, justifiedHead, justifiedState, attestationTargets)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not run fork choice")
|
||||
}
|
||||
newHeadRoot, err := ssz.SigningRoot(newHead)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not hash new head block")
|
||||
}
|
||||
c.canonicalRootsLock.Lock()
|
||||
defer c.canonicalRootsLock.Unlock()
|
||||
c.canonicalRoots[newHead.Slot] = newHeadRoot[:]
|
||||
|
||||
currentHead, err := c.beaconDB.(*db.BeaconDB).ChainHead()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not retrieve chain head")
|
||||
}
|
||||
currentHeadRoot, err := ssz.SigningRoot(currentHead)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not hash current head block")
|
||||
}
|
||||
|
||||
isDescendant, err := c.isDescendant(currentHead, newHead)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not check if block is descendant")
|
||||
}
|
||||
|
||||
newState := postState
|
||||
if !isDescendant && !proto.Equal(currentHead, newHead) {
|
||||
log.WithFields(logrus.Fields{
|
||||
"currentSlot": currentHead.Slot,
|
||||
"currentRoot": fmt.Sprintf("%#x", bytesutil.Trunc(currentHeadRoot[:])),
|
||||
"newSlot": newHead.Slot,
|
||||
"newRoot": fmt.Sprintf("%#x", bytesutil.Trunc(newHeadRoot[:])),
|
||||
}).Warn("Reorg happened")
|
||||
// Only regenerate head state if there was a reorg.
|
||||
newState, err = c.beaconDB.(*db.BeaconDB).HistoricalStateFromSlot(ctx, newHead.Slot, newHeadRoot)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not gen state")
|
||||
}
|
||||
|
||||
for revertedSlot := currentHead.Slot; revertedSlot > newHead.Slot; revertedSlot-- {
|
||||
delete(c.canonicalRoots, revertedSlot)
|
||||
}
|
||||
reorgCount.Inc()
|
||||
}
|
||||
|
||||
if proto.Equal(currentHead, newHead) {
|
||||
log.WithFields(logrus.Fields{
|
||||
"currentSlot": currentHead.Slot,
|
||||
"currentRoot": fmt.Sprintf("%#x", bytesutil.Trunc(currentHeadRoot[:])),
|
||||
}).Warn("Head did not change after fork choice, current head has the most votes")
|
||||
}
|
||||
|
||||
// If we receive forked blocks.
|
||||
if newHead.Slot != newState.Slot {
|
||||
newState, err = c.beaconDB.(*db.BeaconDB).HistoricalStateFromSlot(ctx, newHead.Slot, newHeadRoot)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not gen state")
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.beaconDB.(*db.BeaconDB).UpdateChainHead(ctx, newHead, newState); err != nil {
|
||||
return errors.Wrap(err, "failed to update chain")
|
||||
}
|
||||
h, err := ssz.SigningRoot(newHead)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not hash head")
|
||||
}
|
||||
log.WithFields(logrus.Fields{
|
||||
"headRoot": fmt.Sprintf("%#x", bytesutil.Trunc(h[:])),
|
||||
"headSlot": newHead.Slot,
|
||||
"stateSlot": newState.Slot,
|
||||
}).Info("Chain head block and state updated")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// lmdGhost applies the Latest Message Driven, Greediest Heaviest Observed Sub-Tree
|
||||
// fork-choice rule defined in the Ethereum Serenity specification for the beacon chain.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def lmd_ghost(store: Store, start_state: BeaconState, start_block: BeaconBlock) -> BeaconBlock:
|
||||
// """
|
||||
// Execute the LMD-GHOST algorithm to find the head ``BeaconBlock``.
|
||||
// """
|
||||
// validators = start_state.validator_registry
|
||||
// active_validator_indices = get_active_validator_indices(validators, slot_to_epoch(start_state.slot))
|
||||
// attestation_targets = [
|
||||
// (validator_index, get_latest_attestation_target(store, validator_index))
|
||||
// for validator_index in active_validator_indices
|
||||
// ]
|
||||
//
|
||||
// def get_vote_count(block: BeaconBlock) -> int:
|
||||
// return sum(
|
||||
// get_effective_balance(start_state.validator_balances[validator_index]) // FORK_CHOICE_BALANCE_INCREMENT
|
||||
// for validator_index, target in attestation_targets
|
||||
// if get_ancestor(store, target, block.slot) == block
|
||||
// )
|
||||
//
|
||||
// head = start_block
|
||||
// while 1:
|
||||
// children = get_children(store, head)
|
||||
// if len(children) == 0:
|
||||
// return head
|
||||
// head = max(children, key=get_vote_count)
|
||||
func (c *ChainService) lmdGhost(
|
||||
ctx context.Context,
|
||||
startBlock *ethpb.BeaconBlock,
|
||||
startState *pb.BeaconState,
|
||||
voteTargets map[uint64]*pb.AttestationTarget,
|
||||
) (*ethpb.BeaconBlock, error) {
|
||||
highestSlot := c.beaconDB.(*db.BeaconDB).HighestBlockSlot()
|
||||
head := startBlock
|
||||
for {
|
||||
children, err := c.BlockChildren(ctx, head, highestSlot)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not fetch block children")
|
||||
}
|
||||
if len(children) == 0 {
|
||||
return head, nil
|
||||
}
|
||||
maxChild := children[0]
|
||||
|
||||
maxChildVotes, err := VoteCount(maxChild, startState, voteTargets, c.beaconDB.(*db.BeaconDB))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to determine vote count for block")
|
||||
}
|
||||
for i := 1; i < len(children); i++ {
|
||||
candidateChildVotes, err := VoteCount(children[i], startState, voteTargets, c.beaconDB.(*db.BeaconDB))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to determine vote count for block")
|
||||
}
|
||||
maxChildRoot, err := ssz.SigningRoot(maxChild)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
candidateChildRoot, err := ssz.SigningRoot(children[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if candidateChildVotes > maxChildVotes ||
|
||||
(candidateChildVotes == maxChildVotes && bytesutil.LowerThan(maxChildRoot[:], candidateChildRoot[:])) {
|
||||
maxChild = children[i]
|
||||
}
|
||||
}
|
||||
head = maxChild
|
||||
}
|
||||
}
|
||||
|
||||
// BlockChildren returns the child blocks of the given block up to a given
|
||||
// highest slot.
|
||||
//
|
||||
// ex:
|
||||
// /- C - E
|
||||
// A - B - D - F
|
||||
// \- G
|
||||
// Input: B. Output: [C, D, G]
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// get_children(store: Store, block: BeaconBlock) -> List[BeaconBlock]
|
||||
// returns the child blocks of the given block.
|
||||
func (c *ChainService) BlockChildren(ctx context.Context, block *ethpb.BeaconBlock, highestSlot uint64) ([]*ethpb.BeaconBlock, error) {
|
||||
blockRoot, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var children []*ethpb.BeaconBlock
|
||||
startSlot := block.Slot + 1
|
||||
for i := startSlot; i <= highestSlot; i++ {
|
||||
kids, err := c.beaconDB.(*db.BeaconDB).BlocksBySlot(ctx, i)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get block by slot")
|
||||
}
|
||||
children = append(children, kids...)
|
||||
}
|
||||
|
||||
filteredChildren := []*ethpb.BeaconBlock{}
|
||||
for _, kid := range children {
|
||||
parentRoot := bytesutil.ToBytes32(kid.ParentRoot)
|
||||
if blockRoot == parentRoot {
|
||||
filteredChildren = append(filteredChildren, kid)
|
||||
}
|
||||
}
|
||||
return filteredChildren, nil
|
||||
}
|
||||
|
||||
// isDescendant checks if the new head block is a descendant block of the current head.
|
||||
func (c *ChainService) isDescendant(currentHead *ethpb.BeaconBlock, newHead *ethpb.BeaconBlock) (bool, error) {
|
||||
currentHeadRoot, err := ssz.SigningRoot(currentHead)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
for newHead.Slot > currentHead.Slot {
|
||||
if bytesutil.ToBytes32(newHead.ParentRoot) == currentHeadRoot {
|
||||
return true, nil
|
||||
}
|
||||
newHead, err = c.beaconDB.(*db.BeaconDB).BlockDeprecated(bytesutil.ToBytes32(newHead.ParentRoot))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if newHead == nil {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// AttestationTargets retrieves the list of attestation targets since last finalized epoch,
|
||||
// each attestation target consists of validator index and its attestation target (i.e. the block
|
||||
// which the validator attested to)
|
||||
func (c *ChainService) AttestationTargets(state *pb.BeaconState) (map[uint64]*pb.AttestationTarget, error) {
|
||||
indices, err := helpers.ActiveValidatorIndices(state, helpers.CurrentEpoch(state))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attestationTargets := make(map[uint64]*pb.AttestationTarget)
|
||||
for i, index := range indices {
|
||||
target, err := c.attsService.LatestAttestationTarget(state, index)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not retrieve attestation target")
|
||||
}
|
||||
if target == nil {
|
||||
continue
|
||||
}
|
||||
attestationTargets[uint64(i)] = target
|
||||
}
|
||||
return attestationTargets, nil
|
||||
}
|
||||
|
||||
// VoteCount determines the number of votes on a beacon block by counting the number
|
||||
// of target blocks that have such beacon block as a common ancestor.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_vote_count(block: BeaconBlock) -> int:
|
||||
// return sum(
|
||||
// get_effective_balance(start_state.validator_balances[validator_index]) // FORK_CHOICE_BALANCE_INCREMENT
|
||||
// for validator_index, target in attestation_targets
|
||||
// if get_ancestor(store, target, block.slot) == block
|
||||
// )
|
||||
func VoteCount(block *ethpb.BeaconBlock, state *pb.BeaconState, targets map[uint64]*pb.AttestationTarget, beaconDB *db.BeaconDB) (int, error) {
|
||||
balances := 0
|
||||
var ancestorRoot []byte
|
||||
var err error
|
||||
|
||||
blockRoot, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
for validatorIndex, target := range targets {
|
||||
ancestorRoot, err = cachedAncestor(target, block.Slot, beaconDB)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// This covers the following case, we start at B5, and want to process B6 and B7
|
||||
// B6 can be processed, B7 can not be processed because it's pointed to the
|
||||
// block older than current block 5.
|
||||
// B4 - B5 - B6
|
||||
// \ - - - - - B7
|
||||
if ancestorRoot == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if bytes.Equal(blockRoot[:], ancestorRoot) {
|
||||
balances += int(state.Validators[validatorIndex].EffectiveBalance)
|
||||
}
|
||||
}
|
||||
return balances, nil
|
||||
}
|
||||
|
||||
// BlockAncestor obtains the ancestor at of a block at a certain slot.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// def get_ancestor(store: Store, block: BeaconBlock, slot: Slot) -> BeaconBlock:
|
||||
// """
|
||||
// Get the ancestor of ``block`` with slot number ``slot``; return ``None`` if not found.
|
||||
// """
|
||||
// if block.slot == slot:
|
||||
// return block
|
||||
// elif block.slot < slot:
|
||||
// return None
|
||||
// else:
|
||||
// return get_ancestor(store, store.get_parent(block), slot)
|
||||
func BlockAncestor(targetBlock *pb.AttestationTarget, slot uint64, beaconDB *db.BeaconDB) ([]byte, error) {
|
||||
if targetBlock.Slot == slot {
|
||||
return targetBlock.BeaconBlockRoot[:], nil
|
||||
}
|
||||
if targetBlock.Slot < slot {
|
||||
return nil, nil
|
||||
}
|
||||
parentRoot := bytesutil.ToBytes32(targetBlock.ParentRoot)
|
||||
parent, err := beaconDB.BlockDeprecated(parentRoot)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get parent block")
|
||||
}
|
||||
if parent == nil {
|
||||
return nil, errors.Wrap(err, "parent block does not exist")
|
||||
}
|
||||
newTarget := &pb.AttestationTarget{
|
||||
Slot: parent.Slot,
|
||||
BeaconBlockRoot: parentRoot[:],
|
||||
ParentRoot: parent.ParentRoot,
|
||||
}
|
||||
return BlockAncestor(newTarget, slot, beaconDB)
|
||||
}
|
||||
|
||||
// cachedAncestor retrieves the cached ancestor target from block ancestor cache,
|
||||
// if it's not there it looks up the block tree get it and cache it.
|
||||
func cachedAncestor(target *pb.AttestationTarget, height uint64, beaconDB *db.BeaconDB) ([]byte, error) {
|
||||
// check if the ancestor block of from a given block height was cached.
|
||||
cachedAncestorInfo, err := blkAncestorCache.AncestorBySlot(target.BeaconBlockRoot, height)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
if cachedAncestorInfo != nil {
|
||||
return cachedAncestorInfo.Target.BeaconBlockRoot, nil
|
||||
}
|
||||
|
||||
ancestorRoot, err := BlockAncestor(target, height, beaconDB)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ancestor, err := beaconDB.BlockDeprecated(bytesutil.ToBytes32(ancestorRoot))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ancestor == nil {
|
||||
return nil, nil
|
||||
}
|
||||
ancestorTarget := &pb.AttestationTarget{
|
||||
Slot: ancestor.Slot,
|
||||
BeaconBlockRoot: ancestorRoot,
|
||||
ParentRoot: ancestor.ParentRoot,
|
||||
}
|
||||
if err := blkAncestorCache.AddBlockAncestor(&cache.AncestorInfo{
|
||||
Height: height,
|
||||
Hash: target.BeaconBlockRoot,
|
||||
Target: ancestorTarget,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ancestorRoot, nil
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,236 +0,0 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
db2 "github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/internal"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
)
|
||||
|
||||
type mockAttestationHandler struct {
|
||||
targets map[uint64]*pb.AttestationTarget
|
||||
}
|
||||
|
||||
func (m *mockAttestationHandler) LatestAttestationTarget(beaconState *pb.BeaconState, idx uint64) (*pb.AttestationTarget, error) {
|
||||
return m.targets[idx], nil
|
||||
}
|
||||
|
||||
func (m *mockAttestationHandler) BatchUpdateLatestAttestations(ctx context.Context, atts []*ethpb.Attestation) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestApplyForkChoice_ChainSplitReorg(t *testing.T) {
|
||||
// TODO(#2307): Fix test once v0.6 is merged.
|
||||
t.Skip()
|
||||
hook := logTest.NewGlobal()
|
||||
beaconDB := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, beaconDB)
|
||||
|
||||
ctx := context.Background()
|
||||
deposits, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
justifiedState, err := state.GenesisBeaconState(deposits, 0, ðpb.Eth1Data{})
|
||||
if err != nil {
|
||||
t.Fatalf("Can't generate genesis state: %v", err)
|
||||
}
|
||||
justifiedState.StateRoots = make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
|
||||
justifiedState.LatestBlockHeader = ðpb.BeaconBlockHeader{
|
||||
StateRoot: []byte{},
|
||||
}
|
||||
|
||||
chainService := setupBeaconChain(t, beaconDB, nil)
|
||||
|
||||
// Construct a forked chain that looks as follows:
|
||||
// /------B1 ----B3 ----- B5 (current head)
|
||||
// B0 --B2 -------------B4
|
||||
blocks, roots := constructForkedChain(t, justifiedState)
|
||||
|
||||
// We then setup a canonical chain of the following blocks:
|
||||
// B0->B1->B3->B5.
|
||||
if err := chainService.beaconDB.(*db2.BeaconDB).SaveBlockDeprecated(blocks[0]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := chainService.beaconDB.(*db2.BeaconDB).SaveJustifiedState(justifiedState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := chainService.beaconDB.(*db2.BeaconDB).SaveJustifiedBlock(blocks[0]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := chainService.beaconDB.(*db2.BeaconDB).UpdateChainHead(ctx, blocks[0], justifiedState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
canonicalBlockIndices := []int{1, 3, 5}
|
||||
postState := proto.Clone(justifiedState).(*pb.BeaconState)
|
||||
for _, canonicalIndex := range canonicalBlockIndices {
|
||||
postState, err = chainService.AdvanceStateDeprecated(ctx, postState, blocks[canonicalIndex])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := chainService.beaconDB.(*db2.BeaconDB).SaveBlockDeprecated(blocks[canonicalIndex]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := chainService.beaconDB.(*db2.BeaconDB).UpdateChainHead(ctx, blocks[canonicalIndex], postState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
chainHead, err := chainService.beaconDB.(*db2.BeaconDB).ChainHead()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if chainHead.Slot != justifiedState.Slot+5 {
|
||||
t.Errorf(
|
||||
"Expected chain head with slot %d, received %d",
|
||||
justifiedState.Slot+5,
|
||||
chainHead.Slot,
|
||||
)
|
||||
}
|
||||
|
||||
// We then save forked blocks and their historical states (but do not update chain head).
|
||||
// The fork is from B0->B2->B4.
|
||||
forkedBlockIndices := []int{2, 4}
|
||||
forkState := proto.Clone(justifiedState).(*pb.BeaconState)
|
||||
for _, forkIndex := range forkedBlockIndices {
|
||||
forkState, err = chainService.AdvanceStateDeprecated(ctx, forkState, blocks[forkIndex])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := chainService.beaconDB.(*db2.BeaconDB).SaveBlockDeprecated(blocks[forkIndex]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := chainService.beaconDB.(*db2.BeaconDB).SaveHistoricalState(ctx, forkState, roots[forkIndex]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Give the block from the forked chain, B4, the most votes.
|
||||
voteTargets := make(map[uint64]*pb.AttestationTarget)
|
||||
voteTargets[0] = &pb.AttestationTarget{
|
||||
Slot: blocks[5].Slot,
|
||||
BeaconBlockRoot: roots[5][:],
|
||||
ParentRoot: blocks[5].ParentRoot,
|
||||
}
|
||||
for i := 1; i < len(deposits); i++ {
|
||||
voteTargets[uint64(i)] = &pb.AttestationTarget{
|
||||
Slot: blocks[4].Slot,
|
||||
BeaconBlockRoot: roots[4][:],
|
||||
ParentRoot: blocks[4].ParentRoot,
|
||||
}
|
||||
}
|
||||
attHandler := &mockAttestationHandler{
|
||||
targets: voteTargets,
|
||||
}
|
||||
chainService.attsService = attHandler
|
||||
|
||||
block4State, err := chainService.beaconDB.(*db2.BeaconDB).HistoricalStateFromSlot(ctx, blocks[4].Slot, roots[4])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Applying the fork choice rule should reorg to B4 successfully.
|
||||
if err := chainService.ApplyForkChoiceRuleDeprecated(ctx, blocks[4], block4State); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
newHead, err := chainService.beaconDB.(*db2.BeaconDB).ChainHead()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !proto.Equal(newHead, blocks[4]) {
|
||||
t.Errorf(
|
||||
"Expected chain head %v, received %v",
|
||||
blocks[4],
|
||||
newHead,
|
||||
)
|
||||
}
|
||||
want := "Reorg happened"
|
||||
testutil.AssertLogsContain(t, hook, want)
|
||||
}
|
||||
|
||||
func constructForkedChain(t *testing.T, beaconState *pb.BeaconState) ([]*ethpb.BeaconBlock, [][32]byte) {
|
||||
// Construct the following chain:
|
||||
// /------B1 ----B3 ----- B5 (current head)
|
||||
// B0 --B2 -------------B4
|
||||
blocks := make([]*ethpb.BeaconBlock, 6)
|
||||
roots := make([][32]byte, 6)
|
||||
var err error
|
||||
blocks[0] = ðpb.BeaconBlock{
|
||||
Slot: beaconState.Slot,
|
||||
ParentRoot: []byte{'A'},
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
Eth1Data: ðpb.Eth1Data{},
|
||||
},
|
||||
}
|
||||
roots[0], err = ssz.SigningRoot(blocks[0])
|
||||
if err != nil {
|
||||
t.Fatalf("Could not hash block: %v", err)
|
||||
}
|
||||
|
||||
blocks[1] = ðpb.BeaconBlock{
|
||||
Slot: beaconState.Slot + 2,
|
||||
ParentRoot: roots[0][:],
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
Eth1Data: ðpb.Eth1Data{},
|
||||
},
|
||||
}
|
||||
roots[1], err = ssz.SigningRoot(blocks[1])
|
||||
if err != nil {
|
||||
t.Fatalf("Could not hash block: %v", err)
|
||||
}
|
||||
|
||||
blocks[2] = ðpb.BeaconBlock{
|
||||
Slot: beaconState.Slot + 1,
|
||||
ParentRoot: roots[0][:],
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
Eth1Data: ðpb.Eth1Data{},
|
||||
},
|
||||
}
|
||||
roots[2], err = ssz.SigningRoot(blocks[2])
|
||||
if err != nil {
|
||||
t.Fatalf("Could not hash block: %v", err)
|
||||
}
|
||||
|
||||
blocks[3] = ðpb.BeaconBlock{
|
||||
Slot: beaconState.Slot + 3,
|
||||
ParentRoot: roots[1][:],
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
Eth1Data: ðpb.Eth1Data{},
|
||||
},
|
||||
}
|
||||
roots[3], err = ssz.SigningRoot(blocks[3])
|
||||
if err != nil {
|
||||
t.Fatalf("Could not hash block: %v", err)
|
||||
}
|
||||
|
||||
blocks[4] = ðpb.BeaconBlock{
|
||||
Slot: beaconState.Slot + 4,
|
||||
ParentRoot: roots[2][:],
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
Eth1Data: ðpb.Eth1Data{},
|
||||
},
|
||||
}
|
||||
roots[4], err = ssz.SigningRoot(blocks[4])
|
||||
if err != nil {
|
||||
t.Fatalf("Could not hash block: %v", err)
|
||||
}
|
||||
|
||||
blocks[5] = ðpb.BeaconBlock{
|
||||
Slot: beaconState.Slot + 5,
|
||||
ParentRoot: roots[3][:],
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
Eth1Data: ðpb.Eth1Data{},
|
||||
},
|
||||
}
|
||||
roots[5], err = ssz.SigningRoot(blocks[5])
|
||||
if err != nil {
|
||||
t.Fatalf("Could not hash block: %v", err)
|
||||
}
|
||||
return blocks, roots
|
||||
}
|
||||
@@ -1,351 +0,0 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
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/event"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
// BlockReceiver interface defines the methods in the blockchain service which
|
||||
// directly receives a new block from other services and applies the full processing pipeline.
|
||||
type BlockReceiver interface {
|
||||
CanonicalBlockFeed() *event.Feed
|
||||
ReceiveBlockDeprecated(ctx context.Context, block *ethpb.BeaconBlock) (*pb.BeaconState, error)
|
||||
IsCanonical(slot uint64, hash []byte) bool
|
||||
UpdateCanonicalRoots(block *ethpb.BeaconBlock, root [32]byte)
|
||||
}
|
||||
|
||||
// BlockProcessor defines a common interface for methods useful for directly applying state transitions
|
||||
// to beacon blocks and generating a new beacon state from the Ethereum 2.0 core primitives.
|
||||
type BlockProcessor interface {
|
||||
VerifyBlockValidity(ctx context.Context, block *ethpb.BeaconBlock, beaconState *pb.BeaconState) error
|
||||
AdvanceStateDeprecated(ctx context.Context, beaconState *pb.BeaconState, block *ethpb.BeaconBlock) (*pb.BeaconState, error)
|
||||
CleanupBlockOperations(ctx context.Context, block *ethpb.BeaconBlock) error
|
||||
}
|
||||
|
||||
// BlockFailedProcessingErr represents a block failing a state transition function.
|
||||
type BlockFailedProcessingErr struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (b *BlockFailedProcessingErr) Error() string {
|
||||
return fmt.Sprintf("block failed processing: %v", b.err)
|
||||
}
|
||||
|
||||
// ReceiveBlockDeprecated is a function that defines the operations that are preformed on
|
||||
// any block that is received from p2p layer or rpc. It performs the following actions: It checks the block to see
|
||||
// 1. Verify a block passes pre-processing conditions
|
||||
// 2. Save and broadcast the block via p2p to other peers
|
||||
// 3. Apply the block state transition function and account for skip slots.
|
||||
// 4. Process and cleanup any block operations, such as attestations and deposits, which would need to be
|
||||
// either included or flushed from the beacon node's runtime.
|
||||
func (c *ChainService) ReceiveBlockDeprecated(ctx context.Context, block *ethpb.BeaconBlock) (*pb.BeaconState, error) {
|
||||
c.receiveBlockLock.Lock()
|
||||
defer c.receiveBlockLock.Unlock()
|
||||
ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.ReceiveBlock")
|
||||
defer span.End()
|
||||
// TODO(3219): Fix with new fork choice service.
|
||||
db, isLegacyDB := c.beaconDB.(*db.BeaconDB)
|
||||
if !isLegacyDB {
|
||||
panic("Deprecated receive block only works with deprecated database impl.")
|
||||
}
|
||||
|
||||
parentRoot := bytesutil.ToBytes32(block.ParentRoot)
|
||||
parent, err := db.BlockDeprecated(parentRoot)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get parent block")
|
||||
}
|
||||
if parent == nil {
|
||||
return nil, errors.New("parent does not exist in DB")
|
||||
}
|
||||
beaconState, err := db.HistoricalStateFromSlot(ctx, parent.Slot, parentRoot)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not retrieve beacon state")
|
||||
}
|
||||
|
||||
blockRoot, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not hash beacon block")
|
||||
}
|
||||
// We first verify the block's basic validity conditions.
|
||||
if err := c.VerifyBlockValidity(ctx, block, beaconState); err != nil {
|
||||
return beaconState, errors.Wrapf(err, "block with slot %d is not ready for processing", block.Slot)
|
||||
}
|
||||
|
||||
// We save the block to the DB and broadcast it to our peers.
|
||||
if err := c.SaveAndBroadcastBlock(ctx, block); err != nil {
|
||||
return beaconState, fmt.Errorf(
|
||||
"could not save and broadcast beacon block with slot %d: %v",
|
||||
block.Slot, err,
|
||||
)
|
||||
}
|
||||
|
||||
log.WithField("slot", block.Slot).Info("Executing state transition")
|
||||
|
||||
// We then apply the block state transition accordingly to obtain the resulting beacon state.
|
||||
beaconState, err = c.AdvanceStateDeprecated(ctx, beaconState, block)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case *BlockFailedProcessingErr:
|
||||
// If the block fails processing, we mark it as blacklisted and delete it from our DB.
|
||||
db.MarkEvilBlockHash(blockRoot)
|
||||
if err := db.DeleteBlockDeprecated(block); err != nil {
|
||||
return nil, errors.Wrap(err, "could not delete bad block from db")
|
||||
}
|
||||
return beaconState, err
|
||||
default:
|
||||
return beaconState, errors.Wrap(err, "could not apply block state transition")
|
||||
}
|
||||
}
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"slot": block.Slot,
|
||||
"epoch": helpers.SlotToEpoch(block.Slot),
|
||||
}).Info("State transition complete")
|
||||
|
||||
// We process the block's contained deposits, attestations, and other operations
|
||||
// and that may need to be stored or deleted from the beacon node's persistent storage.
|
||||
if err := c.CleanupBlockOperations(ctx, block); err != nil {
|
||||
return beaconState, errors.Wrap(err, "could not process block deposits, attestations, and other operations")
|
||||
}
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"slot": block.Slot,
|
||||
"attestations": len(block.Body.Attestations),
|
||||
"deposits": len(block.Body.Deposits),
|
||||
}).Info("Finished processing beacon block")
|
||||
|
||||
return beaconState, nil
|
||||
}
|
||||
|
||||
// VerifyBlockValidity cross-checks the block against the pre-processing conditions from
|
||||
// Ethereum 2.0, namely:
|
||||
// The parent block with root block.parent_root has been processed and accepted.
|
||||
// The node has processed its state up to slot, block.slot - 1.
|
||||
// The Ethereum 1.0 block pointed to by the state.processed_pow_receipt_root has been processed and accepted.
|
||||
// The node's local clock time is greater than or equal to state.genesis_time + block.slot * SECONDS_PER_SLOT.
|
||||
func (c *ChainService) VerifyBlockValidity(
|
||||
ctx context.Context,
|
||||
block *ethpb.BeaconBlock,
|
||||
beaconState *pb.BeaconState,
|
||||
) error {
|
||||
if block.Slot == 0 {
|
||||
return fmt.Errorf("cannot process a genesis block: received block with slot %d",
|
||||
block.Slot)
|
||||
}
|
||||
powBlockFetcher := c.web3Service.Client().BlockByHash
|
||||
if err := b.IsValidBlock(ctx, beaconState, block,
|
||||
c.beaconDB.HasBlock, powBlockFetcher, c.genesisTime); err != nil {
|
||||
return errors.Wrap(err, "block does not fulfill pre-processing conditions")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SaveAndBroadcastBlock stores the block in persistent storage and then broadcasts it to
|
||||
// peers via p2p. Blocks which have already been saved are not processed again via p2p, which is why
|
||||
// the order of operations is important in this function to prevent infinite p2p loops.
|
||||
func (c *ChainService) SaveAndBroadcastBlock(ctx context.Context, block *ethpb.BeaconBlock) error {
|
||||
blockRoot, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not tree hash incoming block")
|
||||
}
|
||||
if err := c.beaconDB.SaveBlock(ctx, block); err != nil {
|
||||
return errors.Wrap(err, "failed to save block")
|
||||
}
|
||||
// TODO(3219): Update after new fork choice service.
|
||||
db, isLegacyDB := c.beaconDB.(*db.BeaconDB)
|
||||
if isLegacyDB {
|
||||
if err := db.SaveAttestationTarget(ctx, &pb.AttestationTarget{
|
||||
Slot: block.Slot,
|
||||
BeaconBlockRoot: blockRoot[:],
|
||||
ParentRoot: block.ParentRoot,
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "failed to save attestation target")
|
||||
}
|
||||
}
|
||||
// Announce the new block to the network.
|
||||
c.p2p.Broadcast(ctx, &pb.BeaconBlockAnnounce{
|
||||
Hash: blockRoot[:],
|
||||
SlotNumber: block.Slot,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanupBlockOperations processes and cleans up any block operations relevant to the beacon node
|
||||
// such as attestations, exits, and deposits. We update the latest seen attestation by validator
|
||||
// in the local node's runtime, cleanup and remove pending deposits which have been included in the block
|
||||
// from our node's local cache, and process validator exits and more.
|
||||
func (c *ChainService) CleanupBlockOperations(ctx context.Context, block *ethpb.BeaconBlock) error {
|
||||
// Forward processed block to operation pool to remove individual operation from DB.
|
||||
if c.opsPoolService.IncomingProcessedBlockFeed().Send(block) == 0 {
|
||||
log.Error("Sent processed block to no subscribers")
|
||||
}
|
||||
|
||||
if err := c.attsService.BatchUpdateLatestAttestations(ctx, block.Body.Attestations); err != nil {
|
||||
return errors.Wrap(err, "failed to update latest attestation for store")
|
||||
}
|
||||
|
||||
// Remove pending deposits from the deposit queue.
|
||||
for _, dep := range block.Body.Deposits {
|
||||
c.depositCache.RemovePendingDeposit(ctx, dep)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AdvanceStateDeprecated executes the Ethereum 2.0 core state transition for the beacon chain and
|
||||
// updates important checkpoints and local persistent data during epoch transitions. It serves as a wrapper
|
||||
// around the more low-level, core state transition function primitive.
|
||||
func (c *ChainService) AdvanceStateDeprecated(
|
||||
ctx context.Context,
|
||||
beaconState *pb.BeaconState,
|
||||
block *ethpb.BeaconBlock,
|
||||
) (*pb.BeaconState, error) {
|
||||
finalizedEpoch := beaconState.FinalizedCheckpoint.Epoch
|
||||
newState, err := state.ExecuteStateTransition(
|
||||
ctx,
|
||||
beaconState,
|
||||
block,
|
||||
)
|
||||
if err != nil {
|
||||
return beaconState, &BlockFailedProcessingErr{err}
|
||||
}
|
||||
// Prune the block cache and helper caches on every new finalized epoch.
|
||||
if newState.FinalizedCheckpoint.Epoch > finalizedEpoch {
|
||||
helpers.ClearAllCaches()
|
||||
c.beaconDB.(*db.BeaconDB).ClearBlockCache()
|
||||
}
|
||||
|
||||
log.WithField(
|
||||
"slotsSinceGenesis", newState.Slot,
|
||||
).Info("Slot transition successfully processed")
|
||||
|
||||
if block != nil {
|
||||
log.WithField(
|
||||
"slotsSinceGenesis", newState.Slot,
|
||||
).Info("Block transition successfully processed")
|
||||
|
||||
blockRoot, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Save Historical States.
|
||||
if err := c.beaconDB.(*db.BeaconDB).SaveHistoricalState(ctx, beaconState, blockRoot); err != nil {
|
||||
return nil, errors.Wrap(err, "could not save historical state")
|
||||
}
|
||||
}
|
||||
|
||||
if helpers.IsEpochStart(newState.Slot) {
|
||||
// Save activated validators of this epoch to public key -> index DB.
|
||||
if err := c.saveValidatorIdx(ctx, newState); err != nil {
|
||||
return newState, errors.Wrap(err, "could not save validator index")
|
||||
}
|
||||
// Delete exited validators of this epoch to public key -> index DB.
|
||||
if err := c.deleteValidatorIdx(ctx, newState); err != nil {
|
||||
return newState, errors.Wrap(err, "could not delete validator index")
|
||||
}
|
||||
// Update FFG checkpoints in DB.
|
||||
if err := c.updateFFGCheckPts(ctx, newState); err != nil {
|
||||
return newState, errors.Wrap(err, "could not update FFG checkpts")
|
||||
}
|
||||
logEpochData(newState)
|
||||
}
|
||||
return newState, nil
|
||||
}
|
||||
|
||||
// saveValidatorIdx saves the validators public key to index mapping in DB, these
|
||||
// validators were activated from current epoch. After it saves, current epoch key
|
||||
// is deleted from ActivatedValidators mapping.
|
||||
func (c *ChainService) saveValidatorIdx(ctx context.Context, state *pb.BeaconState) error {
|
||||
nextEpoch := helpers.CurrentEpoch(state) + 1
|
||||
activatedValidators := validators.ActivatedValFromEpoch(nextEpoch)
|
||||
var idxNotInState []uint64
|
||||
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.
|
||||
if int(idx) >= len(state.Validators) {
|
||||
idxNotInState = append(idxNotInState, idx)
|
||||
continue
|
||||
}
|
||||
pubKey := state.Validators[idx].PublicKey
|
||||
if err := c.beaconDB.SaveValidatorIndex(ctx, bytesutil.ToBytes48(pubKey), idx); err != nil {
|
||||
return errors.Wrap(err, "could not save validator index")
|
||||
}
|
||||
}
|
||||
// Since we are processing next epoch, save the can't processed validator indices
|
||||
// to the epoch after that.
|
||||
validators.InsertActivatedIndices(nextEpoch+1, idxNotInState)
|
||||
validators.DeleteActivatedVal(helpers.CurrentEpoch(state))
|
||||
return nil
|
||||
}
|
||||
|
||||
// deleteValidatorIdx deletes the validators public key to index mapping in DB, the
|
||||
// validators were exited from current epoch. After it deletes, current epoch key
|
||||
// is deleted from ExitedValidators mapping.
|
||||
func (c *ChainService) deleteValidatorIdx(ctx context.Context, state *pb.BeaconState) error {
|
||||
exitedValidators := validators.ExitedValFromEpoch(helpers.CurrentEpoch(state) + 1)
|
||||
for _, idx := range exitedValidators {
|
||||
pubKey := state.Validators[idx].PublicKey
|
||||
if err := c.beaconDB.DeleteValidatorIndex(ctx, bytesutil.ToBytes48(pubKey)); err != nil {
|
||||
return errors.Wrap(err, "could not delete validator index")
|
||||
}
|
||||
}
|
||||
validators.DeleteExitedVal(helpers.CurrentEpoch(state))
|
||||
return nil
|
||||
}
|
||||
|
||||
// This gets called to update canonical root mapping.
|
||||
func (c *ChainService) saveHead(ctx context.Context, b *ethpb.BeaconBlock, r [32]byte) error {
|
||||
c.canonicalRootsLock.Lock()
|
||||
defer c.canonicalRootsLock.Unlock()
|
||||
c.headSlot = b.Slot
|
||||
c.canonicalRoots[b.Slot] = r[:]
|
||||
if err := c.beaconDB.SaveHeadBlockRoot(ctx, r); err != nil {
|
||||
return errors.Wrap(err, "could not save head root in DB")
|
||||
}
|
||||
log.WithFields(logrus.Fields{
|
||||
"slots": b.Slot,
|
||||
"root": hex.EncodeToString(r[:]),
|
||||
}).Info("Saved head info")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// logs epoch related data in each epoch transition
|
||||
func logEpochData(beaconState *pb.BeaconState) {
|
||||
|
||||
log.WithField("currentEpochAttestations", len(beaconState.CurrentEpochAttestations)).Info("Number of current epoch attestations")
|
||||
log.WithField("prevEpochAttestations", len(beaconState.PreviousEpochAttestations)).Info("Number of previous epoch attestations")
|
||||
log.WithField(
|
||||
"previousJustifiedEpoch", beaconState.PreviousJustifiedCheckpoint.Epoch,
|
||||
).Info("Previous justified epoch")
|
||||
log.WithField(
|
||||
"justifiedEpoch", beaconState.CurrentJustifiedCheckpoint.Epoch,
|
||||
).Info("Justified epoch")
|
||||
log.WithField(
|
||||
"finalizedEpoch", beaconState.FinalizedCheckpoint.Epoch,
|
||||
).Info("Finalized epoch")
|
||||
log.WithField(
|
||||
"Deposit Index", beaconState.Eth1DepositIndex,
|
||||
).Info("ETH1 Deposit Index")
|
||||
log.WithField(
|
||||
"numValidators", len(beaconState.Validators),
|
||||
).Info("Validator registry length")
|
||||
|
||||
log.WithField(
|
||||
"SlotsSinceGenesis", beaconState.Slot,
|
||||
).Info("Epoch transition successfully processed")
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,256 +0,0 @@
|
||||
// Package blockchain defines the life-cycle and status of the beacon chain
|
||||
// as well as the Ethereum Serenity beacon chain fork-choice rule based on
|
||||
// Casper Proof of Stake finality.
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/attestation"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache"
|
||||
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/operations"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/powchain"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
var log = logrus.WithField("prefix", "blockchain")
|
||||
|
||||
// ChainFeeds interface defines the methods of the ChainService which provide
|
||||
// information feeds.
|
||||
type ChainFeeds interface {
|
||||
StateInitializedFeed() *event.Feed
|
||||
}
|
||||
|
||||
// ChainService represents a service that handles the internal
|
||||
// logic of managing the full PoS beacon chain.
|
||||
type ChainService struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
beaconDB db.Database
|
||||
depositCache *depositcache.DepositCache
|
||||
web3Service *powchain.Web3Service
|
||||
attsService attestation.TargetHandler
|
||||
opsPoolService operations.OperationFeeds
|
||||
chainStartChan chan time.Time
|
||||
canonicalBlockFeed *event.Feed
|
||||
genesisTime time.Time
|
||||
finalizedEpoch uint64
|
||||
stateInitializedFeed *event.Feed
|
||||
p2p p2p.Broadcaster
|
||||
canonicalRoots map[uint64][]byte
|
||||
canonicalRootsLock sync.RWMutex
|
||||
receiveBlockLock sync.Mutex
|
||||
maxRoutines int64
|
||||
headSlot uint64
|
||||
}
|
||||
|
||||
// Config options for the service.
|
||||
type Config struct {
|
||||
BeaconBlockBuf int
|
||||
Web3Service *powchain.Web3Service
|
||||
AttsService attestation.TargetHandler
|
||||
BeaconDB db.Database
|
||||
DepositCache *depositcache.DepositCache
|
||||
OpsPoolService operations.OperationFeeds
|
||||
DevMode bool
|
||||
P2p p2p.Broadcaster
|
||||
MaxRoutines int64
|
||||
}
|
||||
|
||||
// NewChainService instantiates a new service instance that will
|
||||
// be registered into a running beacon node.
|
||||
func NewChainService(ctx context.Context, cfg *Config) (*ChainService, error) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
return &ChainService{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
beaconDB: cfg.BeaconDB,
|
||||
depositCache: cfg.DepositCache,
|
||||
web3Service: cfg.Web3Service,
|
||||
opsPoolService: cfg.OpsPoolService,
|
||||
attsService: cfg.AttsService,
|
||||
canonicalBlockFeed: new(event.Feed),
|
||||
chainStartChan: make(chan time.Time),
|
||||
stateInitializedFeed: new(event.Feed),
|
||||
p2p: cfg.P2p,
|
||||
canonicalRoots: make(map[uint64][]byte),
|
||||
maxRoutines: cfg.MaxRoutines,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Start a blockchain service's main event loop.
|
||||
func (c *ChainService) Start() {
|
||||
beaconState, err := c.beaconDB.HeadState(c.ctx)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not fetch beacon state: %v", err)
|
||||
}
|
||||
// If the chain has already been initialized, simply start the block processing routine.
|
||||
if beaconState != nil {
|
||||
log.Info("Beacon chain data already exists, starting service")
|
||||
c.genesisTime = time.Unix(int64(beaconState.GenesisTime), 0)
|
||||
c.finalizedEpoch = beaconState.FinalizedCheckpoint.Epoch
|
||||
} else {
|
||||
log.Info("Waiting for ChainStart log from the Validator Deposit Contract to start the beacon chain...")
|
||||
if c.web3Service == nil {
|
||||
log.Fatal("Not configured web3Service for POW chain")
|
||||
return // return need for TestStartUninitializedChainWithoutConfigPOWChain.
|
||||
}
|
||||
subChainStart := c.web3Service.ChainStartFeed().Subscribe(c.chainStartChan)
|
||||
go func() {
|
||||
genesisTime := <-c.chainStartChan
|
||||
c.processChainStartTime(genesisTime, subChainStart)
|
||||
return
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// processChainStartTime initializes a series of deposits from the ChainStart deposits in the eth1
|
||||
// deposit contract, initializes the beacon chain's state, and kicks off the beacon chain.
|
||||
func (c *ChainService) processChainStartTime(genesisTime time.Time, chainStartSub event.Subscription) {
|
||||
initialDeposits := c.web3Service.ChainStartDeposits()
|
||||
beaconState, err := c.initializeBeaconChain(genesisTime, initialDeposits, c.web3Service.ChainStartETH1Data())
|
||||
if err != nil {
|
||||
log.Fatalf("Could not initialize beacon chain: %v", err)
|
||||
}
|
||||
c.finalizedEpoch = beaconState.FinalizedCheckpoint.Epoch
|
||||
c.stateInitializedFeed.Send(genesisTime)
|
||||
chainStartSub.Unsubscribe()
|
||||
}
|
||||
|
||||
// initializes the state and genesis block of the beacon chain to persistent storage
|
||||
// based on a genesis timestamp value obtained from the ChainStart event emitted
|
||||
// by the ETH1.0 Deposit Contract and the POWChain service of the node.
|
||||
func (c *ChainService) initializeBeaconChain(genesisTime time.Time, deposits []*ethpb.Deposit, eth1data *ethpb.Eth1Data) (*pb.BeaconState, error) {
|
||||
ctx, span := trace.StartSpan(context.Background(), "beacon-chain.ChainService.initializeBeaconChain")
|
||||
defer span.End()
|
||||
log.Info("ChainStart time reached, starting the beacon chain!")
|
||||
c.genesisTime = genesisTime
|
||||
unixTime := uint64(genesisTime.Unix())
|
||||
// TODO(3219): Use new fork choice service.
|
||||
db, isLegacyDB := c.beaconDB.(*db.BeaconDB)
|
||||
if !isLegacyDB {
|
||||
panic("Chain service cannot operate with the new database interface due to in-progress fork choice changes")
|
||||
}
|
||||
|
||||
if err := db.InitializeState(ctx, unixTime, deposits, eth1data); err != nil {
|
||||
return nil, errors.Wrap(err, "could not initialize beacon state to disk")
|
||||
}
|
||||
beaconState, err := c.beaconDB.HeadState(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not attempt fetch beacon state")
|
||||
}
|
||||
|
||||
stateRoot, err := ssz.HashTreeRoot(beaconState)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not hash beacon state")
|
||||
}
|
||||
genBlock := b.NewGenesisBlock(stateRoot[:])
|
||||
genBlockRoot, err := ssz.SigningRoot(genBlock)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not hash beacon block")
|
||||
}
|
||||
|
||||
if err := c.beaconDB.SaveBlock(ctx, genBlock); err != nil {
|
||||
return nil, errors.Wrap(err, "could not save genesis block to disk")
|
||||
}
|
||||
|
||||
if err := db.SaveAttestationTarget(ctx, &pb.AttestationTarget{
|
||||
Slot: genBlock.Slot,
|
||||
BeaconBlockRoot: genBlockRoot[:],
|
||||
ParentRoot: genBlock.ParentRoot,
|
||||
}); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to save attestation target")
|
||||
}
|
||||
if err := db.UpdateChainHead(ctx, genBlock, beaconState); err != nil {
|
||||
return nil, errors.Wrap(err, "could not set chain head")
|
||||
}
|
||||
if err := db.SaveJustifiedBlock(genBlock); err != nil {
|
||||
return nil, errors.Wrap(err, "could not save genesis block as justified block")
|
||||
}
|
||||
if err := db.SaveFinalizedBlock(genBlock); err != nil {
|
||||
return nil, errors.Wrap(err, "could not save genesis block as finalized block")
|
||||
}
|
||||
if err := db.SaveJustifiedState(beaconState); err != nil {
|
||||
return nil, errors.Wrap(err, "could not save genesis state as justified state")
|
||||
}
|
||||
if err := db.SaveFinalizedState(beaconState); err != nil {
|
||||
return nil, errors.Wrap(err, "could not save genesis state as finalized state")
|
||||
}
|
||||
return beaconState, nil
|
||||
}
|
||||
|
||||
// Stop the blockchain service's main event loop and associated goroutines.
|
||||
func (c *ChainService) Stop() error {
|
||||
defer c.cancel()
|
||||
|
||||
log.Info("Stopping service")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Status always returns nil.
|
||||
// TODO(1202): Add service health checks.
|
||||
func (c *ChainService) Status() error {
|
||||
if runtime.NumGoroutine() > int(c.maxRoutines) {
|
||||
return fmt.Errorf("too many goroutines %d", runtime.NumGoroutine())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanonicalBlockFeed returns a channel that is written to
|
||||
// whenever a new block is determined to be canonical in the chain.
|
||||
func (c *ChainService) CanonicalBlockFeed() *event.Feed {
|
||||
return c.canonicalBlockFeed
|
||||
}
|
||||
|
||||
// StateInitializedFeed returns a feed that is written to
|
||||
// when the beacon state is first initialized.
|
||||
func (c *ChainService) StateInitializedFeed() *event.Feed {
|
||||
return c.stateInitializedFeed
|
||||
}
|
||||
|
||||
// ChainHeadRoot returns the hash root of the last beacon block processed by the
|
||||
// block chain service.
|
||||
func (c *ChainService) ChainHeadRoot(ctx context.Context) ([32]byte, error) {
|
||||
head, err := c.beaconDB.HeadBlock(ctx)
|
||||
if err != nil {
|
||||
return [32]byte{}, errors.Wrap(err, "could not retrieve chain head")
|
||||
}
|
||||
|
||||
root, err := ssz.SigningRoot(head)
|
||||
if err != nil {
|
||||
return [32]byte{}, errors.Wrap(err, "could not tree hash parent block")
|
||||
}
|
||||
return root, nil
|
||||
}
|
||||
|
||||
// UpdateCanonicalRoots sets a new head into the canonical block roots map.
|
||||
func (c *ChainService) UpdateCanonicalRoots(newHead *ethpb.BeaconBlock, newHeadRoot [32]byte) {
|
||||
c.canonicalRootsLock.Lock()
|
||||
defer c.canonicalRootsLock.Unlock()
|
||||
c.canonicalRoots[newHead.Slot] = newHeadRoot[:]
|
||||
}
|
||||
|
||||
// IsCanonical returns true if the input block hash of the corresponding slot
|
||||
// is part of the canonical chain. False otherwise.
|
||||
func (c *ChainService) IsCanonical(slot uint64, hash []byte) bool {
|
||||
c.canonicalRootsLock.RLock()
|
||||
defer c.canonicalRootsLock.RUnlock()
|
||||
if canonicalHash, ok := c.canonicalRoots[slot]; ok {
|
||||
return bytes.Equal(canonicalHash, hash)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,276 +0,0 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
gethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/attestation"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache"
|
||||
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/internal"
|
||||
p2p "github.com/prysmaticlabs/prysm/beacon-chain/p2p"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/powchain"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/sirupsen/logrus"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
)
|
||||
|
||||
// Ensure ChainService implements interfaces.
|
||||
var _ = ChainFeeds(&ChainService{})
|
||||
|
||||
func init() {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.SetOutput(ioutil.Discard)
|
||||
}
|
||||
|
||||
type mockOperationService struct{}
|
||||
|
||||
func (ms *mockOperationService) IncomingProcessedBlockFeed() *event.Feed {
|
||||
return new(event.Feed)
|
||||
}
|
||||
|
||||
func (ms *mockOperationService) IncomingAttFeed() *event.Feed {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ms *mockOperationService) IncomingExitFeed() *event.Feed {
|
||||
return nil
|
||||
}
|
||||
|
||||
type mockClient struct{}
|
||||
|
||||
func (m *mockClient) SubscribeNewHead(ctx context.Context, ch chan<- *gethTypes.Header) (ethereum.Subscription, error) {
|
||||
return new(event.Feed).Subscribe(ch), nil
|
||||
}
|
||||
|
||||
func (m *mockClient) BlockByHash(ctx context.Context, hash common.Hash) (*gethTypes.Block, error) {
|
||||
head := &gethTypes.Header{Number: big.NewInt(0), Difficulty: big.NewInt(100)}
|
||||
return gethTypes.NewBlockWithHeader(head), nil
|
||||
}
|
||||
|
||||
func (m *mockClient) BlockByNumber(ctx context.Context, number *big.Int) (*gethTypes.Block, error) {
|
||||
head := &gethTypes.Header{Number: big.NewInt(0), Difficulty: big.NewInt(100)}
|
||||
return gethTypes.NewBlockWithHeader(head), nil
|
||||
}
|
||||
|
||||
func (m *mockClient) HeaderByNumber(ctx context.Context, number *big.Int) (*gethTypes.Header, error) {
|
||||
return &gethTypes.Header{Number: big.NewInt(0), Difficulty: big.NewInt(100)}, nil
|
||||
}
|
||||
|
||||
func (m *mockClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- gethTypes.Log) (ethereum.Subscription, error) {
|
||||
return new(event.Feed).Subscribe(ch), nil
|
||||
}
|
||||
|
||||
func (m *mockClient) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
|
||||
return []byte{'t', 'e', 's', 't'}, nil
|
||||
}
|
||||
|
||||
func (m *mockClient) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) {
|
||||
return []byte{'t', 'e', 's', 't'}, nil
|
||||
}
|
||||
|
||||
func (m *mockClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]gethTypes.Log, error) {
|
||||
logs := make([]gethTypes.Log, 3)
|
||||
for i := 0; i < len(logs); i++ {
|
||||
logs[i].Address = common.Address{}
|
||||
logs[i].Topics = make([]common.Hash, 5)
|
||||
logs[i].Topics[0] = common.Hash{'a'}
|
||||
logs[i].Topics[1] = common.Hash{'b'}
|
||||
logs[i].Topics[2] = common.Hash{'c'}
|
||||
|
||||
}
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
func (m *mockClient) LatestBlockHash() common.Hash {
|
||||
return common.BytesToHash([]byte{'A'})
|
||||
}
|
||||
|
||||
type faultyClient struct{}
|
||||
|
||||
func (f *faultyClient) SubscribeNewHead(ctx context.Context, ch chan<- *gethTypes.Header) (ethereum.Subscription, error) {
|
||||
return new(event.Feed).Subscribe(ch), nil
|
||||
}
|
||||
|
||||
func (f *faultyClient) BlockByHash(ctx context.Context, hash common.Hash) (*gethTypes.Block, error) {
|
||||
return nil, errors.New("failed")
|
||||
}
|
||||
|
||||
func (f *faultyClient) BlockByNumber(ctx context.Context, number *big.Int) (*gethTypes.Block, error) {
|
||||
return nil, errors.New("failed")
|
||||
}
|
||||
|
||||
func (f *faultyClient) HeaderByNumber(ctx context.Context, number *big.Int) (*gethTypes.Header, error) {
|
||||
return nil, errors.New("failed")
|
||||
}
|
||||
|
||||
func (f *faultyClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- gethTypes.Log) (ethereum.Subscription, error) {
|
||||
return new(event.Feed).Subscribe(ch), nil
|
||||
}
|
||||
|
||||
func (f *faultyClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]gethTypes.Log, error) {
|
||||
return nil, errors.New("unable to retrieve logs")
|
||||
}
|
||||
|
||||
func (f *faultyClient) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
|
||||
return []byte{}, errors.New("unable to retrieve contract code")
|
||||
}
|
||||
|
||||
func (f *faultyClient) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) {
|
||||
return []byte{}, errors.New("unable to retrieve contract code")
|
||||
}
|
||||
|
||||
func (f *faultyClient) LatestBlockHash() common.Hash {
|
||||
return common.BytesToHash([]byte{'A'})
|
||||
}
|
||||
|
||||
type mockBroadcaster struct {
|
||||
broadcastCalled bool
|
||||
}
|
||||
|
||||
func (mb *mockBroadcaster) Broadcast(_ context.Context, _ proto.Message) error {
|
||||
mb.broadcastCalled = true
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ = p2p.Broadcaster(&mockBroadcaster{})
|
||||
|
||||
func setupGenesisBlock(t *testing.T, cs *ChainService) ([32]byte, *ethpb.BeaconBlock) {
|
||||
genesis := b.NewGenesisBlock([]byte{})
|
||||
if err := cs.beaconDB.SaveBlock(context.Background(), genesis); err != nil {
|
||||
t.Fatalf("could not save block to db: %v", err)
|
||||
}
|
||||
parentHash, err := ssz.SigningRoot(genesis)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get tree hash root of canonical head: %v", err)
|
||||
}
|
||||
return parentHash, genesis
|
||||
}
|
||||
|
||||
func setupBeaconChain(t *testing.T, beaconDB db.Database, attsService *attestation.Service) *ChainService {
|
||||
endpoint := "ws://127.0.0.1"
|
||||
ctx := context.Background()
|
||||
var web3Service *powchain.Web3Service
|
||||
var err error
|
||||
client := &mockClient{}
|
||||
web3Service, err = powchain.NewWeb3Service(ctx, &powchain.Web3ServiceConfig{
|
||||
Endpoint: endpoint,
|
||||
DepositContract: common.Address{},
|
||||
Reader: client,
|
||||
Client: client,
|
||||
Logger: client,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unable to set up web3 service: %v", err)
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
BeaconBlockBuf: 0,
|
||||
BeaconDB: beaconDB,
|
||||
DepositCache: depositcache.NewDepositCache(),
|
||||
Web3Service: web3Service,
|
||||
OpsPoolService: &mockOperationService{},
|
||||
AttsService: attsService,
|
||||
P2p: &mockBroadcaster{},
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("could not register blockchain service: %v", err)
|
||||
}
|
||||
chainService, err := NewChainService(ctx, cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to setup chain service: %v", err)
|
||||
}
|
||||
|
||||
return chainService
|
||||
}
|
||||
|
||||
func SetSlotInState(service *ChainService, slot uint64) error {
|
||||
bState, err := service.beaconDB.HeadState(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bState.Slot = slot
|
||||
return service.beaconDB.SaveState(context.Background(), bState, [32]byte{})
|
||||
}
|
||||
|
||||
func TestChainStartStop_Uninitialized(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
|
||||
hook := logTest.NewGlobal()
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
chainService := setupBeaconChain(t, db, nil)
|
||||
|
||||
// Test the start function.
|
||||
genesisChan := make(chan time.Time, 0)
|
||||
sub := chainService.stateInitializedFeed.Subscribe(genesisChan)
|
||||
defer sub.Unsubscribe()
|
||||
chainService.Start()
|
||||
chainService.chainStartChan <- time.Unix(0, 0)
|
||||
genesisTime := <-genesisChan
|
||||
if genesisTime != time.Unix(0, 0) {
|
||||
t.Errorf(
|
||||
"Expected genesis time to equal chainstart time (%v), received %v",
|
||||
time.Unix(0, 0),
|
||||
genesisTime,
|
||||
)
|
||||
}
|
||||
|
||||
beaconState, err := db.HeadState(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if beaconState == nil || beaconState.Slot != 0 {
|
||||
t.Error("Expected canonical state feed to send a state with genesis block")
|
||||
}
|
||||
if err := chainService.Stop(); err != nil {
|
||||
t.Fatalf("Unable to stop chain service: %v", err)
|
||||
}
|
||||
// The context should have been canceled.
|
||||
if chainService.ctx.Err() != context.Canceled {
|
||||
t.Error("Context was not canceled")
|
||||
}
|
||||
testutil.AssertLogsContain(t, hook, "Waiting for ChainStart log from the Validator Deposit Contract to start the beacon chain...")
|
||||
testutil.AssertLogsContain(t, hook, "ChainStart time reached, starting the beacon chain!")
|
||||
}
|
||||
|
||||
func TestChainStartStop_Initialized(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
|
||||
chainService := setupBeaconChain(t, db, nil)
|
||||
|
||||
unixTime := uint64(time.Now().Unix())
|
||||
deposits, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
if err := db.InitializeState(context.Background(), unixTime, deposits, ðpb.Eth1Data{}); err != nil {
|
||||
t.Fatalf("Could not initialize beacon state to disk: %v", err)
|
||||
}
|
||||
setupGenesisBlock(t, chainService)
|
||||
// Test the start function.
|
||||
chainService.Start()
|
||||
|
||||
if err := chainService.Stop(); err != nil {
|
||||
t.Fatalf("unable to stop chain service: %v", err)
|
||||
}
|
||||
|
||||
// The context should have been canceled.
|
||||
if chainService.ctx.Err() != context.Canceled {
|
||||
t.Error("context was not canceled")
|
||||
}
|
||||
testutil.AssertLogsContain(t, hook, "Beacon chain data already exists, starting service")
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"metrics.go",
|
||||
"querier.go",
|
||||
"receive_block.go",
|
||||
"regular_sync.go",
|
||||
"service.go",
|
||||
],
|
||||
deprecation = "Use github.com/prysmaticlabs/prysm/sync",
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/deprecated-sync",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
"//beacon-chain/cache/depositcache:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/deprecated-blockchain:go_default_library",
|
||||
"//beacon-chain/deprecated-sync/initial-sync:go_default_library",
|
||||
"//beacon-chain/operations:go_default_library",
|
||||
"//beacon-chain/p2p:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/deprecated-p2p:go_default_library",
|
||||
"//shared/event:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/logutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p_peer//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@io_opencensus_go//trace:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
srcs = [
|
||||
"querier_test.go",
|
||||
"receive_block_test.go",
|
||||
"regular_sync_test.go",
|
||||
"service_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/internal:go_default_library",
|
||||
"//beacon-chain/sync:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/deprecated-p2p:go_default_library",
|
||||
"//shared/event:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p_core//network:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p_peer//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -1,61 +0,0 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"helpers.go",
|
||||
"metrics.go",
|
||||
"service.go",
|
||||
"sync_blocks.go",
|
||||
"sync_state.go",
|
||||
],
|
||||
deprecation = "Use github.com/prysmaticlabs/prysm/sync/initial-sync",
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/deprecated-sync/initial-sync",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
"//beacon-chain/cache/depositcache:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/core/validators:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/deprecated-blockchain:go_default_library",
|
||||
"//beacon-chain/p2p:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/deprecated-p2p:go_default_library",
|
||||
"//shared/event:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p_peer//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@io_opencensus_go//trace:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
srcs = ["service_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/internal:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/deprecated-p2p:go_default_library",
|
||||
"//shared/event:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p_core//network:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p_peer//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -1,67 +0,0 @@
|
||||
package initialsync
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
p2p "github.com/prysmaticlabs/prysm/shared/deprecated-p2p"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
const noMsgData = "message contains no data"
|
||||
|
||||
func (s *InitialSync) checkBlockValidity(ctx context.Context, block *ethpb.BeaconBlock) error {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon-chain.sync.initial-sync.checkBlockValidity")
|
||||
defer span.End()
|
||||
beaconState, err := s.db.HeadState(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get beacon state")
|
||||
}
|
||||
|
||||
if block.Slot < helpers.StartSlot(beaconState.FinalizedCheckpoint.Epoch) {
|
||||
return errors.New("discarding received block with a slot number smaller than the last finalized slot")
|
||||
}
|
||||
// AttestationDeprecated from proposer not verified as, other nodes only store blocks not proposer
|
||||
// attestations.
|
||||
return nil
|
||||
}
|
||||
|
||||
// safelyHandleMessage will recover and log any panic that occurs from the
|
||||
// function argument.
|
||||
func safelyHandleMessage(fn func(p2p.Message) error, msg p2p.Message) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
printedMsg := noMsgData
|
||||
if msg.Data != nil {
|
||||
printedMsg = proto.MarshalTextString(msg.Data)
|
||||
}
|
||||
log.WithFields(logrus.Fields{
|
||||
"r": r,
|
||||
"msg": printedMsg,
|
||||
}).Error("Panicked when handling p2p message! Recovering...")
|
||||
|
||||
debug.PrintStack()
|
||||
|
||||
if msg.Ctx == nil {
|
||||
return
|
||||
}
|
||||
if span := trace.FromContext(msg.Ctx); span != nil {
|
||||
span.SetStatus(trace.Status{
|
||||
Code: trace.StatusCodeInternal,
|
||||
Message: fmt.Sprintf("Panic: %v", r),
|
||||
})
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Fingers crossed that it doesn't panic...
|
||||
if err := fn(msg); err != nil {
|
||||
log.WithError(err).Error("Failed to process message")
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package initialsync
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
var (
|
||||
// Metrics
|
||||
sentBatchedBlockReq = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "initsync_sent_batched_block_req",
|
||||
Help: "The number of sent batched block req",
|
||||
})
|
||||
batchedBlockReq = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "initsync_batched_block_req",
|
||||
Help: "The number of received batch blocks responses",
|
||||
})
|
||||
recBlock = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "initsync_received_blocks",
|
||||
Help: "The number of received blocks",
|
||||
})
|
||||
stateReq = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "initsync_state_req",
|
||||
Help: "The number of sent state requests",
|
||||
})
|
||||
recState = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "initsync_received_state",
|
||||
Help: "The number of received state",
|
||||
})
|
||||
)
|
||||
@@ -1,305 +0,0 @@
|
||||
// Package initialsync is run by the beacon node when the local chain is
|
||||
// behind the network's longest chain. Initial sync works as follows:
|
||||
// The node requests for the slot number of the most recent finalized block.
|
||||
// The node then builds from the most recent finalized block by requesting for subsequent
|
||||
// blocks by slot number. Once the service detects that the local chain is caught up with
|
||||
// the network, the service hands over control to the regular sync service.
|
||||
// Note: The behavior of initialsync will likely change as the specification changes.
|
||||
// The most significant and highly probable change will be determining where to sync from.
|
||||
// The beacon chain may sync from a block in the pasts X months in order to combat long-range attacks
|
||||
// (see here: https://github.com/ethereum/wiki/wiki/Proof-of-Stake-FAQs#what-is-weak-subjectivity)
|
||||
package initialsync
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
blockchain "github.com/prysmaticlabs/prysm/beacon-chain/deprecated-blockchain"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
deprecatedp2p "github.com/prysmaticlabs/prysm/shared/deprecated-p2p"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var log = logrus.WithField("prefix", "initial-sync")
|
||||
|
||||
var (
|
||||
// ErrCanonicalStateMismatch can occur when the node has processed all blocks
|
||||
// from a peer, but arrived at a different state root.
|
||||
ErrCanonicalStateMismatch = errors.New("canonical state did not match after syncing with peer")
|
||||
)
|
||||
|
||||
// Config defines the configurable properties of InitialSync.
|
||||
//
|
||||
type Config struct {
|
||||
SyncPollingInterval time.Duration
|
||||
BatchedBlockBufferSize int
|
||||
StateBufferSize int
|
||||
BeaconDB *db.BeaconDB
|
||||
DepositCache *depositcache.DepositCache
|
||||
P2P p2pAPI
|
||||
SyncService syncService
|
||||
ChainService chainService
|
||||
PowChain powChainService
|
||||
}
|
||||
|
||||
// DefaultConfig provides the default configuration for a sync service.
|
||||
// SyncPollingInterval determines how frequently the service checks that initial sync is complete.
|
||||
// BlockBufferSize determines that buffer size of the `blockBuf` channel.
|
||||
// StateBufferSize determines the buffer size of the `stateBuf` channel.
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
SyncPollingInterval: time.Duration(params.BeaconConfig().SyncPollingInterval) * time.Second,
|
||||
BatchedBlockBufferSize: params.BeaconConfig().DefaultBufferSize,
|
||||
StateBufferSize: params.BeaconConfig().DefaultBufferSize,
|
||||
}
|
||||
}
|
||||
|
||||
type p2pAPI interface {
|
||||
p2p.Sender
|
||||
p2p.DeprecatedSubscriber
|
||||
}
|
||||
|
||||
type powChainService interface {
|
||||
BlockExists(ctx context.Context, hash common.Hash) (bool, *big.Int, error)
|
||||
}
|
||||
|
||||
type chainService interface {
|
||||
blockchain.BlockProcessor
|
||||
blockchain.ForkChoice
|
||||
}
|
||||
|
||||
// SyncService is the interface for the Sync service.
|
||||
// InitialSync calls `Start` when initial sync completes.
|
||||
type syncService interface {
|
||||
Start()
|
||||
ResumeSync()
|
||||
}
|
||||
|
||||
// InitialSync defines the main class in this package.
|
||||
// See the package comments for a general description of the service's functions.
|
||||
type InitialSync struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
p2p p2pAPI
|
||||
syncService syncService
|
||||
chainService chainService
|
||||
db *db.BeaconDB
|
||||
depositCache *depositcache.DepositCache
|
||||
powchain powChainService
|
||||
batchedBlockBuf chan deprecatedp2p.Message
|
||||
stateBuf chan deprecatedp2p.Message
|
||||
syncPollingInterval time.Duration
|
||||
syncedFeed *event.Feed
|
||||
stateReceived bool
|
||||
mutex *sync.Mutex
|
||||
nodeIsSynced bool
|
||||
}
|
||||
|
||||
// NewInitialSyncService constructs a new InitialSyncService.
|
||||
// This method is normally called by the main node.
|
||||
func NewInitialSyncService(ctx context.Context,
|
||||
cfg *Config,
|
||||
) *InitialSync {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
stateBuf := make(chan deprecatedp2p.Message, cfg.StateBufferSize)
|
||||
batchedBlockBuf := make(chan deprecatedp2p.Message, cfg.BatchedBlockBufferSize)
|
||||
|
||||
return &InitialSync{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
p2p: cfg.P2P,
|
||||
syncService: cfg.SyncService,
|
||||
db: cfg.BeaconDB,
|
||||
depositCache: cfg.DepositCache,
|
||||
powchain: cfg.PowChain,
|
||||
chainService: cfg.ChainService,
|
||||
stateBuf: stateBuf,
|
||||
batchedBlockBuf: batchedBlockBuf,
|
||||
syncPollingInterval: cfg.SyncPollingInterval,
|
||||
syncedFeed: new(event.Feed),
|
||||
stateReceived: false,
|
||||
mutex: new(sync.Mutex),
|
||||
}
|
||||
}
|
||||
|
||||
// Start begins the goroutine.
|
||||
func (s *InitialSync) Start(chainHeadResponses map[peer.ID]*pb.ChainHeadResponse) {
|
||||
go s.run(chainHeadResponses)
|
||||
}
|
||||
|
||||
// Stop kills the initial sync goroutine.
|
||||
func (s *InitialSync) Stop() error {
|
||||
log.Info("Stopping service")
|
||||
s.cancel()
|
||||
return nil
|
||||
}
|
||||
|
||||
// NodeIsSynced checks that the node has been caught up with the network.
|
||||
func (s *InitialSync) NodeIsSynced() bool {
|
||||
return s.nodeIsSynced
|
||||
}
|
||||
|
||||
func (s *InitialSync) exitInitialSync(ctx context.Context, block *ethpb.BeaconBlock, chainHead *pb.ChainHeadResponse) error {
|
||||
if s.nodeIsSynced {
|
||||
return nil
|
||||
}
|
||||
parentRoot := bytesutil.ToBytes32(block.ParentRoot)
|
||||
parent, err := s.db.BlockDeprecated(parentRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
state, err := s.db.HistoricalStateFromSlot(ctx, parent.Slot, parentRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.chainService.VerifyBlockValidity(ctx, block, state); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.db.SaveBlockDeprecated(block); err != nil {
|
||||
return err
|
||||
}
|
||||
root, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to tree hash block")
|
||||
}
|
||||
if err := s.db.SaveAttestationTarget(ctx, &pb.AttestationTarget{
|
||||
Slot: block.Slot,
|
||||
BeaconBlockRoot: root[:],
|
||||
ParentRoot: block.ParentRoot,
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "failed to save attestation target")
|
||||
}
|
||||
state, err = s.chainService.AdvanceStateDeprecated(ctx, state, block)
|
||||
if err != nil {
|
||||
log.Error("OH NO - looks like you synced with a bad peer, try restarting your node!")
|
||||
switch err.(type) {
|
||||
case *blockchain.BlockFailedProcessingErr:
|
||||
// If the block fails processing, we delete it from our DB.
|
||||
if err := s.db.DeleteBlockDeprecated(block); err != nil {
|
||||
return errors.Wrap(err, "could not delete bad block from db")
|
||||
}
|
||||
return errors.Wrap(err, "could not apply block state transition")
|
||||
default:
|
||||
return errors.Wrap(err, "could not apply block state transition")
|
||||
}
|
||||
}
|
||||
if err := s.chainService.CleanupBlockOperations(ctx, block); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.db.UpdateChainHead(ctx, block, state); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stateRoot := s.db.HeadStateRoot()
|
||||
|
||||
if stateRoot != bytesutil.ToBytes32(chainHead.CanonicalStateRootHash32) {
|
||||
log.Errorf(
|
||||
"Canonical state root %#x does not match highest observed root from peer %#x",
|
||||
stateRoot,
|
||||
chainHead.CanonicalStateRootHash32,
|
||||
)
|
||||
|
||||
return ErrCanonicalStateMismatch
|
||||
}
|
||||
log.WithField("canonicalStateSlot", state.Slot).Info("Exiting init sync and starting regular sync")
|
||||
s.syncService.ResumeSync()
|
||||
s.cancel()
|
||||
s.nodeIsSynced = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// run is the main goroutine for the initial sync service.
|
||||
// delayChan is explicitly passed into this function to facilitate tests that don't require a timeout.
|
||||
// It is assumed that the goroutine `run` is only called once per instance.
|
||||
func (s *InitialSync) run(chainHeadResponses map[peer.ID]*pb.ChainHeadResponse) {
|
||||
batchedBlocksub := s.p2p.Subscribe(&pb.BatchedBeaconBlockResponse{}, s.batchedBlockBuf)
|
||||
beaconStateSub := s.p2p.Subscribe(&pb.BeaconStateResponse{}, s.stateBuf)
|
||||
defer func() {
|
||||
beaconStateSub.Unsubscribe()
|
||||
batchedBlocksub.Unsubscribe()
|
||||
close(s.batchedBlockBuf)
|
||||
close(s.stateBuf)
|
||||
}()
|
||||
|
||||
ctx := s.ctx
|
||||
|
||||
var peers []peer.ID
|
||||
for k := range chainHeadResponses {
|
||||
peers = append(peers, k)
|
||||
}
|
||||
|
||||
// Sort peers in descending order based on their canonical slot.
|
||||
sort.Slice(peers, func(i, j int) bool {
|
||||
return chainHeadResponses[peers[i]].CanonicalSlot > chainHeadResponses[peers[j]].CanonicalSlot
|
||||
})
|
||||
|
||||
for _, peer := range peers {
|
||||
chainHead := chainHeadResponses[peer]
|
||||
if err := s.syncToPeer(ctx, chainHead, peer); err != nil {
|
||||
log.WithError(err).WithField("peer", peer.Pretty()).Warn("Failed to sync with peer, trying next best peer")
|
||||
continue
|
||||
}
|
||||
log.Info("Synced!")
|
||||
break
|
||||
}
|
||||
|
||||
if !s.nodeIsSynced {
|
||||
log.Fatal("Failed to sync with anyone...")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *InitialSync) syncToPeer(ctx context.Context, chainHeadResponse *pb.ChainHeadResponse, peer peer.ID) error {
|
||||
fields := logrus.Fields{
|
||||
"peer": peer.Pretty(),
|
||||
"canonicalSlot": chainHeadResponse.CanonicalSlot,
|
||||
}
|
||||
|
||||
log.WithFields(fields).Info("Requesting state from peer")
|
||||
if err := s.requestStateFromPeer(ctx, bytesutil.ToBytes32(chainHeadResponse.FinalizedStateRootHash32S), peer); err != nil {
|
||||
log.Errorf("Could not request state from peer %v", err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(s.ctx, 20*time.Second)
|
||||
defer cancel()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
|
||||
return ctx.Err()
|
||||
case msg := <-s.stateBuf:
|
||||
log.WithFields(fields).Info("Received state resp from peer")
|
||||
if err := s.processState(msg, chainHeadResponse); err != nil {
|
||||
return err
|
||||
}
|
||||
case msg := <-s.batchedBlockBuf:
|
||||
if msg.Peer != peer {
|
||||
continue
|
||||
}
|
||||
log.WithFields(fields).Info("Received batched blocks from peer")
|
||||
if err := s.processBatchedBlocks(msg, chainHeadResponse); err != nil {
|
||||
log.WithError(err).WithField("peer", peer).Error("Failed to sync with peer.")
|
||||
continue
|
||||
}
|
||||
if !s.nodeIsSynced {
|
||||
return errors.New("node still not in sync after receiving batch blocks")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,247 +0,0 @@
|
||||
package initialsync
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/libp2p/go-libp2p-core/network"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/internal"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
p2p "github.com/prysmaticlabs/prysm/shared/deprecated-p2p"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
)
|
||||
|
||||
type mockP2P struct {
|
||||
}
|
||||
|
||||
func (mp *mockP2P) Subscribe(msg proto.Message, channel chan p2p.Message) event.Subscription {
|
||||
return new(event.Feed).Subscribe(channel)
|
||||
}
|
||||
|
||||
func (mp *mockP2P) Broadcast(ctx context.Context, msg proto.Message) {}
|
||||
|
||||
func (mp *mockP2P) Send(ctx context.Context, msg proto.Message, peerID peer.ID) (network.Stream, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (mp *mockP2P) Reputation(_ peer.ID, _ int) {
|
||||
|
||||
}
|
||||
|
||||
type mockSyncService struct {
|
||||
hasStarted bool
|
||||
isSynced bool
|
||||
}
|
||||
|
||||
func (ms *mockSyncService) Start() {
|
||||
ms.hasStarted = true
|
||||
}
|
||||
|
||||
func (ms *mockSyncService) IsSyncedWithNetwork() bool {
|
||||
return ms.isSynced
|
||||
}
|
||||
|
||||
func (ms *mockSyncService) ResumeSync() {
|
||||
|
||||
}
|
||||
|
||||
type mockChainService struct{}
|
||||
|
||||
func (ms *mockChainService) CanonicalBlockFeed() *event.Feed {
|
||||
return new(event.Feed)
|
||||
}
|
||||
|
||||
func (ms *mockChainService) ReceiveBlock(ctx context.Context, block *ethpb.BeaconBlock) (*pb.BeaconState, error) {
|
||||
return &pb.BeaconState{}, nil
|
||||
}
|
||||
|
||||
func (ms *mockChainService) AdvanceStateDeprecated(
|
||||
ctx context.Context, beaconState *pb.BeaconState, block *ethpb.BeaconBlock,
|
||||
) (*pb.BeaconState, error) {
|
||||
return &pb.BeaconState{
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ms *mockChainService) VerifyBlockValidity(
|
||||
ctx context.Context,
|
||||
block *ethpb.BeaconBlock,
|
||||
beaconState *pb.BeaconState,
|
||||
) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ms *mockChainService) ApplyForkChoiceRuleDeprecated(ctx context.Context, block *ethpb.BeaconBlock, computedState *pb.BeaconState) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ms *mockChainService) CleanupBlockOperations(ctx context.Context, block *ethpb.BeaconBlock) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func setUpGenesisStateAndBlock(beaconDB *db.BeaconDB, t *testing.T) {
|
||||
ctx := context.Background()
|
||||
genesisTime := time.Now()
|
||||
unixTime := uint64(genesisTime.Unix())
|
||||
if err := beaconDB.InitializeState(context.Background(), unixTime, []*ethpb.Deposit{}, ðpb.Eth1Data{}); err != nil {
|
||||
t.Fatalf("could not initialize beacon state to disk: %v", err)
|
||||
}
|
||||
beaconState, err := beaconDB.HeadState(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("could not attempt fetch beacon state: %v", err)
|
||||
}
|
||||
stateRoot, err := hashutil.HashProto(beaconState)
|
||||
if err != nil {
|
||||
t.Errorf("unable to marshal the beacon state: %v", err)
|
||||
return
|
||||
}
|
||||
genBlock := b.NewGenesisBlock(stateRoot[:])
|
||||
if err := beaconDB.SaveBlockDeprecated(genBlock); err != nil {
|
||||
t.Fatalf("could not save genesis block to disk: %v", err)
|
||||
}
|
||||
if err := beaconDB.UpdateChainHead(ctx, genBlock, beaconState); err != nil {
|
||||
t.Fatalf("could not set chain head, %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessingBatchedBlocks_OK(t *testing.T) {
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
setUpGenesisStateAndBlock(db, t)
|
||||
|
||||
cfg := &Config{
|
||||
P2P: &mockP2P{},
|
||||
SyncService: &mockSyncService{},
|
||||
ChainService: &mockChainService{},
|
||||
BeaconDB: db,
|
||||
}
|
||||
ss := NewInitialSyncService(context.Background(), cfg)
|
||||
|
||||
batchSize := 20
|
||||
batchedBlocks := make([]*ethpb.BeaconBlock, batchSize)
|
||||
blocks, err := ss.db.BlocksBySlot(ss.ctx, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
genBlock := blocks[0]
|
||||
parentRoot, err := ssz.SigningRoot(genBlock)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i := 1; i <= batchSize; i++ {
|
||||
block := ðpb.BeaconBlock{
|
||||
Slot: uint64(i),
|
||||
ParentRoot: parentRoot[:],
|
||||
}
|
||||
batchedBlocks[i-1] = block
|
||||
parentRoot, err = ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
// edge case: handle out of order block list. Specifically with the highest
|
||||
// block first. This is swapping the first and last blocks in the list.
|
||||
batchedBlocks[0], batchedBlocks[batchSize-1] = batchedBlocks[batchSize-1], batchedBlocks[0]
|
||||
|
||||
msg := p2p.Message{
|
||||
Ctx: context.Background(),
|
||||
Data: &pb.BatchedBeaconBlockResponse{
|
||||
BatchedBlocks: batchedBlocks,
|
||||
},
|
||||
}
|
||||
|
||||
chainHead := &pb.ChainHeadResponse{}
|
||||
|
||||
ss.processBatchedBlocks(msg, chainHead)
|
||||
}
|
||||
|
||||
func TestProcessingBlocks_SkippedSlots(t *testing.T) {
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
setUpGenesisStateAndBlock(db, t)
|
||||
ctx := context.Background()
|
||||
|
||||
cfg := &Config{
|
||||
P2P: &mockP2P{},
|
||||
SyncService: &mockSyncService{},
|
||||
ChainService: &mockChainService{},
|
||||
BeaconDB: db,
|
||||
}
|
||||
ss := NewInitialSyncService(context.Background(), cfg)
|
||||
|
||||
batchSize := 20
|
||||
blks, err := ss.db.BlocksBySlot(ctx, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to get genesis block %v", err)
|
||||
}
|
||||
h, err := ssz.SigningRoot(blks[0])
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to hash block %v", err)
|
||||
}
|
||||
parentHash := h[:]
|
||||
|
||||
for i := 1; i <= batchSize; i++ {
|
||||
// skip slots
|
||||
if i == 4 || i == 6 || i == 13 || i == 17 {
|
||||
continue
|
||||
}
|
||||
block := ðpb.BeaconBlock{
|
||||
Slot: uint64(i),
|
||||
ParentRoot: parentHash,
|
||||
}
|
||||
|
||||
chainHead := &pb.ChainHeadResponse{}
|
||||
|
||||
ss.processBlock(context.Background(), block, chainHead)
|
||||
|
||||
// Save the block and set the parent hash of the next block
|
||||
// as the hash of the current block.
|
||||
if err := ss.db.SaveBlockDeprecated(block); err != nil {
|
||||
t.Fatalf("BlockDeprecated unable to be saved %v", err)
|
||||
}
|
||||
|
||||
hash, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not hash block %v", err)
|
||||
}
|
||||
parentHash = hash[:]
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSafelyHandleMessage(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
|
||||
safelyHandleMessage(func(_ p2p.Message) error {
|
||||
panic("bad!")
|
||||
}, p2p.Message{
|
||||
Data: ðpb.BeaconBlock{},
|
||||
})
|
||||
|
||||
testutil.AssertLogsContain(t, hook, "Panicked when handling p2p message!")
|
||||
}
|
||||
|
||||
func TestSafelyHandleMessage_NoData(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
|
||||
safelyHandleMessage(func(_ p2p.Message) error {
|
||||
panic("bad!")
|
||||
}, p2p.Message{})
|
||||
|
||||
entry := hook.LastEntry()
|
||||
if entry.Data["msg"] != "message contains no data" {
|
||||
t.Errorf("Message logged was not what was expected: %s", entry.Data["msg"])
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
package initialsync
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
p2p "github.com/prysmaticlabs/prysm/shared/deprecated-p2p"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
// processBlock is the main method that validates each block which is received
|
||||
// for initial sync. It checks if the blocks are valid and then will continue to
|
||||
// process and save it into the db.
|
||||
func (s *InitialSync) processBlock(ctx context.Context, block *ethpb.BeaconBlock, chainHead *pb.ChainHeadResponse) error {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon-chain.sync.initial-sync.processBlock")
|
||||
defer span.End()
|
||||
recBlock.Inc()
|
||||
|
||||
if block.Slot == chainHead.CanonicalSlot {
|
||||
if err := s.exitInitialSync(s.ctx, block, chainHead); err != nil {
|
||||
log.Errorf("Could not exit initial sync: %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := s.validateAndSaveNextBlock(ctx, block); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// processBatchedBlocks processes all the received blocks from
|
||||
// the p2p message.
|
||||
func (s *InitialSync) processBatchedBlocks(msg p2p.Message, chainHead *pb.ChainHeadResponse) error {
|
||||
ctx, span := trace.StartSpan(msg.Ctx, "beacon-chain.sync.initial-sync.processBatchedBlocks")
|
||||
defer span.End()
|
||||
batchedBlockReq.Inc()
|
||||
|
||||
response := msg.Data.(*pb.BatchedBeaconBlockResponse)
|
||||
batchedBlocks := response.BatchedBlocks
|
||||
if len(batchedBlocks) == 0 {
|
||||
// Do not process empty responses.
|
||||
return nil
|
||||
}
|
||||
|
||||
log.WithField("blocks", len(batchedBlocks)).Info("Processing batched block response")
|
||||
// Sort batchBlocks in ascending order.
|
||||
sort.Slice(batchedBlocks, func(i, j int) bool {
|
||||
return batchedBlocks[i].Slot < batchedBlocks[j].Slot
|
||||
})
|
||||
|
||||
for _, block := range batchedBlocks {
|
||||
if err := s.processBlock(ctx, block, chainHead); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
log.Debug("Finished processing batched blocks")
|
||||
return nil
|
||||
}
|
||||
|
||||
// requestBatchedBlocks sends out a request for multiple blocks that's between finalized roots
|
||||
// and head roots.
|
||||
func (s *InitialSync) requestBatchedBlocks(ctx context.Context, FinalizedRoot []byte, canonicalRoot []byte, peer peer.ID) {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon-chain.sync.initial-sync.requestBatchedBlocks")
|
||||
defer span.End()
|
||||
sentBatchedBlockReq.Inc()
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"finalizedBlkRoot": fmt.Sprintf("%#x", bytesutil.Trunc(FinalizedRoot[:])),
|
||||
"headBlkRoot": fmt.Sprintf("%#x", bytesutil.Trunc(canonicalRoot[:]))},
|
||||
).Debug("Requesting batched blocks")
|
||||
if _, err := s.p2p.Send(ctx, &pb.BatchedBeaconBlockRequest{
|
||||
FinalizedRoot: FinalizedRoot,
|
||||
CanonicalRoot: canonicalRoot,
|
||||
}, peer); err != nil {
|
||||
log.Errorf("Could not send batch block request to peer %s: %v", peer.Pretty(), err)
|
||||
}
|
||||
}
|
||||
|
||||
// validateAndSaveNextBlock will validate whether blocks received from the blockfetcher
|
||||
// routine can be added to the chain.
|
||||
func (s *InitialSync) validateAndSaveNextBlock(ctx context.Context, block *ethpb.BeaconBlock) error {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon-chain.sync.initial-sync.validateAndSaveNextBlock")
|
||||
defer span.End()
|
||||
if block == nil {
|
||||
return errors.New("received nil block")
|
||||
}
|
||||
root, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if s.db.HasBlockDeprecated(root) {
|
||||
log.WithField("block", fmt.Sprintf("%#x", root)).
|
||||
Warn("Skipping block in db already")
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := s.checkBlockValidity(ctx, block); err != nil {
|
||||
return err
|
||||
}
|
||||
log.WithFields(logrus.Fields{
|
||||
"root": fmt.Sprintf("%#x", bytesutil.Trunc(root[:])),
|
||||
"slot": block.Slot,
|
||||
}).Info("Saving block")
|
||||
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
parentRoot := bytesutil.ToBytes32(block.ParentRoot)
|
||||
parentBlock, err := s.db.BlockDeprecated(parentRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if parentBlock == nil {
|
||||
return fmt.Errorf("parent block with root %#x doesnt exist in the db", parentRoot)
|
||||
}
|
||||
|
||||
state, err := s.db.HistoricalStateFromSlot(ctx, parentBlock.Slot, parentRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.chainService.VerifyBlockValidity(ctx, block, state); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.db.SaveBlockDeprecated(block); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.db.SaveAttestationTarget(ctx, &pb.AttestationTarget{
|
||||
Slot: block.Slot,
|
||||
BeaconBlockRoot: root[:],
|
||||
ParentRoot: block.ParentRoot,
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "could not to save attestation target")
|
||||
}
|
||||
state, err = s.chainService.AdvanceStateDeprecated(ctx, state, block)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not apply block state transition")
|
||||
}
|
||||
if err := s.chainService.CleanupBlockOperations(ctx, block); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.db.UpdateChainHead(ctx, block, state)
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
package initialsync
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
p2p "github.com/prysmaticlabs/prysm/shared/deprecated-p2p"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
func (s *InitialSync) processState(msg p2p.Message, chainHead *pb.ChainHeadResponse) error {
|
||||
ctx, span := trace.StartSpan(msg.Ctx, "beacon-chain.sync.initial-sync.processState")
|
||||
defer span.End()
|
||||
data := msg.Data.(*pb.BeaconStateResponse)
|
||||
finalizedState := data.FinalizedState
|
||||
finalizedBlock := data.FinalizedBlock
|
||||
recState.Inc()
|
||||
|
||||
if err := s.db.SaveFinalizedState(finalizedState); err != nil {
|
||||
log.Errorf("Unable to set received last finalized state in db: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := s.db.SaveFinalizedBlock(finalizedBlock); err != nil {
|
||||
log.Errorf("Could not save finalized block %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := s.db.SaveBlockDeprecated(finalizedBlock); err != nil {
|
||||
log.Errorf("Could not save block %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
finalizedBlockRoot, err := ssz.SigningRoot(finalizedBlock)
|
||||
if err != nil {
|
||||
log.Errorf("Could not hash finalized block %v", err)
|
||||
return nil
|
||||
}
|
||||
log.Infof("finalized block root %#x", finalizedBlockRoot)
|
||||
|
||||
if err := s.db.SaveHistoricalState(ctx, finalizedState, finalizedBlockRoot); err != nil {
|
||||
log.Errorf("Could not save new historical state: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := s.db.SaveAttestationTarget(ctx, &pb.AttestationTarget{
|
||||
Slot: finalizedState.LatestBlockHeader.Slot,
|
||||
BeaconBlockRoot: finalizedBlockRoot[:],
|
||||
ParentRoot: finalizedState.LatestBlockHeader.ParentRoot,
|
||||
}); err != nil {
|
||||
log.Errorf("Could not to save attestation target: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := s.db.SaveJustifiedState(finalizedState); err != nil {
|
||||
log.Errorf("Could not set beacon state for initial sync %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := s.db.SaveJustifiedBlock(finalizedBlock); err != nil {
|
||||
log.Errorf("Could not save finalized block %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
exists, _, err := s.powchain.BlockExists(ctx, bytesutil.ToBytes32(finalizedState.Eth1Data.BlockHash))
|
||||
if err != nil {
|
||||
log.Errorf("Unable to get powchain block %v", err)
|
||||
}
|
||||
|
||||
if !exists {
|
||||
log.Error("Latest ETH1 block doesn't exist in the pow chain")
|
||||
return nil
|
||||
}
|
||||
|
||||
s.depositCache.PrunePendingDeposits(ctx, int(finalizedState.Eth1DepositIndex))
|
||||
|
||||
if err := s.db.UpdateChainHead(ctx, finalizedBlock, finalizedState); err != nil {
|
||||
log.Errorf("Could not update chain head: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
validators.InitializeValidatorStore(finalizedState)
|
||||
|
||||
s.stateReceived = true
|
||||
log.Debugf(
|
||||
"Successfully saved beacon state with the last finalized slot: %d",
|
||||
finalizedState.Slot,
|
||||
)
|
||||
log.WithField("peer", msg.Peer.Pretty()).Info("Requesting batch blocks from peer")
|
||||
s.requestBatchedBlocks(ctx, finalizedBlockRoot[:], chainHead.CanonicalBlockRoot, msg.Peer)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// requestStateFromPeer requests for the canonical state, finalized state, and justified state from a peer.
|
||||
// DEPRECATED: Use github.com/prysmaticlabs/prysm/sync/initial-sync
|
||||
func (s *InitialSync) requestStateFromPeer(ctx context.Context, lastFinalizedRoot [32]byte, peer peer.ID) error {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon-chain.sync.initial-sync.requestStateFromPeer")
|
||||
defer span.End()
|
||||
stateReq.Inc()
|
||||
_, err := s.p2p.Send(ctx, &pb.BeaconStateRequest{
|
||||
FinalizedStateRootHash32S: lastFinalizedRoot[:],
|
||||
}, peer)
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// Metrics
|
||||
batchedBlockReq = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "regsync_batched_block_req",
|
||||
Help: "The number of received batch block requests",
|
||||
})
|
||||
blockReqHash = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "regsync_block_req_by_hash",
|
||||
Help: "The number of received block requests by hash",
|
||||
})
|
||||
recBlock = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "regsync_received_blocks",
|
||||
Help: "The number of received blocks",
|
||||
})
|
||||
forkedBlock = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "regsync_received_forked_blocks",
|
||||
Help: "The number of received forked blocks",
|
||||
})
|
||||
recBlockAnnounce = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "regsync_received_block_announce",
|
||||
Help: "The number of received block announcements",
|
||||
})
|
||||
sentBlockAnnounce = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "regsync_sent_block_announce",
|
||||
Help: "The number of sent block announcements",
|
||||
})
|
||||
sentBlockReq = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "regsync_sent_block_request",
|
||||
Help: "The number of sent block request",
|
||||
})
|
||||
sentBlocks = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "regsync_sent_blocks",
|
||||
Help: "The number of sent blocks",
|
||||
})
|
||||
sentBatchedBlocks = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "regsync_sent_batched_blocks",
|
||||
Help: "The number of sent batched blocks",
|
||||
})
|
||||
stateReq = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "regsync_state_req",
|
||||
Help: "The number of state requests",
|
||||
})
|
||||
sentState = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "regsync_sent_state",
|
||||
Help: "The number of sent state",
|
||||
})
|
||||
recAttestation = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "regsync_received_attestation",
|
||||
Help: "The number of received attestations",
|
||||
})
|
||||
sentAttestation = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "regsync_sent_attestation",
|
||||
Help: "The number of sent attestations",
|
||||
})
|
||||
recExit = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "regsync_received_exits",
|
||||
Help: "The number of received exits",
|
||||
})
|
||||
sentExit = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "regsync_sent_exits",
|
||||
Help: "The number of sent exits",
|
||||
})
|
||||
chainHeadReq = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "regsync_chain_head_req",
|
||||
Help: "The number of sent attestation requests",
|
||||
})
|
||||
sentChainHead = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Name: "regsync_chain_head_sent",
|
||||
Help: "The number of sent chain head responses",
|
||||
})
|
||||
)
|
||||
@@ -1,247 +0,0 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
deprecatedp2p "github.com/prysmaticlabs/prysm/shared/deprecated-p2p"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var queryLog = logrus.WithField("prefix", "syncQuerier")
|
||||
var logQueryInterval = 1 * time.Second
|
||||
|
||||
type powChainService interface {
|
||||
HasChainStarted() bool
|
||||
BlockExists(ctx context.Context, hash common.Hash) (bool, *big.Int, error)
|
||||
ChainStartFeed() *event.Feed
|
||||
AreAllDepositsProcessed() (bool, error)
|
||||
}
|
||||
|
||||
// QuerierConfig defines the configurable properties of SyncQuerier.
|
||||
type QuerierConfig struct {
|
||||
ResponseBufferSize int
|
||||
P2P p2pAPI
|
||||
BeaconDB *db.BeaconDB
|
||||
PowChain powChainService
|
||||
CurrentHeadSlot uint64
|
||||
ChainService chainService
|
||||
}
|
||||
|
||||
// DefaultQuerierConfig provides the default configuration for a sync service.
|
||||
// ResponseBufferSize determines that buffer size of the `responseBuf` channel.
|
||||
func DefaultQuerierConfig() *QuerierConfig {
|
||||
return &QuerierConfig{
|
||||
ResponseBufferSize: params.BeaconConfig().DefaultBufferSize,
|
||||
}
|
||||
}
|
||||
|
||||
// Querier defines the main class in this package.
|
||||
// See the package comments for a general description of the service's functions.
|
||||
type Querier struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
p2p p2pAPI
|
||||
db *db.BeaconDB
|
||||
chainService chainService
|
||||
currentHeadSlot uint64
|
||||
currentStateRoot []byte
|
||||
currentFinalizedStateRoot [32]byte
|
||||
responseBuf chan deprecatedp2p.Message
|
||||
chainStartBuf chan time.Time
|
||||
powchain powChainService
|
||||
chainStarted bool
|
||||
atGenesis bool
|
||||
bestPeer peer.ID
|
||||
chainHeadResponses map[peer.ID]*pb.ChainHeadResponse
|
||||
canonicalBlockRoot []byte
|
||||
finalizedBlockRoot []byte
|
||||
}
|
||||
|
||||
// NewQuerierService constructs a new Sync Querier Service.
|
||||
// This method is normally called by the main node.
|
||||
func NewQuerierService(ctx context.Context,
|
||||
cfg *QuerierConfig,
|
||||
) *Querier {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
responseBuf := make(chan deprecatedp2p.Message, cfg.ResponseBufferSize)
|
||||
|
||||
return &Querier{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
p2p: cfg.P2P,
|
||||
db: cfg.BeaconDB,
|
||||
chainService: cfg.ChainService,
|
||||
responseBuf: responseBuf,
|
||||
currentHeadSlot: cfg.CurrentHeadSlot,
|
||||
chainStarted: false,
|
||||
atGenesis: true,
|
||||
powchain: cfg.PowChain,
|
||||
chainStartBuf: make(chan time.Time, 1),
|
||||
chainHeadResponses: make(map[peer.ID]*pb.ChainHeadResponse),
|
||||
}
|
||||
}
|
||||
|
||||
// Start begins the goroutine.
|
||||
func (q *Querier) Start() {
|
||||
q.waitForAllDepositsToBeProcessed()
|
||||
hasChainStarted := q.powchain.HasChainStarted()
|
||||
|
||||
q.chainStarted = hasChainStarted
|
||||
q.atGenesis = !hasChainStarted
|
||||
|
||||
bState, err := q.db.HeadState(q.ctx)
|
||||
if err != nil {
|
||||
queryLog.Errorf("Unable to retrieve beacon state %v", err)
|
||||
}
|
||||
|
||||
// we handle both the cases where either chainstart has not occurred or
|
||||
// if beacon state has been initialized. If chain start has occurred but
|
||||
// beacon state has not been initialized we wait for the POW chain service
|
||||
// to accumulate all the deposits and process them.
|
||||
if !hasChainStarted || bState == nil {
|
||||
q.listenForStateInitialization()
|
||||
|
||||
// Return, if the node is at genesis.
|
||||
if q.atGenesis {
|
||||
return
|
||||
}
|
||||
}
|
||||
q.run()
|
||||
}
|
||||
|
||||
// Stop kills the sync querier goroutine.
|
||||
func (q *Querier) Stop() error {
|
||||
queryLog.Info("Stopping service")
|
||||
q.cancel()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *Querier) listenForStateInitialization() {
|
||||
sub := q.chainService.StateInitializedFeed().Subscribe(q.chainStartBuf)
|
||||
defer sub.Unsubscribe()
|
||||
for {
|
||||
select {
|
||||
case <-q.chainStartBuf:
|
||||
queryLog.Info("State has been initialized")
|
||||
q.chainStarted = true
|
||||
return
|
||||
case <-sub.Err():
|
||||
log.Fatal("Subscriber closed, unable to continue on with sync")
|
||||
return
|
||||
case <-q.ctx.Done():
|
||||
log.Debug("RPC context closed, exiting goroutine")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Querier) run() {
|
||||
responseSub := q.p2p.Subscribe(&pb.ChainHeadResponse{}, q.responseBuf)
|
||||
// Ticker so that service will keep on requesting for chain head
|
||||
// until they get a response.
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
|
||||
defer func() {
|
||||
responseSub.Unsubscribe()
|
||||
close(q.responseBuf)
|
||||
ticker.Stop()
|
||||
}()
|
||||
|
||||
queryLog.Info("Polling peers for latest chain head...")
|
||||
hasReceivedResponse := false
|
||||
var timeout <-chan time.Time
|
||||
for {
|
||||
select {
|
||||
case <-q.ctx.Done():
|
||||
queryLog.Info("Finished querying state of the network, importing blocks...")
|
||||
return
|
||||
case <-ticker.C:
|
||||
q.RequestLatestHead()
|
||||
case <-timeout:
|
||||
queryLog.WithField("peerID", q.bestPeer.Pretty()).Info("Peer with highest canonical head")
|
||||
queryLog.Infof(
|
||||
"Latest chain head is at slot: %d and state root: %#x",
|
||||
q.currentHeadSlot, q.currentStateRoot,
|
||||
)
|
||||
ticker.Stop()
|
||||
responseSub.Unsubscribe()
|
||||
q.cancel()
|
||||
case msg := <-q.responseBuf:
|
||||
// If this is the first response a node receives, we start
|
||||
// a timeout that will keep listening for more responses over a
|
||||
// certain time interval to ensure we get the best head from our peers.
|
||||
if !hasReceivedResponse {
|
||||
timeout = time.After(10 * time.Second)
|
||||
hasReceivedResponse = true
|
||||
}
|
||||
response := msg.Data.(*pb.ChainHeadResponse)
|
||||
if _, ok := q.chainHeadResponses[msg.Peer]; !ok {
|
||||
queryLog.WithFields(logrus.Fields{
|
||||
"peerID": msg.Peer.Pretty(),
|
||||
"highestSlot": response.CanonicalSlot,
|
||||
}).Info("Received chain head from peer")
|
||||
q.chainHeadResponses[msg.Peer] = response
|
||||
}
|
||||
if response.CanonicalSlot > q.currentHeadSlot {
|
||||
q.bestPeer = msg.Peer
|
||||
q.currentHeadSlot = response.CanonicalSlot
|
||||
q.currentStateRoot = response.CanonicalStateRootHash32
|
||||
q.currentFinalizedStateRoot = bytesutil.ToBytes32(response.FinalizedStateRootHash32S)
|
||||
q.canonicalBlockRoot = response.CanonicalBlockRoot
|
||||
q.finalizedBlockRoot = response.FinalizedBlockRoot
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Querier) waitForAllDepositsToBeProcessed() {
|
||||
for {
|
||||
processed, err := q.powchain.AreAllDepositsProcessed()
|
||||
if err != nil {
|
||||
queryLog.Errorf("Could not check status of deposits %v", err)
|
||||
continue
|
||||
}
|
||||
if processed {
|
||||
break
|
||||
}
|
||||
time.Sleep(logQueryInterval)
|
||||
}
|
||||
}
|
||||
|
||||
// RequestLatestHead broadcasts a request for
|
||||
// the latest chain head slot and state root to a peer.
|
||||
func (q *Querier) RequestLatestHead() {
|
||||
request := &pb.ChainHeadRequest{}
|
||||
q.p2p.Broadcast(context.Background(), request)
|
||||
}
|
||||
|
||||
// IsSynced checks if the node is currently synced with the
|
||||
// rest of the network.
|
||||
func (q *Querier) IsSynced() (bool, error) {
|
||||
if !q.chainStarted {
|
||||
return true, nil
|
||||
}
|
||||
if q.atGenesis {
|
||||
return true, nil
|
||||
}
|
||||
block, err := q.db.ChainHead()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if block == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return block.Slot >= q.currentHeadSlot, nil
|
||||
}
|
||||
@@ -1,319 +0,0 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/internal"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
p2p "github.com/prysmaticlabs/prysm/shared/deprecated-p2p"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
)
|
||||
|
||||
type genesisPowChain struct {
|
||||
feed *event.Feed
|
||||
depositsProcessed bool
|
||||
}
|
||||
|
||||
func (mp *genesisPowChain) HasChainStarted() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (mp *genesisPowChain) BlockExists(ctx context.Context, hash common.Hash) (bool, *big.Int, error) {
|
||||
return true, big.NewInt(0), nil
|
||||
}
|
||||
|
||||
func (mp *genesisPowChain) ChainStartFeed() *event.Feed {
|
||||
return mp.feed
|
||||
}
|
||||
|
||||
func (mp *genesisPowChain) AreAllDepositsProcessed() (bool, error) {
|
||||
return mp.depositsProcessed, nil
|
||||
}
|
||||
|
||||
type afterGenesisPowChain struct {
|
||||
feed *event.Feed
|
||||
}
|
||||
|
||||
func (mp *afterGenesisPowChain) HasChainStarted() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (mp *afterGenesisPowChain) BlockExists(ctx context.Context, hash common.Hash) (bool, *big.Int, error) {
|
||||
return true, big.NewInt(0), nil
|
||||
}
|
||||
|
||||
func (mp *afterGenesisPowChain) ChainStartFeed() *event.Feed {
|
||||
return mp.feed
|
||||
}
|
||||
|
||||
func (mp *afterGenesisPowChain) AreAllDepositsProcessed() (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func TestQuerier_StartStop(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
cfg := &QuerierConfig{
|
||||
P2P: &mockP2P{},
|
||||
ResponseBufferSize: 100,
|
||||
PowChain: &afterGenesisPowChain{},
|
||||
BeaconDB: db,
|
||||
ChainService: &mockChainService{},
|
||||
}
|
||||
sq := NewQuerierService(context.Background(), cfg)
|
||||
|
||||
exitRoutine := make(chan bool)
|
||||
|
||||
defer func() {
|
||||
close(exitRoutine)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
sq.Start()
|
||||
exitRoutine <- true
|
||||
}()
|
||||
|
||||
sq.Stop()
|
||||
<-exitRoutine
|
||||
|
||||
testutil.AssertLogsContain(t, hook, "Stopping service")
|
||||
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
func TestListenForStateInitialization_ContextCancelled(t *testing.T) {
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
cfg := &QuerierConfig{
|
||||
P2P: &mockP2P{},
|
||||
ResponseBufferSize: 100,
|
||||
ChainService: &mockChainService{},
|
||||
BeaconDB: db,
|
||||
}
|
||||
sq := NewQuerierService(context.Background(), cfg)
|
||||
exitRoutine := make(chan bool)
|
||||
|
||||
defer func() {
|
||||
close(exitRoutine)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
sq.listenForStateInitialization()
|
||||
exitRoutine <- true
|
||||
}()
|
||||
|
||||
sq.cancel()
|
||||
<-exitRoutine
|
||||
|
||||
if sq.ctx.Done() == nil {
|
||||
t.Error("Despite context being canceled, the done channel is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListenForStateInitialization(t *testing.T) {
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
cfg := &QuerierConfig{
|
||||
P2P: &mockP2P{},
|
||||
ResponseBufferSize: 100,
|
||||
ChainService: &mockChainService{},
|
||||
BeaconDB: db,
|
||||
}
|
||||
sq := NewQuerierService(context.Background(), cfg)
|
||||
|
||||
sq.chainStartBuf <- time.Now()
|
||||
sq.listenForStateInitialization()
|
||||
|
||||
if !sq.chainStarted {
|
||||
t.Fatal("ChainStart in the querier service is not true despite the log being fired")
|
||||
}
|
||||
sq.cancel()
|
||||
}
|
||||
|
||||
func TestQuerier_ChainReqResponse(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
cfg := &QuerierConfig{
|
||||
P2P: &mockP2P{},
|
||||
ResponseBufferSize: 100,
|
||||
PowChain: &afterGenesisPowChain{},
|
||||
}
|
||||
sq := NewQuerierService(context.Background(), cfg)
|
||||
|
||||
exitRoutine := make(chan bool)
|
||||
go func() {
|
||||
sq.run()
|
||||
exitRoutine <- true
|
||||
}()
|
||||
|
||||
response := &pb.ChainHeadResponse{
|
||||
CanonicalSlot: 1,
|
||||
CanonicalStateRootHash32: []byte{'a', 'b'},
|
||||
}
|
||||
|
||||
msg := p2p.Message{
|
||||
Data: response,
|
||||
}
|
||||
|
||||
sq.responseBuf <- msg
|
||||
|
||||
expMsg := fmt.Sprintf(
|
||||
"Latest chain head is at slot: %d and state root: %#x",
|
||||
response.CanonicalSlot, response.CanonicalStateRootHash32,
|
||||
)
|
||||
|
||||
<-exitRoutine
|
||||
testutil.AssertLogsContain(t, hook, expMsg)
|
||||
close(exitRoutine)
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
func TestQuerier_BestPeerAssignment(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
cfg := &QuerierConfig{
|
||||
P2P: &mockP2P{},
|
||||
ResponseBufferSize: 100,
|
||||
PowChain: &afterGenesisPowChain{},
|
||||
}
|
||||
sq := NewQuerierService(context.Background(), cfg)
|
||||
|
||||
exitRoutine := make(chan bool)
|
||||
go func() {
|
||||
sq.run()
|
||||
exitRoutine <- true
|
||||
}()
|
||||
|
||||
response := &pb.ChainHeadResponse{
|
||||
CanonicalSlot: 1,
|
||||
CanonicalStateRootHash32: []byte{'a', 'b'},
|
||||
}
|
||||
|
||||
msg := p2p.Message{
|
||||
Data: response,
|
||||
Peer: "TestQuerier_BestPeerAssignment",
|
||||
}
|
||||
|
||||
sq.responseBuf <- msg
|
||||
|
||||
<-exitRoutine
|
||||
testutil.AssertLogsContain(t, hook, "level=info msg=\"Peer with highest canonical head\" peerID=HupjP1BPtXeX766WHAeYyATx9MJ3RFe5MZCwC3UEw")
|
||||
|
||||
close(exitRoutine)
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
func TestSyncedInGenesis(t *testing.T) {
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
cfg := &QuerierConfig{
|
||||
P2P: &mockP2P{},
|
||||
ResponseBufferSize: 100,
|
||||
ChainService: &mockChainService{},
|
||||
BeaconDB: db,
|
||||
PowChain: &genesisPowChain{depositsProcessed: true},
|
||||
}
|
||||
sq := NewQuerierService(context.Background(), cfg)
|
||||
|
||||
sq.chainStartBuf <- time.Now()
|
||||
sq.Start()
|
||||
|
||||
synced, err := sq.IsSynced()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to check if the node is synced")
|
||||
}
|
||||
if !synced {
|
||||
t.Errorf("node is not synced when it is supposed to be")
|
||||
}
|
||||
sq.cancel()
|
||||
}
|
||||
|
||||
func TestSyncedInRestarts(t *testing.T) {
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
cfg := &QuerierConfig{
|
||||
P2P: &mockP2P{},
|
||||
ResponseBufferSize: 100,
|
||||
ChainService: &mockChainService{},
|
||||
BeaconDB: db,
|
||||
PowChain: &afterGenesisPowChain{},
|
||||
}
|
||||
sq := NewQuerierService(context.Background(), cfg)
|
||||
|
||||
bState := &pb.BeaconState{Slot: 0}
|
||||
blk := ðpb.BeaconBlock{Slot: 0}
|
||||
if err := db.SaveStateDeprecated(context.Background(), bState); err != nil {
|
||||
t.Fatalf("Could not save state: %v", err)
|
||||
}
|
||||
if err := db.SaveBlockDeprecated(blk); err != nil {
|
||||
t.Fatalf("Could not save state: %v", err)
|
||||
}
|
||||
if err := db.UpdateChainHead(context.Background(), blk, bState); err != nil {
|
||||
t.Fatalf("Could not update chainhead: %v", err)
|
||||
}
|
||||
|
||||
exitRoutine := make(chan bool)
|
||||
go func() {
|
||||
sq.Start()
|
||||
exitRoutine <- true
|
||||
}()
|
||||
|
||||
response := &pb.ChainHeadResponse{
|
||||
CanonicalSlot: 10,
|
||||
CanonicalStateRootHash32: []byte{'a', 'b'},
|
||||
}
|
||||
|
||||
msg := p2p.Message{
|
||||
Data: response,
|
||||
}
|
||||
|
||||
sq.responseBuf <- msg
|
||||
|
||||
<-exitRoutine
|
||||
|
||||
synced, err := sq.IsSynced()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to check if the node is synced; %v", err)
|
||||
}
|
||||
if synced {
|
||||
t.Errorf("node is synced when it is not supposed to be in a restart")
|
||||
}
|
||||
sq.cancel()
|
||||
}
|
||||
|
||||
func TestWaitForDepositsProcessed_OK(t *testing.T) {
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
powchain := &genesisPowChain{depositsProcessed: false}
|
||||
cfg := &QuerierConfig{
|
||||
P2P: &mockP2P{},
|
||||
ResponseBufferSize: 100,
|
||||
ChainService: &mockChainService{},
|
||||
BeaconDB: db,
|
||||
PowChain: powchain,
|
||||
}
|
||||
sq := NewQuerierService(context.Background(), cfg)
|
||||
|
||||
sq.chainStartBuf <- time.Now()
|
||||
exitRoutine := make(chan bool)
|
||||
go func() {
|
||||
sq.waitForAllDepositsToBeProcessed()
|
||||
exitRoutine <- true
|
||||
}()
|
||||
if len(exitRoutine) == 1 {
|
||||
t.Fatal("Deposits processed despite not being ready")
|
||||
}
|
||||
|
||||
powchain.depositsProcessed = true
|
||||
<-exitRoutine
|
||||
|
||||
sq.cancel()
|
||||
close(exitRoutine)
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
p2p "github.com/prysmaticlabs/prysm/shared/deprecated-p2p"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
// receiveBlockAnnounce accepts a block hash, determines if we do not contain
|
||||
// the block in our local DB, and then request the full block data.
|
||||
func (rs *RegularSync) receiveBlockAnnounce(msg p2p.Message) error {
|
||||
ctx, span := trace.StartSpan(msg.Ctx, "beacon-chain.sync.receiveBlockAnnounce")
|
||||
defer span.End()
|
||||
recBlockAnnounce.Inc()
|
||||
|
||||
data := msg.Data.(*pb.BeaconBlockAnnounce)
|
||||
h := bytesutil.ToBytes32(data.Hash[:32])
|
||||
|
||||
isEvilBlock := rs.db.IsEvilBlockHash(h)
|
||||
span.AddAttributes(trace.BoolAttribute("isEvilBlock", isEvilBlock))
|
||||
|
||||
if isEvilBlock {
|
||||
log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(h[:]))).
|
||||
Debug("Received blacklisted block")
|
||||
return nil
|
||||
}
|
||||
|
||||
// This prevents us from processing a block announcement we have already received.
|
||||
// TODO(#2072): If the peer failed to give the block, broadcast request to the whole network.
|
||||
rs.blockAnnouncementsLock.Lock()
|
||||
defer rs.blockAnnouncementsLock.Unlock()
|
||||
if _, ok := rs.blockAnnouncements[data.SlotNumber]; ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
hasBlock := rs.db.HasBlockDeprecated(h)
|
||||
span.AddAttributes(trace.BoolAttribute("hasBlock", hasBlock))
|
||||
|
||||
if hasBlock {
|
||||
log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(h[:]))).Debug("Already processed")
|
||||
return nil
|
||||
}
|
||||
|
||||
log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(h[:]))).Debug("Received incoming block root, requesting full block data from sender")
|
||||
// Request the full block data from peer that sent the block hash.
|
||||
if _, err := rs.p2p.Send(ctx, &pb.BeaconBlockRequest{Hash: h[:]}, msg.Peer); err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
rs.blockAnnouncements[data.SlotNumber] = data.Hash
|
||||
sentBlockReq.Inc()
|
||||
return nil
|
||||
}
|
||||
|
||||
// receiveBlock processes a block message from the p2p layer and requests for its
|
||||
// parents recursively if they are not yet contained in the local node's persistent storage.
|
||||
func (rs *RegularSync) receiveBlock(msg p2p.Message) error {
|
||||
ctx, span := trace.StartSpan(msg.Ctx, "beacon-chain.sync.receiveBlock")
|
||||
defer span.End()
|
||||
recBlock.Inc()
|
||||
rs.blockProcessingLock.Lock()
|
||||
defer rs.blockProcessingLock.Unlock()
|
||||
return rs.processBlockAndFetchAncestors(ctx, msg)
|
||||
}
|
||||
|
||||
// processBlockAndFetchAncestors verifies if a block has a child in the pending blocks map - if so, then
|
||||
// we recursively call processBlock which applies block state transitions and updates the chain service.
|
||||
// At the end of the recursive call, we'll have a block which has no children in the map, and at that point
|
||||
// we can apply the fork choice rule for ETH 2.0.
|
||||
func (rs *RegularSync) processBlockAndFetchAncestors(ctx context.Context, msg p2p.Message) error {
|
||||
block, _, isValid, err := rs.validateAndProcessBlock(ctx, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isValid {
|
||||
return nil
|
||||
}
|
||||
|
||||
blockRoot, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if rs.db.IsEvilBlockHash(blockRoot) {
|
||||
log.WithField("blockRoot", bytesutil.Trunc(blockRoot[:])).Debug("Skipping blacklisted block")
|
||||
return nil
|
||||
}
|
||||
|
||||
// If the block has a child, we then clear it from the blocks pending processing
|
||||
// and call receiveBlock recursively. The recursive function call will stop once
|
||||
// the block we process no longer has children.
|
||||
if child, ok := rs.hasChild(blockRoot); ok {
|
||||
// We clear the block root from the pending processing map.
|
||||
rs.clearPendingBlock(blockRoot)
|
||||
return rs.processBlockAndFetchAncestors(ctx, child)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rs *RegularSync) validateAndProcessBlock(
|
||||
ctx context.Context, blockMsg p2p.Message,
|
||||
) (*ethpb.BeaconBlock, *pb.BeaconState, bool, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon-chain.sync.validateAndProcessBlock")
|
||||
defer span.End()
|
||||
|
||||
response := blockMsg.Data.(*pb.BeaconBlockResponse)
|
||||
block := response.Block
|
||||
blockRoot, err := ssz.SigningRoot(block)
|
||||
if err != nil {
|
||||
log.Errorf("Could not hash received block: %v", err)
|
||||
span.AddAttributes(trace.BoolAttribute("invalidBlock", true))
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(blockRoot[:]))).
|
||||
Debug("Processing response to block request")
|
||||
hasBlock := rs.db.HasBlockDeprecated(blockRoot)
|
||||
if hasBlock {
|
||||
log.Debug("Received a block that already exists. Exiting...")
|
||||
span.AddAttributes(trace.BoolAttribute("invalidBlock", true))
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
beaconState, err := rs.db.HeadState(ctx)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to get beacon state: %v", err)
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
finalizedSlot := helpers.StartSlot(beaconState.FinalizedCheckpoint.Epoch)
|
||||
slot := block.Slot
|
||||
span.AddAttributes(
|
||||
trace.Int64Attribute("block.Slot", int64(slot)),
|
||||
trace.Int64Attribute("finalized slot", int64(finalizedSlot)),
|
||||
)
|
||||
if block.Slot < beaconState.FinalizedCheckpoint.Epoch*params.BeaconConfig().SlotsPerEpoch {
|
||||
log.Debug("Discarding received block with a slot number smaller than the last finalized slot")
|
||||
span.AddAttributes(trace.BoolAttribute("invalidBlock", true))
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
// We check if we have the block's parents saved locally.
|
||||
parentRoot := bytesutil.ToBytes32(block.ParentRoot)
|
||||
hasParent := rs.db.HasBlockDeprecated(parentRoot)
|
||||
span.AddAttributes(trace.BoolAttribute("hasParent", hasParent))
|
||||
|
||||
if !hasParent {
|
||||
// If we do not have the parent, we insert it into a pending block's map.
|
||||
rs.insertPendingBlock(ctx, parentRoot, blockMsg)
|
||||
// We update the last observed slot to the received canonical block's slot.
|
||||
if block.Slot > rs.highestObservedSlot {
|
||||
rs.highestObservedSlot = block.Slot
|
||||
}
|
||||
return nil, nil, false, nil
|
||||
}
|
||||
|
||||
log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(blockRoot[:]))).Debug(
|
||||
"Sending newly received block to chain service")
|
||||
// We then process the block by passing it through the ChainService and running
|
||||
// a fork choice rule.
|
||||
beaconState, err = rs.chainService.ReceiveBlockDeprecated(ctx, block)
|
||||
if err != nil {
|
||||
log.Errorf("Could not process beacon block: %v", err)
|
||||
span.AddAttributes(trace.BoolAttribute("invalidBlock", true))
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
head, err := rs.db.ChainHead()
|
||||
if err != nil {
|
||||
log.Errorf("Could not retrieve chainhead %v", err)
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
headRoot, err := ssz.SigningRoot(head)
|
||||
if err != nil {
|
||||
log.Errorf("Could not hash head block: %v", err)
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
if headRoot != bytesutil.ToBytes32(block.ParentRoot) {
|
||||
// Save historical state from forked block.
|
||||
forkedBlock.Inc()
|
||||
log.WithFields(logrus.Fields{
|
||||
"slot": block.Slot,
|
||||
"root": fmt.Sprintf("%#x", bytesutil.Trunc(blockRoot[:]))},
|
||||
).Warn("Received BlockDeprecated from a forked chain")
|
||||
if err := rs.db.SaveHistoricalState(ctx, beaconState, blockRoot); err != nil {
|
||||
log.Errorf("Could not save historical state %v", err)
|
||||
return nil, nil, false, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := rs.chainService.ApplyForkChoiceRuleDeprecated(ctx, block, beaconState); err != nil {
|
||||
log.WithError(err).Error("Could not run fork choice on block")
|
||||
return nil, nil, false, err
|
||||
}
|
||||
sentBlocks.Inc()
|
||||
// We update the last observed slot to the received canonical block's slot.
|
||||
if block.Slot > rs.highestObservedSlot {
|
||||
rs.highestObservedSlot = block.Slot
|
||||
}
|
||||
span.AddAttributes(trace.Int64Attribute("highestObservedSlot", int64(rs.highestObservedSlot)))
|
||||
return block, beaconState, true, nil
|
||||
}
|
||||
|
||||
func (rs *RegularSync) insertPendingBlock(ctx context.Context, blockRoot [32]byte, blockMsg p2p.Message) {
|
||||
rs.blocksAwaitingProcessingLock.Lock()
|
||||
defer rs.blocksAwaitingProcessingLock.Unlock()
|
||||
// Do not reinsert into the map if block root was previously added.
|
||||
if _, ok := rs.blocksAwaitingProcessing[blockRoot]; ok {
|
||||
return
|
||||
}
|
||||
rs.blocksAwaitingProcessing[blockRoot] = blockMsg
|
||||
blocksAwaitingProcessingGauge.Inc()
|
||||
rs.p2p.Broadcast(ctx, &pb.BeaconBlockRequest{Hash: blockRoot[:]})
|
||||
}
|
||||
|
||||
func (rs *RegularSync) clearPendingBlock(blockRoot [32]byte) {
|
||||
rs.blocksAwaitingProcessingLock.Lock()
|
||||
defer rs.blocksAwaitingProcessingLock.Unlock()
|
||||
delete(rs.blocksAwaitingProcessing, blockRoot)
|
||||
blocksAwaitingProcessingGauge.Dec()
|
||||
}
|
||||
|
||||
func (rs *RegularSync) hasChild(blockRoot [32]byte) (p2p.Message, bool) {
|
||||
rs.blocksAwaitingProcessingLock.Lock()
|
||||
defer rs.blocksAwaitingProcessingLock.Unlock()
|
||||
child, ok := rs.blocksAwaitingProcessing[blockRoot]
|
||||
return child, ok
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/internal"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
p2p "github.com/prysmaticlabs/prysm/shared/deprecated-p2p"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
)
|
||||
|
||||
// totalMissingParents describes the number of missing parent requests we want to test.
|
||||
var totalMissingParents = 50
|
||||
|
||||
func setupBlockParents(t *testing.T, genesisRoot [32]byte) ([]*ethpb.BeaconBlock, [][32]byte) {
|
||||
parents := []*ethpb.BeaconBlock{}
|
||||
parentRoots := [][32]byte{}
|
||||
// Sets up a list of block parents of the form:
|
||||
// Parent 1: {Slot: 1, Parent: genesisBlock},
|
||||
// Parent 2: {Slot: 3, Parent: Parent1},
|
||||
// Parent 3: {Slot: 5, Parent: parent2},
|
||||
// ...
|
||||
for slot := 1; slot < totalMissingParents; slot += 2 {
|
||||
parent := ðpb.BeaconBlock{
|
||||
Slot: uint64(slot),
|
||||
}
|
||||
// At slot 1, the parent is the genesis block.
|
||||
if slot == 1 {
|
||||
parent.ParentRoot = genesisRoot[:]
|
||||
} else {
|
||||
parent.ParentRoot = parentRoots[len(parentRoots)-1][:]
|
||||
}
|
||||
parentRoot, err := ssz.SigningRoot(parent)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
parents = append(parents, parent)
|
||||
parentRoots = append(parentRoots, parentRoot)
|
||||
}
|
||||
return parents, parentRoots
|
||||
}
|
||||
|
||||
func setupBlocksMissingParent(parents []*ethpb.BeaconBlock, parentRoots [][32]byte) []*ethpb.BeaconBlock {
|
||||
blocksMissingParent := []*ethpb.BeaconBlock{}
|
||||
// Sets up a list of block with missing parents of the form:
|
||||
// Parent 1: {Slot: 6, Parent: parents[0]},
|
||||
// Parent 2: {Slot: 4, Parent: parents[1]},
|
||||
// Parent 3: {Slot: 2, Parent: parents[2]},
|
||||
// ...
|
||||
for slot := parents[len(parents)-1].Slot + 1; slot >= 2; slot -= 2 {
|
||||
blocksMissingParent = append(blocksMissingParent, ðpb.BeaconBlock{
|
||||
Slot: slot,
|
||||
})
|
||||
}
|
||||
for i := range parentRoots {
|
||||
blocksMissingParent[i].ParentRoot = parentRoots[i][:]
|
||||
}
|
||||
return blocksMissingParent
|
||||
}
|
||||
|
||||
func TestReceiveBlockAnnounce_SkipsBlacklistedBlock(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
|
||||
rsCfg := DefaultRegularSyncConfig()
|
||||
rsCfg.ChainService = &mockChainService{
|
||||
db: db,
|
||||
}
|
||||
rsCfg.BeaconDB = db
|
||||
rs := NewRegularSyncService(context.Background(), rsCfg)
|
||||
evilBlockHash := []byte("evil-block")
|
||||
blockRoot := bytesutil.ToBytes32(evilBlockHash)
|
||||
db.MarkEvilBlockHash(blockRoot)
|
||||
msg := p2p.Message{
|
||||
Ctx: context.Background(),
|
||||
Data: &pb.BeaconBlockAnnounce{
|
||||
Hash: blockRoot[:],
|
||||
},
|
||||
}
|
||||
if err := rs.receiveBlockAnnounce(msg); err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
testutil.AssertLogsContain(t, hook, "Received blacklisted block")
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
func TestReceiveBlock_RecursivelyProcessesChildren(t *testing.T) {
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
ctx := context.Background()
|
||||
|
||||
rsCfg := DefaultRegularSyncConfig()
|
||||
rsCfg.ChainService = &mockChainService{
|
||||
db: db,
|
||||
}
|
||||
rsCfg.BeaconDB = db
|
||||
rsCfg.P2P = &mockP2P{}
|
||||
rs := NewRegularSyncService(context.Background(), rsCfg)
|
||||
genesisBlock := ðpb.BeaconBlock{
|
||||
Slot: 0,
|
||||
}
|
||||
genesisRoot, err := ssz.SigningRoot(genesisBlock)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
genesisState := &pb.BeaconState{
|
||||
Slot: 0,
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 0},
|
||||
}
|
||||
if err := db.SaveBlockDeprecated(genesisBlock); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.SaveStateDeprecated(ctx, genesisState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.UpdateChainHead(ctx, genesisBlock, genesisState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
parents, parentRoots := setupBlockParents(t, genesisRoot)
|
||||
blocksMissingParent := setupBlocksMissingParent(parents, parentRoots)
|
||||
|
||||
for _, block := range blocksMissingParent {
|
||||
msg := p2p.Message{
|
||||
Data: &pb.BeaconBlockResponse{
|
||||
Block: block,
|
||||
},
|
||||
Ctx: context.Background(),
|
||||
}
|
||||
if err := rs.receiveBlock(msg); err != nil {
|
||||
t.Fatalf("Could not receive block: %v", err)
|
||||
}
|
||||
}
|
||||
if len(rs.blocksAwaitingProcessing) != len(blocksMissingParent) {
|
||||
t.Errorf(
|
||||
"Expected blocks awaiting processing map len = %d, received len = %d",
|
||||
len(blocksMissingParent),
|
||||
len(rs.blocksAwaitingProcessing),
|
||||
)
|
||||
}
|
||||
for _, block := range parents {
|
||||
msg := p2p.Message{
|
||||
Data: &pb.BeaconBlockResponse{
|
||||
Block: block,
|
||||
},
|
||||
Ctx: context.Background(),
|
||||
}
|
||||
if err := rs.receiveBlock(msg); err != nil {
|
||||
t.Fatalf("Could not receive block: %v", err)
|
||||
}
|
||||
}
|
||||
if len(rs.blocksAwaitingProcessing) > 0 {
|
||||
t.Errorf("Expected blocks awaiting processing map to be empty, received len = %d", len(rs.blocksAwaitingProcessing))
|
||||
}
|
||||
}
|
||||
@@ -1,572 +0,0 @@
|
||||
// Package sync defines the utilities for the beacon-chain to sync with the network.
|
||||
package sync
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
blockchain "github.com/prysmaticlabs/prysm/beacon-chain/deprecated-blockchain"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/operations"
|
||||
p2p "github.com/prysmaticlabs/prysm/beacon-chain/p2p"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
deprecatedp2p "github.com/prysmaticlabs/prysm/shared/deprecated-p2p"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/logutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
var (
|
||||
log = logrus.WithField("prefix", "regular-sync")
|
||||
blocksAwaitingProcessingGauge = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "regsync_blocks_awaiting_processing",
|
||||
Help: "Number of blocks which do not have a parent and are awaiting processing by the chain service",
|
||||
})
|
||||
)
|
||||
|
||||
type chainService interface {
|
||||
blockchain.BlockReceiver
|
||||
blockchain.BlockProcessor
|
||||
blockchain.ForkChoice
|
||||
blockchain.ChainFeeds
|
||||
}
|
||||
|
||||
type attsService interface {
|
||||
IncomingAttestationFeed() *event.Feed
|
||||
}
|
||||
|
||||
type p2pAPI interface {
|
||||
p2p.Broadcaster
|
||||
p2p.Sender
|
||||
p2p.DeprecatedSubscriber
|
||||
}
|
||||
|
||||
// RegularSync is the gateway and the bridge between the p2p network and the local beacon chain.
|
||||
// In broad terms, a new block is synced in 4 steps:
|
||||
// 1. Receive a block hash from a peer
|
||||
// 2. Request the block for the hash from the network
|
||||
// 3. Receive the block
|
||||
// 4. Forward block to the beacon service for full validation
|
||||
//
|
||||
// In addition, RegularSync will handle the following responsibilities:
|
||||
// * Decide which messages are forwarded to other peers
|
||||
// * Filter redundant data and unwanted data
|
||||
// * Drop peers that send invalid data
|
||||
// * Throttle incoming requests
|
||||
type RegularSync struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
p2p p2pAPI
|
||||
chainService chainService
|
||||
attsService attsService
|
||||
operationsService operations.OperationFeeds
|
||||
db *db.BeaconDB
|
||||
blockAnnouncementFeed *event.Feed
|
||||
announceBlockBuf chan deprecatedp2p.Message
|
||||
blockBuf chan deprecatedp2p.Message
|
||||
blockRequestByHash chan deprecatedp2p.Message
|
||||
batchedRequestBuf chan deprecatedp2p.Message
|
||||
stateRequestBuf chan deprecatedp2p.Message
|
||||
chainHeadReqBuf chan deprecatedp2p.Message
|
||||
attestationBuf chan deprecatedp2p.Message
|
||||
exitBuf chan deprecatedp2p.Message
|
||||
canonicalBuf chan *pb.BeaconBlockAnnounce
|
||||
highestObservedSlot uint64
|
||||
blocksAwaitingProcessing map[[32]byte]deprecatedp2p.Message
|
||||
blocksAwaitingProcessingLock sync.RWMutex
|
||||
blockProcessingLock sync.RWMutex
|
||||
blockAnnouncements map[uint64][]byte
|
||||
blockAnnouncementsLock sync.RWMutex
|
||||
}
|
||||
|
||||
// RegularSyncConfig allows the channel's buffer sizes to be changed.
|
||||
type RegularSyncConfig struct {
|
||||
BlockAnnounceBufferSize int
|
||||
BlockBufferSize int
|
||||
BlockReqHashBufferSize int
|
||||
BatchedBufferSize int
|
||||
StateReqBufferSize int
|
||||
AttestationBufferSize int
|
||||
ExitBufferSize int
|
||||
ChainHeadReqBufferSize int
|
||||
CanonicalBufferSize int
|
||||
ChainService chainService
|
||||
OperationService operations.OperationFeeds
|
||||
AttsService attsService
|
||||
BeaconDB *db.BeaconDB
|
||||
P2P p2pAPI
|
||||
}
|
||||
|
||||
// DefaultRegularSyncConfig provides the default configuration for a sync service.
|
||||
func DefaultRegularSyncConfig() *RegularSyncConfig {
|
||||
return &RegularSyncConfig{
|
||||
BlockAnnounceBufferSize: params.BeaconConfig().DefaultBufferSize,
|
||||
BlockBufferSize: params.BeaconConfig().DefaultBufferSize,
|
||||
BlockReqHashBufferSize: params.BeaconConfig().DefaultBufferSize,
|
||||
BatchedBufferSize: params.BeaconConfig().DefaultBufferSize,
|
||||
StateReqBufferSize: params.BeaconConfig().DefaultBufferSize,
|
||||
ChainHeadReqBufferSize: params.BeaconConfig().DefaultBufferSize,
|
||||
AttestationBufferSize: params.BeaconConfig().DefaultBufferSize,
|
||||
ExitBufferSize: params.BeaconConfig().DefaultBufferSize,
|
||||
CanonicalBufferSize: params.BeaconConfig().DefaultBufferSize,
|
||||
}
|
||||
}
|
||||
|
||||
// NewRegularSyncService accepts a context and returns a new Service.
|
||||
func NewRegularSyncService(ctx context.Context, cfg *RegularSyncConfig) *RegularSync {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
return &RegularSync{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
p2p: cfg.P2P,
|
||||
chainService: cfg.ChainService,
|
||||
db: cfg.BeaconDB,
|
||||
operationsService: cfg.OperationService,
|
||||
attsService: cfg.AttsService,
|
||||
blockAnnouncementFeed: new(event.Feed),
|
||||
announceBlockBuf: make(chan deprecatedp2p.Message, cfg.BlockAnnounceBufferSize),
|
||||
blockBuf: make(chan deprecatedp2p.Message, cfg.BlockBufferSize),
|
||||
blockRequestByHash: make(chan deprecatedp2p.Message, cfg.BlockReqHashBufferSize),
|
||||
batchedRequestBuf: make(chan deprecatedp2p.Message, cfg.BatchedBufferSize),
|
||||
stateRequestBuf: make(chan deprecatedp2p.Message, cfg.StateReqBufferSize),
|
||||
attestationBuf: make(chan deprecatedp2p.Message, cfg.AttestationBufferSize),
|
||||
exitBuf: make(chan deprecatedp2p.Message, cfg.ExitBufferSize),
|
||||
chainHeadReqBuf: make(chan deprecatedp2p.Message, cfg.ChainHeadReqBufferSize),
|
||||
canonicalBuf: make(chan *pb.BeaconBlockAnnounce, cfg.CanonicalBufferSize),
|
||||
blocksAwaitingProcessing: make(map[[32]byte]deprecatedp2p.Message),
|
||||
blockAnnouncements: make(map[uint64][]byte),
|
||||
}
|
||||
}
|
||||
|
||||
// Start begins the block processing goroutine.
|
||||
func (rs *RegularSync) Start() {
|
||||
go rs.run()
|
||||
}
|
||||
|
||||
// ResumeSync resumes normal sync after initial sync is complete.
|
||||
func (rs *RegularSync) ResumeSync() {
|
||||
go rs.run()
|
||||
}
|
||||
|
||||
// Stop kills the block processing goroutine, but does not wait until the goroutine exits.
|
||||
func (rs *RegularSync) Stop() error {
|
||||
log.Info("Stopping service")
|
||||
rs.cancel()
|
||||
return nil
|
||||
}
|
||||
|
||||
// BlockAnnouncementFeed returns an event feed processes can subscribe to for
|
||||
// newly received, incoming p2p blocks.
|
||||
func (rs *RegularSync) BlockAnnouncementFeed() *event.Feed {
|
||||
return rs.blockAnnouncementFeed
|
||||
}
|
||||
|
||||
// run handles incoming block sync.
|
||||
func (rs *RegularSync) run() {
|
||||
announceBlockSub := rs.p2p.Subscribe(&pb.BeaconBlockAnnounce{}, rs.announceBlockBuf)
|
||||
blockSub := rs.p2p.Subscribe(&pb.BeaconBlockResponse{}, rs.blockBuf)
|
||||
blockRequestHashSub := rs.p2p.Subscribe(&pb.BeaconBlockRequest{}, rs.blockRequestByHash)
|
||||
batchedBlockRequestSub := rs.p2p.Subscribe(&pb.BatchedBeaconBlockRequest{}, rs.batchedRequestBuf)
|
||||
stateRequestSub := rs.p2p.Subscribe(&pb.BeaconStateRequest{}, rs.stateRequestBuf)
|
||||
attestationSub := rs.p2p.Subscribe(ðpb.Attestation{}, rs.attestationBuf)
|
||||
exitSub := rs.p2p.Subscribe(ðpb.VoluntaryExit{}, rs.exitBuf)
|
||||
chainHeadReqSub := rs.p2p.Subscribe(&pb.ChainHeadRequest{}, rs.chainHeadReqBuf)
|
||||
canonicalBlockSub := rs.chainService.CanonicalBlockFeed().Subscribe(rs.canonicalBuf)
|
||||
|
||||
defer announceBlockSub.Unsubscribe()
|
||||
defer blockSub.Unsubscribe()
|
||||
defer blockRequestHashSub.Unsubscribe()
|
||||
defer batchedBlockRequestSub.Unsubscribe()
|
||||
defer stateRequestSub.Unsubscribe()
|
||||
defer chainHeadReqSub.Unsubscribe()
|
||||
defer attestationSub.Unsubscribe()
|
||||
defer exitSub.Unsubscribe()
|
||||
defer canonicalBlockSub.Unsubscribe()
|
||||
|
||||
log.Info("Listening for regular sync messages from peers")
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-rs.ctx.Done():
|
||||
log.Debug("Exiting goroutine")
|
||||
return
|
||||
case msg := <-rs.announceBlockBuf:
|
||||
go safelyHandleMessage(rs.receiveBlockAnnounce, msg)
|
||||
case msg := <-rs.attestationBuf:
|
||||
go safelyHandleMessage(rs.receiveAttestation, msg)
|
||||
case msg := <-rs.exitBuf:
|
||||
go safelyHandleMessage(rs.receiveExitRequest, msg)
|
||||
case msg := <-rs.blockBuf:
|
||||
go safelyHandleMessage(rs.receiveBlock, msg)
|
||||
case msg := <-rs.blockRequestByHash:
|
||||
go safelyHandleMessage(rs.handleBlockRequestByHash, msg)
|
||||
case msg := <-rs.batchedRequestBuf:
|
||||
go safelyHandleMessage(rs.handleBatchedBlockRequest, msg)
|
||||
case msg := <-rs.stateRequestBuf:
|
||||
go safelyHandleMessage(rs.handleStateRequest, msg)
|
||||
case msg := <-rs.chainHeadReqBuf:
|
||||
go safelyHandleMessage(rs.handleChainHeadRequest, msg)
|
||||
case blockAnnounce := <-rs.canonicalBuf:
|
||||
go rs.broadcastCanonicalBlock(rs.ctx, blockAnnounce)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// safelyHandleMessage will recover and log any panic that occurs from the
|
||||
// function argument.
|
||||
func safelyHandleMessage(fn func(deprecatedp2p.Message) error, msg deprecatedp2p.Message) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
printedMsg := "message contains no data"
|
||||
if msg.Data != nil {
|
||||
printedMsg = proto.MarshalTextString(msg.Data)
|
||||
}
|
||||
log.WithFields(logrus.Fields{
|
||||
"r": r,
|
||||
"msg": printedMsg,
|
||||
}).Error("Panicked when handling p2p message! Recovering...")
|
||||
|
||||
debug.PrintStack()
|
||||
|
||||
if msg.Ctx == nil {
|
||||
return
|
||||
}
|
||||
if span := trace.FromContext(msg.Ctx); span != nil {
|
||||
span.SetStatus(trace.Status{
|
||||
Code: trace.StatusCodeInternal,
|
||||
Message: fmt.Sprintf("Panic: %v", r),
|
||||
})
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// messages received in sync should be processed in a timely manner
|
||||
ctx, cancel := context.WithTimeout(msg.Ctx, 30*time.Second)
|
||||
defer cancel()
|
||||
msg.Ctx = ctx
|
||||
|
||||
// Fingers crossed that it doesn't panic...
|
||||
if err := fn(msg); err != nil {
|
||||
// Report any error to the span, if one exists.
|
||||
if span := trace.FromContext(msg.Ctx); span != nil {
|
||||
span.SetStatus(trace.Status{
|
||||
Code: trace.StatusCodeInternal,
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
log.WithField("method", logutil.FunctionName(fn)).Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (rs *RegularSync) handleStateRequest(msg deprecatedp2p.Message) error {
|
||||
ctx, span := trace.StartSpan(msg.Ctx, "beacon-chain.sync.handleStateRequest")
|
||||
defer span.End()
|
||||
stateReq.Inc()
|
||||
req, ok := msg.Data.(*pb.BeaconStateRequest)
|
||||
if !ok {
|
||||
log.Error("Message is of the incorrect type")
|
||||
return errors.New("incoming message is not *pb.BeaconStateRequest")
|
||||
}
|
||||
fState, err := rs.db.FinalizedState()
|
||||
if err != nil {
|
||||
log.Errorf("Unable to retrieve beacon state, %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
root, err := hashutil.HashProto(fState)
|
||||
if err != nil {
|
||||
log.Errorf("unable to marshal the beacon state: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if root != bytesutil.ToBytes32(req.FinalizedStateRootHash32S) {
|
||||
log.WithFields(logrus.Fields{
|
||||
"requested": fmt.Sprintf("%#x", req.FinalizedStateRootHash32S),
|
||||
"local": fmt.Sprintf("%#x", root)},
|
||||
).Debug("Requested state root is diff than local state root")
|
||||
return err
|
||||
}
|
||||
finalizedBlk, err := rs.db.FinalizedBlock()
|
||||
if err != nil {
|
||||
log.Error("could not get finalized block")
|
||||
return err
|
||||
}
|
||||
|
||||
log.WithField(
|
||||
"beaconState", fmt.Sprintf("%#x", root),
|
||||
).Debug("Sending finalized state and block to peer")
|
||||
defer sentState.Inc()
|
||||
resp := &pb.BeaconStateResponse{
|
||||
FinalizedState: fState,
|
||||
FinalizedBlock: finalizedBlk,
|
||||
}
|
||||
if _, err := rs.p2p.Send(ctx, resp, msg.Peer); err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rs *RegularSync) handleChainHeadRequest(msg deprecatedp2p.Message) error {
|
||||
ctx, span := trace.StartSpan(msg.Ctx, "beacon-chain.sync.handleChainHeadRequest")
|
||||
defer span.End()
|
||||
chainHeadReq.Inc()
|
||||
if _, ok := msg.Data.(*pb.ChainHeadRequest); !ok {
|
||||
log.Error("message is of the incorrect type")
|
||||
return errors.New("incoming message is not *pb.ChainHeadRequest")
|
||||
}
|
||||
|
||||
head, err := rs.db.ChainHead()
|
||||
if err != nil {
|
||||
log.Errorf("Could not retrieve chain head: %v", err)
|
||||
return err
|
||||
}
|
||||
headBlkRoot, err := ssz.SigningRoot(head)
|
||||
if err != nil {
|
||||
log.Errorf("Could not hash chain head: %v", err)
|
||||
}
|
||||
finalizedBlk, err := rs.db.FinalizedBlock()
|
||||
if err != nil {
|
||||
log.Errorf("Could not retrieve finalized block: %v", err)
|
||||
return err
|
||||
}
|
||||
finalizedBlkRoot, err := ssz.SigningRoot(finalizedBlk)
|
||||
if err != nil {
|
||||
log.Errorf("Could not hash finalized block: %v", err)
|
||||
}
|
||||
|
||||
stateRoot := rs.db.HeadStateRoot()
|
||||
finalizedState, err := rs.db.FinalizedState()
|
||||
if err != nil {
|
||||
log.Errorf("Could not retrieve finalized state: %v", err)
|
||||
return err
|
||||
}
|
||||
finalizedRoot, err := hashutil.HashProto(finalizedState)
|
||||
if err != nil {
|
||||
log.Errorf("Could not tree hash block: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
req := &pb.ChainHeadResponse{
|
||||
CanonicalSlot: head.Slot,
|
||||
CanonicalStateRootHash32: stateRoot[:],
|
||||
FinalizedStateRootHash32S: finalizedRoot[:],
|
||||
CanonicalBlockRoot: headBlkRoot[:],
|
||||
FinalizedBlockRoot: finalizedBlkRoot[:],
|
||||
}
|
||||
ctx, ChainHead := trace.StartSpan(ctx, "sendChainHead")
|
||||
defer ChainHead.End()
|
||||
defer sentChainHead.Inc()
|
||||
if _, err := rs.p2p.Send(ctx, req, msg.Peer); err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// receiveAttestation accepts an broadcasted attestation from the p2p layer,
|
||||
// discard the attestation if we have gotten before, send it to attestation
|
||||
// pool if we have not.
|
||||
func (rs *RegularSync) receiveAttestation(msg deprecatedp2p.Message) error {
|
||||
ctx, span := trace.StartSpan(msg.Ctx, "beacon-chain.sync.receiveAttestation")
|
||||
defer span.End()
|
||||
recAttestation.Inc()
|
||||
|
||||
attestation := msg.Data.(*ethpb.Attestation)
|
||||
attestationDataHash, err := hashutil.HashProto(attestation.Data)
|
||||
if err != nil {
|
||||
log.Errorf("Could not hash received attestation: %v", err)
|
||||
return err
|
||||
}
|
||||
log.WithFields(logrus.Fields{
|
||||
"headRoot": fmt.Sprintf("%#x", bytesutil.Trunc(attestation.Data.BeaconBlockRoot)),
|
||||
"justifiedEpoch": attestation.Data.Source.Epoch,
|
||||
}).Debug("Received an attestation")
|
||||
|
||||
// Skip if attestation has been seen before.
|
||||
hasAttestation := rs.db.HasAttestationDeprecated(attestationDataHash)
|
||||
span.AddAttributes(trace.BoolAttribute("hasAttestation", hasAttestation))
|
||||
if hasAttestation {
|
||||
dbAttestation, err := rs.db.AttestationDeprecated(attestationDataHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dbAttestation.AggregationBits.Contains(attestation.AggregationBits) {
|
||||
log.WithField("attestationDataHash", fmt.Sprintf("%#x", bytesutil.Trunc(attestationDataHash[:]))).
|
||||
Debug("Received, skipping attestation")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Skip if attestation slot is older than last finalized slot in state.
|
||||
headState, err := rs.db.HeadState(rs.ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
attTargetEpoch := attestation.Data.Target.Epoch
|
||||
headFinalizedEpoch := headState.FinalizedCheckpoint.Epoch
|
||||
span.AddAttributes(
|
||||
trace.Int64Attribute("attestation.target.epoch", int64(attTargetEpoch)),
|
||||
trace.Int64Attribute("finalized.epoch", int64(headFinalizedEpoch)),
|
||||
)
|
||||
|
||||
if attTargetEpoch < headFinalizedEpoch {
|
||||
log.WithFields(logrus.Fields{
|
||||
"receivedTargetEpoch": attTargetEpoch,
|
||||
"finalizedEpoch": headFinalizedEpoch},
|
||||
).Debug("Skipping received attestation with target epoch less than current finalized epoch")
|
||||
return nil
|
||||
}
|
||||
|
||||
_, sendAttestationSpan := trace.StartSpan(ctx, "beacon-chain.sync.sendAttestation")
|
||||
log.Debug("Sending newly received attestation to subscribers")
|
||||
rs.operationsService.IncomingAttFeed().Send(attestation)
|
||||
rs.attsService.IncomingAttestationFeed().Send(attestation)
|
||||
sentAttestation.Inc()
|
||||
sendAttestationSpan.End()
|
||||
return nil
|
||||
}
|
||||
|
||||
// receiveExitRequest accepts an broadcasted exit from the p2p layer,
|
||||
// discard the exit if we have gotten before, send it to operation
|
||||
// service if we have not.
|
||||
func (rs *RegularSync) receiveExitRequest(msg deprecatedp2p.Message) error {
|
||||
_, span := trace.StartSpan(msg.Ctx, "beacon-chain.sync.receiveExitRequest")
|
||||
defer span.End()
|
||||
recExit.Inc()
|
||||
exit := msg.Data.(*ethpb.VoluntaryExit)
|
||||
h, err := hashutil.HashProto(exit)
|
||||
if err != nil {
|
||||
log.Errorf("Could not hash incoming exit request: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
hasExit := rs.db.HasExit(h)
|
||||
span.AddAttributes(trace.BoolAttribute("hasExit", hasExit))
|
||||
if hasExit {
|
||||
log.WithField("exitRoot", fmt.Sprintf("%#x", h)).
|
||||
Debug("Received, skipping exit request")
|
||||
return nil
|
||||
}
|
||||
log.WithField("exitReqHash", fmt.Sprintf("%#x", h)).
|
||||
Debug("Forwarding validator exit request to subscribed services")
|
||||
rs.operationsService.IncomingExitFeed().Send(exit)
|
||||
sentExit.Inc()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rs *RegularSync) handleBlockRequestByHash(msg deprecatedp2p.Message) error {
|
||||
ctx, span := trace.StartSpan(msg.Ctx, "beacon-chain.sync.handleBlockRequestByHash")
|
||||
defer span.End()
|
||||
blockReqHash.Inc()
|
||||
|
||||
data := msg.Data.(*pb.BeaconBlockRequest)
|
||||
root := bytesutil.ToBytes32(data.Hash)
|
||||
block, err := rs.db.BlockDeprecated(root)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
if block == nil {
|
||||
return errors.New("block does not exist")
|
||||
}
|
||||
|
||||
defer sentBlocks.Inc()
|
||||
if _, err := rs.p2p.Send(ctx, &pb.BeaconBlockResponse{
|
||||
Block: block,
|
||||
}, msg.Peer); err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleBatchedBlockRequest receives p2p messages which consist of requests for batched blocks
|
||||
// which are bounded by a start slot and end slot.
|
||||
func (rs *RegularSync) handleBatchedBlockRequest(msg deprecatedp2p.Message) error {
|
||||
ctx, span := trace.StartSpan(msg.Ctx, "beacon-chain.sync.handleBatchedBlockRequest")
|
||||
defer span.End()
|
||||
batchedBlockReq.Inc()
|
||||
req := msg.Data.(*pb.BatchedBeaconBlockRequest)
|
||||
|
||||
// To prevent circuit in the chain and the potentiality peer can bomb a node building block list.
|
||||
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
|
||||
response, err := rs.respondBatchedBlocks(ctx, req.FinalizedRoot, req.CanonicalRoot)
|
||||
cancel()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not build canonical block list")
|
||||
}
|
||||
log.WithField("peer", msg.Peer).Debug("Sending response for batch blocks")
|
||||
|
||||
defer sentBatchedBlocks.Inc()
|
||||
if _, err := rs.p2p.Send(ctx, &pb.BatchedBeaconBlockResponse{
|
||||
BatchedBlocks: response,
|
||||
}, msg.Peer); err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rs *RegularSync) broadcastCanonicalBlock(ctx context.Context, announce *pb.BeaconBlockAnnounce) {
|
||||
ctx, span := trace.StartSpan(ctx, "beacon-chain.sync.broadcastCanonicalBlock")
|
||||
defer span.End()
|
||||
log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(announce.Hash))).
|
||||
Debug("Announcing canonical block")
|
||||
rs.p2p.Broadcast(ctx, announce)
|
||||
sentBlockAnnounce.Inc()
|
||||
}
|
||||
|
||||
// respondBatchedBlocks returns the requested block list inclusive of head block but not inclusive of the finalized block.
|
||||
// the return should look like (finalizedBlock... headBlock].
|
||||
func (rs *RegularSync) respondBatchedBlocks(ctx context.Context, finalizedRoot []byte, headRoot []byte) ([]*ethpb.BeaconBlock, error) {
|
||||
// if head block was the same as the finalized block.
|
||||
if bytes.Equal(headRoot, finalizedRoot) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
b, err := rs.db.BlockDeprecated(bytesutil.ToBytes32(headRoot))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if b == nil {
|
||||
return nil, fmt.Errorf("nil block %#x from db", bytesutil.Trunc(headRoot))
|
||||
}
|
||||
|
||||
bList := []*ethpb.BeaconBlock{b}
|
||||
parentRoot := b.ParentRoot
|
||||
for !bytes.Equal(parentRoot, finalizedRoot) {
|
||||
if ctx.Err() != nil {
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
b, err = rs.db.BlockDeprecated(bytesutil.ToBytes32(parentRoot))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if b == nil {
|
||||
return nil, fmt.Errorf("nil parent block %#x from db", bytesutil.Trunc(parentRoot[:]))
|
||||
}
|
||||
|
||||
// Prepend parent to the beginning of the list.
|
||||
bList = append([]*ethpb.BeaconBlock{b}, bList...)
|
||||
|
||||
parentRoot = b.ParentRoot
|
||||
}
|
||||
return bList, nil
|
||||
}
|
||||
@@ -1,681 +0,0 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/libp2p/go-libp2p-core/network"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/internal"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
p2p "github.com/prysmaticlabs/prysm/shared/deprecated-p2p"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/sirupsen/logrus"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
)
|
||||
|
||||
func init() {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.SetOutput(ioutil.Discard)
|
||||
}
|
||||
|
||||
type mockP2P struct {
|
||||
sentMsg proto.Message
|
||||
}
|
||||
|
||||
func (mp *mockP2P) Subscribe(msg proto.Message, channel chan p2p.Message) event.Subscription {
|
||||
return new(event.Feed).Subscribe(channel)
|
||||
}
|
||||
|
||||
func (mp *mockP2P) Broadcast(ctx context.Context, msg proto.Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mp *mockP2P) Send(ctx context.Context, msg proto.Message, peerID peer.ID) (network.Stream, error) {
|
||||
mp.sentMsg = msg
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (mp *mockP2P) Reputation(_ peer.ID, val int) {
|
||||
|
||||
}
|
||||
|
||||
type mockChainService struct {
|
||||
sFeed *event.Feed
|
||||
cFeed *event.Feed
|
||||
db *db.BeaconDB
|
||||
}
|
||||
|
||||
func (ms *mockChainService) StateInitializedFeed() *event.Feed {
|
||||
if ms.sFeed == nil {
|
||||
return new(event.Feed)
|
||||
}
|
||||
return ms.sFeed
|
||||
}
|
||||
|
||||
func (ms *mockChainService) CanonicalBlockFeed() *event.Feed {
|
||||
if ms.cFeed == nil {
|
||||
return new(event.Feed)
|
||||
}
|
||||
return ms.cFeed
|
||||
}
|
||||
|
||||
func (ms *mockChainService) ReceiveBlockDeprecated(ctx context.Context, block *ethpb.BeaconBlock) (*pb.BeaconState, error) {
|
||||
if err := ms.db.SaveBlockDeprecated(block); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.BeaconState{}, nil
|
||||
}
|
||||
|
||||
func (ms *mockChainService) AdvanceStateDeprecated(
|
||||
ctx context.Context, beaconState *pb.BeaconState, block *ethpb.BeaconBlock,
|
||||
) (*pb.BeaconState, error) {
|
||||
return &pb.BeaconState{}, nil
|
||||
}
|
||||
|
||||
func (ms *mockChainService) VerifyBlockValidity(ctx context.Context, block *ethpb.BeaconBlock, beaconState *pb.BeaconState) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ms *mockChainService) ApplyForkChoiceRuleDeprecated(ctx context.Context, block *ethpb.BeaconBlock, computedState *pb.BeaconState) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ms *mockChainService) CleanupBlockOperations(ctx context.Context, block *ethpb.BeaconBlock) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ms *mockChainService) IsCanonical(slot uint64, hash []byte) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (ms *mockChainService) UpdateCanonicalRoots(block *ethpb.BeaconBlock, root [32]byte) {
|
||||
}
|
||||
|
||||
type mockOperationService struct{}
|
||||
|
||||
func (ms *mockOperationService) IncomingProcessedBlockFeed() *event.Feed {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ms *mockOperationService) IncomingAttFeed() *event.Feed {
|
||||
return new(event.Feed)
|
||||
}
|
||||
|
||||
func (ms *mockOperationService) IncomingExitFeed() *event.Feed {
|
||||
return new(event.Feed)
|
||||
}
|
||||
|
||||
type mockAttestationService struct{}
|
||||
|
||||
func (ma *mockAttestationService) IncomingAttestationFeed() *event.Feed {
|
||||
return new(event.Feed)
|
||||
}
|
||||
|
||||
func setupService(db *db.BeaconDB) *RegularSync {
|
||||
cfg := &RegularSyncConfig{
|
||||
BlockAnnounceBufferSize: 0,
|
||||
BlockBufferSize: 0,
|
||||
ChainService: &mockChainService{},
|
||||
P2P: &mockP2P{},
|
||||
BeaconDB: db,
|
||||
}
|
||||
return NewRegularSyncService(context.Background(), cfg)
|
||||
}
|
||||
|
||||
func TestProcessBlockRoot_OK(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
|
||||
// set the channel's buffer to 0 to make channel interactions blocking
|
||||
cfg := &RegularSyncConfig{
|
||||
BlockAnnounceBufferSize: 0,
|
||||
BlockBufferSize: 0,
|
||||
ChainService: &mockChainService{},
|
||||
P2P: &mockP2P{},
|
||||
BeaconDB: db,
|
||||
}
|
||||
ss := NewRegularSyncService(context.Background(), cfg)
|
||||
|
||||
announceHash := hashutil.Hash([]byte{})
|
||||
hashAnnounce := &pb.BeaconBlockAnnounce{
|
||||
Hash: announceHash[:],
|
||||
}
|
||||
|
||||
msg := p2p.Message{
|
||||
Ctx: context.Background(),
|
||||
Peer: "",
|
||||
Data: hashAnnounce,
|
||||
}
|
||||
|
||||
// if a new hash is processed
|
||||
if err := ss.receiveBlockAnnounce(msg); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
testutil.AssertLogsContain(t, hook, "requesting full block data from sender")
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
func TestProcessBlock_OK(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
validators := make([]*ethpb.Validator, 10)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = ðpb.Validator{
|
||||
PublicKey: []byte(strconv.Itoa(i)),
|
||||
}
|
||||
}
|
||||
genesisTime := uint64(time.Now().Unix())
|
||||
deposits, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
if err := db.InitializeState(context.Background(), genesisTime, deposits, ðpb.Eth1Data{}); err != nil {
|
||||
t.Fatalf("Failed to initialize state: %v", err)
|
||||
}
|
||||
|
||||
cfg := &RegularSyncConfig{
|
||||
BlockAnnounceBufferSize: 0,
|
||||
BlockBufferSize: 0,
|
||||
ChainService: &mockChainService{
|
||||
db: db,
|
||||
},
|
||||
P2P: &mockP2P{},
|
||||
BeaconDB: db,
|
||||
OperationService: &mockOperationService{},
|
||||
}
|
||||
ss := NewRegularSyncService(context.Background(), cfg)
|
||||
|
||||
parentBlock := ðpb.BeaconBlock{
|
||||
Slot: 0,
|
||||
}
|
||||
if err := db.SaveBlockDeprecated(parentBlock); err != nil {
|
||||
t.Fatalf("failed to save block: %v", err)
|
||||
}
|
||||
parentRoot, err := ssz.SigningRoot(parentBlock)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get parent root: %v", err)
|
||||
}
|
||||
|
||||
data := ðpb.BeaconBlock{
|
||||
ParentRoot: parentRoot[:],
|
||||
Slot: 0,
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
Eth1Data: ðpb.Eth1Data{
|
||||
DepositRoot: []byte{1, 2, 3, 4, 5},
|
||||
BlockHash: []byte{6, 7, 8, 9, 10},
|
||||
},
|
||||
},
|
||||
}
|
||||
attestation := ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 0,
|
||||
DataRoot: []byte{'A'},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
responseBlock := &pb.BeaconBlockResponse{
|
||||
Block: data,
|
||||
Attestation: attestation,
|
||||
}
|
||||
|
||||
msg := p2p.Message{
|
||||
Ctx: context.Background(),
|
||||
Peer: "",
|
||||
Data: responseBlock,
|
||||
}
|
||||
|
||||
if err := ss.receiveBlock(msg); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
testutil.AssertLogsContain(t, hook, "Sending newly received block to chain service")
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
func TestProcessBlock_MultipleBlocksProcessedOK(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
|
||||
validators := make([]*ethpb.Validator, 10)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = ðpb.Validator{
|
||||
PublicKey: []byte(strconv.Itoa(i)),
|
||||
}
|
||||
}
|
||||
genesisTime := uint64(time.Now().Unix())
|
||||
deposits, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
if err := db.InitializeState(context.Background(), genesisTime, deposits, ðpb.Eth1Data{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cfg := &RegularSyncConfig{
|
||||
BlockAnnounceBufferSize: 0,
|
||||
BlockBufferSize: 0,
|
||||
ChainService: &mockChainService{
|
||||
db: db,
|
||||
},
|
||||
P2P: &mockP2P{},
|
||||
BeaconDB: db,
|
||||
OperationService: &mockOperationService{},
|
||||
}
|
||||
ss := NewRegularSyncService(context.Background(), cfg)
|
||||
|
||||
parentBlock := ðpb.BeaconBlock{
|
||||
Slot: 0,
|
||||
}
|
||||
if err := db.SaveBlockDeprecated(parentBlock); err != nil {
|
||||
t.Fatalf("failed to save block: %v", err)
|
||||
}
|
||||
parentRoot, err := ssz.SigningRoot(parentBlock)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get parent root: %v", err)
|
||||
}
|
||||
|
||||
data1 := ðpb.BeaconBlock{
|
||||
ParentRoot: parentRoot[:],
|
||||
Slot: 1,
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
Eth1Data: ðpb.Eth1Data{
|
||||
DepositRoot: []byte{1, 2, 3, 4, 5},
|
||||
BlockHash: []byte{6, 7, 8, 9, 10},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
responseBlock1 := &pb.BeaconBlockResponse{
|
||||
Block: data1,
|
||||
Attestation: ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 0,
|
||||
DataRoot: []byte{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
msg1 := p2p.Message{
|
||||
Ctx: context.Background(),
|
||||
Peer: "",
|
||||
Data: responseBlock1,
|
||||
}
|
||||
|
||||
data2 := ðpb.BeaconBlock{
|
||||
ParentRoot: []byte{},
|
||||
Slot: 1,
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
Eth1Data: ðpb.Eth1Data{
|
||||
DepositRoot: []byte{11, 12, 13, 14, 15},
|
||||
BlockHash: []byte{16, 17, 18, 19, 20},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
responseBlock2 := &pb.BeaconBlockResponse{
|
||||
Block: data2,
|
||||
Attestation: ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 0,
|
||||
DataRoot: []byte{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
msg2 := p2p.Message{
|
||||
Ctx: context.Background(),
|
||||
Peer: "",
|
||||
Data: responseBlock2,
|
||||
}
|
||||
|
||||
if err := ss.receiveBlock(msg1); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := ss.receiveBlock(msg2); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
testutil.AssertLogsContain(t, hook, "Sending newly received block to chain service")
|
||||
testutil.AssertLogsContain(t, hook, "Sending newly received block to chain service")
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
func TestReceiveAttestation_OK(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
ms := &mockChainService{}
|
||||
os := &mockOperationService{}
|
||||
ctx := context.Background()
|
||||
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
beaconState := &pb.BeaconState{
|
||||
Slot: 2,
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{},
|
||||
}
|
||||
if err := db.SaveStateDeprecated(ctx, beaconState); err != nil {
|
||||
t.Fatalf("Could not save state: %v", err)
|
||||
}
|
||||
beaconBlock := ðpb.BeaconBlock{
|
||||
Slot: beaconState.Slot,
|
||||
}
|
||||
if err := db.SaveBlockDeprecated(beaconBlock); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.UpdateChainHead(ctx, beaconBlock, beaconState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cfg := &RegularSyncConfig{
|
||||
ChainService: ms,
|
||||
AttsService: &mockAttestationService{},
|
||||
OperationService: os,
|
||||
P2P: &mockP2P{},
|
||||
BeaconDB: db,
|
||||
}
|
||||
ss := NewRegularSyncService(context.Background(), cfg)
|
||||
|
||||
request1 := ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 1,
|
||||
},
|
||||
Source: ðpb.Checkpoint{},
|
||||
Target: ðpb.Checkpoint{},
|
||||
},
|
||||
}
|
||||
|
||||
msg1 := p2p.Message{
|
||||
Ctx: context.Background(),
|
||||
Data: request1,
|
||||
Peer: "",
|
||||
}
|
||||
|
||||
if err := ss.receiveAttestation(msg1); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
testutil.AssertLogsContain(t, hook, "Sending newly received attestation to subscribers")
|
||||
}
|
||||
|
||||
func TestReceiveAttestation_OlderThanPrevEpoch(t *testing.T) {
|
||||
helpers.ClearAllCaches()
|
||||
|
||||
hook := logTest.NewGlobal()
|
||||
ms := &mockChainService{}
|
||||
os := &mockOperationService{}
|
||||
ctx := context.Background()
|
||||
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
state := &pb.BeaconState{
|
||||
Slot: 2 * params.BeaconConfig().SlotsPerEpoch,
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 1},
|
||||
}
|
||||
if err := db.SaveStateDeprecated(ctx, state); err != nil {
|
||||
t.Fatalf("Could not save state: %v", err)
|
||||
}
|
||||
headBlock := ðpb.BeaconBlock{Slot: state.Slot}
|
||||
if err := db.SaveBlockDeprecated(headBlock); err != nil {
|
||||
t.Fatalf("failed to save block: %v", err)
|
||||
}
|
||||
if err := db.UpdateChainHead(ctx, headBlock, state); err != nil {
|
||||
t.Fatalf("failed to update chain head: %v", err)
|
||||
}
|
||||
cfg := &RegularSyncConfig{
|
||||
AttsService: &mockAttestationService{},
|
||||
ChainService: ms,
|
||||
OperationService: os,
|
||||
P2P: &mockP2P{},
|
||||
BeaconDB: db,
|
||||
}
|
||||
ss := NewRegularSyncService(context.Background(), cfg)
|
||||
|
||||
request1 := ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 900,
|
||||
},
|
||||
Source: ðpb.Checkpoint{},
|
||||
Target: ðpb.Checkpoint{},
|
||||
},
|
||||
}
|
||||
|
||||
msg1 := p2p.Message{
|
||||
Ctx: context.Background(),
|
||||
Data: request1,
|
||||
Peer: "",
|
||||
}
|
||||
|
||||
if err := ss.receiveAttestation(msg1); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
testutil.AssertLogsContain(t, hook, "Skipping received attestation with target epoch less than current finalized epoch")
|
||||
}
|
||||
|
||||
func TestReceiveExitReq_OK(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
os := &mockOperationService{}
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
|
||||
cfg := &RegularSyncConfig{
|
||||
OperationService: os,
|
||||
P2P: &mockP2P{},
|
||||
BeaconDB: db,
|
||||
ChainService: &mockChainService{},
|
||||
}
|
||||
ss := NewRegularSyncService(context.Background(), cfg)
|
||||
|
||||
request1 := ðpb.VoluntaryExit{
|
||||
Epoch: 100,
|
||||
}
|
||||
|
||||
msg1 := p2p.Message{
|
||||
Ctx: context.Background(),
|
||||
Data: request1,
|
||||
Peer: "",
|
||||
}
|
||||
if err := ss.receiveExitRequest(msg1); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
testutil.AssertLogsContain(t, hook, "Forwarding validator exit request to subscribed services")
|
||||
}
|
||||
|
||||
func TestHandleStateReq_NOState(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
|
||||
ss := setupService(db)
|
||||
|
||||
genesisTime := uint64(time.Now().Unix())
|
||||
deposits, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
if err := db.InitializeState(context.Background(), genesisTime, deposits, ðpb.Eth1Data{}); err != nil {
|
||||
t.Fatalf("Failed to initialize state: %v", err)
|
||||
}
|
||||
|
||||
request1 := &pb.BeaconStateRequest{
|
||||
FinalizedStateRootHash32S: []byte{'a'},
|
||||
}
|
||||
|
||||
msg1 := p2p.Message{
|
||||
Ctx: context.Background(),
|
||||
Data: request1,
|
||||
Peer: "",
|
||||
}
|
||||
|
||||
if err := ss.handleStateRequest(msg1); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
testutil.AssertLogsContain(t, hook, "Requested state root is diff than local state root")
|
||||
|
||||
}
|
||||
|
||||
func TestHandleStateReq_OK(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
ctx := context.Background()
|
||||
helpers.ClearAllCaches()
|
||||
|
||||
genesisTime := time.Now()
|
||||
unixTime := uint64(genesisTime.Unix())
|
||||
if err := db.InitializeState(context.Background(), unixTime, []*ethpb.Deposit{}, ðpb.Eth1Data{}); err != nil {
|
||||
t.Fatalf("could not initialize beacon state to disk: %v", err)
|
||||
}
|
||||
beaconState, err := db.HeadState(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("could not attempt fetch beacon state: %v", err)
|
||||
}
|
||||
if err := db.SaveJustifiedState(beaconState); err != nil {
|
||||
t.Fatalf("could not save justified state: %v", err)
|
||||
}
|
||||
if err := db.SaveFinalizedState(beaconState); err != nil {
|
||||
t.Fatalf("could not save justified state: %v", err)
|
||||
}
|
||||
stateRoot, err := hashutil.HashProto(beaconState)
|
||||
if err != nil {
|
||||
t.Fatalf("could not hash beacon state: %v", err)
|
||||
}
|
||||
genBlock := b.NewGenesisBlock(stateRoot[:])
|
||||
if err := db.SaveFinalizedBlock(genBlock); err != nil {
|
||||
t.Fatalf("could not save genesis block: %v", err)
|
||||
}
|
||||
|
||||
ss := setupService(db)
|
||||
|
||||
request1 := &pb.BeaconStateRequest{
|
||||
FinalizedStateRootHash32S: stateRoot[:],
|
||||
}
|
||||
|
||||
msg1 := p2p.Message{
|
||||
Ctx: context.Background(),
|
||||
Data: request1,
|
||||
Peer: "",
|
||||
}
|
||||
|
||||
if err := ss.handleStateRequest(msg1); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
testutil.AssertLogsContain(t, hook, "Sending finalized state and block to peer")
|
||||
}
|
||||
|
||||
func TestCanonicalBlockList_CanRetrieveCanonical(t *testing.T) {
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
ss := setupService(db)
|
||||
|
||||
// Construct the following chain:
|
||||
// /- B3
|
||||
// B1 - B2 - B4
|
||||
block1 := ðpb.BeaconBlock{Slot: 1, ParentRoot: []byte{'A'}}
|
||||
root1, err := ssz.SigningRoot(block1)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not hash block: %v", err)
|
||||
}
|
||||
if err = ss.db.SaveBlockDeprecated(block1); err != nil {
|
||||
t.Fatalf("Could not save block: %v", err)
|
||||
}
|
||||
block2 := ðpb.BeaconBlock{Slot: 2, ParentRoot: root1[:]}
|
||||
root2, _ := ssz.SigningRoot(block2)
|
||||
if err = ss.db.SaveBlockDeprecated(block2); err != nil {
|
||||
t.Fatalf("Could not save block: %v", err)
|
||||
}
|
||||
block3 := ðpb.BeaconBlock{Slot: 3, ParentRoot: root1[:]}
|
||||
if err = ss.db.SaveBlockDeprecated(block3); err != nil {
|
||||
t.Fatalf("Could not save block: %v", err)
|
||||
}
|
||||
block4 := ðpb.BeaconBlock{Slot: 4, ParentRoot: root2[:]}
|
||||
root4, _ := ssz.SigningRoot(block4)
|
||||
if err = ss.db.SaveBlockDeprecated(block4); err != nil {
|
||||
t.Fatalf("Could not save block: %v", err)
|
||||
}
|
||||
|
||||
// Verify passing in roots of B4 and B1 give us the canonical lists.
|
||||
list, err := ss.respondBatchedBlocks(context.Background(), root1[:], root4[:])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wantList := []*ethpb.BeaconBlock{block2, block4}
|
||||
if !reflect.DeepEqual(list, wantList) {
|
||||
t.Error("Did not retrieve the correct canonical lists")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanonicalBlockList_SameFinalizedAndHead(t *testing.T) {
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
ss := setupService(db)
|
||||
|
||||
// Construct the following chain:
|
||||
// B1 (finalized and head)
|
||||
block1 := ðpb.BeaconBlock{Slot: 1, ParentRoot: []byte{'A'}}
|
||||
root1, err := ssz.SigningRoot(block1)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not hash block: %v", err)
|
||||
}
|
||||
if err = ss.db.SaveBlockDeprecated(block1); err != nil {
|
||||
t.Fatalf("Could not save block: %v", err)
|
||||
}
|
||||
|
||||
// Verify passing in roots of B1 and B1 give us the canonical lists which should be an empty list.
|
||||
list, err := ss.respondBatchedBlocks(context.Background(), root1[:], root1[:])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(list) != 0 {
|
||||
t.Error("Did not retrieve the correct canonical lists")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanonicalBlockList_NilBlock(t *testing.T) {
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
ss := setupService(db)
|
||||
|
||||
want := "nil block 0x42 from db"
|
||||
if _, err := ss.respondBatchedBlocks(context.Background(), []byte{'A'}, []byte{'B'}); err.Error() != want {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanonicalBlockList_NilParentBlock(t *testing.T) {
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
ss := setupService(db)
|
||||
|
||||
block1 := ðpb.BeaconBlock{Slot: 1, ParentRoot: []byte{'B'}}
|
||||
root1, err := ssz.SigningRoot(block1)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not hash block: %v", err)
|
||||
}
|
||||
if err = ss.db.SaveBlockDeprecated(block1); err != nil {
|
||||
t.Fatalf("Could not save block: %v", err)
|
||||
}
|
||||
|
||||
want := fmt.Sprintf("nil parent block %#x from db", []byte{'B'})
|
||||
if _, err := ss.respondBatchedBlocks(context.Background(), []byte{}, root1[:]); err.Error() != want {
|
||||
t.Log(want)
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
initialsync "github.com/prysmaticlabs/prysm/beacon-chain/deprecated-sync/initial-sync"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/operations"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var slog = logrus.WithField("prefix", "sync")
|
||||
|
||||
// Service defines the main routines used in the sync service.
|
||||
type Service struct {
|
||||
RegularSync *RegularSync
|
||||
InitialSync *initialsync.InitialSync
|
||||
Querier *Querier
|
||||
querierFinished bool
|
||||
}
|
||||
|
||||
// Config defines the configured services required for sync to work.
|
||||
type Config struct {
|
||||
ChainService chainService
|
||||
BeaconDB db.Database
|
||||
DepositCache *depositcache.DepositCache
|
||||
P2P p2pAPI
|
||||
AttsService attsService
|
||||
OperationService operations.OperationFeeds
|
||||
PowChainService powChainService
|
||||
}
|
||||
|
||||
// NewSyncService creates a new instance of SyncService using the config
|
||||
// given.
|
||||
func NewSyncService(ctx context.Context, cfg *Config) *Service {
|
||||
sqCfg := DefaultQuerierConfig()
|
||||
sqCfg.BeaconDB = cfg.BeaconDB.(*db.BeaconDB)
|
||||
sqCfg.P2P = cfg.P2P
|
||||
sqCfg.PowChain = cfg.PowChainService
|
||||
sqCfg.ChainService = cfg.ChainService
|
||||
|
||||
isCfg := initialsync.DefaultConfig()
|
||||
isCfg.BeaconDB = cfg.BeaconDB.(*db.BeaconDB)
|
||||
isCfg.DepositCache = cfg.DepositCache
|
||||
isCfg.P2P = cfg.P2P
|
||||
isCfg.PowChain = cfg.PowChainService
|
||||
isCfg.ChainService = cfg.ChainService
|
||||
|
||||
rsCfg := DefaultRegularSyncConfig()
|
||||
rsCfg.ChainService = cfg.ChainService
|
||||
rsCfg.BeaconDB = cfg.BeaconDB.(*db.BeaconDB)
|
||||
rsCfg.P2P = cfg.P2P
|
||||
rsCfg.AttsService = cfg.AttsService
|
||||
rsCfg.OperationService = cfg.OperationService
|
||||
|
||||
sq := NewQuerierService(ctx, sqCfg)
|
||||
rs := NewRegularSyncService(ctx, rsCfg)
|
||||
|
||||
isCfg.SyncService = rs
|
||||
is := initialsync.NewInitialSyncService(ctx, isCfg)
|
||||
|
||||
return &Service{
|
||||
RegularSync: rs,
|
||||
InitialSync: is,
|
||||
Querier: sq,
|
||||
querierFinished: false,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Start kicks off the sync service
|
||||
func (ss *Service) Start() {
|
||||
slog.Info("Starting service")
|
||||
go ss.run()
|
||||
}
|
||||
|
||||
// Stop ends all the currently running routines
|
||||
// which are part of the sync service.
|
||||
func (ss *Service) Stop() error {
|
||||
err := ss.Querier.Stop()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ss.InitialSync.Stop()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ss.RegularSync.Stop()
|
||||
}
|
||||
|
||||
// Status checks the status of the node. It returns nil if it's synced
|
||||
// with the rest of the network and no errors occurred. Otherwise, it returns an error.
|
||||
func (ss *Service) Status() error {
|
||||
if !ss.querierFinished && !ss.Querier.atGenesis {
|
||||
return errors.New("querier is still running")
|
||||
}
|
||||
synced, err := ss.Querier.IsSynced()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if synced {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !ss.InitialSync.NodeIsSynced() {
|
||||
return errors.New("not initially synced")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Syncing verifies the sync service is fully synced with the network
|
||||
// and returns the result as a boolean.
|
||||
func (ss *Service) Syncing() bool {
|
||||
querierSynced, err := ss.Querier.IsSynced()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
isSynced := querierSynced && ss.InitialSync.NodeIsSynced()
|
||||
return !isSynced
|
||||
}
|
||||
|
||||
func (ss *Service) run() {
|
||||
ss.Querier.Start()
|
||||
|
||||
synced, err := ss.Querier.IsSynced()
|
||||
if err != nil {
|
||||
slog.Fatalf("Unable to retrieve result from sync querier %v", err)
|
||||
}
|
||||
ss.querierFinished = true
|
||||
|
||||
if synced {
|
||||
ss.RegularSync.Start()
|
||||
return
|
||||
}
|
||||
|
||||
ss.InitialSync.Start(ss.Querier.chainHeadResponses)
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/internal"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/sync"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
)
|
||||
|
||||
var _ = sync.Checker(&Service{})
|
||||
|
||||
func NotSyncQuerierConfig() *QuerierConfig {
|
||||
return &QuerierConfig{
|
||||
ResponseBufferSize: 100,
|
||||
CurrentHeadSlot: 10,
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
featureconfig.InitFeatureConfig(&featureconfig.FeatureFlagConfig{})
|
||||
}
|
||||
|
||||
func initializeTestSyncService(ctx context.Context, cfg *Config, synced bool) *Service {
|
||||
var sqCfg *QuerierConfig
|
||||
if synced {
|
||||
sqCfg = DefaultQuerierConfig()
|
||||
} else {
|
||||
sqCfg = NotSyncQuerierConfig()
|
||||
}
|
||||
|
||||
services := NewSyncService(ctx, cfg)
|
||||
|
||||
sqCfg.BeaconDB = cfg.BeaconDB.(*db.BeaconDB)
|
||||
sqCfg.P2P = cfg.P2P
|
||||
sq := NewQuerierService(ctx, sqCfg)
|
||||
|
||||
services.Querier = sq
|
||||
|
||||
return services
|
||||
}
|
||||
|
||||
func setupTestSyncService(t *testing.T, synced bool) (*Service, *db.BeaconDB) {
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
|
||||
unixTime := uint64(time.Now().Unix())
|
||||
deposits, _ := testutil.SetupInitialDeposits(t, 100)
|
||||
if err := db.InitializeState(context.Background(), unixTime, deposits, ðpb.Eth1Data{}); err != nil {
|
||||
t.Fatalf("Failed to initialize state: %v", err)
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
ChainService: &mockChainService{
|
||||
db: db,
|
||||
},
|
||||
P2P: &mockP2P{},
|
||||
BeaconDB: db,
|
||||
OperationService: &mockOperationService{},
|
||||
}
|
||||
service := initializeTestSyncService(context.Background(), cfg, synced)
|
||||
return service, db
|
||||
|
||||
}
|
||||
|
||||
func TestStatus_NotSynced(t *testing.T) {
|
||||
serviceNotSynced, db := setupTestSyncService(t, false)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
synced := serviceNotSynced.InitialSync.NodeIsSynced()
|
||||
if synced {
|
||||
t.Error("Wanted false, but got true")
|
||||
}
|
||||
}
|
||||
@@ -6,16 +6,13 @@ go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"beacon_service_mock.go",
|
||||
"db_test_util.go",
|
||||
"validator_service_mock.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/internal",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//proto/beacon/rpc/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"@com_github_gogo_protobuf//types:go_default_library",
|
||||
"@com_github_golang_mock//gomock:go_default_library",
|
||||
"@org_golang_google_grpc//metadata:go_default_library",
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
)
|
||||
|
||||
// SetupDBDeprecated instantiates and returns a BeaconDB instance.
|
||||
// This is deprecated and used to set up the pre refactored db for testing.
|
||||
// DEPRECATED: Use beacon-chain/db/testing.SetupDB
|
||||
func SetupDBDeprecated(t testing.TB) *db.BeaconDB {
|
||||
randPath, err := rand.Int(rand.Reader, big.NewInt(1000000))
|
||||
if err != nil {
|
||||
t.Fatalf("Could not generate random file path: %v", err)
|
||||
}
|
||||
path := path.Join(testutil.TempDir(), fmt.Sprintf("/%d", randPath))
|
||||
if err := os.RemoveAll(path); err != nil {
|
||||
t.Fatalf("Failed to remove directory: %v", err)
|
||||
}
|
||||
db, err := db.NewDBDeprecated(path)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not setup DB: %v", err)
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
// TeardownDBDeprecated cleans up a BeaconDB instance.
|
||||
// This is deprecated and used to tear up the pre refactored db for testing.
|
||||
// DEPRECATED: Use beacon-chain/db/testing.TeardownDB
|
||||
func TeardownDBDeprecated(t testing.TB, db *db.BeaconDB) {
|
||||
if err := db.Close(); err != nil {
|
||||
t.Fatalf("Failed to close database: %v", err)
|
||||
}
|
||||
if err := os.RemoveAll(db.DatabasePath()); err != nil {
|
||||
t.Fatalf("Could not remove tmp db dir: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -5,17 +5,13 @@ go_library(
|
||||
srcs = [
|
||||
"fetch_contract_address.go",
|
||||
"node.go",
|
||||
"p2p_config.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/node",
|
||||
visibility = ["//beacon-chain:__subpackages__"],
|
||||
deps = [
|
||||
"//beacon-chain/attestation:go_default_library",
|
||||
"//beacon-chain/blockchain:go_default_library",
|
||||
"//beacon-chain/cache/depositcache:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/deprecated-blockchain:go_default_library",
|
||||
"//beacon-chain/deprecated-sync:go_default_library",
|
||||
"//beacon-chain/flags:go_default_library",
|
||||
"//beacon-chain/gateway:go_default_library",
|
||||
"//beacon-chain/operations:go_default_library",
|
||||
@@ -24,13 +20,9 @@ go_library(
|
||||
"//beacon-chain/rpc:go_default_library",
|
||||
"//beacon-chain/sync:go_default_library",
|
||||
"//beacon-chain/sync/initial-sync:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared:go_default_library",
|
||||
"//shared/cmd:go_default_library",
|
||||
"//shared/debug:go_default_library",
|
||||
"//shared/deprecated-p2p:go_default_library",
|
||||
"//shared/deprecated-p2p/adapter/metric:go_default_library",
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/prometheus:go_default_library",
|
||||
@@ -39,7 +31,6 @@ go_library(
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//ethclient:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_urfave_cli//:go_default_library",
|
||||
|
||||
@@ -15,12 +15,9 @@ import (
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
gethRPC "github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/attestation"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
dblockchain "github.com/prysmaticlabs/prysm/beacon-chain/deprecated-blockchain"
|
||||
rbcsync "github.com/prysmaticlabs/prysm/beacon-chain/deprecated-sync"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/flags"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/gateway"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/operations"
|
||||
@@ -32,7 +29,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/shared"
|
||||
"github.com/prysmaticlabs/prysm/shared/cmd"
|
||||
"github.com/prysmaticlabs/prysm/shared/debug"
|
||||
deprecatedp2p "github.com/prysmaticlabs/prysm/shared/deprecated-p2p"
|
||||
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/prometheus"
|
||||
@@ -99,10 +95,6 @@ func NewBeaconNode(ctx *cli.Context) (*BeaconNode, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := beacon.registerAttestationService(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := beacon.registerOperationService(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -186,22 +178,19 @@ func (b *BeaconNode) Close() {
|
||||
func (b *BeaconNode) startDB(ctx *cli.Context) error {
|
||||
baseDir := ctx.GlobalString(cmd.DataDirFlag.Name)
|
||||
dbPath := path.Join(baseDir, beaconChainDBName)
|
||||
if b.ctx.GlobalBool(cmd.ClearDB.Name) {
|
||||
if err := db.ClearDB(dbPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var d db.Database
|
||||
var err error
|
||||
if featureconfig.FeatureConfig().UseNewDatabase {
|
||||
d, err = db.NewDB(dbPath)
|
||||
} else {
|
||||
d, err = db.NewDBDeprecated(dbPath)
|
||||
}
|
||||
d, err := db.NewDB(dbPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if b.ctx.GlobalBool(cmd.ClearDB.Name) {
|
||||
if err := d.ClearDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
d, err = db.NewDB(dbPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.WithField("path", dbPath).Info("Checking db")
|
||||
b.db = d
|
||||
@@ -210,44 +199,27 @@ func (b *BeaconNode) startDB(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
func (b *BeaconNode) registerP2P(ctx *cli.Context) error {
|
||||
if featureconfig.FeatureConfig().UseNewP2P {
|
||||
svc, err := p2p.NewService(&p2p.Config{
|
||||
NoDiscovery: ctx.GlobalBool(cmd.NoDiscovery.Name),
|
||||
StaticPeers: ctx.GlobalStringSlice(cmd.StaticPeers.Name),
|
||||
BootstrapNodeAddr: ctx.GlobalString(cmd.BootstrapNode.Name),
|
||||
RelayNodeAddr: ctx.GlobalString(cmd.RelayNode.Name),
|
||||
HostAddress: ctx.GlobalString(cmd.P2PHost.Name),
|
||||
PrivateKey: ctx.GlobalString(cmd.P2PPrivKey.Name),
|
||||
Port: ctx.GlobalUint(cmd.P2PPort.Name),
|
||||
MaxPeers: ctx.GlobalUint(cmd.P2PMaxPeers.Name),
|
||||
WhitelistCIDR: ctx.GlobalString(cmd.P2PWhitelist.Name),
|
||||
EnableUPnP: ctx.GlobalBool(cmd.EnableUPnPFlag.Name),
|
||||
Encoding: ctx.GlobalString(cmd.P2PEncoding.Name),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return b.services.RegisterService(svc)
|
||||
}
|
||||
|
||||
beaconp2p, err := deprecatedConfigureP2P(ctx)
|
||||
svc, err := p2p.NewService(&p2p.Config{
|
||||
NoDiscovery: ctx.GlobalBool(cmd.NoDiscovery.Name),
|
||||
StaticPeers: ctx.GlobalStringSlice(cmd.StaticPeers.Name),
|
||||
BootstrapNodeAddr: ctx.GlobalString(cmd.BootstrapNode.Name),
|
||||
RelayNodeAddr: ctx.GlobalString(cmd.RelayNode.Name),
|
||||
HostAddress: ctx.GlobalString(cmd.P2PHost.Name),
|
||||
PrivateKey: ctx.GlobalString(cmd.P2PPrivKey.Name),
|
||||
Port: ctx.GlobalUint(cmd.P2PPort.Name),
|
||||
MaxPeers: ctx.GlobalUint(cmd.P2PMaxPeers.Name),
|
||||
WhitelistCIDR: ctx.GlobalString(cmd.P2PWhitelist.Name),
|
||||
EnableUPnP: ctx.GlobalBool(cmd.EnableUPnPFlag.Name),
|
||||
Encoding: ctx.GlobalString(cmd.P2PEncoding.Name),
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not register deprecatedp2p service")
|
||||
return err
|
||||
}
|
||||
|
||||
return b.services.RegisterService(beaconp2p)
|
||||
return b.services.RegisterService(svc)
|
||||
}
|
||||
|
||||
func (b *BeaconNode) fetchP2P(ctx *cli.Context) p2p.P2P {
|
||||
if featureconfig.FeatureConfig().UseNewP2P {
|
||||
var p *p2p.Service
|
||||
if err := b.services.FetchService(&p); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
var p *deprecatedp2p.Server
|
||||
var p *p2p.Service
|
||||
if err := b.services.FetchService(&p); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -266,40 +238,18 @@ func (b *BeaconNode) registerBlockchainService(ctx *cli.Context) error {
|
||||
|
||||
maxRoutines := ctx.GlobalInt64(cmd.MaxGoroutines.Name)
|
||||
|
||||
if featureconfig.FeatureConfig().UseNewBlockChainService {
|
||||
blockchainService, err := blockchain.NewChainService(context.Background(), &blockchain.Config{
|
||||
BeaconDB: b.db,
|
||||
DepositCache: b.depositCache,
|
||||
Web3Service: web3Service,
|
||||
OpsPoolService: opsService,
|
||||
P2p: b.fetchP2P(ctx),
|
||||
MaxRoutines: maxRoutines,
|
||||
PreloadStatePath: ctx.GlobalString(flags.GenesisState.Name),
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not register blockchain service")
|
||||
}
|
||||
return b.services.RegisterService(blockchainService)
|
||||
}
|
||||
|
||||
var attsService *attestation.Service
|
||||
if err := b.services.FetchService(&attsService); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deprecatedBlockchainService, err := dblockchain.NewChainService(context.Background(), &dblockchain.Config{
|
||||
blockchainService, err := blockchain.NewChainService(context.Background(), &blockchain.Config{
|
||||
BeaconDB: b.db,
|
||||
DepositCache: b.depositCache,
|
||||
Web3Service: web3Service,
|
||||
OpsPoolService: opsService,
|
||||
AttsService: attsService,
|
||||
P2p: b.fetchP2P(ctx),
|
||||
MaxRoutines: maxRoutines,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not register deprecated blockchain service")
|
||||
return errors.Wrap(err, "could not register blockchain service")
|
||||
}
|
||||
return b.services.RegisterService(deprecatedBlockchainService)
|
||||
return b.services.RegisterService(blockchainService)
|
||||
}
|
||||
|
||||
func (b *BeaconNode) registerOperationService(ctx *cli.Context) error {
|
||||
@@ -376,93 +326,52 @@ func (b *BeaconNode) registerSyncService(ctx *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var attsService *attestation.Service
|
||||
if err := b.services.FetchService(&attsService); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var web3Service *powchain.Web3Service
|
||||
if err := b.services.FetchService(&web3Service); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if featureconfig.FeatureConfig().UseNewSync {
|
||||
var chainService *blockchain.ChainService
|
||||
if err := b.services.FetchService(&chainService); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rs := prysmsync.NewRegularSync(&prysmsync.Config{
|
||||
DB: b.db,
|
||||
P2P: b.fetchP2P(ctx),
|
||||
Operations: operationService,
|
||||
Chain: chainService,
|
||||
})
|
||||
|
||||
return b.services.RegisterService(rs)
|
||||
}
|
||||
|
||||
var chainService *dblockchain.ChainService
|
||||
var chainService *blockchain.ChainService
|
||||
if err := b.services.FetchService(&chainService); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg := &rbcsync.Config{
|
||||
ChainService: chainService,
|
||||
P2P: b.fetchP2P(ctx),
|
||||
BeaconDB: b.db,
|
||||
DepositCache: b.depositCache,
|
||||
OperationService: operationService,
|
||||
PowChainService: web3Service,
|
||||
AttsService: attsService,
|
||||
}
|
||||
rs := prysmsync.NewRegularSync(&prysmsync.Config{
|
||||
DB: b.db,
|
||||
P2P: b.fetchP2P(ctx),
|
||||
Operations: operationService,
|
||||
Chain: chainService,
|
||||
})
|
||||
|
||||
syncService := rbcsync.NewSyncService(context.Background(), cfg)
|
||||
return b.services.RegisterService(syncService)
|
||||
return b.services.RegisterService(rs)
|
||||
}
|
||||
|
||||
func (b *BeaconNode) registerInitialSyncService(ctx *cli.Context) error {
|
||||
var attsService *attestation.Service
|
||||
if err := b.services.FetchService(&attsService); err != nil {
|
||||
|
||||
var chainService *blockchain.ChainService
|
||||
if err := b.services.FetchService(&chainService); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if featureconfig.FeatureConfig().UseNewSync {
|
||||
var chainService *blockchain.ChainService
|
||||
if err := b.services.FetchService(&chainService); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var regSync *prysmsync.RegularSync
|
||||
if err := b.services.FetchService(®Sync); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
is := initialsync.NewInitialSync(&initialsync.Config{
|
||||
Chain: chainService,
|
||||
RegSync: regSync,
|
||||
P2P: b.fetchP2P(ctx),
|
||||
})
|
||||
|
||||
return b.services.RegisterService(is)
|
||||
var regSync *prysmsync.RegularSync
|
||||
if err := b.services.FetchService(®Sync); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
is := initialsync.NewInitialSync(&initialsync.Config{
|
||||
Chain: chainService,
|
||||
RegSync: regSync,
|
||||
P2P: b.fetchP2P(ctx),
|
||||
})
|
||||
|
||||
return b.services.RegisterService(is)
|
||||
|
||||
}
|
||||
|
||||
func (b *BeaconNode) registerRPCService(ctx *cli.Context) error {
|
||||
var chainService interface{}
|
||||
if featureconfig.FeatureConfig().UseNewBlockChainService {
|
||||
var newChain *blockchain.ChainService
|
||||
if err := b.services.FetchService(&newChain); err != nil {
|
||||
return err
|
||||
}
|
||||
chainService = newChain
|
||||
} else {
|
||||
var deprecatedChain *dblockchain.ChainService
|
||||
if err := b.services.FetchService(&deprecatedChain); err != nil {
|
||||
return err
|
||||
}
|
||||
chainService = deprecatedChain
|
||||
var chainService *blockchain.ChainService
|
||||
if err := b.services.FetchService(&chainService); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var operationService *operations.Service
|
||||
@@ -475,19 +384,9 @@ func (b *BeaconNode) registerRPCService(ctx *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var syncChecker prysmsync.Checker
|
||||
if featureconfig.FeatureConfig().UseNewSync {
|
||||
var syncService *prysmsync.RegularSync
|
||||
if err := b.services.FetchService(&syncService); err != nil {
|
||||
return err
|
||||
}
|
||||
syncChecker = syncService
|
||||
} else {
|
||||
var syncService *rbcsync.Service
|
||||
if err := b.services.FetchService(&syncService); err != nil {
|
||||
return err
|
||||
}
|
||||
syncChecker = syncService
|
||||
var syncService *prysmsync.RegularSync
|
||||
if err := b.services.FetchService(&syncService); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
port := ctx.GlobalString(flags.RPCPort.Name)
|
||||
@@ -502,7 +401,7 @@ func (b *BeaconNode) registerRPCService(ctx *cli.Context) error {
|
||||
ChainService: chainService,
|
||||
OperationService: operationService,
|
||||
POWChainService: web3Service,
|
||||
SyncService: syncChecker,
|
||||
SyncService: syncService,
|
||||
DepositCache: b.depositCache,
|
||||
})
|
||||
|
||||
@@ -511,15 +410,13 @@ func (b *BeaconNode) registerRPCService(ctx *cli.Context) error {
|
||||
|
||||
func (b *BeaconNode) registerPrometheusService(ctx *cli.Context) error {
|
||||
var additionalHandlers []prometheus.Handler
|
||||
if featureconfig.FeatureConfig().UseNewP2P {
|
||||
var p *p2p.Service
|
||||
if err := b.services.FetchService(&p); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
additionalHandlers = append(additionalHandlers, prometheus.Handler{Path: "/p2p", Handler: p.InfoHandler})
|
||||
var p *p2p.Service
|
||||
if err := b.services.FetchService(&p); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
additionalHandlers = append(additionalHandlers, prometheus.Handler{Path: "/p2p", Handler: p.InfoHandler})
|
||||
|
||||
service := prometheus.NewPrometheusService(
|
||||
fmt.Sprintf(":%d", ctx.GlobalInt64(cmd.MonitoringPortFlag.Name)),
|
||||
b.services,
|
||||
@@ -530,15 +427,6 @@ func (b *BeaconNode) registerPrometheusService(ctx *cli.Context) error {
|
||||
return b.services.RegisterService(service)
|
||||
}
|
||||
|
||||
func (b *BeaconNode) registerAttestationService() error {
|
||||
attsService := attestation.NewAttestationService(context.Background(),
|
||||
&attestation.Config{
|
||||
BeaconDB: b.db,
|
||||
})
|
||||
|
||||
return b.services.RegisterService(attsService)
|
||||
}
|
||||
|
||||
func (b *BeaconNode) registerGRPCGateway(ctx *cli.Context) error {
|
||||
gatewayPort := ctx.GlobalInt(flags.GRPCGatewayPort.Name)
|
||||
if gatewayPort > 0 {
|
||||
|
||||
@@ -23,6 +23,7 @@ func TestNodeClose_OK(t *testing.T) {
|
||||
set.String("web3provider", "ws//127.0.0.1:8546", "web3 provider ws or IPC endpoint")
|
||||
set.Bool("test-skip-pow", true, "skip pow dial")
|
||||
set.String("datadir", tmp, "node data directory")
|
||||
set.String("p2p-encoding", "ssz", "p2p encoding scheme")
|
||||
set.Bool("demo-config", true, "demo configuration")
|
||||
set.String("deposit-contract", "0x0000000000000000000000000000000000000000", "deposit contract address")
|
||||
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/flags"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/cmd"
|
||||
p2p "github.com/prysmaticlabs/prysm/shared/deprecated-p2p"
|
||||
"github.com/prysmaticlabs/prysm/shared/deprecated-p2p/adapter/metric"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// Deprecated: Do not use. See #3147.
|
||||
var deprecatedTopicMappings = map[pb.Topic]proto.Message{
|
||||
pb.Topic_BEACON_BLOCK_ANNOUNCE: &pb.BeaconBlockAnnounce{},
|
||||
pb.Topic_BEACON_BLOCK_REQUEST: &pb.BeaconBlockRequest{},
|
||||
pb.Topic_BEACON_BLOCK_REQUEST_BY_SLOT_NUMBER: &pb.BeaconBlockRequestBySlotNumber{},
|
||||
pb.Topic_BEACON_BLOCK_RESPONSE: &pb.BeaconBlockResponse{},
|
||||
pb.Topic_BATCHED_BEACON_BLOCK_REQUEST: &pb.BatchedBeaconBlockRequest{},
|
||||
pb.Topic_BATCHED_BEACON_BLOCK_RESPONSE: &pb.BatchedBeaconBlockResponse{},
|
||||
pb.Topic_CHAIN_HEAD_REQUEST: &pb.ChainHeadRequest{},
|
||||
pb.Topic_CHAIN_HEAD_RESPONSE: &pb.ChainHeadResponse{},
|
||||
pb.Topic_BEACON_STATE_HASH_ANNOUNCE: &pb.BeaconStateHashAnnounce{},
|
||||
pb.Topic_BEACON_STATE_REQUEST: &pb.BeaconStateRequest{},
|
||||
pb.Topic_BEACON_STATE_RESPONSE: &pb.BeaconStateResponse{},
|
||||
pb.Topic_BEACON_ATTESTATION: ðpb.Attestation{},
|
||||
}
|
||||
|
||||
// Deprecated: Do not use. See #3147.
|
||||
func deprecatedConfigureP2P(ctx *cli.Context) (*p2p.Server, error) {
|
||||
contractAddress := ctx.GlobalString(flags.DepositContractFlag.Name)
|
||||
if contractAddress == "" {
|
||||
var err error
|
||||
contractAddress, err = fetchDepositContract()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
staticPeers := []string{}
|
||||
for _, entry := range ctx.GlobalStringSlice(cmd.StaticPeers.Name) {
|
||||
peers := strings.Split(entry, ",")
|
||||
staticPeers = append(staticPeers, peers...)
|
||||
}
|
||||
|
||||
s, err := p2p.NewServer(&p2p.ServerConfig{
|
||||
NoDiscovery: ctx.GlobalBool(cmd.NoDiscovery.Name),
|
||||
StaticPeers: staticPeers,
|
||||
BootstrapNodeAddr: ctx.GlobalString(cmd.BootstrapNode.Name),
|
||||
RelayNodeAddr: ctx.GlobalString(cmd.RelayNode.Name),
|
||||
HostAddress: ctx.GlobalString(cmd.P2PHost.Name),
|
||||
Port: ctx.GlobalInt(cmd.P2PPort.Name),
|
||||
MaxPeers: ctx.GlobalInt(cmd.P2PMaxPeers.Name),
|
||||
PrvKey: ctx.GlobalString(cmd.P2PPrivKey.Name),
|
||||
DepositContractAddress: contractAddress,
|
||||
WhitelistCIDR: ctx.GlobalString(cmd.P2PWhitelist.Name),
|
||||
EnableUPnP: ctx.GlobalBool(cmd.EnableUPnPFlag.Name),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
adapters := []p2p.Adapter{}
|
||||
if !ctx.GlobalBool(cmd.DisableMonitoringFlag.Name) {
|
||||
adapters = append(adapters, metric.New())
|
||||
}
|
||||
|
||||
for k, v := range deprecatedTopicMappings {
|
||||
s.RegisterTopic(k.String(), v, adapters...)
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
@@ -10,21 +10,20 @@ import (
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
handler "github.com/prysmaticlabs/prysm/shared/messagehandler"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
var log = logrus.WithField("prefix", "operation")
|
||||
@@ -249,17 +248,9 @@ func (s *Service) HandleAttestation(ctx context.Context, message proto.Message)
|
||||
return err
|
||||
}
|
||||
|
||||
var root [32]byte
|
||||
if _, isLegacyDB := s.beaconDB.(*db.BeaconDB); isLegacyDB {
|
||||
root, err = hashutil.HashProto(attestation.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
root, err = ssz.HashTreeRoot(attestation.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
root, err := ssz.HashTreeRoot(attestation.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
incomingAttBits := attestation.AggregationBits
|
||||
@@ -336,18 +327,9 @@ func (s *Service) handleProcessedBlock(ctx context.Context, message proto.Messag
|
||||
// after they have been included in a beacon block.
|
||||
func (s *Service) removeAttestationsFromPool(ctx context.Context, attestations []*ethpb.Attestation) error {
|
||||
for _, attestation := range attestations {
|
||||
var root [32]byte
|
||||
var err error
|
||||
if _, isLegacyDB := s.beaconDB.(*db.BeaconDB); isLegacyDB {
|
||||
root, err = hashutil.HashProto(attestation.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
root, err = ssz.HashTreeRoot(attestation.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
root, err := ssz.HashTreeRoot(attestation.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s.beaconDB.HasAttestation(ctx, root) {
|
||||
|
||||
@@ -5,7 +5,6 @@ go_library(
|
||||
srcs = [
|
||||
"broadcaster.go",
|
||||
"config.go",
|
||||
"deprecated.go",
|
||||
"discovery.go",
|
||||
"doc.go",
|
||||
"gossip_topic_mappings.go",
|
||||
@@ -27,8 +26,6 @@ go_library(
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared:go_default_library",
|
||||
"//shared/deprecated-p2p:go_default_library",
|
||||
"//shared/event:go_default_library",
|
||||
"//shared/iputils:go_default_library",
|
||||
"@com_github_btcsuite_btcd//btcec:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//crypto:go_default_library",
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package p2p
|
||||
|
||||
import (
|
||||
"github.com/gogo/protobuf/proto"
|
||||
deprecatedp2p "github.com/prysmaticlabs/prysm/shared/deprecated-p2p"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
)
|
||||
|
||||
// DeprecatedSubscriber exists for backwards compatibility.
|
||||
// DEPRECATED: Do not use. This exists for backwards compatibility but may be removed.
|
||||
type DeprecatedSubscriber interface {
|
||||
Subscribe(msg proto.Message, channel chan deprecatedp2p.Message) event.Subscription
|
||||
}
|
||||
@@ -21,7 +21,6 @@ type P2P interface {
|
||||
HandshakeManager
|
||||
Sender
|
||||
ConnectionHandler
|
||||
DeprecatedSubscriber
|
||||
}
|
||||
|
||||
// Broadcaster broadcasts messages to peers over the p2p pubsub protocol.
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p/discv5"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/karlseguin/ccache"
|
||||
"github.com/libp2p/go-libp2p"
|
||||
"github.com/libp2p/go-libp2p-core/host"
|
||||
@@ -19,8 +18,6 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/encoder"
|
||||
"github.com/prysmaticlabs/prysm/shared"
|
||||
deprecatedp2p "github.com/prysmaticlabs/prysm/shared/deprecated-p2p"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
)
|
||||
|
||||
var _ = shared.Service(&Service{})
|
||||
@@ -252,10 +249,3 @@ func logIP4Addr(id peer.ID, addrs ...ma.Multiaddr) {
|
||||
}
|
||||
log.Infof("Node's listening multiaddr is %s", correctAddr.String()+"/p2p/"+id.String())
|
||||
}
|
||||
|
||||
// Subscribe to some topic.
|
||||
// TODO(3147): Remove
|
||||
// DEPRECATED: Do not use.
|
||||
func (s *Service) Subscribe(_ proto.Message, _ chan deprecatedp2p.Message) event.Subscription {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -12,8 +12,6 @@ go_library(
|
||||
deps = [
|
||||
"//beacon-chain/p2p/encoder:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/deprecated-p2p:go_default_library",
|
||||
"//shared/event:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p_blankhost//:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p_core//:go_default_library",
|
||||
|
||||
@@ -19,8 +19,6 @@ import (
|
||||
swarmt "github.com/libp2p/go-libp2p-swarm/testing"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/encoder"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
deprecatedp2p "github.com/prysmaticlabs/prysm/shared/deprecated-p2p"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -199,12 +197,6 @@ func (p *TestP2P) Send(ctx context.Context, msg proto.Message, pid peer.ID) (net
|
||||
return stream, nil
|
||||
}
|
||||
|
||||
// Subscribe to some topic. Not implemented.
|
||||
func (p *TestP2P) Subscribe(msg proto.Message, ch chan deprecatedp2p.Message) event.Subscription {
|
||||
// TODO(3147): remove this.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Started always returns true.
|
||||
func (p *TestP2P) Started() bool {
|
||||
return true
|
||||
|
||||
@@ -52,7 +52,8 @@ go_test(
|
||||
deps = [
|
||||
"//beacon-chain/cache/depositcache:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/db/kv:go_default_library",
|
||||
"//beacon-chain/db/testing:go_default_library",
|
||||
"//contracts/deposit-contract:go_default_library",
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/bls:go_default_library",
|
||||
|
||||
@@ -7,11 +7,10 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
gethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
|
||||
)
|
||||
|
||||
var endpoint = "ws://127.0.0.1"
|
||||
@@ -21,11 +20,8 @@ func TestLatestMainchainInfo_OK(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to set up simulated backend %v", err)
|
||||
}
|
||||
|
||||
beaconDB, err := db.SetupDB()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to set up simulated db instance: %v", err)
|
||||
}
|
||||
beaconDB := dbutil.SetupDB(t)
|
||||
defer dbutil.TeardownDB(t, beaconDB)
|
||||
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{
|
||||
Endpoint: endpoint,
|
||||
DepositContract: testAcc.ContractAddr,
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db/kv"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
@@ -25,7 +25,7 @@ func TestProcessDeposit_OK(t *testing.T) {
|
||||
Reader: &goodReader{},
|
||||
Logger: &goodLogger{},
|
||||
HTTPLogger: &goodLogger{},
|
||||
BeaconDB: &db.BeaconDB{},
|
||||
BeaconDB: &kv.Store{},
|
||||
BlockFetcher: &goodFetcher{},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -66,7 +66,7 @@ func TestProcessDeposit_InvalidMerkleBranch(t *testing.T) {
|
||||
Reader: &goodReader{},
|
||||
Logger: &goodLogger{},
|
||||
HTTPLogger: &goodLogger{},
|
||||
BeaconDB: &db.BeaconDB{},
|
||||
BeaconDB: &kv.Store{},
|
||||
BlockFetcher: &goodFetcher{},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -113,7 +113,7 @@ func TestProcessDeposit_InvalidPublicKey(t *testing.T) {
|
||||
Reader: &goodReader{},
|
||||
Logger: &goodLogger{},
|
||||
HTTPLogger: &goodLogger{},
|
||||
BeaconDB: &db.BeaconDB{},
|
||||
BeaconDB: &kv.Store{},
|
||||
BlockFetcher: &goodFetcher{},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -157,7 +157,7 @@ func TestProcessDeposit_InvalidSignature(t *testing.T) {
|
||||
Reader: &goodReader{},
|
||||
Logger: &goodLogger{},
|
||||
HTTPLogger: &goodLogger{},
|
||||
BeaconDB: &db.BeaconDB{},
|
||||
BeaconDB: &kv.Store{},
|
||||
BlockFetcher: &goodFetcher{},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -204,7 +204,7 @@ func TestProcessDeposit_UnableToVerify(t *testing.T) {
|
||||
Reader: &goodReader{},
|
||||
Logger: &goodLogger{},
|
||||
HTTPLogger: &goodLogger{},
|
||||
BeaconDB: &db.BeaconDB{},
|
||||
BeaconDB: &kv.Store{},
|
||||
BlockFetcher: &goodFetcher{},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -236,7 +236,7 @@ func TestProcessDeposit_IncompleteDeposit(t *testing.T) {
|
||||
Reader: &goodReader{},
|
||||
Logger: &goodLogger{},
|
||||
HTTPLogger: &goodLogger{},
|
||||
BeaconDB: &db.BeaconDB{},
|
||||
BeaconDB: &kv.Store{},
|
||||
BlockFetcher: &goodFetcher{},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -290,7 +290,7 @@ func TestProcessDeposit_AllDepositedSuccessfully(t *testing.T) {
|
||||
Reader: &goodReader{},
|
||||
Logger: &goodLogger{},
|
||||
HTTPLogger: &goodLogger{},
|
||||
BeaconDB: &db.BeaconDB{},
|
||||
BeaconDB: &kv.Store{},
|
||||
BlockFetcher: &goodFetcher{},
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db/kv"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
@@ -40,7 +40,7 @@ func TestProcessDepositLog_OK(t *testing.T) {
|
||||
Logger: &goodLogger{},
|
||||
HTTPLogger: &goodLogger{},
|
||||
ContractBackend: testAcc.Backend,
|
||||
BeaconDB: &db.BeaconDB{},
|
||||
BeaconDB: &kv.Store{},
|
||||
DepositCache: depositcache.NewDepositCache(),
|
||||
BlockFetcher: &goodFetcher{},
|
||||
})
|
||||
@@ -102,7 +102,7 @@ func TestProcessDepositLog_InsertsPendingDeposit(t *testing.T) {
|
||||
Logger: &goodLogger{},
|
||||
HTTPLogger: &goodLogger{},
|
||||
ContractBackend: testAcc.Backend,
|
||||
BeaconDB: &db.BeaconDB{},
|
||||
BeaconDB: &kv.Store{},
|
||||
DepositCache: depositcache.NewDepositCache(),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -249,7 +249,7 @@ func TestProcessETH2GenesisLog_8DuplicatePubkeys(t *testing.T) {
|
||||
Logger: &goodLogger{},
|
||||
HTTPLogger: &goodLogger{},
|
||||
ContractBackend: testAcc.Backend,
|
||||
BeaconDB: &db.BeaconDB{},
|
||||
BeaconDB: &kv.Store{},
|
||||
DepositCache: depositcache.NewDepositCache(),
|
||||
BlockFetcher: &goodFetcher{},
|
||||
})
|
||||
@@ -318,7 +318,7 @@ func TestProcessETH2GenesisLog(t *testing.T) {
|
||||
Logger: &goodLogger{},
|
||||
HTTPLogger: &goodLogger{},
|
||||
ContractBackend: testAcc.Backend,
|
||||
BeaconDB: &db.BeaconDB{},
|
||||
BeaconDB: &kv.Store{},
|
||||
DepositCache: depositcache.NewDepositCache(),
|
||||
BlockFetcher: &goodFetcher{},
|
||||
})
|
||||
@@ -398,7 +398,7 @@ func TestWeb3ServiceProcessDepositLog_RequestMissedDeposits(t *testing.T) {
|
||||
Logger: &goodLogger{},
|
||||
HTTPLogger: testAcc.Backend,
|
||||
ContractBackend: testAcc.Backend,
|
||||
BeaconDB: &db.BeaconDB{},
|
||||
BeaconDB: &kv.Store{},
|
||||
DepositCache: depositcache.NewDepositCache(),
|
||||
BlockFetcher: &goodFetcher{},
|
||||
})
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
gethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
|
||||
depositcontract "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
@@ -136,16 +136,12 @@ func TestNewWeb3Service_OK(t *testing.T) {
|
||||
|
||||
func TestStart_OK(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
|
||||
beaconDB := dbutil.SetupDB(t)
|
||||
defer dbutil.TeardownDB(t, beaconDB)
|
||||
testAcc, err := contracts.Setup()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to set up simulated backend %v", err)
|
||||
}
|
||||
|
||||
beaconDB, err := db.SetupDB()
|
||||
if err != nil {
|
||||
t.Fatalf("Could not set up simulated beacon DB: %v", err)
|
||||
}
|
||||
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{
|
||||
Endpoint: endpoint,
|
||||
DepositContract: testAcc.ContractAddr,
|
||||
|
||||
@@ -25,7 +25,6 @@ go_library(
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/db/filters:go_default_library",
|
||||
"//beacon-chain/db/kv:go_default_library",
|
||||
"//beacon-chain/deprecated-blockchain:go_default_library",
|
||||
"//beacon-chain/operations:go_default_library",
|
||||
"//beacon-chain/p2p:go_default_library",
|
||||
"//beacon-chain/sync:go_default_library",
|
||||
@@ -34,7 +33,6 @@ go_library(
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/event:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/pagination:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/trieutil:go_default_library",
|
||||
|
||||
@@ -6,95 +6,46 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
|
||||
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/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"
|
||||
)
|
||||
|
||||
type attestationReceiver interface {
|
||||
ReceiveAttestation(ctx context.Context, att *ethpb.Attestation) error
|
||||
}
|
||||
|
||||
// AttesterServer defines a server implementation of the gRPC Attester service,
|
||||
// providing RPC methods for validators acting as attesters to broadcast votes on beacon blocks.
|
||||
type AttesterServer struct {
|
||||
p2p p2p.Broadcaster
|
||||
beaconDB db.Database
|
||||
operationService operationService
|
||||
chainService interface{}
|
||||
chainService chainService
|
||||
cache *cache.AttestationCache
|
||||
}
|
||||
|
||||
// SubmitAttestation is a function called by an attester in a sharding validator to vote
|
||||
// on a block via an attestation object as defined in the Ethereum Serenity specification.
|
||||
func (as *AttesterServer) SubmitAttestation(ctx context.Context, att *ethpb.Attestation) (*pb.AttestResponse, error) {
|
||||
if srv, ok := as.chainService.(attestationReceiver); ok {
|
||||
root, err := ssz.SigningRoot(att)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to sign root attestation")
|
||||
}
|
||||
|
||||
if err := as.operationService.HandleAttestation(ctx, att); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := srv.ReceiveAttestation(ctx, att); err != nil {
|
||||
log.WithError(err).Error("could not receive attestation in chain service")
|
||||
}
|
||||
}()
|
||||
|
||||
return &pb.AttestResponse{Root: root[:]}, nil
|
||||
root, err := ssz.SigningRoot(att)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to sign root attestation")
|
||||
}
|
||||
|
||||
if err := as.operationService.HandleAttestation(ctx, att); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Update attestation target for RPC server to run necessary fork choice.
|
||||
// We need to retrieve the head block to get its parent root.
|
||||
head, err := as.beaconDB.Block(ctx, bytesutil.ToBytes32(att.Data.BeaconBlockRoot))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// If the head block is nil, we can't save the attestation target.
|
||||
if head == nil {
|
||||
return nil, fmt.Errorf("could not find head %#x in db", bytesutil.Trunc(att.Data.BeaconBlockRoot))
|
||||
}
|
||||
// TODO(#3088): Remove this when fork-choice is updated to the new one.
|
||||
attestationSlot := att.Data.Target.Epoch * params.BeaconConfig().SlotsPerEpoch
|
||||
attTarget := &pbp2p.AttestationTarget{
|
||||
Slot: attestationSlot,
|
||||
BeaconBlockRoot: att.Data.BeaconBlockRoot,
|
||||
ParentRoot: head.ParentRoot,
|
||||
}
|
||||
db, isLegacyDB := as.beaconDB.(*db.BeaconDB)
|
||||
if isLegacyDB {
|
||||
if err := db.SaveAttestationTarget(ctx, attTarget); err != nil {
|
||||
return nil, fmt.Errorf("could not save attestation target")
|
||||
go func() {
|
||||
if err := as.chainService.ReceiveAttestation(ctx, att); err != nil {
|
||||
log.WithError(err).Error("could not receive attestation in chain service")
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err := as.p2p.Broadcast(ctx, att); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hash, err := hashutil.HashProto(att)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pb.AttestResponse{Root: hash[:]}, nil
|
||||
return &pb.AttestResponse{Root: root[:]}, nil
|
||||
}
|
||||
|
||||
// RequestAttestation requests that the beacon node produce an IndexedAttestation,
|
||||
@@ -135,28 +86,8 @@ func (as *AttesterServer) RequestAttestation(ctx context.Context, req *pb.Attest
|
||||
}
|
||||
}()
|
||||
|
||||
// Set the attestation data's beacon block root = hash_tree_root(head) where head
|
||||
// is the validator's view of the head block of the beacon chain during the slot.
|
||||
var headRoot []byte
|
||||
var headState *pbp2p.BeaconState
|
||||
if d, isLegacyDB := as.beaconDB.(*db.BeaconDB); isLegacyDB {
|
||||
headBlock, err := d.ChainHead()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to retrieve chain head")
|
||||
}
|
||||
headState, err = as.beaconDB.HeadState(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not fetch head state")
|
||||
}
|
||||
r, err := ssz.SigningRoot(headBlock)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not tree hash beacon block")
|
||||
}
|
||||
headRoot = r[:]
|
||||
} else {
|
||||
headState = as.chainService.(blockchain.HeadRetriever).HeadState()
|
||||
headRoot = as.chainService.(blockchain.HeadRetriever).HeadRoot()
|
||||
}
|
||||
headState := as.chainService.HeadState()
|
||||
headRoot := as.chainService.HeadRoot()
|
||||
|
||||
headState, err = state.ProcessSlots(ctx, headState, req.Slot)
|
||||
if err != nil {
|
||||
|
||||
@@ -31,6 +31,7 @@ func TestSubmitAttestation_OK(t *testing.T) {
|
||||
|
||||
mockOperationService := &mockOperationService{}
|
||||
attesterServer := &AttesterServer{
|
||||
chainService: &mock.ChainService{},
|
||||
operationService: mockOperationService,
|
||||
p2p: &mockBroadcaster{},
|
||||
beaconDB: db,
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"sort"
|
||||
|
||||
ptypes "github.com/gogo/protobuf/types"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
@@ -13,7 +12,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db/filters"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db/kv"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/operations"
|
||||
pbp2p "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/pagination"
|
||||
@@ -26,9 +24,9 @@ import (
|
||||
// providing RPC endpoints to access data relevant to the Ethereum 2.0 phase 0
|
||||
// beacon chain.
|
||||
type BeaconChainServer struct {
|
||||
beaconDB db.Database
|
||||
head interface{}
|
||||
pool operations.Pool
|
||||
beaconDB db.Database
|
||||
chainService blockchain.HeadRetriever
|
||||
pool operations.Pool
|
||||
}
|
||||
|
||||
// sortableAttestations implements the Sort interface to sort attestations
|
||||
@@ -96,18 +94,9 @@ func (bs *BeaconChainServer) ListAttestations(
|
||||
func (bs *BeaconChainServer) AttestationPool(
|
||||
ctx context.Context, _ *ptypes.Empty,
|
||||
) (*ethpb.AttestationPoolResponse, error) {
|
||||
var headBlock *ethpb.BeaconBlock
|
||||
var err error
|
||||
if d, isLegacyDB := bs.beaconDB.(*db.BeaconDB); isLegacyDB {
|
||||
headBlock, err = d.ChainHead()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
headBlock, err = bs.beaconDB.(*kv.Store).HeadBlock(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
headBlock, err := bs.beaconDB.(*kv.Store).HeadBlock(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if headBlock == nil {
|
||||
return nil, status.Error(codes.Internal, "no head block found in db")
|
||||
@@ -140,22 +129,11 @@ func (bs *BeaconChainServer) ListBlocks(
|
||||
startSlot := q.Epoch * params.BeaconConfig().SlotsPerEpoch
|
||||
endSlot := startSlot + params.BeaconConfig().SlotsPerEpoch - 1
|
||||
|
||||
var blks []*ethpb.BeaconBlock
|
||||
if d, isLegacyDB := bs.beaconDB.(*db.BeaconDB); isLegacyDB {
|
||||
for i := startSlot; i < endSlot; i++ {
|
||||
b, err := d.BlocksBySlot(ctx, i)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "could not retrieve blocks for slot %d: %v", i, err)
|
||||
}
|
||||
blks = append(blks, b...)
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
blks, err = bs.beaconDB.Blocks(ctx, filters.NewFilter().SetStartSlot(startSlot).SetEndSlot(endSlot))
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get blocks: %v", err)
|
||||
}
|
||||
blks, err := bs.beaconDB.Blocks(ctx, filters.NewFilter().SetStartSlot(startSlot).SetEndSlot(endSlot))
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "failed to get blocks: %v", err)
|
||||
}
|
||||
|
||||
numBlks := len(blks)
|
||||
if numBlks == 0 {
|
||||
return ðpb.ListBlocksResponse{Blocks: make([]*ethpb.BeaconBlock, 0), TotalSize: 0}, nil
|
||||
@@ -188,13 +166,7 @@ func (bs *BeaconChainServer) ListBlocks(
|
||||
}, nil
|
||||
|
||||
case *ethpb.ListBlocksRequest_Slot:
|
||||
var blks []*ethpb.BeaconBlock
|
||||
var err error
|
||||
if d, isLegacyDB := bs.beaconDB.(*db.BeaconDB); isLegacyDB {
|
||||
blks, err = d.BlocksBySlot(ctx, q.Slot)
|
||||
} else {
|
||||
blks, err = bs.beaconDB.Blocks(ctx, filters.NewFilter().SetStartSlot(q.Slot).SetEndSlot(q.Slot))
|
||||
}
|
||||
blks, err := bs.beaconDB.Blocks(ctx, filters.NewFilter().SetStartSlot(q.Slot).SetEndSlot(q.Slot))
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "could not retrieve blocks for slot %d: %v", q.Slot, err)
|
||||
}
|
||||
@@ -225,13 +197,13 @@ func (bs *BeaconChainServer) ListBlocks(
|
||||
// This includes the head block slot and root as well as information about
|
||||
// the most recent finalized and justified slots.
|
||||
func (bs *BeaconChainServer) GetChainHead(ctx context.Context, _ *ptypes.Empty) (*ethpb.ChainHead, error) {
|
||||
finalizedCheckpoint := bs.head.(blockchain.HeadRetriever).HeadState().FinalizedCheckpoint
|
||||
justifiedCheckpoint := bs.head.(blockchain.HeadRetriever).HeadState().CurrentJustifiedCheckpoint
|
||||
prevJustifiedCheckpoint := bs.head.(blockchain.HeadRetriever).HeadState().PreviousJustifiedCheckpoint
|
||||
finalizedCheckpoint := bs.chainService.HeadState().FinalizedCheckpoint
|
||||
justifiedCheckpoint := bs.chainService.HeadState().CurrentJustifiedCheckpoint
|
||||
prevJustifiedCheckpoint := bs.chainService.HeadState().PreviousJustifiedCheckpoint
|
||||
|
||||
return ðpb.ChainHead{
|
||||
BlockRoot: bs.head.(blockchain.HeadRetriever).HeadRoot(),
|
||||
BlockSlot: bs.head.(blockchain.HeadRetriever).HeadSlot(),
|
||||
BlockRoot: bs.chainService.HeadRoot(),
|
||||
BlockSlot: bs.chainService.HeadSlot(),
|
||||
FinalizedBlockRoot: finalizedCheckpoint.Root,
|
||||
FinalizedSlot: finalizedCheckpoint.Epoch * params.BeaconConfig().SlotsPerEpoch,
|
||||
JustifiedBlockRoot: justifiedCheckpoint.Root,
|
||||
@@ -266,21 +238,12 @@ func (bs *BeaconChainServer) ListValidatorBalances(
|
||||
continue
|
||||
}
|
||||
|
||||
var index uint64
|
||||
var ok bool
|
||||
if d, isLegacyDB := bs.beaconDB.(*db.BeaconDB); isLegacyDB {
|
||||
index, err = d.ValidatorIndexDeprecated(pubKey)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "could not retrieve validator index: %v", err)
|
||||
}
|
||||
} else {
|
||||
index, ok, err = bs.beaconDB.ValidatorIndex(ctx, bytesutil.ToBytes48(pubKey))
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "could not retrieve validator index: %v", err)
|
||||
}
|
||||
if !ok {
|
||||
return nil, status.Errorf(codes.Internal, "could not find validator index for public key %#x not found", pubKey)
|
||||
}
|
||||
index, ok, err := bs.beaconDB.ValidatorIndex(ctx, bytesutil.ToBytes48(pubKey))
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "could not retrieve validator index: %v", err)
|
||||
}
|
||||
if !ok {
|
||||
return nil, status.Errorf(codes.Internal, "could not find validator index for public key %#x not found", pubKey)
|
||||
}
|
||||
|
||||
filtered[index] = true
|
||||
@@ -327,19 +290,19 @@ func (bs *BeaconChainServer) GetValidators(
|
||||
req.PageSize, params.BeaconConfig().MaxPageSize)
|
||||
}
|
||||
|
||||
head, err := bs.beaconDB.HeadState(ctx)
|
||||
headState, err := bs.beaconDB.HeadState(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "could not get head state %v", err)
|
||||
}
|
||||
|
||||
validatorCount := len(head.Validators)
|
||||
validatorCount := len(headState.Validators)
|
||||
start, end, nextPageToken, err := pagination.StartAndEndPage(req.PageToken, int(req.PageSize), validatorCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := ðpb.Validators{
|
||||
Validators: head.Validators[start:end],
|
||||
Validators: headState.Validators[start:end],
|
||||
TotalSize: int32(validatorCount),
|
||||
NextPageToken: nextPageToken,
|
||||
}
|
||||
@@ -387,21 +350,12 @@ func (bs *BeaconChainServer) ListValidatorAssignments(
|
||||
|
||||
// Filter out assignments by public keys.
|
||||
for _, pubKey := range req.PublicKeys {
|
||||
var index uint64
|
||||
var ok bool
|
||||
if d, isLegacyDB := bs.beaconDB.(*db.BeaconDB); isLegacyDB {
|
||||
index, err = d.ValidatorIndexDeprecated(pubKey)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "could not retrieve validator index: %v", err)
|
||||
}
|
||||
} else {
|
||||
index, ok, err = bs.beaconDB.ValidatorIndex(ctx, bytesutil.ToBytes48(pubKey))
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "could not retrieve validator index: %v", err)
|
||||
}
|
||||
if !ok {
|
||||
return nil, status.Errorf(codes.Internal, "could not find validator index for public key %#x not found", pubKey)
|
||||
}
|
||||
index, ok, err := bs.beaconDB.ValidatorIndex(ctx, bytesutil.ToBytes48(pubKey))
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "could not retrieve validator index: %v", err)
|
||||
}
|
||||
if !ok {
|
||||
return nil, status.Errorf(codes.Internal, "could not find validator index for public key %#x not found", pubKey)
|
||||
}
|
||||
|
||||
filtered[index] = true
|
||||
@@ -506,17 +460,7 @@ func (bs *BeaconChainServer) GetValidatorParticipation(
|
||||
ctx context.Context, req *ethpb.GetValidatorParticipationRequest,
|
||||
) (*ethpb.ValidatorParticipation, error) {
|
||||
|
||||
var headState *pbp2p.BeaconState
|
||||
var err error
|
||||
if _, isLegacyDB := bs.beaconDB.(*db.BeaconDB); isLegacyDB {
|
||||
headState, err = bs.beaconDB.HeadState(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not retrieve beacon state")
|
||||
}
|
||||
} else {
|
||||
headState = bs.head.(blockchain.HeadRetriever).HeadState()
|
||||
}
|
||||
|
||||
headState := bs.chainService.HeadState()
|
||||
currentEpoch := helpers.SlotToEpoch(headState.Slot)
|
||||
finalized := currentEpoch == headState.FinalizedCheckpoint.Epoch
|
||||
|
||||
|
||||
@@ -806,8 +806,8 @@ func TestBeaconChainServer_GetValidatorsParticipation(t *testing.T) {
|
||||
}
|
||||
|
||||
bs := &BeaconChainServer{
|
||||
beaconDB: db,
|
||||
head: &mock.ChainService{State: s},
|
||||
beaconDB: db,
|
||||
chainService: &mock.ChainService{State: s},
|
||||
}
|
||||
|
||||
res, err := bs.GetValidatorParticipation(ctx, ðpb.GetValidatorParticipationRequest{Epoch: epoch})
|
||||
@@ -1000,7 +1000,7 @@ func TestBeaconChainServer_GetChainHead(t *testing.T) {
|
||||
FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 1, Root: []byte{'C'}},
|
||||
}
|
||||
|
||||
bs := &BeaconChainServer{head: &mock.ChainService{State: s}}
|
||||
bs := &BeaconChainServer{chainService: &mock.ChainService{State: s}}
|
||||
|
||||
head, err := bs.GetChainHead(context.Background(), nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -12,16 +12,11 @@ import (
|
||||
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
"github.com/prysmaticlabs/prysm/shared/trieutil"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type stateFeedListener interface {
|
||||
StateInitializedFeed() *event.Feed
|
||||
}
|
||||
|
||||
// BeaconServer defines a server implementation of the gRPC Beacon service,
|
||||
// providing RPC endpoints for obtaining the canonical beacon chain head,
|
||||
// fetching latest observed attestations, and more.
|
||||
@@ -29,7 +24,7 @@ type BeaconServer struct {
|
||||
beaconDB db.Database
|
||||
ctx context.Context
|
||||
powChainService powChainService
|
||||
chainService stateFeedListener
|
||||
chainService chainService
|
||||
operationService operationService
|
||||
incomingAttestation chan *ethpb.Attestation
|
||||
canonicalStateChan chan *pbp2p.BeaconState
|
||||
@@ -75,16 +70,7 @@ func (bs *BeaconServer) WaitForChainStart(req *ptypes.Empty, stream pb.BeaconSer
|
||||
// CanonicalHead of the current beacon chain. This method is requested on-demand
|
||||
// by a validator when it is their time to propose or attest.
|
||||
func (bs *BeaconServer) CanonicalHead(ctx context.Context, req *ptypes.Empty) (*ethpb.BeaconBlock, error) {
|
||||
var headBlock *ethpb.BeaconBlock
|
||||
var err error
|
||||
if d, isLegacyDB := bs.beaconDB.(*db.BeaconDB); isLegacyDB {
|
||||
headBlock, err = d.ChainHead()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get canonical head block")
|
||||
}
|
||||
} else {
|
||||
headBlock = bs.chainService.(blockchain.HeadRetriever).HeadBlock()
|
||||
}
|
||||
headBlock := bs.chainService.(blockchain.HeadRetriever).HeadBlock()
|
||||
return headBlock, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -13,15 +12,12 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
ptypes "github.com/gogo/protobuf/types"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
|
||||
"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"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/trieutil"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
@@ -171,12 +167,6 @@ func (m *mockPOWChainService) ChainStartETH1Data() *ethpb.Eth1Data {
|
||||
return m.eth1Data
|
||||
}
|
||||
|
||||
type mockStateFeedListener struct{}
|
||||
|
||||
func (m *mockStateFeedListener) StateInitializedFeed() *event.Feed {
|
||||
return new(event.Feed)
|
||||
}
|
||||
|
||||
func TestWaitForChainStart_ContextClosed(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
beaconServer := &BeaconServer{
|
||||
@@ -184,7 +174,7 @@ func TestWaitForChainStart_ContextClosed(t *testing.T) {
|
||||
powChainService: &faultyPOWChainService{
|
||||
chainStartFeed: new(event.Feed),
|
||||
},
|
||||
chainService: &mockStateFeedListener{},
|
||||
chainService: &mock.ChainService{},
|
||||
}
|
||||
exitRoutine := make(chan bool)
|
||||
ctrl := gomock.NewController(t)
|
||||
@@ -206,7 +196,7 @@ func TestWaitForChainStart_AlreadyStarted(t *testing.T) {
|
||||
powChainService: &mockPOWChainService{
|
||||
chainStartFeed: new(event.Feed),
|
||||
},
|
||||
chainService: &mockStateFeedListener{},
|
||||
chainService: &mock.ChainService{},
|
||||
}
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
@@ -230,7 +220,7 @@ func TestWaitForChainStart_NotStartedThenLogFired(t *testing.T) {
|
||||
powChainService: &faultyPOWChainService{
|
||||
chainStartFeed: new(event.Feed),
|
||||
},
|
||||
chainService: &mockStateFeedListener{},
|
||||
chainService: &mock.ChainService{},
|
||||
}
|
||||
exitRoutine := make(chan bool)
|
||||
ctrl := gomock.NewController(t)
|
||||
@@ -252,650 +242,3 @@ func TestWaitForChainStart_NotStartedThenLogFired(t *testing.T) {
|
||||
exitRoutine <- true
|
||||
testutil.AssertLogsContain(t, hook, "Sending ChainStart log and genesis time to connected validator clients")
|
||||
}
|
||||
|
||||
func TestBlockTree_OK(t *testing.T) {
|
||||
t.Skip() // TODO(3219): Add after new fork choice service.
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
ctx := context.Background()
|
||||
// We want to ensure that if our block tree looks as follows, the RPC response
|
||||
// returns the correct information.
|
||||
// /->[A, Slot 3, 3 Votes]->[B, Slot 4, 3 Votes]
|
||||
// [Justified Block]->[C, Slot 3, 2 Votes]
|
||||
// \->[D, Slot 3, 2 Votes]->[SKIP SLOT]->[E, Slot 5, 1 Vote]
|
||||
var validators []*ethpb.Validator
|
||||
for i := 0; i < 13; i++ {
|
||||
validators = append(validators, ðpb.Validator{ExitEpoch: params.BeaconConfig().FarFutureEpoch})
|
||||
}
|
||||
justifiedState := &pbp2p.BeaconState{
|
||||
Slot: 0,
|
||||
Balances: make([]uint64, 11),
|
||||
Validators: validators,
|
||||
}
|
||||
for i := 0; i < len(justifiedState.Balances); i++ {
|
||||
justifiedState.Balances[i] = params.BeaconConfig().MaxEffectiveBalance
|
||||
}
|
||||
if err := db.SaveJustifiedState(justifiedState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
justifiedBlock := ðpb.BeaconBlock{
|
||||
Slot: 0,
|
||||
}
|
||||
if err := db.SaveJustifiedBlock(justifiedBlock); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
justifiedRoot, _ := ssz.SigningRoot(justifiedBlock)
|
||||
|
||||
balances := []uint64{params.BeaconConfig().MaxEffectiveBalance}
|
||||
b1 := ðpb.BeaconBlock{
|
||||
Slot: 3,
|
||||
ParentRoot: justifiedRoot[:],
|
||||
StateRoot: []byte{0x1},
|
||||
}
|
||||
b1Root, _ := ssz.SigningRoot(b1)
|
||||
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
||||
Slot: 3,
|
||||
Validators: validators,
|
||||
Balances: balances,
|
||||
}, b1Root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b2 := ðpb.BeaconBlock{
|
||||
Slot: 3,
|
||||
ParentRoot: justifiedRoot[:],
|
||||
StateRoot: []byte{0x2},
|
||||
}
|
||||
b2Root, _ := ssz.SigningRoot(b2)
|
||||
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
||||
Slot: 3,
|
||||
Validators: validators,
|
||||
Balances: balances,
|
||||
}, b2Root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b3 := ðpb.BeaconBlock{
|
||||
Slot: 3,
|
||||
ParentRoot: justifiedRoot[:],
|
||||
StateRoot: []byte{0x3},
|
||||
}
|
||||
b3Root, _ := ssz.SigningRoot(b3)
|
||||
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
||||
Slot: 3,
|
||||
Validators: validators,
|
||||
Balances: balances,
|
||||
}, b3Root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b4 := ðpb.BeaconBlock{
|
||||
Slot: 4,
|
||||
ParentRoot: b1Root[:],
|
||||
StateRoot: []byte{0x4},
|
||||
}
|
||||
b4Root, _ := ssz.SigningRoot(b4)
|
||||
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
||||
Slot: 4,
|
||||
Validators: validators,
|
||||
Balances: balances,
|
||||
}, b4Root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b5 := ðpb.BeaconBlock{
|
||||
Slot: 5,
|
||||
ParentRoot: b3Root[:],
|
||||
StateRoot: []byte{0x5},
|
||||
}
|
||||
b5Root, _ := ssz.SigningRoot(b5)
|
||||
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
||||
Slot: 5,
|
||||
Validators: validators,
|
||||
Balances: balances,
|
||||
}, b5Root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
attestationTargets := make(map[uint64]*pbp2p.AttestationTarget)
|
||||
// We give block A 3 votes.
|
||||
attestationTargets[0] = &pbp2p.AttestationTarget{
|
||||
Slot: b1.Slot,
|
||||
ParentRoot: b1.ParentRoot,
|
||||
BeaconBlockRoot: b1Root[:],
|
||||
}
|
||||
attestationTargets[1] = &pbp2p.AttestationTarget{
|
||||
Slot: b1.Slot,
|
||||
ParentRoot: b1.ParentRoot,
|
||||
BeaconBlockRoot: b1Root[:],
|
||||
}
|
||||
attestationTargets[2] = &pbp2p.AttestationTarget{
|
||||
Slot: b1.Slot,
|
||||
ParentRoot: b1.ParentRoot,
|
||||
BeaconBlockRoot: b1Root[:],
|
||||
}
|
||||
|
||||
// We give block C 2 votes.
|
||||
attestationTargets[3] = &pbp2p.AttestationTarget{
|
||||
Slot: b2.Slot,
|
||||
ParentRoot: b2.ParentRoot,
|
||||
BeaconBlockRoot: b2Root[:],
|
||||
}
|
||||
attestationTargets[4] = &pbp2p.AttestationTarget{
|
||||
Slot: b2.Slot,
|
||||
ParentRoot: b2.ParentRoot,
|
||||
BeaconBlockRoot: b2Root[:],
|
||||
}
|
||||
|
||||
// We give block D 2 votes.
|
||||
attestationTargets[5] = &pbp2p.AttestationTarget{
|
||||
Slot: b3.Slot,
|
||||
ParentRoot: b3.ParentRoot,
|
||||
BeaconBlockRoot: b3Root[:],
|
||||
}
|
||||
attestationTargets[6] = &pbp2p.AttestationTarget{
|
||||
Slot: b3.Slot,
|
||||
ParentRoot: b3.ParentRoot,
|
||||
BeaconBlockRoot: b3Root[:],
|
||||
}
|
||||
|
||||
// We give block B 3 votes.
|
||||
attestationTargets[7] = &pbp2p.AttestationTarget{
|
||||
Slot: b4.Slot,
|
||||
ParentRoot: b4.ParentRoot,
|
||||
BeaconBlockRoot: b4Root[:],
|
||||
}
|
||||
attestationTargets[8] = &pbp2p.AttestationTarget{
|
||||
Slot: b4.Slot,
|
||||
ParentRoot: b4.ParentRoot,
|
||||
BeaconBlockRoot: b4Root[:],
|
||||
}
|
||||
attestationTargets[9] = &pbp2p.AttestationTarget{
|
||||
Slot: b4.Slot,
|
||||
ParentRoot: b4.ParentRoot,
|
||||
BeaconBlockRoot: b4Root[:],
|
||||
}
|
||||
|
||||
// We give block E 1 vote.
|
||||
attestationTargets[10] = &pbp2p.AttestationTarget{
|
||||
Slot: b5.Slot,
|
||||
ParentRoot: b5.ParentRoot,
|
||||
BeaconBlockRoot: b5Root[:],
|
||||
}
|
||||
|
||||
tree := []*pb.BlockTreeResponse_TreeNode{
|
||||
{
|
||||
Block: b1,
|
||||
ParticipatedVotes: 3 * params.BeaconConfig().MaxEffectiveBalance,
|
||||
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
||||
},
|
||||
{
|
||||
Block: b2,
|
||||
ParticipatedVotes: 2 * params.BeaconConfig().MaxEffectiveBalance,
|
||||
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
||||
},
|
||||
{
|
||||
Block: b3,
|
||||
ParticipatedVotes: 2 * params.BeaconConfig().MaxEffectiveBalance,
|
||||
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
||||
},
|
||||
{
|
||||
Block: b4,
|
||||
ParticipatedVotes: 3 * params.BeaconConfig().MaxEffectiveBalance,
|
||||
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
||||
},
|
||||
{
|
||||
Block: b5,
|
||||
ParticipatedVotes: 1 * params.BeaconConfig().MaxEffectiveBalance,
|
||||
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
||||
},
|
||||
}
|
||||
for _, node := range tree {
|
||||
if err := db.SaveBlockDeprecated(node.Block); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
headState := &pbp2p.BeaconState{
|
||||
Slot: b4.Slot,
|
||||
}
|
||||
if err := db.UpdateChainHead(ctx, b4, headState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
bs := &BeaconServer{
|
||||
beaconDB: db,
|
||||
}
|
||||
sort.Slice(tree, func(i, j int) bool {
|
||||
return string(tree[i].Block.StateRoot) < string(tree[j].Block.StateRoot)
|
||||
})
|
||||
|
||||
resp, err := bs.BlockTree(ctx, &ptypes.Empty{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(resp.Tree) != 4 {
|
||||
t.Errorf("Wanted len %d, received %d", 4, len(resp.Tree))
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockTreeBySlots_ArgsValildation(t *testing.T) {
|
||||
t.Skip() // TODO(3219): Add after new fork choice service.
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
ctx := context.Background()
|
||||
// We want to ensure that if our block tree looks as follows, the RPC response
|
||||
// returns the correct information.
|
||||
// /->[A, Slot 3, 3 Votes]->[B, Slot 4, 3 Votes]
|
||||
// [Justified Block]->[C, Slot 3, 2 Votes]
|
||||
// \->[D, Slot 3, 2 Votes]->[SKIP SLOT]->[E, Slot 5, 1 Vote]
|
||||
justifiedState := &pbp2p.BeaconState{
|
||||
Slot: 0,
|
||||
Balances: make([]uint64, 11),
|
||||
}
|
||||
for i := 0; i < len(justifiedState.Balances); i++ {
|
||||
justifiedState.Balances[i] = params.BeaconConfig().MaxEffectiveBalance
|
||||
}
|
||||
if err := db.SaveJustifiedState(justifiedState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
justifiedBlock := ðpb.BeaconBlock{
|
||||
Slot: 0,
|
||||
}
|
||||
if err := db.SaveJustifiedBlock(justifiedBlock); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
justifiedRoot, _ := ssz.SigningRoot(justifiedBlock)
|
||||
validators := []*ethpb.Validator{{ExitEpoch: params.BeaconConfig().FarFutureEpoch}}
|
||||
balances := []uint64{params.BeaconConfig().MaxEffectiveBalance}
|
||||
b1 := ðpb.BeaconBlock{
|
||||
Slot: 3,
|
||||
ParentRoot: justifiedRoot[:],
|
||||
}
|
||||
b1Root, _ := ssz.SigningRoot(b1)
|
||||
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
||||
Slot: 3,
|
||||
Validators: validators,
|
||||
Balances: balances,
|
||||
}, b1Root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b2 := ðpb.BeaconBlock{
|
||||
Slot: 3,
|
||||
ParentRoot: justifiedRoot[:],
|
||||
}
|
||||
b2Root, _ := ssz.SigningRoot(b2)
|
||||
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
||||
Slot: 3,
|
||||
Validators: validators,
|
||||
Balances: balances,
|
||||
}, b2Root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b3 := ðpb.BeaconBlock{
|
||||
Slot: 3,
|
||||
ParentRoot: justifiedRoot[:],
|
||||
}
|
||||
b3Root, _ := ssz.SigningRoot(b3)
|
||||
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
||||
Slot: 3,
|
||||
Validators: validators,
|
||||
Balances: balances,
|
||||
}, b3Root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b4 := ðpb.BeaconBlock{
|
||||
Slot: 4,
|
||||
ParentRoot: b1Root[:],
|
||||
}
|
||||
b4Root, _ := ssz.SigningRoot(b4)
|
||||
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
||||
Slot: 4,
|
||||
Validators: validators,
|
||||
Balances: balances,
|
||||
}, b4Root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b5 := ðpb.BeaconBlock{
|
||||
Slot: 5,
|
||||
ParentRoot: b3Root[:],
|
||||
}
|
||||
b5Root, _ := ssz.SigningRoot(b5)
|
||||
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
||||
Slot: 5,
|
||||
Validators: validators,
|
||||
Balances: balances,
|
||||
}, b5Root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
attestationTargets := make(map[uint64]*pbp2p.AttestationTarget)
|
||||
// We give block A 3 votes.
|
||||
attestationTargets[0] = &pbp2p.AttestationTarget{
|
||||
Slot: b1.Slot,
|
||||
ParentRoot: b1.ParentRoot,
|
||||
BeaconBlockRoot: b1Root[:],
|
||||
}
|
||||
attestationTargets[1] = &pbp2p.AttestationTarget{
|
||||
Slot: b1.Slot,
|
||||
ParentRoot: b1.ParentRoot,
|
||||
BeaconBlockRoot: b1Root[:],
|
||||
}
|
||||
attestationTargets[2] = &pbp2p.AttestationTarget{
|
||||
Slot: b1.Slot,
|
||||
ParentRoot: b1.ParentRoot,
|
||||
BeaconBlockRoot: b1Root[:],
|
||||
}
|
||||
|
||||
// We give block C 2 votes.
|
||||
attestationTargets[3] = &pbp2p.AttestationTarget{
|
||||
Slot: b2.Slot,
|
||||
ParentRoot: b2.ParentRoot,
|
||||
BeaconBlockRoot: b2Root[:],
|
||||
}
|
||||
attestationTargets[4] = &pbp2p.AttestationTarget{
|
||||
Slot: b2.Slot,
|
||||
ParentRoot: b2.ParentRoot,
|
||||
BeaconBlockRoot: b2Root[:],
|
||||
}
|
||||
|
||||
// We give block D 2 votes.
|
||||
attestationTargets[5] = &pbp2p.AttestationTarget{
|
||||
Slot: b3.Slot,
|
||||
ParentRoot: b3.ParentRoot,
|
||||
BeaconBlockRoot: b3Root[:],
|
||||
}
|
||||
attestationTargets[6] = &pbp2p.AttestationTarget{
|
||||
Slot: b3.Slot,
|
||||
ParentRoot: b3.ParentRoot,
|
||||
BeaconBlockRoot: b3Root[:],
|
||||
}
|
||||
|
||||
// We give block B 3 votes.
|
||||
attestationTargets[7] = &pbp2p.AttestationTarget{
|
||||
Slot: b4.Slot,
|
||||
ParentRoot: b4.ParentRoot,
|
||||
BeaconBlockRoot: b4Root[:],
|
||||
}
|
||||
attestationTargets[8] = &pbp2p.AttestationTarget{
|
||||
Slot: b4.Slot,
|
||||
ParentRoot: b4.ParentRoot,
|
||||
BeaconBlockRoot: b4Root[:],
|
||||
}
|
||||
attestationTargets[9] = &pbp2p.AttestationTarget{
|
||||
Slot: b4.Slot,
|
||||
ParentRoot: b4.ParentRoot,
|
||||
BeaconBlockRoot: b4Root[:],
|
||||
}
|
||||
|
||||
// We give block E 1 vote.
|
||||
attestationTargets[10] = &pbp2p.AttestationTarget{
|
||||
Slot: b5.Slot,
|
||||
ParentRoot: b5.ParentRoot,
|
||||
BeaconBlockRoot: b5Root[:],
|
||||
}
|
||||
|
||||
tree := []*pb.BlockTreeResponse_TreeNode{
|
||||
{
|
||||
Block: b1,
|
||||
ParticipatedVotes: 3 * params.BeaconConfig().MaxEffectiveBalance,
|
||||
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
||||
},
|
||||
{
|
||||
Block: b2,
|
||||
ParticipatedVotes: 2 * params.BeaconConfig().MaxEffectiveBalance,
|
||||
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
||||
},
|
||||
{
|
||||
Block: b3,
|
||||
ParticipatedVotes: 2 * params.BeaconConfig().MaxEffectiveBalance,
|
||||
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
||||
},
|
||||
{
|
||||
Block: b4,
|
||||
ParticipatedVotes: 3 * params.BeaconConfig().MaxEffectiveBalance,
|
||||
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
||||
},
|
||||
{
|
||||
Block: b5,
|
||||
ParticipatedVotes: 1 * params.BeaconConfig().MaxEffectiveBalance,
|
||||
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
||||
},
|
||||
}
|
||||
for _, node := range tree {
|
||||
if err := db.SaveBlockDeprecated(node.Block); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
headState := &pbp2p.BeaconState{
|
||||
Slot: b4.Slot,
|
||||
}
|
||||
if err := db.UpdateChainHead(ctx, b4, headState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
bs := &BeaconServer{
|
||||
beaconDB: db,
|
||||
}
|
||||
if _, err := bs.BlockTreeBySlots(ctx, nil); err == nil {
|
||||
// There should be a "argument 'TreeBlockSlotRequest' cannot be nil" error
|
||||
t.Fatal(err)
|
||||
}
|
||||
slotRange := &pb.TreeBlockSlotRequest{
|
||||
SlotFrom: 4,
|
||||
SlotTo: 3,
|
||||
}
|
||||
if _, err := bs.BlockTreeBySlots(ctx, slotRange); err == nil {
|
||||
// There should be a 'Upper limit of slot range cannot be lower than the lower limit' error.
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
func TestBlockTreeBySlots_OK(t *testing.T) {
|
||||
t.Skip() // TODO(3219): Add after new fork choice service.
|
||||
helpers.ClearAllCaches()
|
||||
|
||||
db := internal.SetupDBDeprecated(t)
|
||||
defer internal.TeardownDBDeprecated(t, db)
|
||||
ctx := context.Background()
|
||||
// We want to ensure that if our block tree looks as follows, the RPC response
|
||||
// returns the correct information.
|
||||
// /->[A, Slot 3, 3 Votes]->[B, Slot 4, 3 Votes]
|
||||
// [Justified Block]->[C, Slot 3, 2 Votes]
|
||||
// \->[D, Slot 3, 2 Votes]->[SKIP SLOT]->[E, Slot 5, 1 Vote]
|
||||
justifiedState := &pbp2p.BeaconState{
|
||||
Slot: 0,
|
||||
Balances: make([]uint64, 11),
|
||||
}
|
||||
for i := 0; i < len(justifiedState.Balances); i++ {
|
||||
justifiedState.Balances[i] = params.BeaconConfig().MaxEffectiveBalance
|
||||
}
|
||||
var validators []*ethpb.Validator
|
||||
for i := 0; i < 11; i++ {
|
||||
validators = append(validators, ðpb.Validator{ExitEpoch: params.BeaconConfig().FarFutureEpoch, EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance})
|
||||
}
|
||||
justifiedState.Validators = validators
|
||||
if err := db.SaveJustifiedState(justifiedState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
justifiedBlock := ðpb.BeaconBlock{
|
||||
Slot: 0,
|
||||
}
|
||||
if err := db.SaveJustifiedBlock(justifiedBlock); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
justifiedRoot, _ := ssz.SigningRoot(justifiedBlock)
|
||||
balances := []uint64{params.BeaconConfig().MaxEffectiveBalance}
|
||||
b1 := ðpb.BeaconBlock{
|
||||
Slot: 3,
|
||||
ParentRoot: justifiedRoot[:],
|
||||
}
|
||||
b1Root, _ := ssz.SigningRoot(b1)
|
||||
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
||||
Slot: 3,
|
||||
Validators: validators,
|
||||
Balances: balances,
|
||||
}, b1Root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b2 := ðpb.BeaconBlock{
|
||||
Slot: 3,
|
||||
ParentRoot: justifiedRoot[:],
|
||||
}
|
||||
b2Root, _ := ssz.SigningRoot(b2)
|
||||
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
||||
Slot: 3,
|
||||
Validators: validators,
|
||||
Balances: balances,
|
||||
}, b2Root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b3 := ðpb.BeaconBlock{
|
||||
Slot: 3,
|
||||
ParentRoot: justifiedRoot[:],
|
||||
}
|
||||
b3Root, _ := ssz.SigningRoot(b3)
|
||||
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
||||
Slot: 3,
|
||||
Validators: validators,
|
||||
Balances: balances,
|
||||
}, b3Root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b4 := ðpb.BeaconBlock{
|
||||
Slot: 4,
|
||||
ParentRoot: b1Root[:],
|
||||
}
|
||||
b4Root, _ := ssz.SigningRoot(b4)
|
||||
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
||||
Slot: 4,
|
||||
Validators: validators,
|
||||
Balances: balances,
|
||||
}, b4Root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b5 := ðpb.BeaconBlock{
|
||||
Slot: 5,
|
||||
ParentRoot: b3Root[:],
|
||||
}
|
||||
b5Root, _ := ssz.SigningRoot(b5)
|
||||
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
||||
Slot: 5,
|
||||
Validators: validators,
|
||||
Balances: balances,
|
||||
}, b5Root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
attestationTargets := make(map[uint64]*pbp2p.AttestationTarget)
|
||||
// We give block A 3 votes.
|
||||
attestationTargets[0] = &pbp2p.AttestationTarget{
|
||||
Slot: b1.Slot,
|
||||
ParentRoot: b1.ParentRoot,
|
||||
BeaconBlockRoot: b1Root[:],
|
||||
}
|
||||
attestationTargets[1] = &pbp2p.AttestationTarget{
|
||||
Slot: b1.Slot,
|
||||
ParentRoot: b1.ParentRoot,
|
||||
BeaconBlockRoot: b1Root[:],
|
||||
}
|
||||
attestationTargets[2] = &pbp2p.AttestationTarget{
|
||||
Slot: b1.Slot,
|
||||
ParentRoot: b1.ParentRoot,
|
||||
BeaconBlockRoot: b1Root[:],
|
||||
}
|
||||
|
||||
// We give block C 2 votes.
|
||||
attestationTargets[3] = &pbp2p.AttestationTarget{
|
||||
Slot: b2.Slot,
|
||||
ParentRoot: b2.ParentRoot,
|
||||
BeaconBlockRoot: b2Root[:],
|
||||
}
|
||||
attestationTargets[4] = &pbp2p.AttestationTarget{
|
||||
Slot: b2.Slot,
|
||||
ParentRoot: b2.ParentRoot,
|
||||
BeaconBlockRoot: b2Root[:],
|
||||
}
|
||||
|
||||
// We give block D 2 votes.
|
||||
attestationTargets[5] = &pbp2p.AttestationTarget{
|
||||
Slot: b3.Slot,
|
||||
ParentRoot: b3.ParentRoot,
|
||||
BeaconBlockRoot: b3Root[:],
|
||||
}
|
||||
attestationTargets[6] = &pbp2p.AttestationTarget{
|
||||
Slot: b3.Slot,
|
||||
ParentRoot: b3.ParentRoot,
|
||||
BeaconBlockRoot: b3Root[:],
|
||||
}
|
||||
|
||||
// We give block B 3 votes.
|
||||
attestationTargets[7] = &pbp2p.AttestationTarget{
|
||||
Slot: b4.Slot,
|
||||
ParentRoot: b4.ParentRoot,
|
||||
BeaconBlockRoot: b4Root[:],
|
||||
}
|
||||
attestationTargets[8] = &pbp2p.AttestationTarget{
|
||||
Slot: b4.Slot,
|
||||
ParentRoot: b4.ParentRoot,
|
||||
BeaconBlockRoot: b4Root[:],
|
||||
}
|
||||
attestationTargets[9] = &pbp2p.AttestationTarget{
|
||||
Slot: b4.Slot,
|
||||
ParentRoot: b4.ParentRoot,
|
||||
BeaconBlockRoot: b4Root[:],
|
||||
}
|
||||
|
||||
// We give block E 1 vote.
|
||||
attestationTargets[10] = &pbp2p.AttestationTarget{
|
||||
Slot: b5.Slot,
|
||||
ParentRoot: b5.ParentRoot,
|
||||
BeaconBlockRoot: b5Root[:],
|
||||
}
|
||||
|
||||
tree := []*pb.BlockTreeResponse_TreeNode{
|
||||
{
|
||||
Block: b1,
|
||||
ParticipatedVotes: 3 * params.BeaconConfig().MaxEffectiveBalance,
|
||||
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
||||
},
|
||||
{
|
||||
Block: b2,
|
||||
ParticipatedVotes: 2 * params.BeaconConfig().MaxEffectiveBalance,
|
||||
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
||||
},
|
||||
{
|
||||
Block: b3,
|
||||
ParticipatedVotes: 2 * params.BeaconConfig().MaxEffectiveBalance,
|
||||
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
||||
},
|
||||
{
|
||||
Block: b4,
|
||||
ParticipatedVotes: 3 * params.BeaconConfig().MaxEffectiveBalance,
|
||||
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
||||
},
|
||||
{
|
||||
Block: b5,
|
||||
ParticipatedVotes: 1 * params.BeaconConfig().MaxEffectiveBalance,
|
||||
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
||||
},
|
||||
}
|
||||
for _, node := range tree {
|
||||
if err := db.SaveBlockDeprecated(node.Block); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
headState := &pbp2p.BeaconState{
|
||||
Slot: b4.Slot,
|
||||
}
|
||||
if err := db.UpdateChainHead(ctx, b4, headState); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
bs := &BeaconServer{
|
||||
beaconDB: db,
|
||||
}
|
||||
slotRange := &pb.TreeBlockSlotRequest{
|
||||
SlotFrom: 3,
|
||||
SlotTo: 4,
|
||||
}
|
||||
resp, err := bs.BlockTreeBySlots(ctx, slotRange)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(resp.Tree) != 2 {
|
||||
t.Logf("Incorrect number of nodes in tree, expected: %d, actual: %d", 2, len(resp.Tree))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,11 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/go-ssz"
|
||||
newBlockchain "github.com/prysmaticlabs/prysm/beacon-chain/blockchain"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db/kv"
|
||||
blockchain "github.com/prysmaticlabs/prysm/beacon-chain/deprecated-blockchain"
|
||||
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
@@ -30,7 +27,7 @@ import (
|
||||
// beacon blocks to a beacon node, and more.
|
||||
type ProposerServer struct {
|
||||
beaconDB db.Database
|
||||
chainService interface{}
|
||||
chainService chainService
|
||||
powChainService powChainService
|
||||
operationService operationService
|
||||
canonicalStateChan chan *pbp2p.BeaconState
|
||||
@@ -45,20 +42,7 @@ func (ps *ProposerServer) RequestBlock(ctx context.Context, req *pb.BlockRequest
|
||||
span.AddAttributes(trace.Int64Attribute("slot", int64(req.Slot)))
|
||||
|
||||
// Retrieve the parent block as the current head of the canonical chain
|
||||
var parent *ethpb.BeaconBlock
|
||||
var err error
|
||||
if d, isLegacyDB := ps.beaconDB.(*db.BeaconDB); isLegacyDB {
|
||||
parent, err = d.ChainHead()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get canonical head block")
|
||||
}
|
||||
} else {
|
||||
parent, err = ps.beaconDB.(*kv.Store).HeadBlock(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get canonical head block")
|
||||
}
|
||||
parent = ps.chainService.(newBlockchain.HeadRetriever).HeadBlock()
|
||||
}
|
||||
parent := ps.chainService.HeadBlock()
|
||||
|
||||
parentRoot, err := ssz.SigningRoot(parent)
|
||||
if err != nil {
|
||||
@@ -131,20 +115,8 @@ func (ps *ProposerServer) ProposeBlock(ctx context.Context, blk *ethpb.BeaconBlo
|
||||
log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(root[:]))).Debugf(
|
||||
"Block proposal received via RPC")
|
||||
|
||||
db, isLegacyDB := ps.beaconDB.(*db.BeaconDB)
|
||||
if srv, isLegacyService := ps.chainService.(*blockchain.ChainService); isLegacyService && isLegacyDB {
|
||||
beaconState, err := srv.ReceiveBlockDeprecated(ctx, blk)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not process beacon block")
|
||||
}
|
||||
if err := db.UpdateChainHead(ctx, blk, beaconState); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to update chain")
|
||||
}
|
||||
ps.chainService.(*blockchain.ChainService).UpdateCanonicalRoots(blk, root)
|
||||
} else {
|
||||
if err := ps.chainService.(*newBlockchain.ChainService).ReceiveBlock(ctx, blk); err != nil {
|
||||
return nil, errors.Wrap(err, "could not process beacon block")
|
||||
}
|
||||
if err := ps.chainService.ReceiveBlock(ctx, blk); err != nil {
|
||||
return nil, errors.Wrap(err, "could not process beacon block")
|
||||
}
|
||||
|
||||
return &pb.ProposeResponse{BlockRoot: root[:]}, nil
|
||||
@@ -156,25 +128,15 @@ func (ps *ProposerServer) ProposeBlock(ctx context.Context, blk *ethpb.BeaconBlo
|
||||
// attestations which are ready for inclusion. That is, attestations that satisfy:
|
||||
// attestation.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot.
|
||||
func (ps *ProposerServer) attestations(ctx context.Context, expectedSlot uint64) ([]*ethpb.Attestation, error) {
|
||||
var beaconState *pbp2p.BeaconState
|
||||
var err error
|
||||
if _, isLegacyDB := ps.beaconDB.(*db.BeaconDB); isLegacyDB {
|
||||
beaconState, err = ps.beaconDB.HeadState(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not retrieve beacon state")
|
||||
}
|
||||
} else {
|
||||
beaconState = ps.chainService.(newBlockchain.HeadRetriever).HeadState()
|
||||
}
|
||||
|
||||
headState := ps.chainService.HeadState()
|
||||
atts, err := ps.operationService.AttestationPool(ctx, expectedSlot)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not retrieve pending attestations from operations service")
|
||||
}
|
||||
|
||||
// advance slot, if it is behind
|
||||
if beaconState.Slot < expectedSlot {
|
||||
beaconState, err = state.ProcessSlots(ctx, beaconState, expectedSlot)
|
||||
if headState.Slot < expectedSlot {
|
||||
headState, err = state.ProcessSlots(ctx, headState, expectedSlot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -182,24 +144,24 @@ func (ps *ProposerServer) attestations(ctx context.Context, expectedSlot uint64)
|
||||
|
||||
var attsReadyForInclusion []*ethpb.Attestation
|
||||
for _, att := range atts {
|
||||
slot, err := helpers.AttestationDataSlot(beaconState, att.Data)
|
||||
slot, err := helpers.AttestationDataSlot(headState, att.Data)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get attestation slot")
|
||||
}
|
||||
if slot+params.BeaconConfig().MinAttestationInclusionDelay <= beaconState.Slot &&
|
||||
beaconState.Slot <= slot+params.BeaconConfig().SlotsPerEpoch {
|
||||
if slot+params.BeaconConfig().MinAttestationInclusionDelay <= headState.Slot &&
|
||||
headState.Slot <= slot+params.BeaconConfig().SlotsPerEpoch {
|
||||
attsReadyForInclusion = append(attsReadyForInclusion, att)
|
||||
}
|
||||
}
|
||||
|
||||
validAtts := make([]*ethpb.Attestation, 0, len(attsReadyForInclusion))
|
||||
for _, att := range attsReadyForInclusion {
|
||||
slot, err := helpers.AttestationDataSlot(beaconState, att.Data)
|
||||
slot, err := helpers.AttestationDataSlot(headState, att.Data)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get attestation slot")
|
||||
}
|
||||
|
||||
if _, err := blocks.ProcessAttestationNoVerify(beaconState, att); err != nil {
|
||||
if _, err := blocks.ProcessAttestationNoVerify(headState, att); err != nil {
|
||||
if ctx.Err() != nil {
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
@@ -209,19 +171,12 @@ func (ps *ProposerServer) attestations(ctx context.Context, expectedSlot uint64)
|
||||
"headRoot": fmt.Sprintf("%#x", bytesutil.Trunc(att.Data.BeaconBlockRoot))}).Info(
|
||||
"Deleting failed pending attestation from DB")
|
||||
|
||||
var hash [32]byte
|
||||
if _, isLegacyDB := ps.beaconDB.(*db.BeaconDB); isLegacyDB {
|
||||
hash, err = ssz.HashTreeRoot(att)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
hash, err = ssz.HashTreeRoot(att.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
root, err := ssz.HashTreeRoot(att.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := ps.beaconDB.DeleteAttestation(ctx, hash); err != nil {
|
||||
|
||||
if err := ps.beaconDB.DeleteAttestation(ctx, root); err != nil {
|
||||
return nil, errors.Wrap(err, "could not delete failed attestation")
|
||||
}
|
||||
continue
|
||||
@@ -284,18 +239,9 @@ func (ps *ProposerServer) computeStateRoot(ctx context.Context, block *ethpb.Bea
|
||||
func (ps *ProposerServer) deposits(ctx context.Context, currentVote *ethpb.Eth1Data) ([]*ethpb.Deposit, error) {
|
||||
// Need to fetch if the deposits up to the state's latest eth 1 data matches
|
||||
// the number of all deposits in this RPC call. If not, then we return nil.
|
||||
var beaconState *pbp2p.BeaconState
|
||||
var err error
|
||||
if _, isLegacyDB := ps.beaconDB.(*db.BeaconDB); isLegacyDB {
|
||||
beaconState, err = ps.beaconDB.HeadState(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not retrieve beacon state")
|
||||
}
|
||||
} else {
|
||||
beaconState = ps.chainService.(newBlockchain.HeadRetriever).HeadState()
|
||||
}
|
||||
headState := ps.chainService.HeadState()
|
||||
|
||||
canonicalEth1Data, latestEth1DataHeight, err := ps.canonicalEth1Data(ctx, beaconState, currentVote)
|
||||
canonicalEth1Data, latestEth1DataHeight, err := ps.canonicalEth1Data(ctx, headState, currentVote)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -326,7 +272,7 @@ func (ps *ProposerServer) deposits(ctx context.Context, currentVote *ethpb.Eth1D
|
||||
// deposits are sorted from lowest to highest.
|
||||
var pendingDeps []*depositcache.DepositContainer
|
||||
for _, dep := range allPendingContainers {
|
||||
if uint64(dep.Index) >= beaconState.Eth1DepositIndex && uint64(dep.Index) < canonicalEth1Data.DepositCount {
|
||||
if uint64(dep.Index) >= headState.Eth1DepositIndex && uint64(dep.Index) < canonicalEth1Data.DepositCount {
|
||||
pendingDeps = append(pendingDeps, dep)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,6 @@ func init() {
|
||||
}
|
||||
|
||||
func TestProposeBlock_OK(t *testing.T) {
|
||||
// TODO(3225): Unskip after we have fully deprecated the old chain service.
|
||||
t.Skip("Skip until we have fully deprecated the old chain service.")
|
||||
helpers.ClearAllCaches()
|
||||
db := dbutil.SetupDB(t)
|
||||
defer dbutil.TeardownDB(t, db)
|
||||
@@ -64,10 +62,12 @@ func TestProposeBlock_OK(t *testing.T) {
|
||||
proposerServer := &ProposerServer{
|
||||
beaconDB: db,
|
||||
powChainService: &mockPOWChainService{},
|
||||
chainService: &mock.ChainService{},
|
||||
}
|
||||
req := ðpb.BeaconBlock{
|
||||
Slot: 5,
|
||||
ParentRoot: []byte("parent-hash"),
|
||||
Body: ðpb.BeaconBlockBody{},
|
||||
}
|
||||
if err := proposerServer.beaconDB.SaveBlock(ctx, req); err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
|
||||
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
||||
@@ -38,6 +39,13 @@ func init() {
|
||||
log = logrus.WithField("prefix", "rpc")
|
||||
}
|
||||
|
||||
type chainService interface {
|
||||
blockchain.HeadRetriever
|
||||
blockchain.AttestationReceiver
|
||||
blockchain.BlockReceiver
|
||||
StateInitializedFeed() *event.Feed
|
||||
}
|
||||
|
||||
type operationService interface {
|
||||
operations.Pool
|
||||
HandleAttestation(context.Context, proto.Message) error
|
||||
@@ -65,7 +73,7 @@ type Service struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
beaconDB db.Database
|
||||
chainService interface{}
|
||||
chainService chainService
|
||||
powChainService powChainService
|
||||
operationService operationService
|
||||
syncService sync.Checker
|
||||
@@ -87,7 +95,7 @@ type Config struct {
|
||||
CertFlag string
|
||||
KeyFlag string
|
||||
BeaconDB db.Database
|
||||
ChainService interface{}
|
||||
ChainService chainService
|
||||
POWChainService powChainService
|
||||
OperationService operationService
|
||||
SyncService sync.Checker
|
||||
@@ -156,7 +164,7 @@ func (s *Service) Start() {
|
||||
beaconDB: s.beaconDB,
|
||||
ctx: s.ctx,
|
||||
powChainService: s.powChainService,
|
||||
chainService: s.chainService.(stateFeedListener),
|
||||
chainService: s.chainService,
|
||||
operationService: s.operationService,
|
||||
incomingAttestation: s.incomingAttestation,
|
||||
canonicalStateChan: s.canonicalStateChan,
|
||||
@@ -191,9 +199,9 @@ func (s *Service) Start() {
|
||||
syncChecker: s.syncService,
|
||||
}
|
||||
beaconChainServer := &BeaconChainServer{
|
||||
beaconDB: s.beaconDB,
|
||||
pool: s.operationService,
|
||||
head: s.chainService,
|
||||
beaconDB: s.beaconDB,
|
||||
pool: s.operationService,
|
||||
chainService: s.chainService,
|
||||
}
|
||||
pb.RegisterBeaconServiceServer(s.grpcServer, beaconServer)
|
||||
pb.RegisterProposerServiceServer(s.grpcServer, proposerServer)
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
@@ -94,7 +95,7 @@ func TestLifecycle_OK(t *testing.T) {
|
||||
CertFlag: "alice.crt",
|
||||
KeyFlag: "alice.key",
|
||||
SyncService: &mockSyncService{},
|
||||
ChainService: &mockStateFeedListener{},
|
||||
ChainService: &mock.ChainService{},
|
||||
})
|
||||
|
||||
rpcService.Start()
|
||||
@@ -113,7 +114,7 @@ func TestRPC_BadEndpoint(t *testing.T) {
|
||||
rpcService := NewRPCService(context.Background(), &Config{
|
||||
Port: "ralph merkle!!!",
|
||||
SyncService: &mockSyncService{},
|
||||
ChainService: &mockStateFeedListener{},
|
||||
ChainService: &mock.ChainService{},
|
||||
})
|
||||
|
||||
testutil.AssertLogsDoNotContain(t, hook, "Could not listen to port in Start()")
|
||||
@@ -142,7 +143,7 @@ func TestRPC_InsecureEndpoint(t *testing.T) {
|
||||
rpcService := NewRPCService(context.Background(), &Config{
|
||||
Port: "7777",
|
||||
SyncService: &mockSyncService{},
|
||||
ChainService: &mockStateFeedListener{},
|
||||
ChainService: &mock.ChainService{},
|
||||
})
|
||||
|
||||
rpcService.Start()
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
||||
@@ -30,7 +29,7 @@ import (
|
||||
type ValidatorServer struct {
|
||||
ctx context.Context
|
||||
beaconDB db.Database
|
||||
chainService interface{}
|
||||
chainService chainService
|
||||
canonicalStateChan chan *pbp2p.BeaconState
|
||||
powChainService powChainService
|
||||
depositCache *depositcache.DepositCache
|
||||
@@ -81,23 +80,14 @@ func (vs *ValidatorServer) WaitForActivation(req *pb.ValidatorActivationRequest,
|
||||
// ValidatorIndex is called by a validator to get its index location that corresponds
|
||||
// to the attestation bit fields.
|
||||
func (vs *ValidatorServer) ValidatorIndex(ctx context.Context, req *pb.ValidatorIndexRequest) (*pb.ValidatorIndexResponse, error) {
|
||||
var index uint64
|
||||
var ok bool
|
||||
var err error
|
||||
if d, isLegacyDB := vs.beaconDB.(*db.BeaconDB); isLegacyDB {
|
||||
index, err = d.ValidatorIndexDeprecated(req.PublicKey)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "could not retrieve validator index: %v", err)
|
||||
}
|
||||
} else {
|
||||
index, ok, err = vs.beaconDB.ValidatorIndex(ctx, bytesutil.ToBytes48(req.PublicKey))
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "could not retrieve validator index: %v", err)
|
||||
}
|
||||
if !ok {
|
||||
return nil, status.Errorf(codes.Internal, "could not find validator index for public key %#x not found", req.PublicKey)
|
||||
}
|
||||
index, ok, err := vs.beaconDB.ValidatorIndex(ctx, bytesutil.ToBytes48(req.PublicKey))
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "could not retrieve validator index: %v", err)
|
||||
}
|
||||
if !ok {
|
||||
return nil, status.Errorf(codes.Internal, "could not find validator index for public key %#x not found", req.PublicKey)
|
||||
}
|
||||
|
||||
return &pb.ValidatorIndexResponse{Index: uint64(index)}, nil
|
||||
}
|
||||
|
||||
@@ -106,30 +96,14 @@ func (vs *ValidatorServer) ValidatorIndex(ctx context.Context, req *pb.Validator
|
||||
func (vs *ValidatorServer) ValidatorPerformance(
|
||||
ctx context.Context, req *pb.ValidatorPerformanceRequest,
|
||||
) (*pb.ValidatorPerformanceResponse, error) {
|
||||
var headState *pbp2p.BeaconState
|
||||
var index uint64
|
||||
var ok bool
|
||||
var err error
|
||||
if d, isLegacyDB := vs.beaconDB.(*db.BeaconDB); isLegacyDB {
|
||||
index, err = d.ValidatorIndexDeprecated(req.PublicKey)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "could not retrieve validator index: %v", err)
|
||||
}
|
||||
headState, err = vs.beaconDB.HeadState(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get head")
|
||||
}
|
||||
} else {
|
||||
index, ok, err = vs.beaconDB.ValidatorIndex(ctx, bytesutil.ToBytes48(req.PublicKey))
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "could not retrieve validator index: %v", err)
|
||||
}
|
||||
if !ok {
|
||||
return nil, status.Errorf(codes.Internal, "could not find validator index for public key %#x not found", req.PublicKey)
|
||||
}
|
||||
headState = vs.chainService.(blockchain.HeadRetriever).HeadState()
|
||||
|
||||
index, ok, err := vs.beaconDB.ValidatorIndex(ctx, bytesutil.ToBytes48(req.PublicKey))
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "could not retrieve validator index: %v", err)
|
||||
}
|
||||
if !ok {
|
||||
return nil, status.Errorf(codes.Internal, "could not find validator index for public key %#x not found", req.PublicKey)
|
||||
}
|
||||
headState := vs.chainService.HeadState()
|
||||
|
||||
activeCount, err := helpers.ActiveValidatorCount(headState, helpers.SlotToEpoch(req.Slot))
|
||||
if err != nil {
|
||||
@@ -158,16 +132,8 @@ func (vs *ValidatorServer) ValidatorPerformance(
|
||||
// 3.) The slot at which the committee is assigned.
|
||||
// 4.) The bool signaling if the validator is expected to propose a block at the assigned slot.
|
||||
func (vs *ValidatorServer) CommitteeAssignment(ctx context.Context, req *pb.AssignmentRequest) (*pb.AssignmentResponse, error) {
|
||||
var s *pbp2p.BeaconState
|
||||
var err error
|
||||
if _, isLegacyDB := vs.beaconDB.(*db.BeaconDB); isLegacyDB {
|
||||
s, err = vs.beaconDB.HeadState(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not retrieve beacon state")
|
||||
}
|
||||
} else {
|
||||
s = vs.chainService.(blockchain.HeadRetriever).HeadState()
|
||||
}
|
||||
s := vs.chainService.HeadState()
|
||||
|
||||
// Advance state with empty transitions up to the requested epoch start slot.
|
||||
if epochStartSlot := helpers.StartSlot(req.EpochStart); s.Slot < epochStartSlot {
|
||||
@@ -224,22 +190,13 @@ func (vs *ValidatorServer) assignment(
|
||||
len(pubkey),
|
||||
)
|
||||
}
|
||||
var idx uint64
|
||||
var err error
|
||||
if d, isLegacyDB := vs.beaconDB.(*db.BeaconDB); isLegacyDB {
|
||||
idx, err = d.ValidatorIndexDeprecated(pubkey)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "could not retrieve validator index: %v", err)
|
||||
}
|
||||
} else {
|
||||
var ok bool
|
||||
idx, ok, err = vs.beaconDB.ValidatorIndex(context.Background(), bytesutil.ToBytes48(pubkey))
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "could not retrieve validator index: %v", err)
|
||||
}
|
||||
if !ok {
|
||||
return nil, status.Errorf(codes.Internal, "could not find validator index for public key %#x not found", pubkey)
|
||||
}
|
||||
|
||||
idx, ok, err := vs.beaconDB.ValidatorIndex(context.Background(), bytesutil.ToBytes48(pubkey))
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "could not retrieve validator index: %v", err)
|
||||
}
|
||||
if !ok {
|
||||
return nil, status.Errorf(codes.Internal, "could not find validator index for public key %#x not found", pubkey)
|
||||
}
|
||||
|
||||
committee, shard, slot, isProposer, err :=
|
||||
@@ -269,16 +226,7 @@ func (vs *ValidatorServer) assignment(
|
||||
func (vs *ValidatorServer) ValidatorStatus(
|
||||
ctx context.Context,
|
||||
req *pb.ValidatorIndexRequest) (*pb.ValidatorStatusResponse, error) {
|
||||
var headState *pbp2p.BeaconState
|
||||
var err error
|
||||
if _, isLegacyDB := vs.beaconDB.(*db.BeaconDB); isLegacyDB {
|
||||
headState, err = vs.beaconDB.HeadState(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not retrieve beacon state")
|
||||
}
|
||||
} else {
|
||||
headState = vs.chainService.(blockchain.HeadRetriever).HeadState()
|
||||
}
|
||||
headState := vs.chainService.HeadState()
|
||||
|
||||
chainStarted := vs.powChainService.HasChainStarted()
|
||||
chainStartKeys := vs.chainStartPubkeys()
|
||||
@@ -297,16 +245,8 @@ func (vs *ValidatorServer) MultipleValidatorStatus(
|
||||
}
|
||||
activeValidatorExists := false
|
||||
statusResponses := make([]*pb.ValidatorActivationResponse_Status, len(pubkeys))
|
||||
var headState *pbp2p.BeaconState
|
||||
var err error
|
||||
if _, isLegacyDB := vs.beaconDB.(*db.BeaconDB); isLegacyDB {
|
||||
headState, err = vs.beaconDB.HeadState(ctx)
|
||||
if err != nil {
|
||||
return false, nil, errors.Wrap(err, "could not retrieve beacon state")
|
||||
}
|
||||
} else {
|
||||
headState = vs.chainService.(blockchain.HeadRetriever).HeadState()
|
||||
}
|
||||
headState := vs.chainService.HeadState()
|
||||
|
||||
chainStartKeys := vs.chainStartPubkeys()
|
||||
validatorIndexMap := stateutils.ValidatorIndexMap(headState)
|
||||
for i, key := range pubkeys {
|
||||
@@ -518,17 +458,7 @@ func (vs *ValidatorServer) chainStartPubkeys() map[[96]byte]bool {
|
||||
|
||||
// DomainData fetches the current domain version information from the beacon state.
|
||||
func (vs *ValidatorServer) DomainData(ctx context.Context, request *pb.DomainRequest) (*pb.DomainResponse, error) {
|
||||
var headState *pbp2p.BeaconState
|
||||
var err error
|
||||
if _, isLegacyDB := vs.beaconDB.(*db.BeaconDB); isLegacyDB {
|
||||
headState, err = vs.beaconDB.HeadState(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not retrieve beacon state")
|
||||
}
|
||||
} else {
|
||||
headState = vs.chainService.(blockchain.HeadRetriever).HeadState()
|
||||
}
|
||||
|
||||
headState := vs.chainService.HeadState()
|
||||
dv := helpers.Domain(headState, request.Epoch, request.Domain)
|
||||
return &pb.DomainResponse{
|
||||
SignatureDomain: dv,
|
||||
|
||||
0
shared/adapter/BUILD.bazel
Normal file
0
shared/adapter/BUILD.bazel
Normal file
26
shared/adapter/metric/BUILD.bazel
Normal file
26
shared/adapter/metric/BUILD.bazel
Normal file
@@ -0,0 +1,26 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["metric.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/shared/adapter/metric",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//shared/deprecated-p2p:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus:go_default_library",
|
||||
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
srcs = ["metric_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//proto/eth/v1alpha1:go_default_library",
|
||||
"//shared/deprecated-p2p:go_default_library",
|
||||
"//shared/prometheus:go_default_library",
|
||||
],
|
||||
)
|
||||
53
shared/adapter/metric/metric.go
Normal file
53
shared/adapter/metric/metric.go
Normal file
@@ -0,0 +1,53 @@
|
||||
// Package metric contain some prometheus collectors for p2p services.
|
||||
package metric
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
p2p "github.com/prysmaticlabs/prysm/shared/deprecated-p2p"
|
||||
)
|
||||
|
||||
var (
|
||||
messagesCompleted = promauto.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "p2p_message_sent_total",
|
||||
Help: "Count of messages sent.",
|
||||
},
|
||||
[]string{"message"},
|
||||
)
|
||||
sendLatency = promauto.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "p2p_message_sent_latency_seconds",
|
||||
Help: "Latency of messages sent.",
|
||||
Buckets: []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10, 25, 50, 100},
|
||||
},
|
||||
[]string{"message"},
|
||||
)
|
||||
messageSize = promauto.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "p2p_message_received_bytes",
|
||||
Help: "Size of received messages.",
|
||||
Buckets: prometheus.ExponentialBuckets(32, 32, 6),
|
||||
},
|
||||
[]string{"message"},
|
||||
)
|
||||
)
|
||||
|
||||
// New create and initialize a metric adapter for the p2p service.
|
||||
func New() p2p.Adapter {
|
||||
return func(next p2p.Handler) p2p.Handler {
|
||||
return func(msg p2p.Message) {
|
||||
start := time.Now()
|
||||
messageName := fmt.Sprintf("%T", msg.Data)
|
||||
|
||||
messageSize.WithLabelValues(messageName).Observe(float64(proto.Size(msg.Data)))
|
||||
next(msg)
|
||||
sendLatency.WithLabelValues(messageName).Observe(time.Since(start).Seconds())
|
||||
messagesCompleted.WithLabelValues(messageName).Inc()
|
||||
}
|
||||
}
|
||||
}
|
||||
67
shared/adapter/metric/metric_test.go
Normal file
67
shared/adapter/metric/metric_test.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package metric
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
||||
p2p "github.com/prysmaticlabs/prysm/shared/deprecated-p2p"
|
||||
"github.com/prysmaticlabs/prysm/shared/prometheus"
|
||||
)
|
||||
|
||||
const addr = "127.0.0.1:8989"
|
||||
|
||||
func TestMessageMetrics_OK(t *testing.T) {
|
||||
service := prometheus.NewPrometheusService(addr, nil)
|
||||
go service.Start()
|
||||
defer service.Stop()
|
||||
|
||||
adapter := New()
|
||||
if adapter == nil {
|
||||
t.Error("Expected metric adapter")
|
||||
}
|
||||
data := ðpb.Attestation{
|
||||
AggregationBits: []byte{99},
|
||||
Data: ðpb.AttestationData{
|
||||
Crosslink: ðpb.Crosslink{
|
||||
Shard: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
h := adapter(func(p2p.Message) { time.Sleep(10 * time.Millisecond) })
|
||||
h(p2p.Message{Ctx: context.Background(), Data: data})
|
||||
h = adapter(func(p2p.Message) { time.Sleep(100 * time.Microsecond) })
|
||||
h(p2p.Message{Ctx: context.Background(), Data: data})
|
||||
|
||||
metrics := getMetrics(t)
|
||||
testMetricExists(t, metrics, fmt.Sprintf("p2p_message_sent_total{message=\"%T\"} 2", data))
|
||||
testMetricExists(t, metrics, fmt.Sprintf("p2p_message_sent_latency_seconds_bucket{message=\"%T\",le=\"0.005\"} 1", data))
|
||||
testMetricExists(t, metrics, fmt.Sprintf("p2p_message_sent_latency_seconds_bucket{message=\"%T\",le=\"0.01\"} 1", data))
|
||||
}
|
||||
|
||||
func getMetrics(t *testing.T) []string {
|
||||
resp, err := http.Get(fmt.Sprintf("http://%s/metrics", addr))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
return strings.Split(string(body), "\n")
|
||||
}
|
||||
|
||||
func testMetricExists(t *testing.T, metrics []string, pattern string) string {
|
||||
for _, line := range metrics {
|
||||
if strings.HasPrefix(line, pattern) {
|
||||
return line
|
||||
}
|
||||
}
|
||||
t.Errorf("Pattern \"%s\" not found in metrics", pattern)
|
||||
return ""
|
||||
}
|
||||
@@ -29,10 +29,6 @@ type FeatureFlagConfig struct {
|
||||
DisableGossipSub bool // DisableGossipSub in p2p messaging.
|
||||
EnableExcessDeposits bool // EnableExcessDeposits in validator balances.
|
||||
NoGenesisDelay bool // NoGenesisDelay when processing a chain start genesis event.
|
||||
UseNewP2P bool // UseNewP2P service.
|
||||
UseNewSync bool // UseNewSync services.
|
||||
UseNewDatabase bool // UseNewDatabase service.
|
||||
UseNewBlockChainService bool // UseNewBlockChainService service.
|
||||
|
||||
// Cache toggles.
|
||||
EnableActiveBalanceCache bool // EnableActiveBalanceCache; see https://github.com/prysmaticlabs/prysm/issues/3106.
|
||||
@@ -75,22 +71,6 @@ func ConfigureBeaconFeatures(ctx *cli.Context) {
|
||||
log.Warn("Using non standard genesis delay. This may cause problems in a multi-node environment.")
|
||||
cfg.NoGenesisDelay = true
|
||||
}
|
||||
if ctx.GlobalBool(NextFlag.Name) || ctx.GlobalBool(UseNewP2PFlag.Name) {
|
||||
log.Warn("Using new P2P service.")
|
||||
cfg.UseNewP2P = true
|
||||
}
|
||||
if ctx.GlobalBool(NextFlag.Name) || ctx.GlobalBool(UseNewSyncFlag.Name) {
|
||||
log.Warn("Using new sync services.")
|
||||
cfg.UseNewSync = true
|
||||
}
|
||||
if ctx.GlobalBool(NextFlag.Name) || ctx.GlobalBool(UseNewDatabaseFlag.Name) {
|
||||
log.Warn("Using new database service.")
|
||||
cfg.UseNewDatabase = true
|
||||
}
|
||||
if ctx.GlobalBool(NextFlag.Name) || ctx.GlobalBool(UseNewBlockChainFlag.Name) {
|
||||
log.Warn("Using new blockchain service.")
|
||||
cfg.UseNewBlockChainService = true
|
||||
}
|
||||
if ctx.GlobalBool(EnableActiveBalanceCacheFlag.Name) {
|
||||
log.Warn("Enabled unsafe active balance cache")
|
||||
cfg.EnableActiveBalanceCache = true
|
||||
|
||||
@@ -31,31 +31,6 @@ var (
|
||||
Name: "no-genesis-delay",
|
||||
Usage: "Process genesis event 30s after the ETH1 block time, rather than wait to midnight of the next day.",
|
||||
}
|
||||
// UseNewP2PFlag to start the beacon chain with the new p2p library.
|
||||
UseNewP2PFlag = cli.BoolFlag{
|
||||
Name: "experimental-p2p",
|
||||
Usage: "Use the new experimental p2p library. See issue #3147.",
|
||||
}
|
||||
// UseNewSyncFlag to start the beacon chain using the new sync library.
|
||||
UseNewSyncFlag = cli.BoolFlag{
|
||||
Name: "experimental-sync",
|
||||
Usage: "Use the new experimental sync libraries. See issue #3147.",
|
||||
}
|
||||
// UseNewDatabaseFlag to start the beacon chain using new database library.
|
||||
UseNewDatabaseFlag = cli.BoolFlag{
|
||||
Name: "experimental-db",
|
||||
Usage: "Use the new experimental database library.",
|
||||
}
|
||||
// UseNewBlockChainFlag to start the beacon chain using new blockchain library.
|
||||
UseNewBlockChainFlag = cli.BoolFlag{
|
||||
Name: "experimental-blockchain",
|
||||
Usage: "Use the new experimental blockchain library.",
|
||||
}
|
||||
// NextFlag to enable all experimental features.
|
||||
NextFlag = cli.BoolFlag{
|
||||
Name: "next",
|
||||
Usage: "Use next version experimental features.",
|
||||
}
|
||||
// EnableActiveBalanceCacheFlag see https://github.com/prysmaticlabs/prysm/issues/3106.
|
||||
EnableActiveBalanceCacheFlag = cli.BoolFlag{
|
||||
Name: "enable-active-balance-cache",
|
||||
@@ -103,10 +78,6 @@ var BeaconChainFlags = []cli.Flag{
|
||||
DisableGossipSubFlag,
|
||||
EnableExcessDepositsFlag,
|
||||
NoGenesisDelayFlag,
|
||||
UseNewP2PFlag,
|
||||
UseNewSyncFlag,
|
||||
UseNewDatabaseFlag,
|
||||
NextFlag,
|
||||
EnableActiveBalanceCacheFlag,
|
||||
EnableAttestationCacheFlag,
|
||||
EnableAncestorBlockCacheFlag,
|
||||
|
||||
18
shared/mock/BUILD.bazel
Normal file
18
shared/mock/BUILD.bazel
Normal file
@@ -0,0 +1,18 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
package(default_testonly = True)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"broadcaster_mock.go",
|
||||
"feed_mock.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/shared/mock",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//shared/event:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_golang_mock//gomock:go_default_library",
|
||||
],
|
||||
)
|
||||
45
shared/mock/broadcaster_mock.go
generated
Normal file
45
shared/mock/broadcaster_mock.go
generated
Normal file
@@ -0,0 +1,45 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/prysmaticlabs/prysm/shared/deprecated-p2p (interfaces: Broadcaster)
|
||||
|
||||
// Package mock_p2p is a generated GoMock package.
|
||||
package mock_p2p
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
)
|
||||
|
||||
// MockBroadcaster is a mock of Broadcaster interface
|
||||
type MockBroadcaster struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockBroadcasterMockRecorder
|
||||
}
|
||||
|
||||
// MockBroadcasterMockRecorder is the mock recorder for MockBroadcaster
|
||||
type MockBroadcasterMockRecorder struct {
|
||||
mock *MockBroadcaster
|
||||
}
|
||||
|
||||
// NewMockBroadcaster creates a new mock instance
|
||||
func NewMockBroadcaster(ctrl *gomock.Controller) *MockBroadcaster {
|
||||
mock := &MockBroadcaster{ctrl: ctrl}
|
||||
mock.recorder = &MockBroadcasterMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockBroadcaster) EXPECT() *MockBroadcasterMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Broadcast mocks base method
|
||||
func (m *MockBroadcaster) Broadcast(arg0 proto.Message) {
|
||||
m.ctrl.Call(m, "Broadcast", arg0)
|
||||
}
|
||||
|
||||
// Broadcast indicates an expected call of Broadcast
|
||||
func (mr *MockBroadcasterMockRecorder) Broadcast(arg0 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Broadcast", reflect.TypeOf((*MockBroadcaster)(nil).Broadcast), arg0)
|
||||
}
|
||||
59
shared/mock/feed_mock.go
generated
Normal file
59
shared/mock/feed_mock.go
generated
Normal file
@@ -0,0 +1,59 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/prysmaticlabs/prysm/shared/deprecated-p2p (interfaces: Feed)
|
||||
|
||||
// Package mock_p2p is a generated GoMock package.
|
||||
package mock_p2p
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
event "github.com/prysmaticlabs/prysm/shared/event"
|
||||
)
|
||||
|
||||
// MockFeed is a mock of Feed interface
|
||||
type MockFeed struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockFeedMockRecorder
|
||||
}
|
||||
|
||||
// MockFeedMockRecorder is the mock recorder for MockFeed
|
||||
type MockFeedMockRecorder struct {
|
||||
mock *MockFeed
|
||||
}
|
||||
|
||||
// NewMockFeed creates a new mock instance
|
||||
func NewMockFeed(ctrl *gomock.Controller) *MockFeed {
|
||||
mock := &MockFeed{ctrl: ctrl}
|
||||
mock.recorder = &MockFeedMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockFeed) EXPECT() *MockFeedMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Send mocks base method
|
||||
func (m *MockFeed) Send(arg0 interface{}) int {
|
||||
ret := m.ctrl.Call(m, "Send", arg0)
|
||||
ret0, _ := ret[0].(int)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Send indicates an expected call of Send
|
||||
func (mr *MockFeedMockRecorder) Send(arg0 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockFeed)(nil).Send), arg0)
|
||||
}
|
||||
|
||||
// Subscribe mocks base method
|
||||
func (m *MockFeed) Subscribe(arg0 interface{}) event.Subscription {
|
||||
ret := m.ctrl.Call(m, "Subscribe", arg0)
|
||||
ret0, _ := ret[0].(event.Subscription)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Subscribe indicates an expected call of Subscribe
|
||||
func (mr *MockFeedMockRecorder) Subscribe(arg0 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subscribe", reflect.TypeOf((*MockFeed)(nil).Subscribe), arg0)
|
||||
}
|
||||
Reference in New Issue
Block a user