Files
prysm/validator/node/node.go
Ivan Martinez 4ab0a91e51 Validator Slashing Protection DB (#4389)
* Begin adding DB to validator client

Begin adding ValidatorProposalHistory

Implement most of proposal history

Finish tests

Fix marking a proposal for the first time

Change proposalhistory to not using bit shifting

Add pb.go

Change after proto/slashing added

Finally fix protos

Fix most tests

Fix all tests for double proposal protection

Start initialiing DB in validator client

Add db to validator struct

Add DB to ProposeBlock

Fix test errors and begin mocking

Fix test formatting and pass test for validator protection!

Fix merge issues

Fix renames

Fix tests

* Fix tests

* Fix first startup on DB

* Fix nil check tests

* Fix E2E

* Fix e2e flag

* Fix comments

* Fix for comments

* Move proposal hepers to validator/client to keep DB clean

* Add clear-db flag to validator client

* Fix formatting

* Clear out unintended changes

* Fix build issues

* Fix build issues

* Gazelle

* Fix mock test

* Remove proposal history

* Add terminal confirmation to DB clearing

* Add interface for validatorDB, add context to DB functions

* Add force-clear-db flag

* Cleanup

* Update validator/node/node.go

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>

* Change db to clear file, not whole folder

* Fix db test

* Fix teardown test

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
2020-01-08 13:16:17 -05:00

238 lines
6.9 KiB
Go

// Package node defines a validator client which connects to a
// full beacon node as part of the Ethereum Serenity specification.
package node
import (
"context"
"fmt"
"os"
"os/signal"
"path/filepath"
"sync"
"syscall"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/shared"
"github.com/prysmaticlabs/prysm/shared/cmd"
"github.com/prysmaticlabs/prysm/shared/debug"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/prometheus"
"github.com/prysmaticlabs/prysm/shared/tracing"
"github.com/prysmaticlabs/prysm/shared/version"
"github.com/prysmaticlabs/prysm/validator/client"
"github.com/prysmaticlabs/prysm/validator/flags"
"github.com/prysmaticlabs/prysm/validator/db"
"github.com/prysmaticlabs/prysm/validator/keymanager"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
var log = logrus.WithField("prefix", "node")
// ValidatorClient defines an instance of a sharding validator that manages
// the entire lifecycle of services attached to it participating in
// Ethereum Serenity.
type ValidatorClient struct {
ctx *cli.Context
services *shared.ServiceRegistry // Lifecycle and service store.
lock sync.RWMutex
stop chan struct{} // Channel to wait for termination notifications.
}
// NewValidatorClient creates a new, Ethereum Serenity validator client.
func NewValidatorClient(ctx *cli.Context) (*ValidatorClient, error) {
if err := tracing.Setup(
"validator", // service name
ctx.GlobalString(cmd.TracingProcessNameFlag.Name),
ctx.GlobalString(cmd.TracingEndpointFlag.Name),
ctx.GlobalFloat64(cmd.TraceSampleFractionFlag.Name),
ctx.GlobalBool(cmd.EnableTracingFlag.Name),
); err != nil {
return nil, err
}
verbosity := ctx.GlobalString(cmd.VerbosityFlag.Name)
level, err := logrus.ParseLevel(verbosity)
if err != nil {
return nil, err
}
logrus.SetLevel(level)
registry := shared.NewServiceRegistry()
ValidatorClient := &ValidatorClient{
ctx: ctx,
services: registry,
stop: make(chan struct{}),
}
featureconfig.ConfigureValidator(ctx)
// Use custom config values if the --no-custom-config flag is set.
if !ctx.GlobalBool(flags.NoCustomConfigFlag.Name) {
log.Info("Using custom parameter configuration")
if featureconfig.Get().MinimalConfig {
log.Warn("Using Minimal Config")
params.UseMinimalConfig()
} else {
log.Warn("Using Demo Config")
params.UseDemoBeaconConfig()
}
}
keyManager, err := selectKeyManager(ctx)
if err != nil {
return nil, err
}
clearFlag := ctx.GlobalBool(cmd.ClearDB.Name)
forceClearFlag := ctx.GlobalBool(cmd.ForceClearDB.Name)
if clearFlag || forceClearFlag {
pubkeys, err := keyManager.FetchValidatingKeys()
if err != nil {
return nil, err
}
dataDir := ctx.GlobalString(cmd.DataDirFlag.Name)
if err := clearDB(dataDir, pubkeys, forceClearFlag); err != nil {
return nil, err
}
}
if err := ValidatorClient.registerPrometheusService(ctx); err != nil {
return nil, err
}
if err := ValidatorClient.registerClientService(ctx, keyManager); err != nil {
return nil, err
}
return ValidatorClient, nil
}
// Start every service in the validator client.
func (s *ValidatorClient) Start() {
s.lock.Lock()
log.WithFields(logrus.Fields{
"version": version.GetVersion(),
}).Info("Starting validator node")
s.services.StartAll()
stop := s.stop
s.lock.Unlock()
go func() {
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
defer signal.Stop(sigc)
<-sigc
log.Info("Got interrupt, shutting down...")
debug.Exit(s.ctx) // Ensure trace and CPU profile data are flushed.
go s.Close()
for i := 10; i > 0; i-- {
<-sigc
if i > 1 {
log.Info("Already shutting down, interrupt more to panic.", "times", i-1)
}
}
panic("Panic closing the sharding validator")
}()
// Wait for stop channel to be closed.
<-stop
}
// Close handles graceful shutdown of the system.
func (s *ValidatorClient) Close() {
s.lock.Lock()
defer s.lock.Unlock()
s.services.StopAll()
log.Info("Stopping sharding validator")
close(s.stop)
}
func (s *ValidatorClient) registerPrometheusService(ctx *cli.Context) error {
service := prometheus.NewPrometheusService(
fmt.Sprintf(":%d", ctx.GlobalInt64(cmd.MonitoringPortFlag.Name)),
s.services,
)
logrus.AddHook(prometheus.NewLogrusCollector())
return s.services.RegisterService(service)
}
func (s *ValidatorClient) registerClientService(ctx *cli.Context, keyManager keymanager.KeyManager) error {
endpoint := ctx.GlobalString(flags.BeaconRPCProviderFlag.Name)
dataDir := ctx.GlobalString(cmd.DataDirFlag.Name)
logValidatorBalances := !ctx.GlobalBool(flags.DisablePenaltyRewardLogFlag.Name)
cert := ctx.GlobalString(flags.CertFlag.Name)
graffiti := ctx.GlobalString(flags.GraffitiFlag.Name)
v, err := client.NewValidatorService(context.Background(), &client.Config{
Endpoint: endpoint,
DataDir: dataDir,
KeyManager: keyManager,
LogValidatorBalances: logValidatorBalances,
CertFlag: cert,
GraffitiFlag: graffiti,
})
if err != nil {
return errors.Wrap(err, "could not initialize client service")
}
return s.services.RegisterService(v)
}
// selectKeyManager selects the key manager depending on the options provided by the user.
func selectKeyManager(ctx *cli.Context) (keymanager.KeyManager, error) {
if unencryptedKeys := ctx.String(flags.UnencryptedKeysFlag.Name); unencryptedKeys != "" {
// Fetch keys from unencrypted store.
path, err := filepath.Abs(unencryptedKeys)
if err != nil {
return nil, err
}
r, err := os.Open(path)
if err != nil {
return nil, err
}
defer r.Close()
return keymanager.NewUnencrypted(r)
}
if numValidatorKeys := ctx.GlobalUint64(flags.InteropNumValidators.Name); numValidatorKeys > 0 {
// Generate keys from interop seed.
return keymanager.NewInterop(numValidatorKeys, ctx.GlobalUint64(flags.InteropStartIndex.Name))
}
// Fetch keys from keystore.
return keymanager.NewKeystore(ctx.String(flags.KeystorePathFlag.Name), ctx.String(flags.PasswordFlag.Name))
}
func clearDB(dataDir string, pubkeys [][48]byte, force bool) error {
var err error
clearDBConfirmed := force
if !force {
actionText := "This will delete your validator's historical actions database stored in your data directory. " +
"This may lead to potential slashing - do you want to proceed? (Y/N)"
deniedText := "The historical actions database will not be deleted. No changes have been made."
clearDBConfirmed, err = cmd.ConfirmAction(actionText, deniedText)
if err != nil {
return errors.Wrapf(err, "Could not create DB in dir %s", dataDir)
}
}
if clearDBConfirmed {
valDB, err := db.NewKVStore(dataDir, pubkeys)
if err != nil {
return errors.Wrapf(err, "Could not create DB in dir %s", dataDir)
}
log.Warning("Removing database")
if err := valDB.ClearDB(); err != nil {
return errors.Wrapf(err, "Could not clear DB in dir %s", dataDir)
}
}
return nil
}