github.com/prysmaticlabs/prysm@v1.4.4/validator/node/node.go (about)

     1  // Package node is the main process which handles the lifecycle of
     2  // the runtime services in a validator client process, gracefully shutting
     3  // everything down upon close.
     4  package node
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"net/http"
    10  	"os"
    11  	"os/signal"
    12  	"path/filepath"
    13  	"strings"
    14  	"sync"
    15  	"syscall"
    16  
    17  	gwruntime "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
    18  	"github.com/pkg/errors"
    19  	"github.com/prysmaticlabs/prysm/cmd/validator/flags"
    20  	pb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2"
    21  	"github.com/prysmaticlabs/prysm/shared"
    22  	"github.com/prysmaticlabs/prysm/shared/backuputil"
    23  	"github.com/prysmaticlabs/prysm/shared/cmd"
    24  	"github.com/prysmaticlabs/prysm/shared/debug"
    25  	"github.com/prysmaticlabs/prysm/shared/event"
    26  	"github.com/prysmaticlabs/prysm/shared/featureconfig"
    27  	"github.com/prysmaticlabs/prysm/shared/fileutil"
    28  	"github.com/prysmaticlabs/prysm/shared/gateway"
    29  	"github.com/prysmaticlabs/prysm/shared/params"
    30  	"github.com/prysmaticlabs/prysm/shared/prereq"
    31  	"github.com/prysmaticlabs/prysm/shared/prometheus"
    32  	"github.com/prysmaticlabs/prysm/shared/tracing"
    33  	"github.com/prysmaticlabs/prysm/shared/version"
    34  	accountsiface "github.com/prysmaticlabs/prysm/validator/accounts/iface"
    35  	"github.com/prysmaticlabs/prysm/validator/accounts/wallet"
    36  	"github.com/prysmaticlabs/prysm/validator/client"
    37  	"github.com/prysmaticlabs/prysm/validator/db/kv"
    38  	g "github.com/prysmaticlabs/prysm/validator/graffiti"
    39  	"github.com/prysmaticlabs/prysm/validator/keymanager"
    40  	"github.com/prysmaticlabs/prysm/validator/keymanager/imported"
    41  	"github.com/prysmaticlabs/prysm/validator/rpc"
    42  	slashingprotection "github.com/prysmaticlabs/prysm/validator/slashing-protection"
    43  	"github.com/prysmaticlabs/prysm/validator/slashing-protection/iface"
    44  	"github.com/prysmaticlabs/prysm/validator/web"
    45  	"github.com/sirupsen/logrus"
    46  	"github.com/urfave/cli/v2"
    47  	"google.golang.org/protobuf/encoding/protojson"
    48  )
    49  
    50  // ValidatorClient defines an instance of an Ethereum validator that manages
    51  // the entire lifecycle of services attached to it participating in proof of stake.
    52  type ValidatorClient struct {
    53  	cliCtx            *cli.Context
    54  	ctx               context.Context
    55  	cancel            context.CancelFunc
    56  	db                *kv.Store
    57  	services          *shared.ServiceRegistry // Lifecycle and service store.
    58  	lock              sync.RWMutex
    59  	wallet            *wallet.Wallet
    60  	walletInitialized *event.Feed
    61  	stop              chan struct{} // Channel to wait for termination notifications.
    62  }
    63  
    64  // NewValidatorClient creates a new instance of the Prysm validator client.
    65  func NewValidatorClient(cliCtx *cli.Context) (*ValidatorClient, error) {
    66  	if err := tracing.Setup(
    67  		"validator", // service name
    68  		cliCtx.String(cmd.TracingProcessNameFlag.Name),
    69  		cliCtx.String(cmd.TracingEndpointFlag.Name),
    70  		cliCtx.Float64(cmd.TraceSampleFractionFlag.Name),
    71  		cliCtx.Bool(cmd.EnableTracingFlag.Name),
    72  	); err != nil {
    73  		return nil, err
    74  	}
    75  
    76  	verbosity := cliCtx.String(cmd.VerbosityFlag.Name)
    77  	level, err := logrus.ParseLevel(verbosity)
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  	logrus.SetLevel(level)
    82  
    83  	// Warn if user's platform is not supported
    84  	prereq.WarnIfPlatformNotSupported(cliCtx.Context)
    85  
    86  	registry := shared.NewServiceRegistry()
    87  	ctx, cancel := context.WithCancel(cliCtx.Context)
    88  	validatorClient := &ValidatorClient{
    89  		cliCtx:            cliCtx,
    90  		ctx:               ctx,
    91  		cancel:            cancel,
    92  		services:          registry,
    93  		walletInitialized: new(event.Feed),
    94  		stop:              make(chan struct{}),
    95  	}
    96  
    97  	featureconfig.ConfigureValidator(cliCtx)
    98  	cmd.ConfigureValidator(cliCtx)
    99  
   100  	if cliCtx.IsSet(cmd.ChainConfigFileFlag.Name) {
   101  		chainConfigFileName := cliCtx.String(cmd.ChainConfigFileFlag.Name)
   102  		params.LoadChainConfigFile(chainConfigFileName)
   103  	}
   104  
   105  	// If the --web flag is enabled to administer the validator
   106  	// client via a web portal, we start the validator client in a different way.
   107  	if cliCtx.IsSet(flags.EnableWebFlag.Name) {
   108  		log.Info("Enabling web portal to manage the validator client")
   109  		if err := validatorClient.initializeForWeb(cliCtx); err != nil {
   110  			return nil, err
   111  		}
   112  		return validatorClient, nil
   113  	}
   114  
   115  	if cliCtx.IsSet(cmd.ChainConfigFileFlag.Name) {
   116  		chainConfigFileName := cliCtx.String(cmd.ChainConfigFileFlag.Name)
   117  		params.LoadChainConfigFile(chainConfigFileName)
   118  	}
   119  
   120  	if err := validatorClient.initializeFromCLI(cliCtx); err != nil {
   121  		return nil, err
   122  	}
   123  
   124  	return validatorClient, nil
   125  }
   126  
   127  // Start every service in the validator client.
   128  func (c *ValidatorClient) Start() {
   129  	c.lock.Lock()
   130  
   131  	log.WithFields(logrus.Fields{
   132  		"version": version.Version(),
   133  	}).Info("Starting validator node")
   134  
   135  	c.services.StartAll()
   136  
   137  	stop := c.stop
   138  	c.lock.Unlock()
   139  
   140  	go func() {
   141  		sigc := make(chan os.Signal, 1)
   142  		signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
   143  		defer signal.Stop(sigc)
   144  		<-sigc
   145  		log.Info("Got interrupt, shutting down...")
   146  		debug.Exit(c.cliCtx) // Ensure trace and CPU profile data are flushed.
   147  		go c.Close()
   148  		for i := 10; i > 0; i-- {
   149  			<-sigc
   150  			if i > 1 {
   151  				log.WithField("times", i-1).Info("Already shutting down, interrupt more to panic.")
   152  			}
   153  		}
   154  		panic("Panic closing the validator client")
   155  	}()
   156  
   157  	// Wait for stop channel to be closed.
   158  	<-stop
   159  }
   160  
   161  // Close handles graceful shutdown of the system.
   162  func (c *ValidatorClient) Close() {
   163  	c.lock.Lock()
   164  	defer c.lock.Unlock()
   165  
   166  	c.services.StopAll()
   167  	log.Info("Stopping Prysm validator")
   168  	c.cancel()
   169  	close(c.stop)
   170  }
   171  
   172  func (c *ValidatorClient) initializeFromCLI(cliCtx *cli.Context) error {
   173  	var keyManager keymanager.IKeymanager
   174  	var err error
   175  	if cliCtx.IsSet(flags.InteropNumValidators.Name) {
   176  		numValidatorKeys := cliCtx.Uint64(flags.InteropNumValidators.Name)
   177  		offset := cliCtx.Uint64(flags.InteropStartIndex.Name)
   178  		keyManager, err = imported.NewInteropKeymanager(cliCtx.Context, offset, numValidatorKeys)
   179  		if err != nil {
   180  			return errors.Wrap(err, "could not generate interop keys")
   181  		}
   182  	} else {
   183  		// Read the wallet from the specified path.
   184  		w, err := wallet.OpenWalletOrElseCli(cliCtx, func(cliCtx *cli.Context) (*wallet.Wallet, error) {
   185  			return nil, wallet.ErrNoWalletFound
   186  		})
   187  		if err != nil {
   188  			return errors.Wrap(err, "could not open wallet")
   189  		}
   190  		c.wallet = w
   191  		log.WithFields(logrus.Fields{
   192  			"wallet":          w.AccountsDir(),
   193  			"keymanager-kind": w.KeymanagerKind().String(),
   194  		}).Info("Opened validator wallet")
   195  		keyManager, err = w.InitializeKeymanager(cliCtx.Context, accountsiface.InitKeymanagerConfig{ListenForChanges: true})
   196  		if err != nil {
   197  			return errors.Wrap(err, "could not read keymanager for wallet")
   198  		}
   199  	}
   200  	dataDir := cliCtx.String(flags.WalletDirFlag.Name)
   201  	if c.wallet != nil {
   202  		dataDir = c.wallet.AccountsDir()
   203  	}
   204  	if cliCtx.String(cmd.DataDirFlag.Name) != cmd.DefaultDataDir() {
   205  		dataDir = cliCtx.String(cmd.DataDirFlag.Name)
   206  	}
   207  	clearFlag := cliCtx.Bool(cmd.ClearDB.Name)
   208  	forceClearFlag := cliCtx.Bool(cmd.ForceClearDB.Name)
   209  	if clearFlag || forceClearFlag {
   210  		if dataDir == "" && c.wallet != nil {
   211  			dataDir = c.wallet.AccountsDir()
   212  			if dataDir == "" {
   213  				log.Fatal(
   214  					"Could not determine your system'c HOME path, please specify a --datadir you wish " +
   215  						"to use for your validator data",
   216  				)
   217  			}
   218  
   219  		}
   220  		if err := clearDB(cliCtx.Context, dataDir, forceClearFlag); err != nil {
   221  			return err
   222  		}
   223  	} else {
   224  		dataFile := filepath.Join(dataDir, kv.ProtectionDbFileName)
   225  		if !fileutil.FileExists(dataFile) {
   226  			log.Warnf("Slashing protection file %s is missing.\n"+
   227  				"If you changed your --wallet-dir or --datadir, please copy your previous \"validator.db\" file into your current --datadir.\n"+
   228  				"Disregard this warning if this is the first time you are running this set of keys.", dataFile)
   229  		}
   230  	}
   231  	log.WithField("databasePath", dataDir).Info("Checking DB")
   232  
   233  	valDB, err := kv.NewKVStore(cliCtx.Context, dataDir, &kv.Config{
   234  		PubKeys:         nil,
   235  		InitialMMapSize: cliCtx.Int(cmd.BoltMMapInitialSizeFlag.Name),
   236  	})
   237  	if err != nil {
   238  		return errors.Wrap(err, "could not initialize db")
   239  	}
   240  	c.db = valDB
   241  	if err := valDB.RunUpMigrations(cliCtx.Context); err != nil {
   242  		return errors.Wrap(err, "could not run database migration")
   243  	}
   244  
   245  	if !cliCtx.Bool(cmd.DisableMonitoringFlag.Name) {
   246  		if err := c.registerPrometheusService(cliCtx); err != nil {
   247  			return err
   248  		}
   249  	}
   250  	if featureconfig.Get().SlasherProtection {
   251  		if err := c.registerSlasherService(); err != nil {
   252  			return err
   253  		}
   254  	}
   255  	if err := c.registerValidatorService(keyManager); err != nil {
   256  		return err
   257  	}
   258  	if cliCtx.Bool(flags.EnableRPCFlag.Name) {
   259  		if err := c.registerRPCService(cliCtx, keyManager); err != nil {
   260  			return err
   261  		}
   262  		if err := c.registerRPCGatewayService(cliCtx); err != nil {
   263  			return err
   264  		}
   265  	}
   266  	return nil
   267  }
   268  
   269  func (c *ValidatorClient) initializeForWeb(cliCtx *cli.Context) error {
   270  	var keyManager keymanager.IKeymanager
   271  	var err error
   272  
   273  	// Read the wallet password file from the cli context.
   274  	if err = setWalletPasswordFilePath(cliCtx); err != nil {
   275  		return errors.Wrap(err, "could not read wallet password file")
   276  	}
   277  
   278  	// Read the wallet from the specified path.
   279  	w, err := wallet.OpenWalletOrElseCli(cliCtx, func(cliCtx *cli.Context) (*wallet.Wallet, error) {
   280  		return nil, nil
   281  	})
   282  	if err != nil {
   283  		return errors.Wrap(err, "could not open wallet")
   284  	}
   285  	if w != nil {
   286  		c.wallet = w
   287  		log.WithFields(logrus.Fields{
   288  			"wallet":          w.AccountsDir(),
   289  			"keymanager-kind": w.KeymanagerKind().String(),
   290  		}).Info("Opened validator wallet")
   291  		keyManager, err = w.InitializeKeymanager(cliCtx.Context, accountsiface.InitKeymanagerConfig{ListenForChanges: true})
   292  		if err != nil {
   293  			return errors.Wrap(err, "could not read keymanager for wallet")
   294  		}
   295  	}
   296  	dataDir := cliCtx.String(flags.WalletDirFlag.Name)
   297  	if c.wallet != nil {
   298  		dataDir = c.wallet.AccountsDir()
   299  	}
   300  	if cliCtx.String(cmd.DataDirFlag.Name) != cmd.DefaultDataDir() {
   301  		dataDir = cliCtx.String(cmd.DataDirFlag.Name)
   302  	}
   303  	clearFlag := cliCtx.Bool(cmd.ClearDB.Name)
   304  	forceClearFlag := cliCtx.Bool(cmd.ForceClearDB.Name)
   305  
   306  	if clearFlag || forceClearFlag {
   307  		if dataDir == "" {
   308  			dataDir = cmd.DefaultDataDir()
   309  			if dataDir == "" {
   310  				log.Fatal(
   311  					"Could not determine your system'c HOME path, please specify a --datadir you wish " +
   312  						"to use for your validator data",
   313  				)
   314  			}
   315  
   316  		}
   317  		if err := clearDB(cliCtx.Context, dataDir, forceClearFlag); err != nil {
   318  			return err
   319  		}
   320  	}
   321  	log.WithField("databasePath", dataDir).Info("Checking DB")
   322  	valDB, err := kv.NewKVStore(cliCtx.Context, dataDir, &kv.Config{
   323  		PubKeys:         nil,
   324  		InitialMMapSize: cliCtx.Int(cmd.BoltMMapInitialSizeFlag.Name),
   325  	})
   326  	if err != nil {
   327  		return errors.Wrap(err, "could not initialize db")
   328  	}
   329  	c.db = valDB
   330  	if err := valDB.RunUpMigrations(cliCtx.Context); err != nil {
   331  		return errors.Wrap(err, "could not run database migration")
   332  	}
   333  
   334  	if !cliCtx.Bool(cmd.DisableMonitoringFlag.Name) {
   335  		if err := c.registerPrometheusService(cliCtx); err != nil {
   336  			return err
   337  		}
   338  	}
   339  	if featureconfig.Get().SlasherProtection {
   340  		if err := c.registerSlasherService(); err != nil {
   341  			return err
   342  		}
   343  	}
   344  	if err := c.registerValidatorService(keyManager); err != nil {
   345  		return err
   346  	}
   347  	if err := c.registerRPCService(cliCtx, keyManager); err != nil {
   348  		return err
   349  	}
   350  	if err := c.registerRPCGatewayService(cliCtx); err != nil {
   351  		return err
   352  	}
   353  	gatewayHost := cliCtx.String(flags.GRPCGatewayHost.Name)
   354  	gatewayPort := cliCtx.Int(flags.GRPCGatewayPort.Name)
   355  	webAddress := fmt.Sprintf("http://%s:%d", gatewayHost, gatewayPort)
   356  	log.WithField("address", webAddress).Info(
   357  		"Starting Prysm web UI on address, open in browser to access",
   358  	)
   359  	return nil
   360  }
   361  
   362  func (c *ValidatorClient) registerPrometheusService(cliCtx *cli.Context) error {
   363  	var additionalHandlers []prometheus.Handler
   364  	if cliCtx.IsSet(cmd.EnableBackupWebhookFlag.Name) {
   365  		additionalHandlers = append(
   366  			additionalHandlers,
   367  			prometheus.Handler{
   368  				Path:    "/db/backup",
   369  				Handler: backuputil.BackupHandler(c.db, cliCtx.String(cmd.BackupWebhookOutputDir.Name)),
   370  			},
   371  		)
   372  	}
   373  	service := prometheus.NewService(
   374  		fmt.Sprintf("%s:%d", c.cliCtx.String(cmd.MonitoringHostFlag.Name), c.cliCtx.Int(flags.MonitoringPortFlag.Name)),
   375  		c.services,
   376  		additionalHandlers...,
   377  	)
   378  	logrus.AddHook(prometheus.NewLogrusCollector())
   379  	return c.services.RegisterService(service)
   380  }
   381  
   382  func (c *ValidatorClient) registerValidatorService(
   383  	keyManager keymanager.IKeymanager,
   384  ) error {
   385  	endpoint := c.cliCtx.String(flags.BeaconRPCProviderFlag.Name)
   386  	dataDir := c.cliCtx.String(cmd.DataDirFlag.Name)
   387  	logValidatorBalances := !c.cliCtx.Bool(flags.DisablePenaltyRewardLogFlag.Name)
   388  	emitAccountMetrics := !c.cliCtx.Bool(flags.DisableAccountMetricsFlag.Name)
   389  	cert := c.cliCtx.String(flags.CertFlag.Name)
   390  	graffiti := c.cliCtx.String(flags.GraffitiFlag.Name)
   391  	maxCallRecvMsgSize := c.cliCtx.Int(cmd.GrpcMaxCallRecvMsgSizeFlag.Name)
   392  	grpcRetries := c.cliCtx.Uint(flags.GrpcRetriesFlag.Name)
   393  	grpcRetryDelay := c.cliCtx.Duration(flags.GrpcRetryDelayFlag.Name)
   394  	var sp *slashingprotection.Service
   395  	var protector iface.Protector
   396  	if err := c.services.FetchService(&sp); err == nil {
   397  		protector = sp
   398  	}
   399  
   400  	gStruct := &g.Graffiti{}
   401  	var err error
   402  	if c.cliCtx.IsSet(flags.GraffitiFileFlag.Name) {
   403  		n := c.cliCtx.String(flags.GraffitiFileFlag.Name)
   404  		gStruct, err = g.ParseGraffitiFile(n)
   405  		if err != nil {
   406  			log.WithError(err).Warn("Could not parse graffiti file")
   407  		}
   408  	}
   409  
   410  	v, err := client.NewValidatorService(c.cliCtx.Context, &client.Config{
   411  		Endpoint:                   endpoint,
   412  		DataDir:                    dataDir,
   413  		KeyManager:                 keyManager,
   414  		LogValidatorBalances:       logValidatorBalances,
   415  		EmitAccountMetrics:         emitAccountMetrics,
   416  		CertFlag:                   cert,
   417  		GraffitiFlag:               g.ParseHexGraffiti(graffiti),
   418  		GrpcMaxCallRecvMsgSizeFlag: maxCallRecvMsgSize,
   419  		GrpcRetriesFlag:            grpcRetries,
   420  		GrpcRetryDelay:             grpcRetryDelay,
   421  		GrpcHeadersFlag:            c.cliCtx.String(flags.GrpcHeadersFlag.Name),
   422  		Protector:                  protector,
   423  		ValDB:                      c.db,
   424  		UseWeb:                     c.cliCtx.Bool(flags.EnableWebFlag.Name),
   425  		WalletInitializedFeed:      c.walletInitialized,
   426  		GraffitiStruct:             gStruct,
   427  		LogDutyCountDown:           c.cliCtx.Bool(flags.EnableDutyCountDown.Name),
   428  	})
   429  	if err != nil {
   430  		return errors.Wrap(err, "could not initialize validator service")
   431  	}
   432  
   433  	return c.services.RegisterService(v)
   434  }
   435  func (c *ValidatorClient) registerSlasherService() error {
   436  	endpoint := c.cliCtx.String(flags.SlasherRPCProviderFlag.Name)
   437  	if endpoint == "" {
   438  		return errors.New("external slasher feature flag is set but no slasher endpoint is configured")
   439  
   440  	}
   441  	cert := c.cliCtx.String(flags.SlasherCertFlag.Name)
   442  	maxCallRecvMsgSize := c.cliCtx.Int(cmd.GrpcMaxCallRecvMsgSizeFlag.Name)
   443  	grpcRetries := c.cliCtx.Uint(flags.GrpcRetriesFlag.Name)
   444  	grpcRetryDelay := c.cliCtx.Duration(flags.GrpcRetryDelayFlag.Name)
   445  	sp, err := slashingprotection.NewService(c.cliCtx.Context, &slashingprotection.Config{
   446  		Endpoint:                   endpoint,
   447  		CertFlag:                   cert,
   448  		GrpcMaxCallRecvMsgSizeFlag: maxCallRecvMsgSize,
   449  		GrpcRetriesFlag:            grpcRetries,
   450  		GrpcRetryDelay:             grpcRetryDelay,
   451  		GrpcHeadersFlag:            c.cliCtx.String(flags.GrpcHeadersFlag.Name),
   452  	})
   453  	if err != nil {
   454  		return errors.Wrap(err, "could not initialize slasher service")
   455  	}
   456  	return c.services.RegisterService(sp)
   457  }
   458  
   459  func (c *ValidatorClient) registerRPCService(cliCtx *cli.Context, km keymanager.IKeymanager) error {
   460  	var vs *client.ValidatorService
   461  	if err := c.services.FetchService(&vs); err != nil {
   462  		return err
   463  	}
   464  	validatorGatewayHost := cliCtx.String(flags.GRPCGatewayHost.Name)
   465  	validatorGatewayPort := cliCtx.Int(flags.GRPCGatewayPort.Name)
   466  	validatorMonitoringHost := cliCtx.String(cmd.MonitoringHostFlag.Name)
   467  	validatorMonitoringPort := cliCtx.Int(flags.MonitoringPortFlag.Name)
   468  	rpcHost := cliCtx.String(flags.RPCHost.Name)
   469  	rpcPort := cliCtx.Int(flags.RPCPort.Name)
   470  	nodeGatewayEndpoint := cliCtx.String(flags.BeaconRPCGatewayProviderFlag.Name)
   471  	beaconClientEndpoint := cliCtx.String(flags.BeaconRPCProviderFlag.Name)
   472  	maxCallRecvMsgSize := c.cliCtx.Int(cmd.GrpcMaxCallRecvMsgSizeFlag.Name)
   473  	grpcRetries := c.cliCtx.Uint(flags.GrpcRetriesFlag.Name)
   474  	grpcRetryDelay := c.cliCtx.Duration(flags.GrpcRetryDelayFlag.Name)
   475  	walletDir := cliCtx.String(flags.WalletDirFlag.Name)
   476  	grpcHeaders := c.cliCtx.String(flags.GrpcHeadersFlag.Name)
   477  	clientCert := c.cliCtx.String(flags.CertFlag.Name)
   478  	server := rpc.NewServer(cliCtx.Context, &rpc.Config{
   479  		ValDB:                    c.db,
   480  		Host:                     rpcHost,
   481  		Port:                     fmt.Sprintf("%d", rpcPort),
   482  		WalletInitializedFeed:    c.walletInitialized,
   483  		ValidatorService:         vs,
   484  		SyncChecker:              vs,
   485  		GenesisFetcher:           vs,
   486  		NodeGatewayEndpoint:      nodeGatewayEndpoint,
   487  		WalletDir:                walletDir,
   488  		Wallet:                   c.wallet,
   489  		Keymanager:               km,
   490  		ValidatorGatewayHost:     validatorGatewayHost,
   491  		ValidatorGatewayPort:     validatorGatewayPort,
   492  		ValidatorMonitoringHost:  validatorMonitoringHost,
   493  		ValidatorMonitoringPort:  validatorMonitoringPort,
   494  		BeaconClientEndpoint:     beaconClientEndpoint,
   495  		ClientMaxCallRecvMsgSize: maxCallRecvMsgSize,
   496  		ClientGrpcRetries:        grpcRetries,
   497  		ClientGrpcRetryDelay:     grpcRetryDelay,
   498  		ClientGrpcHeaders:        strings.Split(grpcHeaders, ","),
   499  		ClientWithCert:           clientCert,
   500  	})
   501  	return c.services.RegisterService(server)
   502  }
   503  
   504  func (c *ValidatorClient) registerRPCGatewayService(cliCtx *cli.Context) error {
   505  	gatewayHost := cliCtx.String(flags.GRPCGatewayHost.Name)
   506  	if gatewayHost != flags.DefaultGatewayHost {
   507  		log.WithField("web-host", gatewayHost).Warn(
   508  			"You are using a non-default web host. Web traffic is served by HTTP, so be wary of " +
   509  				"changing this parameter if you are exposing this host to the Internet!",
   510  		)
   511  	}
   512  	gatewayPort := cliCtx.Int(flags.GRPCGatewayPort.Name)
   513  	rpcHost := cliCtx.String(flags.RPCHost.Name)
   514  	rpcPort := cliCtx.Int(flags.RPCPort.Name)
   515  	rpcAddr := fmt.Sprintf("%s:%d", rpcHost, rpcPort)
   516  	gatewayAddress := fmt.Sprintf("%s:%d", gatewayHost, gatewayPort)
   517  	allowedOrigins := strings.Split(cliCtx.String(flags.GPRCGatewayCorsDomain.Name), ",")
   518  	maxCallSize := cliCtx.Uint64(cmd.GrpcMaxCallRecvMsgSizeFlag.Name)
   519  
   520  	registrations := []gateway.PbHandlerRegistration{
   521  		pb.RegisterAuthHandler,
   522  		pb.RegisterWalletHandler,
   523  		pb.RegisterHealthHandler,
   524  		pb.RegisterAccountsHandler,
   525  		pb.RegisterBeaconHandler,
   526  		pb.RegisterSlashingProtectionHandler,
   527  	}
   528  	mux := gwruntime.NewServeMux(
   529  		gwruntime.WithMarshalerOption(gwruntime.MIMEWildcard, &gwruntime.HTTPBodyMarshaler{
   530  			Marshaler: &gwruntime.JSONPb{
   531  				MarshalOptions: protojson.MarshalOptions{
   532  					EmitUnpopulated: true,
   533  				},
   534  				UnmarshalOptions: protojson.UnmarshalOptions{
   535  					DiscardUnknown: true,
   536  				},
   537  			},
   538  		}),
   539  		gwruntime.WithMarshalerOption(
   540  			"text/event-stream", &gwruntime.EventSourceJSONPb{},
   541  		),
   542  	)
   543  	muxHandler := func(h http.Handler, w http.ResponseWriter, req *http.Request) {
   544  		if strings.HasPrefix(req.URL.Path, "/api") {
   545  			http.StripPrefix("/api", h).ServeHTTP(w, req)
   546  		} else {
   547  			web.Handler(w, req)
   548  		}
   549  	}
   550  
   551  	pbHandler := gateway.PbMux{
   552  		Registrations: registrations,
   553  		Patterns:      []string{"/accounts/", "/v2/"},
   554  		Mux:           mux,
   555  	}
   556  
   557  	gw := gateway.New(
   558  		cliCtx.Context,
   559  		[]gateway.PbMux{pbHandler},
   560  		muxHandler,
   561  		rpcAddr,
   562  		gatewayAddress,
   563  	).WithAllowedOrigins(allowedOrigins).WithMaxCallRecvMsgSize(maxCallSize)
   564  
   565  	return c.services.RegisterService(gw)
   566  }
   567  
   568  func setWalletPasswordFilePath(cliCtx *cli.Context) error {
   569  	walletDir := cliCtx.String(flags.WalletDirFlag.Name)
   570  	defaultWalletPasswordFilePath := filepath.Join(walletDir, wallet.DefaultWalletPasswordFile)
   571  	if fileutil.FileExists(defaultWalletPasswordFilePath) {
   572  		// Ensure file has proper permissions.
   573  		hasPerms, err := fileutil.HasReadWritePermissions(defaultWalletPasswordFilePath)
   574  		if err != nil {
   575  			return err
   576  		}
   577  		if !hasPerms {
   578  			return fmt.Errorf(
   579  				"wallet password file %s does not have proper 0600 permissions",
   580  				defaultWalletPasswordFilePath,
   581  			)
   582  		}
   583  
   584  		// Set the filepath into the cli context.
   585  		if err := cliCtx.Set(flags.WalletPasswordFileFlag.Name, defaultWalletPasswordFilePath); err != nil {
   586  			return errors.Wrap(err, "could not set default wallet password file path")
   587  		}
   588  	}
   589  	return nil
   590  }
   591  
   592  func clearDB(ctx context.Context, dataDir string, force bool) error {
   593  	var err error
   594  	clearDBConfirmed := force
   595  
   596  	if !force {
   597  		actionText := "This will delete your validator's historical actions database stored in your data directory. " +
   598  			"This may lead to potential slashing - do you want to proceed? (Y/N)"
   599  		deniedText := "The historical actions database will not be deleted. No changes have been made."
   600  		clearDBConfirmed, err = cmd.ConfirmAction(actionText, deniedText)
   601  		if err != nil {
   602  			return errors.Wrapf(err, "Could not clear DB in dir %s", dataDir)
   603  		}
   604  	}
   605  
   606  	if clearDBConfirmed {
   607  		valDB, err := kv.NewKVStore(ctx, dataDir, &kv.Config{})
   608  		if err != nil {
   609  			return errors.Wrapf(err, "Could not create DB in dir %s", dataDir)
   610  		}
   611  		if err := valDB.Close(); err != nil {
   612  			return errors.Wrapf(err, "could not close DB in dir %s", dataDir)
   613  		}
   614  
   615  		log.Warning("Removing database")
   616  		if err := valDB.ClearDB(); err != nil {
   617  			return errors.Wrapf(err, "Could not clear DB in dir %s", dataDir)
   618  		}
   619  	}
   620  
   621  	return nil
   622  }