operator

package
v1.3.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 1, 2024 License: GPL-3.0 Imports: 58 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var GenerateDocCmd = &cobra.Command{
	Use:   "doc",
	Short: "Generate CLI documentation for the node",
	Run: func(cmd *cobra.Command, args []string) {
		var cfg config
		t := reflect.TypeOf(cfg)

		var docs []ArgumentDoc
		getAllFields(t, nil, nil, &docs)

		tbl := table.New(os.Stdout)
		tbl.SetHeaders("YAML", "ENV", "Default", "Description")
		for _, doc := range docs {
			yamlName := strings.Join(doc.YAMLPath, ".")
			tbl.AddRow(yamlName, doc.EnvName, doc.Default, doc.Description)
		}
		tbl.Render()
	},
}
View Source
var StartNodeCmd = &cobra.Command{
	Use:   "start-node",
	Short: "Starts an instance of SSV node",
	Run: func(cmd *cobra.Command, args []string) {
		commons.SetBuildData(cmd.Parent().Short, cmd.Parent().Version)

		logger, err := setupGlobal()
		if err != nil {
			log.Fatal("could not create logger", err)
		}

		defer logging.CapturePanic(logger)

		logger.Info(fmt.Sprintf("starting %v", commons.GetBuildData()))

		metricsReporter := metricsreporter.New(
			metricsreporter.WithLogger(logger),
		)

		networkConfig, err := setupSSVNetwork(logger)
		if err != nil {
			logger.Fatal("could not setup network", zap.Error(err))
		}
		cfg.DBOptions.Ctx = cmd.Context()
		db, err := setupDB(logger, networkConfig.Beacon.GetNetwork())
		if err != nil {
			logger.Fatal("could not setup db", zap.Error(err))
		}

		var operatorPrivKey keys.OperatorPrivateKey
		var operatorPrivKeyText string
		if cfg.KeyStore.PrivateKeyFile != "" {

			encryptedJSON, err := os.ReadFile(cfg.KeyStore.PrivateKeyFile)
			if err != nil {
				logger.Fatal("could not read PEM file", zap.Error(err))
			}

			keyStorePassword, err := os.ReadFile(cfg.KeyStore.PasswordFile)
			if err != nil {
				logger.Fatal("could not read password file", zap.Error(err))
			}

			decryptedKeystore, err := keystore.DecryptKeystore(encryptedJSON, string(keyStorePassword))
			if err != nil {
				logger.Fatal("could not decrypt operator private key keystore", zap.Error(err))
			}
			operatorPrivKey, err = keys.PrivateKeyFromBytes(decryptedKeystore)
			if err != nil {
				logger.Fatal("could not extract operator private key from file", zap.Error(err))
			}

			operatorPrivKeyText = base64.StdEncoding.EncodeToString(decryptedKeystore)
		} else {
			operatorPrivKey, err = keys.PrivateKeyFromString(cfg.OperatorPrivateKey)
			if err != nil {
				logger.Fatal("could not decode operator private key", zap.Error(err))
			}
			operatorPrivKeyText = cfg.OperatorPrivateKey
		}
		cfg.P2pNetworkConfig.OperatorSigner = operatorPrivKey

		nodeStorage, operatorData := setupOperatorStorage(logger, db, operatorPrivKey, operatorPrivKeyText)
		operatorDataStore := operatordatastore.New(operatorData)

		usingLocalEvents := len(cfg.LocalEventsPath) != 0

		verifyConfig(logger, nodeStorage, networkConfig.Name, usingLocalEvents)

		ekmHashedKey, err := operatorPrivKey.EKMHash()
		if err != nil {
			logger.Fatal("could not get operator private key hash", zap.Error(err))
		}

		keyManager, err := ekm.NewETHKeyManagerSigner(logger, db, networkConfig, cfg.SSVOptions.ValidatorOptions.BuilderProposals, ekmHashedKey)
		if err != nil {
			logger.Fatal("could not create new eth-key-manager signer", zap.Error(err))
		}

		cfg.P2pNetworkConfig.Ctx = cmd.Context()

		permissioned := func() bool {
			currentEpoch := networkConfig.Beacon.EstimatedCurrentEpoch()
			return currentEpoch < networkConfig.PermissionlessActivationEpoch
		}

		slotTickerProvider := func() slotticker.SlotTicker {
			return slotticker.New(logger, slotticker.Config{
				SlotDuration: networkConfig.SlotDurationSec(),
				GenesisTime:  networkConfig.GetGenesisTime(),
			})
		}

		cfg.ConsensusClient.Context = cmd.Context()
		cfg.ConsensusClient.Graffiti = []byte("SSV.Network")
		cfg.ConsensusClient.GasLimit = spectypes.DefaultGasLimit
		cfg.ConsensusClient.Network = networkConfig.Beacon.GetNetwork()

		consensusClient := setupConsensusClient(logger, operatorDataStore, slotTickerProvider)

		executionClient, err := executionclient.New(
			cmd.Context(),
			cfg.ExecutionClient.Addr,
			ethcommon.HexToAddress(networkConfig.RegistryContractAddr),
			executionclient.WithLogger(logger),
			executionclient.WithMetrics(metricsReporter),
			executionclient.WithFollowDistance(executionclient.DefaultFollowDistance),
			executionclient.WithConnectionTimeout(cfg.ExecutionClient.ConnectionTimeout),
			executionclient.WithReconnectionInitialInterval(executionclient.DefaultReconnectionInitialInterval),
			executionclient.WithReconnectionMaxInterval(executionclient.DefaultReconnectionMaxInterval),
		)
		if err != nil {
			logger.Fatal("could not connect to execution client", zap.Error(err))
		}

		cfg.P2pNetworkConfig.Permissioned = permissioned
		cfg.P2pNetworkConfig.NodeStorage = nodeStorage
		cfg.P2pNetworkConfig.OperatorPubKeyHash = format.OperatorID(operatorData.PublicKey)
		cfg.P2pNetworkConfig.OperatorDataStore = operatorDataStore
		cfg.P2pNetworkConfig.FullNode = cfg.SSVOptions.ValidatorOptions.FullNode
		cfg.P2pNetworkConfig.Network = networkConfig

		validatorsMap := validatorsmap.New(cmd.Context())

		dutyStore := dutystore.New()
		cfg.SSVOptions.DutyStore = dutyStore

		messageValidator := validation.NewMessageValidator(
			networkConfig,
			validation.WithNodeStorage(nodeStorage),
			validation.WithLogger(logger),
			validation.WithMetrics(metricsReporter),
			validation.WithDutyStore(dutyStore),
			validation.WithOwnOperatorID(operatorDataStore),
		)

		cfg.P2pNetworkConfig.Metrics = metricsReporter
		cfg.P2pNetworkConfig.MessageValidator = messageValidator
		cfg.SSVOptions.ValidatorOptions.MessageValidator = messageValidator

		p2pNetwork := setupP2P(logger, db, metricsReporter)

		cfg.SSVOptions.Context = cmd.Context()
		cfg.SSVOptions.DB = db
		cfg.SSVOptions.BeaconNode = consensusClient
		cfg.SSVOptions.ExecutionClient = executionClient
		cfg.SSVOptions.Network = networkConfig
		cfg.SSVOptions.P2PNetwork = p2pNetwork
		cfg.SSVOptions.ValidatorOptions.BeaconNetwork = networkConfig.Beacon.GetNetwork()
		cfg.SSVOptions.ValidatorOptions.Context = cmd.Context()
		cfg.SSVOptions.ValidatorOptions.DB = db
		cfg.SSVOptions.ValidatorOptions.Network = p2pNetwork
		cfg.SSVOptions.ValidatorOptions.Beacon = consensusClient
		cfg.SSVOptions.ValidatorOptions.KeyManager = keyManager
		cfg.SSVOptions.ValidatorOptions.ValidatorsMap = validatorsMap

		cfg.SSVOptions.ValidatorOptions.OperatorDataStore = operatorDataStore
		cfg.SSVOptions.ValidatorOptions.RegistryStorage = nodeStorage
		cfg.SSVOptions.ValidatorOptions.RecipientsStorage = nodeStorage
		cfg.SSVOptions.ValidatorOptions.GasLimit = cfg.ConsensusClient.GasLimit

		if cfg.WsAPIPort != 0 {
			ws := exporterapi.NewWsServer(cmd.Context(), nil, http.NewServeMux(), cfg.WithPing)
			cfg.SSVOptions.WS = ws
			cfg.SSVOptions.WsAPIPort = cfg.WsAPIPort
			cfg.SSVOptions.ValidatorOptions.NewDecidedHandler = decided.NewStreamPublisher(logger, ws)
		}

		cfg.SSVOptions.ValidatorOptions.DutyRoles = []spectypes.BeaconRole{spectypes.BNRoleAttester}

		storageRoles := []spectypes.BeaconRole{
			spectypes.BNRoleAttester,
			spectypes.BNRoleProposer,
			spectypes.BNRoleAggregator,
			spectypes.BNRoleSyncCommittee,
			spectypes.BNRoleSyncCommitteeContribution,
			spectypes.BNRoleValidatorRegistration,
			spectypes.BNRoleVoluntaryExit,
		}
		storageMap := ibftstorage.NewStores()

		for _, storageRole := range storageRoles {
			storageMap.Add(storageRole, ibftstorage.New(cfg.SSVOptions.ValidatorOptions.DB, storageRole.String()))
		}

		cfg.SSVOptions.ValidatorOptions.StorageMap = storageMap
		cfg.SSVOptions.ValidatorOptions.Metrics = metricsReporter
		cfg.SSVOptions.Metrics = metricsReporter

		validatorCtrl := validator.NewController(logger, cfg.SSVOptions.ValidatorOptions)
		cfg.SSVOptions.ValidatorController = validatorCtrl

		operatorNode = operator.New(logger, cfg.SSVOptions, slotTickerProvider)

		if cfg.MetricsAPIPort > 0 {
			go startMetricsHandler(cmd.Context(), logger, db, metricsReporter, cfg.MetricsAPIPort, cfg.EnableProfile)
		}

		nodeProber := nodeprobe.NewProber(
			logger,
			func() {
				logger.Fatal("ethereum node(s) are either out of sync or down. Ensure the nodes are healthy to resume.")
			},
			map[string]nodeprobe.Node{
				"execution client": executionClient,

				"consensus client": consensusClient.(nodeprobe.Node),
			},
		)

		nodeProber.Start(cmd.Context())
		nodeProber.Wait()
		logger.Info("ethereum node(s) are healthy")

		metricsReporter.SSVNodeHealthy()

		eventSyncer := setupEventHandling(
			cmd.Context(),
			logger,
			executionClient,
			validatorCtrl,
			storageMap,
			metricsReporter,
			networkConfig,
			nodeStorage,
			operatorDataStore,
			operatorPrivKey,
		)
		nodeProber.AddNode("event syncer", eventSyncer)

		cfg.P2pNetworkConfig.GetValidatorStats = func() (uint64, uint64, uint64, error) {
			return validatorCtrl.GetValidatorStats()
		}
		if err := p2pNetwork.Setup(logger); err != nil {
			logger.Fatal("failed to setup network", zap.Error(err))
		}
		if err := p2pNetwork.Start(logger); err != nil {
			logger.Fatal("failed to start network", zap.Error(err))
		}

		if cfg.SSVAPIPort > 0 {
			apiServer := apiserver.New(
				logger,
				fmt.Sprintf(":%d", cfg.SSVAPIPort),
				&handlers.Node{

					ListenAddresses: []string{fmt.Sprintf("tcp://%s:%d", cfg.P2pNetworkConfig.HostAddress, cfg.P2pNetworkConfig.TCPPort), fmt.Sprintf("udp://%s:%d", cfg.P2pNetworkConfig.HostAddress, cfg.P2pNetworkConfig.UDPPort)},
					PeersIndex:      p2pNetwork.(p2pv1.PeersIndexProvider).PeersIndex(),
					Network:         p2pNetwork.(p2pv1.HostProvider).Host().Network(),
					TopicIndex:      p2pNetwork.(handlers.TopicIndex),
					NodeProber:      nodeProber,
				},
				&handlers.Validators{
					Shares: nodeStorage.Shares(),
				},
			)
			go func() {
				err := apiServer.Run()
				if err != nil {
					logger.Fatal("failed to start API server", zap.Error(err))
				}
			}()
		}

		if err := operatorNode.Start(logger); err != nil {
			logger.Fatal("failed to start SSV node", zap.Error(err))
		}
	},
}

StartNodeCmd is the command to start SSV node

Functions

This section is empty.

Types

type ArgumentDoc added in v1.1.0

type ArgumentDoc struct {
	FieldPath   []string
	YAMLPath    []string
	EnvName     string
	Default     string
	Description string
}

type KeyStore added in v1.1.0

type KeyStore struct {
	PrivateKeyFile string `yaml:"PrivateKeyFile" env:"PRIVATE_KEY_FILE" env-description:"Operator private key file"`
	PasswordFile   string `yaml:"PasswordFile" env:"PASSWORD_FILE" env-description:"Password for operator private key file decryption"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL