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:
terence tsao
2019-09-05 09:04:06 -07:00
committed by Raul Jordan
parent 75bce9b7e1
commit 14c59b2ff9
95 changed files with 509 additions and 13677 deletions

View File

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

View File

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

View File

@@ -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, &ethpb.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 := &ethpb.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 := &ethpb.Attestation{
AggregationBits: bitfield.Bitlist{0x03},
Data: &ethpb.AttestationData{
Crosslink: &ethpb.Crosslink{
Shard: 1,
},
Target: &ethpb.Checkpoint{Epoch: 1},
Source: &ethpb.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, &ethpb.Validator{
PublicKey: []byte{byte(i)},
ActivationEpoch: 0,
ExitEpoch: 10,
})
}
beaconState := &pb.BeaconState{
Slot: 1,
Validators: validators,
}
block := &ethpb.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 := &ethpb.Attestation{
AggregationBits: bitfield.Bitlist{0x80, 0x01},
Data: &ethpb.AttestationData{
Crosslink: &ethpb.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 := &ethpb.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 := &ethpb.Attestation{
Data: &ethpb.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, &ethpb.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 := &ethpb.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 := &ethpb.Attestation{
AggregationBits: bitfield.Bitlist{0xC0, 0x01},
Data: &ethpb.AttestationData{
Crosslink: &ethpb.Crosslink{
Shard: 1,
},
Target: &ethpb.Checkpoint{},
Source: &ethpb.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, &ethpb.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 := &ethpb.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 := &ethpb.Attestation{
AggregationBits: bitfield.Bitlist{0x80},
Data: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{Epoch: 2},
Source: &ethpb.Checkpoint{},
Crosslink: &ethpb.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, &ethpb.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 := &ethpb.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, &ethpb.Attestation{
AggregationBits: bitfield.Bitlist{0x80, 0x01},
Data: &ethpb.AttestationData{
Crosslink: &ethpb.Crosslink{
Shard: 1,
},
Target: &ethpb.Checkpoint{},
Source: &ethpb.Checkpoint{},
},
})
}
if err := service.BatchUpdateLatestAttestations(ctx, attestations); err != nil {
t.Fatalf("could not update latest attestation: %v", err)
}
}

View File

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

View File

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

View File

@@ -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 := &ethpb.BeaconBlock{
Slot: params.BeaconConfig().SlotsPerEpoch,
Body: &ethpb.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 := &ethpb.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")
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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, &ethpb.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), &ethpb.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, &ethpb.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), &ethpb.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, &ethpb.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), &ethpb.Eth1Data{})
if err != nil {
t.Fatal(err)
}

View File

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

View File

@@ -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, &ethpb.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")
}
}

View File

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

View File

@@ -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 := &ethpb.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
}

View File

@@ -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 := &ethpb.Attestation{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.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] = &ethpb.Attestation{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.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 := &ethpb.Attestation{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.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 := &ethpb.Attestation{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.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")
}
}

View File

@@ -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 := &ethpb.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")
}

View File

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

View File

@@ -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 := &ethpb.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")
}
}

View File

@@ -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 := &ethpb.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 := &ethpb.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 := &ethpb.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 := &ethpb.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 := &ethpb.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 := &ethpb.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 := &ethpb.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 := &ethpb.BeaconBlock{
Slot: slotNum,
ParentRoot: []byte("A"),
Body: &ethpb.BeaconBlockBody{
RandaoReveal: []byte("A"),
},
}
b2 := &ethpb.BeaconBlock{
Slot: slotNum,
ParentRoot: []byte("B"),
Body: &ethpb.BeaconBlockBody{
RandaoReveal: []byte("B"),
}}
b3 := &ethpb.BeaconBlock{
Slot: slotNum,
ParentRoot: []byte("C"),
Body: &ethpb.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, &ethpb.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 := &ethpb.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, &ethpb.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 := &ethpb.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, &ethpb.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 := &ethpb.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 := &ethpb.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 := &ethpb.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 := &ethpb.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 := &ethpb.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 := &ethpb.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 := &ethpb.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 := &ethpb.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")
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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, &ethpb.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, &ethpb.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, &ethpb.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: &ethpb.Checkpoint{Epoch: 1},
},
},
{
state: &pb.BeaconState{
Slot: 72,
FinalizedCheckpoint: &ethpb.Checkpoint{Epoch: 1},
},
},
{
state: &pb.BeaconState{
Slot: 96,
FinalizedCheckpoint: &ethpb.Checkpoint{Epoch: 1},
},
},
{
state: &pb.BeaconState{
Slot: 130,
FinalizedCheckpoint: &ethpb.Checkpoint{Epoch: 2},
},
},
{
state: &pb.BeaconState{
Slot: 300,
FinalizedCheckpoint: &ethpb.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: &ethpb.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)
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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, &ethpb.Eth1Data{})
if err != nil {
t.Fatalf("Can't generate genesis state: %v", err)
}
justifiedState.StateRoots = make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
justifiedState.LatestBlockHeader = &ethpb.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] = &ethpb.BeaconBlock{
Slot: beaconState.Slot,
ParentRoot: []byte{'A'},
Body: &ethpb.BeaconBlockBody{
Eth1Data: &ethpb.Eth1Data{},
},
}
roots[0], err = ssz.SigningRoot(blocks[0])
if err != nil {
t.Fatalf("Could not hash block: %v", err)
}
blocks[1] = &ethpb.BeaconBlock{
Slot: beaconState.Slot + 2,
ParentRoot: roots[0][:],
Body: &ethpb.BeaconBlockBody{
Eth1Data: &ethpb.Eth1Data{},
},
}
roots[1], err = ssz.SigningRoot(blocks[1])
if err != nil {
t.Fatalf("Could not hash block: %v", err)
}
blocks[2] = &ethpb.BeaconBlock{
Slot: beaconState.Slot + 1,
ParentRoot: roots[0][:],
Body: &ethpb.BeaconBlockBody{
Eth1Data: &ethpb.Eth1Data{},
},
}
roots[2], err = ssz.SigningRoot(blocks[2])
if err != nil {
t.Fatalf("Could not hash block: %v", err)
}
blocks[3] = &ethpb.BeaconBlock{
Slot: beaconState.Slot + 3,
ParentRoot: roots[1][:],
Body: &ethpb.BeaconBlockBody{
Eth1Data: &ethpb.Eth1Data{},
},
}
roots[3], err = ssz.SigningRoot(blocks[3])
if err != nil {
t.Fatalf("Could not hash block: %v", err)
}
blocks[4] = &ethpb.BeaconBlock{
Slot: beaconState.Slot + 4,
ParentRoot: roots[2][:],
Body: &ethpb.BeaconBlockBody{
Eth1Data: &ethpb.Eth1Data{},
},
}
roots[4], err = ssz.SigningRoot(blocks[4])
if err != nil {
t.Fatalf("Could not hash block: %v", err)
}
blocks[5] = &ethpb.BeaconBlock{
Slot: beaconState.Slot + 5,
ParentRoot: roots[3][:],
Body: &ethpb.BeaconBlockBody{
Eth1Data: &ethpb.Eth1Data{},
},
}
roots[5], err = ssz.SigningRoot(blocks[5])
if err != nil {
t.Fatalf("Could not hash block: %v", err)
}
return blocks, roots
}

View File

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

View File

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

View File

@@ -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, &ethpb.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")
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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: &ethpb.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{}, &ethpb.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 := &ethpb.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 := &ethpb.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: &ethpb.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"])
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 := &ethpb.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)
}

View File

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

View File

@@ -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 := &ethpb.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, &ethpb.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 := &ethpb.BeaconBlock{
Slot: 0,
}
genesisRoot, err := ssz.SigningRoot(genesisBlock)
if err != nil {
t.Fatal(err)
}
genesisState := &pb.BeaconState{
Slot: 0,
FinalizedCheckpoint: &ethpb.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))
}
}

View File

@@ -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(&ethpb.Attestation{}, rs.attestationBuf)
exitSub := rs.p2p.Subscribe(&ethpb.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
}

View File

@@ -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] = &ethpb.Validator{
PublicKey: []byte(strconv.Itoa(i)),
}
}
genesisTime := uint64(time.Now().Unix())
deposits, _ := testutil.SetupInitialDeposits(t, 100)
if err := db.InitializeState(context.Background(), genesisTime, deposits, &ethpb.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 := &ethpb.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 := &ethpb.BeaconBlock{
ParentRoot: parentRoot[:],
Slot: 0,
Body: &ethpb.BeaconBlockBody{
Eth1Data: &ethpb.Eth1Data{
DepositRoot: []byte{1, 2, 3, 4, 5},
BlockHash: []byte{6, 7, 8, 9, 10},
},
},
}
attestation := &ethpb.Attestation{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.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] = &ethpb.Validator{
PublicKey: []byte(strconv.Itoa(i)),
}
}
genesisTime := uint64(time.Now().Unix())
deposits, _ := testutil.SetupInitialDeposits(t, 100)
if err := db.InitializeState(context.Background(), genesisTime, deposits, &ethpb.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 := &ethpb.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 := &ethpb.BeaconBlock{
ParentRoot: parentRoot[:],
Slot: 1,
Body: &ethpb.BeaconBlockBody{
Eth1Data: &ethpb.Eth1Data{
DepositRoot: []byte{1, 2, 3, 4, 5},
BlockHash: []byte{6, 7, 8, 9, 10},
},
},
}
responseBlock1 := &pb.BeaconBlockResponse{
Block: data1,
Attestation: &ethpb.Attestation{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.Crosslink{
Shard: 0,
DataRoot: []byte{},
},
},
},
}
msg1 := p2p.Message{
Ctx: context.Background(),
Peer: "",
Data: responseBlock1,
}
data2 := &ethpb.BeaconBlock{
ParentRoot: []byte{},
Slot: 1,
Body: &ethpb.BeaconBlockBody{
Eth1Data: &ethpb.Eth1Data{
DepositRoot: []byte{11, 12, 13, 14, 15},
BlockHash: []byte{16, 17, 18, 19, 20},
},
},
}
responseBlock2 := &pb.BeaconBlockResponse{
Block: data2,
Attestation: &ethpb.Attestation{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.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: &ethpb.Checkpoint{},
}
if err := db.SaveStateDeprecated(ctx, beaconState); err != nil {
t.Fatalf("Could not save state: %v", err)
}
beaconBlock := &ethpb.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 := &ethpb.Attestation{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.Crosslink{
Shard: 1,
},
Source: &ethpb.Checkpoint{},
Target: &ethpb.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: &ethpb.Checkpoint{Epoch: 1},
}
if err := db.SaveStateDeprecated(ctx, state); err != nil {
t.Fatalf("Could not save state: %v", err)
}
headBlock := &ethpb.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 := &ethpb.Attestation{
Data: &ethpb.AttestationData{
Crosslink: &ethpb.Crosslink{
Shard: 900,
},
Source: &ethpb.Checkpoint{},
Target: &ethpb.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 := &ethpb.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, &ethpb.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{}, &ethpb.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 := &ethpb.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 := &ethpb.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 := &ethpb.BeaconBlock{Slot: 3, ParentRoot: root1[:]}
if err = ss.db.SaveBlockDeprecated(block3); err != nil {
t.Fatalf("Could not save block: %v", err)
}
block4 := &ethpb.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 := &ethpb.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 := &ethpb.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)
}
}

View File

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

View File

@@ -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, &ethpb.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")
}
}

View File

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

View File

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

View File

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

View File

@@ -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(&regSync); 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(&regSync); 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 {

View File

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

View File

@@ -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: &ethpb.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
}

View File

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

View File

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

View File

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

View File

@@ -21,7 +21,6 @@ type P2P interface {
HandshakeManager
Sender
ConnectionHandler
DeprecatedSubscriber
}
// Broadcaster broadcasts messages to peers over the p2p pubsub protocol.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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{},
})

View File

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

View File

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

View File

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

View File

@@ -31,6 +31,7 @@ func TestSubmitAttestation_OK(t *testing.T) {
mockOperationService := &mockOperationService{}
attesterServer := &AttesterServer{
chainService: &mock.ChainService{},
operationService: mockOperationService,
p2p: &mockBroadcaster{},
beaconDB: db,

View File

@@ -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 &ethpb.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 &ethpb.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 := &ethpb.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

View File

@@ -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, &ethpb.GetValidatorParticipationRequest{Epoch: epoch})
@@ -1000,7 +1000,7 @@ func TestBeaconChainServer_GetChainHead(t *testing.T) {
FinalizedCheckpoint: &ethpb.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 {

View File

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

View File

@@ -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, &ethpb.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 := &ethpb.BeaconBlock{
Slot: 0,
}
if err := db.SaveJustifiedBlock(justifiedBlock); err != nil {
t.Fatal(err)
}
justifiedRoot, _ := ssz.SigningRoot(justifiedBlock)
balances := []uint64{params.BeaconConfig().MaxEffectiveBalance}
b1 := &ethpb.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 := &ethpb.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 := &ethpb.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 := &ethpb.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 := &ethpb.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 := &ethpb.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 := &ethpb.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 := &ethpb.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 := &ethpb.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 := &ethpb.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 := &ethpb.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, &ethpb.Validator{ExitEpoch: params.BeaconConfig().FarFutureEpoch, EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance})
}
justifiedState.Validators = validators
if err := db.SaveJustifiedState(justifiedState); err != nil {
t.Fatal(err)
}
justifiedBlock := &ethpb.BeaconBlock{
Slot: 0,
}
if err := db.SaveJustifiedBlock(justifiedBlock); err != nil {
t.Fatal(err)
}
justifiedRoot, _ := ssz.SigningRoot(justifiedBlock)
balances := []uint64{params.BeaconConfig().MaxEffectiveBalance}
b1 := &ethpb.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 := &ethpb.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 := &ethpb.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 := &ethpb.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 := &ethpb.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))
}
}

View File

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

View File

@@ -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 := &ethpb.BeaconBlock{
Slot: 5,
ParentRoot: []byte("parent-hash"),
Body: &ethpb.BeaconBlockBody{},
}
if err := proposerServer.beaconDB.SaveBlock(ctx, req); err != nil {
t.Fatal(err)

View File

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

View File

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

View File

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

View File

View 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",
],
)

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

View 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 := &ethpb.Attestation{
AggregationBits: []byte{99},
Data: &ethpb.AttestationData{
Crosslink: &ethpb.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 ""
}

View File

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

View File

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