mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-22 03:30:37 +00:00
[caplin/sentinel] config reorganizations (#8844)
This commit is contained in:
parent
3e8a028cbb
commit
a2673c62c5
124
cmd/caplin/caplincli/config.go
Normal file
124
cmd/caplin/caplincli/config.go
Normal file
@ -0,0 +1,124 @@
|
||||
package caplincli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ledgerwatch/erigon-lib/common/datadir"
|
||||
"github.com/ledgerwatch/erigon/cl/clparams"
|
||||
"github.com/ledgerwatch/erigon/cl/phase1/core/state"
|
||||
"github.com/ledgerwatch/erigon/cmd/caplin/caplinflags"
|
||||
"github.com/ledgerwatch/erigon/cmd/sentinel/sentinelcli"
|
||||
"github.com/ledgerwatch/erigon/cmd/sentinel/sentinelflags"
|
||||
"github.com/ledgerwatch/erigon/cmd/utils"
|
||||
"github.com/ledgerwatch/erigon/common"
|
||||
"github.com/ledgerwatch/log/v3"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
type CaplinCliCfg struct {
|
||||
*sentinelcli.SentinelCliCfg
|
||||
|
||||
CheckpointUri string `json:"checkpoint_uri"`
|
||||
Chaindata string `json:"chaindata"`
|
||||
ErigonPrivateApi string `json:"erigon_private_api"`
|
||||
TransitionChain bool `json:"transition_chain"`
|
||||
InitialSync bool `json:"initial_sync"`
|
||||
NoBeaconApi bool `json:"no_beacon_api"`
|
||||
BeaconApiReadTimeout time.Duration `json:"beacon_api_read_timeout"`
|
||||
BeaconApiWriteTimeout time.Duration `json:"beacon_api_write_timeout"`
|
||||
BeaconAddr string `json:"beacon_addr"`
|
||||
BeaconProtocol string `json:"beacon_protocol"`
|
||||
RecordMode bool `json:"record_mode"`
|
||||
RecordDir string `json:"record_dir"`
|
||||
DataDir string `json:"data_dir"`
|
||||
RunEngineAPI bool `json:"run_engine_api"`
|
||||
EngineAPIAddr string `json:"engine_api_addr"`
|
||||
EngineAPIPort int `json:"engine_api_port"`
|
||||
JwtSecret []byte
|
||||
|
||||
InitalState *state.CachingBeaconState
|
||||
Dirs datadir.Dirs
|
||||
}
|
||||
|
||||
func SetupCaplinCli(ctx *cli.Context) (cfg *CaplinCliCfg, err error) {
|
||||
cfg = &CaplinCliCfg{}
|
||||
cfg.SentinelCliCfg, err = sentinelcli.SetupSentinelCli(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg.ErigonPrivateApi = ctx.String(caplinflags.ErigonPrivateApiFlag.Name)
|
||||
|
||||
if ctx.String(sentinelflags.BeaconConfigFlag.Name) != "" {
|
||||
var stateByte []byte
|
||||
// Now parse genesis time and genesis fork
|
||||
if *cfg.GenesisCfg, stateByte, err = clparams.ParseGenesisSSZToGenesisConfig(
|
||||
ctx.String(sentinelflags.GenesisSSZFlag.Name),
|
||||
cfg.BeaconCfg.GetCurrentStateVersion(0)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg.InitalState = state.New(cfg.BeaconCfg)
|
||||
if cfg.InitalState.DecodeSSZ(stateByte, int(cfg.BeaconCfg.GetCurrentStateVersion(0))); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
cfg.NoBeaconApi = ctx.Bool(caplinflags.NoBeaconApi.Name)
|
||||
cfg.BeaconApiReadTimeout = time.Duration(ctx.Uint64(caplinflags.BeaconApiReadTimeout.Name)) * time.Second
|
||||
cfg.BeaconApiWriteTimeout = time.Duration(ctx.Uint(caplinflags.BeaconApiWriteTimeout.Name)) * time.Second
|
||||
cfg.BeaconAddr = fmt.Sprintf("%s:%d", ctx.String(caplinflags.BeaconApiAddr.Name), ctx.Int(caplinflags.BeaconApiPort.Name))
|
||||
cfg.BeaconProtocol = "tcp"
|
||||
cfg.RecordMode = ctx.Bool(caplinflags.RecordModeFlag.Name)
|
||||
cfg.RecordDir = ctx.String(caplinflags.RecordModeDir.Name)
|
||||
cfg.DataDir = ctx.String(utils.DataDirFlag.Name)
|
||||
cfg.Dirs = datadir.New(cfg.DataDir)
|
||||
|
||||
cfg.RunEngineAPI = ctx.Bool(caplinflags.RunEngineAPI.Name)
|
||||
cfg.EngineAPIAddr = ctx.String(caplinflags.EngineApiHostFlag.Name)
|
||||
cfg.EngineAPIPort = ctx.Int(caplinflags.EngineApiPortFlag.Name)
|
||||
if cfg.RunEngineAPI {
|
||||
secret, err := ObtainJwtSecret(ctx)
|
||||
if err != nil {
|
||||
log.Error("Failed to obtain jwt secret", "err", err)
|
||||
cfg.RunEngineAPI = false
|
||||
} else {
|
||||
cfg.JwtSecret = secret
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.String(caplinflags.CheckpointSyncUrlFlag.Name) != "" {
|
||||
cfg.CheckpointUri = ctx.String(caplinflags.CheckpointSyncUrlFlag.Name)
|
||||
} else {
|
||||
cfg.CheckpointUri = clparams.GetCheckpointSyncEndpoint(cfg.NetworkType)
|
||||
}
|
||||
|
||||
cfg.Chaindata = ctx.String(caplinflags.ChaindataFlag.Name)
|
||||
|
||||
cfg.TransitionChain = ctx.Bool(caplinflags.TransitionChainFlag.Name)
|
||||
cfg.InitialSync = ctx.Bool(caplinflags.InitSyncFlag.Name)
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func ObtainJwtSecret(ctx *cli.Context) ([]byte, error) {
|
||||
path := ctx.String(caplinflags.JwtSecret.Name)
|
||||
if len(strings.TrimSpace(path)) == 0 {
|
||||
return nil, errors.New("Missing jwt secret path")
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jwtSecret := common.FromHex(strings.TrimSpace(string(data)))
|
||||
if len(jwtSecret) == 32 {
|
||||
return jwtSecret, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Invalid JWT secret at %s, invalid size", path)
|
||||
}
|
@ -1,34 +1,35 @@
|
||||
package flags
|
||||
package caplinflags
|
||||
|
||||
import (
|
||||
"github.com/ledgerwatch/erigon/cmd/utils"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var CliFlags = []cli.Flag{
|
||||
&NoBeaconApi,
|
||||
&BeaconApiReadTimeout,
|
||||
&BeaconApiWriteTimeout,
|
||||
&BeaconApiPort,
|
||||
&BeaconApiAddr,
|
||||
&ChaindataFlag,
|
||||
&BeaconDBModeFlag,
|
||||
&CheckpointSyncUrlFlag,
|
||||
&TransitionChainFlag,
|
||||
&InitSyncFlag,
|
||||
&RecordModeDir,
|
||||
&RecordModeFlag,
|
||||
&RunEngineAPI,
|
||||
&EngineApiHostFlag,
|
||||
&EngineApiPortFlag,
|
||||
&JwtSecret,
|
||||
&utils.DataDirFlag,
|
||||
}
|
||||
|
||||
var (
|
||||
SentinelDiscoveryPort = cli.IntFlag{
|
||||
Name: "discovery.port",
|
||||
Usage: "sets the lightclient port",
|
||||
Value: 4000,
|
||||
}
|
||||
SentinelDiscoveryAddr = cli.StringFlag{
|
||||
Name: "discovery.addr",
|
||||
Usage: "sets the lightclient discovery addr",
|
||||
Value: "127.0.0.1",
|
||||
}
|
||||
SentinelTcpPort = cli.UintFlag{
|
||||
Name: "sentinel.tcp.port",
|
||||
Usage: "sets lightclient tcp port",
|
||||
Value: 4001,
|
||||
}
|
||||
SentinelServerPort = cli.IntFlag{
|
||||
Name: "sentinel.port",
|
||||
Usage: "sets the lightclient server port",
|
||||
Value: 7777,
|
||||
}
|
||||
SentinelServerAddr = cli.StringFlag{
|
||||
Name: "sentinel.addr",
|
||||
Usage: "sets the lightclient server host addr",
|
||||
Value: "localhost",
|
||||
ChaindataFlag = cli.StringFlag{
|
||||
Name: "chaindata",
|
||||
Usage: "chaindata of database",
|
||||
Value: "",
|
||||
}
|
||||
NoBeaconApi = cli.BoolFlag{
|
||||
Name: "no-beacon-api",
|
||||
@ -55,41 +56,7 @@ var (
|
||||
Usage: "sets the port to listen for beacon api requests",
|
||||
Value: 5555,
|
||||
}
|
||||
BootnodesFlag = cli.StringFlag{
|
||||
Name: "sentinel.bootnodes",
|
||||
Usage: "Comma separated enode URLs for P2P discovery bootstrap",
|
||||
Value: "",
|
||||
}
|
||||
BeaconConfigFlag = cli.StringFlag{
|
||||
Name: "beacon-config",
|
||||
Usage: "Path to beacon config",
|
||||
Value: "",
|
||||
}
|
||||
GenesisSSZFlag = cli.StringFlag{
|
||||
Name: "genesis-ssz",
|
||||
Usage: "Path to genesis ssz",
|
||||
Value: "",
|
||||
}
|
||||
Chain = cli.StringFlag{
|
||||
Name: "chain",
|
||||
Usage: "sets the chain specs for the lightclient",
|
||||
Value: "mainnet",
|
||||
}
|
||||
NoDiscovery = cli.BoolFlag{
|
||||
Name: "no-discovery",
|
||||
Usage: "turn off or on the lightclient finding peers",
|
||||
Value: false,
|
||||
}
|
||||
LocalDiscovery = cli.BoolFlag{
|
||||
Name: "local-discovery",
|
||||
Usage: "enable to also attempt to find peers over private ips. turning this on may cause issues with hosts such as hetzner",
|
||||
Value: false,
|
||||
}
|
||||
ChaindataFlag = cli.StringFlag{
|
||||
Name: "chaindata",
|
||||
Usage: "chaindata of database",
|
||||
Value: "",
|
||||
}
|
||||
|
||||
BeaconDBModeFlag = cli.StringFlag{
|
||||
Name: "beacon-db-mode",
|
||||
Usage: "level of storing on beacon chain, minimal(only 500k blocks stored), full (all blocks stored), light (no blocks stored)",
|
||||
@ -100,6 +67,25 @@ var (
|
||||
Usage: "checkpoint sync endpoint",
|
||||
Value: "",
|
||||
}
|
||||
TransitionChainFlag = cli.BoolFlag{
|
||||
Name: "transition-chain",
|
||||
Usage: "enable chain transition",
|
||||
}
|
||||
InitSyncFlag = cli.BoolFlag{
|
||||
Value: false,
|
||||
Name: "initial-sync",
|
||||
Usage: "use initial-sync",
|
||||
}
|
||||
RecordModeFlag = cli.BoolFlag{
|
||||
Value: false,
|
||||
Name: "record-mode",
|
||||
Usage: "enable/disable record mode",
|
||||
}
|
||||
RecordModeDir = cli.StringFlag{
|
||||
Value: "caplin-recordings",
|
||||
Name: "record-dir",
|
||||
Usage: "directory for states and block recordings",
|
||||
}
|
||||
ErigonPrivateApiFlag = cli.StringFlag{
|
||||
Name: "private.api.addr",
|
||||
Usage: "connect to existing erigon instance",
|
||||
@ -125,28 +111,4 @@ var (
|
||||
Usage: "Path to the token that ensures safe connection between CL and EL",
|
||||
Value: "",
|
||||
}
|
||||
SentinelStaticPeersFlag = cli.StringFlag{
|
||||
Name: "sentinel.staticpeers",
|
||||
Usage: "connect to comma-separated Consensus static peers",
|
||||
Value: "",
|
||||
}
|
||||
TransitionChainFlag = cli.BoolFlag{
|
||||
Name: "transition-chain",
|
||||
Usage: "enable chain transition",
|
||||
}
|
||||
InitSyncFlag = cli.BoolFlag{
|
||||
Value: false,
|
||||
Name: "initial-sync",
|
||||
Usage: "use initial-sync",
|
||||
}
|
||||
RecordModeFlag = cli.BoolFlag{
|
||||
Value: false,
|
||||
Name: "record-mode",
|
||||
Usage: "enable/disable record mode",
|
||||
}
|
||||
RecordModeDir = cli.StringFlag{
|
||||
Value: "caplin-recordings",
|
||||
Name: "record-dir",
|
||||
Usage: "directory for states and block recordings",
|
||||
}
|
||||
)
|
@ -1,15 +1,13 @@
|
||||
/*
|
||||
Copyright 2022 Erigon-Lightclient contributors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
// Copyright 2022 Erigon-Lightclient contributors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
@ -32,14 +30,16 @@ import (
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/ledgerwatch/erigon/cmd/caplin/caplin1"
|
||||
lcCli "github.com/ledgerwatch/erigon/cmd/sentinel/cli"
|
||||
"github.com/ledgerwatch/erigon/cmd/sentinel/cli/flags"
|
||||
"github.com/ledgerwatch/erigon/cmd/caplin/caplincli"
|
||||
"github.com/ledgerwatch/erigon/cmd/caplin/caplinflags"
|
||||
"github.com/ledgerwatch/erigon/cmd/sentinel/sentinelflags"
|
||||
"github.com/ledgerwatch/erigon/cmd/utils"
|
||||
"github.com/ledgerwatch/erigon/turbo/app"
|
||||
"github.com/ledgerwatch/erigon/turbo/debug"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := app.MakeApp("caplin", runCaplinNode, flags.CLDefaultFlags)
|
||||
app := app.MakeApp("caplin", runCaplinNode, append(caplinflags.CliFlags, sentinelflags.CliFlags...))
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
_, printErr := fmt.Fprintln(os.Stderr, err)
|
||||
if printErr != nil {
|
||||
@ -50,10 +50,7 @@ func main() {
|
||||
}
|
||||
|
||||
func runCaplinNode(cliCtx *cli.Context) error {
|
||||
ctx, cn := context.WithCancel(context.Background())
|
||||
defer cn()
|
||||
|
||||
cfg, err := lcCli.SetupConsensusClientCfg(cliCtx)
|
||||
cfg, err := caplincli.SetupCaplinCli(cliCtx)
|
||||
if err != nil {
|
||||
log.Error("[Phase1] Could not initialize caplin", "err", err)
|
||||
}
|
||||
@ -61,9 +58,11 @@ func runCaplinNode(cliCtx *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(cfg.LogLvl), log.StderrHandler))
|
||||
log.Info("[Phase1]", "chain", cliCtx.String(flags.Chain.Name))
|
||||
log.Info("[Phase1]", "chain", cliCtx.String(utils.ChainFlag.Name))
|
||||
log.Info("[Phase1] Running Caplin")
|
||||
// Either start from genesis or a checkpoint
|
||||
ctx, cn := context.WithCancel(context.Background())
|
||||
defer cn()
|
||||
var state *state.CachingBeaconState
|
||||
if cfg.InitialSync {
|
||||
state = cfg.InitalState
|
||||
|
@ -1,23 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/ledgerwatch/log/v3"
|
||||
"github.com/pelletier/go-toml"
|
||||
"github.com/urfave/cli/v2"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/ledgerwatch/erigon-lib/common/datadir"
|
||||
"github.com/ledgerwatch/erigon-lib/common/dbg"
|
||||
"github.com/ledgerwatch/erigon-lib/metrics"
|
||||
"github.com/ledgerwatch/erigon/cmd/utils"
|
||||
"github.com/ledgerwatch/erigon/diagnostics"
|
||||
"github.com/ledgerwatch/erigon/params"
|
||||
erigonapp "github.com/ledgerwatch/erigon/turbo/app"
|
||||
@ -48,14 +41,6 @@ func main() {
|
||||
}
|
||||
|
||||
func runErigon(cliCtx *cli.Context) error {
|
||||
configFilePath := cliCtx.String(utils.ConfigFlag.Name)
|
||||
if configFilePath != "" {
|
||||
if err := setFlagsFromConfigFile(cliCtx, configFilePath); err != nil {
|
||||
log.Error("failed setting config flags from yaml/toml file", "err", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var logger log.Logger
|
||||
var err error
|
||||
var metricsMux *http.ServeMux
|
||||
@ -93,55 +78,3 @@ func runErigon(cliCtx *cli.Context) error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func setFlagsFromConfigFile(ctx *cli.Context, filePath string) error {
|
||||
fileExtension := filepath.Ext(filePath)
|
||||
|
||||
fileConfig := make(map[string]interface{})
|
||||
|
||||
if fileExtension == ".yaml" {
|
||||
yamlFile, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = yaml.Unmarshal(yamlFile, fileConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if fileExtension == ".toml" {
|
||||
tomlFile, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = toml.Unmarshal(tomlFile, &fileConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return errors.New("config files only accepted are .yaml and .toml")
|
||||
}
|
||||
// sets global flags to value in yaml/toml file
|
||||
for key, value := range fileConfig {
|
||||
if !ctx.IsSet(key) {
|
||||
if reflect.ValueOf(value).Kind() == reflect.Slice {
|
||||
sliceInterface := value.([]interface{})
|
||||
s := make([]string, len(sliceInterface))
|
||||
for i, v := range sliceInterface {
|
||||
s[i] = fmt.Sprintf("%v", v)
|
||||
}
|
||||
err := ctx.Set(key, strings.Join(s, ","))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed setting %s flag with values=%s error=%s", key, s, err)
|
||||
}
|
||||
} else {
|
||||
err := ctx.Set(key, fmt.Sprintf("%v", value))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed setting %s flag with value=%v error=%s", key, value, err)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -1,161 +0,0 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ledgerwatch/erigon/cl/clparams"
|
||||
"github.com/ledgerwatch/erigon/cl/phase1/core/state"
|
||||
|
||||
common2 "github.com/ledgerwatch/erigon-lib/common"
|
||||
|
||||
"github.com/ledgerwatch/erigon-lib/common/datadir"
|
||||
"github.com/ledgerwatch/erigon/common"
|
||||
|
||||
"github.com/ledgerwatch/erigon/cmd/utils"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/ledgerwatch/erigon/cmd/sentinel/cli/flags"
|
||||
"github.com/ledgerwatch/erigon/turbo/logging"
|
||||
|
||||
"github.com/ledgerwatch/log/v3"
|
||||
)
|
||||
|
||||
type ConsensusClientCliCfg struct {
|
||||
GenesisCfg *clparams.GenesisConfig
|
||||
BeaconCfg *clparams.BeaconChainConfig
|
||||
NetworkCfg *clparams.NetworkConfig
|
||||
Port uint `json:"port"`
|
||||
Addr string `json:"address"`
|
||||
ServerAddr string `json:"server_addr"`
|
||||
ServerProtocol string `json:"server_protocol"`
|
||||
ServerTcpPort uint `json:"server_tcp_port"`
|
||||
LogLvl uint `json:"log_level"`
|
||||
NoDiscovery bool `json:"no_discovery"`
|
||||
LocalDiscovery bool `json:"local_discovery"`
|
||||
CheckpointUri string `json:"checkpoint_uri"`
|
||||
Chaindata string `json:"chaindata"`
|
||||
ErigonPrivateApi string `json:"erigon_private_api"`
|
||||
TransitionChain bool `json:"transition_chain"`
|
||||
NetworkType clparams.NetworkType
|
||||
InitialSync bool `json:"initial_sync"`
|
||||
NoBeaconApi bool `json:"no_beacon_api"`
|
||||
BeaconApiReadTimeout time.Duration `json:"beacon_api_read_timeout"`
|
||||
BeaconApiWriteTimeout time.Duration `json:"beacon_api_write_timeout"`
|
||||
BeaconAddr string `json:"beacon_addr"`
|
||||
BeaconProtocol string `json:"beacon_protocol"`
|
||||
RecordMode bool `json:"record_mode"`
|
||||
RecordDir string `json:"record_dir"`
|
||||
DataDir string `json:"data_dir"`
|
||||
RunEngineAPI bool `json:"run_engine_api"`
|
||||
EngineAPIAddr string `json:"engine_api_addr"`
|
||||
EngineAPIPort int `json:"engine_api_port"`
|
||||
JwtSecret []byte
|
||||
|
||||
InitalState *state.CachingBeaconState
|
||||
Dirs datadir.Dirs
|
||||
}
|
||||
|
||||
func SetupConsensusClientCfg(ctx *cli.Context) (*ConsensusClientCliCfg, error) {
|
||||
cfg := &ConsensusClientCliCfg{}
|
||||
chainName := ctx.String(flags.Chain.Name)
|
||||
var err error
|
||||
cfg.GenesisCfg, cfg.NetworkCfg, cfg.BeaconCfg, cfg.NetworkType, err = clparams.GetConfigsByNetworkName(chainName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg.ErigonPrivateApi = ctx.String(flags.ErigonPrivateApiFlag.Name)
|
||||
if ctx.String(flags.BeaconConfigFlag.Name) != "" {
|
||||
cfg.BeaconCfg = new(clparams.BeaconChainConfig)
|
||||
if *cfg.BeaconCfg, err = clparams.CustomConfig(ctx.String(flags.BeaconConfigFlag.Name)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ctx.String(flags.GenesisSSZFlag.Name) == "" {
|
||||
return nil, fmt.Errorf("no genesis file provided")
|
||||
}
|
||||
cfg.GenesisCfg = new(clparams.GenesisConfig)
|
||||
var stateByte []byte
|
||||
// Now parse genesis time and genesis fork
|
||||
if *cfg.GenesisCfg, stateByte, err = clparams.ParseGenesisSSZToGenesisConfig(
|
||||
ctx.String(flags.GenesisSSZFlag.Name),
|
||||
cfg.BeaconCfg.GetCurrentStateVersion(0)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg.InitalState = state.New(cfg.BeaconCfg)
|
||||
if cfg.InitalState.DecodeSSZ(stateByte, int(cfg.BeaconCfg.GetCurrentStateVersion(0))); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
cfg.ServerAddr = fmt.Sprintf("%s:%d", ctx.String(flags.SentinelServerAddr.Name), ctx.Int(flags.SentinelServerPort.Name))
|
||||
cfg.ServerProtocol = "tcp"
|
||||
|
||||
cfg.NoBeaconApi = ctx.Bool(flags.NoBeaconApi.Name)
|
||||
cfg.BeaconApiReadTimeout = time.Duration(ctx.Uint64(flags.BeaconApiReadTimeout.Name)) * time.Second
|
||||
cfg.BeaconApiWriteTimeout = time.Duration(ctx.Uint(flags.BeaconApiWriteTimeout.Name)) * time.Second
|
||||
cfg.BeaconAddr = fmt.Sprintf("%s:%d", ctx.String(flags.BeaconApiAddr.Name), ctx.Int(flags.BeaconApiPort.Name))
|
||||
cfg.BeaconProtocol = "tcp"
|
||||
cfg.RecordMode = ctx.Bool(flags.RecordModeFlag.Name)
|
||||
cfg.RecordDir = ctx.String(flags.RecordModeDir.Name)
|
||||
cfg.DataDir = ctx.String(utils.DataDirFlag.Name)
|
||||
cfg.Dirs = datadir.New(cfg.DataDir)
|
||||
|
||||
cfg.RunEngineAPI = ctx.Bool(flags.RunEngineAPI.Name)
|
||||
cfg.EngineAPIAddr = ctx.String(flags.EngineApiHostFlag.Name)
|
||||
cfg.EngineAPIPort = ctx.Int(flags.EngineApiPortFlag.Name)
|
||||
if cfg.RunEngineAPI {
|
||||
secret, err := ObtainJwtSecret(ctx)
|
||||
if err != nil {
|
||||
log.Error("Failed to obtain jwt secret", "err", err)
|
||||
cfg.RunEngineAPI = false
|
||||
} else {
|
||||
cfg.JwtSecret = secret
|
||||
}
|
||||
}
|
||||
|
||||
cfg.Port = uint(ctx.Int(flags.SentinelDiscoveryPort.Name))
|
||||
cfg.Addr = ctx.String(flags.SentinelDiscoveryAddr.Name)
|
||||
|
||||
cfg.LogLvl = ctx.Uint(logging.LogVerbosityFlag.Name)
|
||||
if cfg.LogLvl == uint(log.LvlInfo) || cfg.LogLvl == 0 {
|
||||
cfg.LogLvl = uint(log.LvlDebug)
|
||||
}
|
||||
cfg.NoDiscovery = ctx.Bool(flags.NoDiscovery.Name)
|
||||
cfg.LocalDiscovery = ctx.Bool(flags.LocalDiscovery.Name)
|
||||
if ctx.String(flags.CheckpointSyncUrlFlag.Name) != "" {
|
||||
cfg.CheckpointUri = ctx.String(flags.CheckpointSyncUrlFlag.Name)
|
||||
} else {
|
||||
cfg.CheckpointUri = clparams.GetCheckpointSyncEndpoint(cfg.NetworkType)
|
||||
}
|
||||
cfg.Chaindata = ctx.String(flags.ChaindataFlag.Name)
|
||||
// Process bootnodes
|
||||
if ctx.String(flags.BootnodesFlag.Name) != "" {
|
||||
cfg.NetworkCfg.BootNodes = common2.CliString2Array(ctx.String(flags.BootnodesFlag.Name))
|
||||
}
|
||||
if ctx.String(flags.SentinelStaticPeersFlag.Name) != "" {
|
||||
cfg.NetworkCfg.StaticPeers = common2.CliString2Array(ctx.String(flags.SentinelStaticPeersFlag.Name))
|
||||
}
|
||||
cfg.TransitionChain = ctx.Bool(flags.TransitionChainFlag.Name)
|
||||
cfg.InitialSync = ctx.Bool(flags.InitSyncFlag.Name)
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func ObtainJwtSecret(ctx *cli.Context) ([]byte, error) {
|
||||
path := ctx.String(flags.JwtSecret.Name)
|
||||
if len(strings.TrimSpace(path)) == 0 {
|
||||
return nil, errors.New("Missing jwt secret path")
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jwtSecret := common.FromHex(strings.TrimSpace(string(data)))
|
||||
if len(jwtSecret) == 32 {
|
||||
return jwtSecret, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Invalid JWT secret at %s, invalid size", path)
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package flags
|
||||
|
||||
import (
|
||||
"github.com/ledgerwatch/erigon/cmd/utils"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var CLDefaultFlags = []cli.Flag{
|
||||
&SentinelDiscoveryPort,
|
||||
&SentinelDiscoveryAddr,
|
||||
&SentinelServerPort,
|
||||
&SentinelServerAddr,
|
||||
&NoBeaconApi,
|
||||
&BeaconApiReadTimeout,
|
||||
&BeaconApiWriteTimeout,
|
||||
&BeaconApiPort,
|
||||
&BeaconApiAddr,
|
||||
&Chain,
|
||||
&SentinelTcpPort,
|
||||
&NoDiscovery,
|
||||
&ChaindataFlag,
|
||||
&BeaconDBModeFlag,
|
||||
&BootnodesFlag,
|
||||
&BeaconConfigFlag,
|
||||
&GenesisSSZFlag,
|
||||
&CheckpointSyncUrlFlag,
|
||||
&SentinelStaticPeersFlag,
|
||||
&TransitionChainFlag,
|
||||
&InitSyncFlag,
|
||||
&RecordModeDir,
|
||||
&RecordModeFlag,
|
||||
&RunEngineAPI,
|
||||
&EngineApiHostFlag,
|
||||
&EngineApiPortFlag,
|
||||
&JwtSecret,
|
||||
&utils.DataDirFlag,
|
||||
}
|
@ -1,35 +1,34 @@
|
||||
/*
|
||||
Copyright 2022 Erigon-Lightclient contributors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
// Copyright 2022 Erigon-Lightclient contributors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/ledgerwatch/erigon/cl/sentinel"
|
||||
"github.com/ledgerwatch/erigon/cl/sentinel/service"
|
||||
"os"
|
||||
"github.com/ledgerwatch/erigon/cmd/sentinel/sentinelcli"
|
||||
"github.com/ledgerwatch/erigon/cmd/sentinel/sentinelflags"
|
||||
|
||||
"github.com/ledgerwatch/log/v3"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
lcCli "github.com/ledgerwatch/erigon/cmd/sentinel/cli"
|
||||
"github.com/ledgerwatch/erigon/cmd/sentinel/cli/flags"
|
||||
sentinelapp "github.com/ledgerwatch/erigon/turbo/app"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := sentinelapp.MakeApp("sentinel", runSentinelNode, flags.CLDefaultFlags)
|
||||
app := sentinelapp.MakeApp("sentinel", runSentinelNode, sentinelflags.CliFlags)
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
_, printErr := fmt.Fprintln(os.Stderr, err)
|
||||
if printErr != nil {
|
||||
@ -40,11 +39,13 @@ func main() {
|
||||
}
|
||||
|
||||
func runSentinelNode(cliCtx *cli.Context) error {
|
||||
cfg, _ := lcCli.SetupConsensusClientCfg(cliCtx)
|
||||
|
||||
cfg, err := sentinelcli.SetupSentinelCli(cliCtx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(cfg.LogLvl), log.StderrHandler))
|
||||
log.Info("[Sentinel] running sentinel with configuration", "cfg", cfg)
|
||||
_, err := service.StartSentinelService(&sentinel.SentinelConfig{
|
||||
_, err = service.StartSentinelService(&sentinel.SentinelConfig{
|
||||
IpAddr: cfg.Addr,
|
||||
Port: int(cfg.Port),
|
||||
TCPPort: cfg.ServerTcpPort,
|
||||
|
74
cmd/sentinel/sentinelcli/cliSettings.go
Normal file
74
cmd/sentinel/sentinelcli/cliSettings.go
Normal file
@ -0,0 +1,74 @@
|
||||
package sentinelcli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ledgerwatch/erigon/cl/clparams"
|
||||
"github.com/ledgerwatch/erigon/cmd/sentinel/sentinelflags"
|
||||
"github.com/ledgerwatch/erigon/cmd/utils"
|
||||
|
||||
"github.com/ledgerwatch/erigon-lib/common"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/ledgerwatch/erigon/turbo/logging"
|
||||
|
||||
"github.com/ledgerwatch/log/v3"
|
||||
)
|
||||
|
||||
type SentinelCliCfg struct {
|
||||
GenesisCfg *clparams.GenesisConfig
|
||||
BeaconCfg *clparams.BeaconChainConfig
|
||||
NetworkCfg *clparams.NetworkConfig
|
||||
NetworkType clparams.NetworkType
|
||||
Port uint `json:"port"`
|
||||
Addr string `json:"address"`
|
||||
ServerAddr string `json:"server_addr"`
|
||||
ServerProtocol string `json:"server_protocol"`
|
||||
ServerTcpPort uint `json:"server_tcp_port"`
|
||||
LogLvl uint `json:"log_level"`
|
||||
NoDiscovery bool `json:"no_discovery"`
|
||||
LocalDiscovery bool `json:"local_discovery"`
|
||||
}
|
||||
|
||||
func SetupSentinelCli(ctx *cli.Context) (*SentinelCliCfg, error) {
|
||||
cfg := &SentinelCliCfg{}
|
||||
chainName := ctx.String(utils.ChainFlag.Name)
|
||||
var err error
|
||||
cfg.GenesisCfg, cfg.NetworkCfg, cfg.BeaconCfg, cfg.NetworkType, err = clparams.GetConfigsByNetworkName(chainName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ctx.String(sentinelflags.BeaconConfigFlag.Name) != "" {
|
||||
cfg.BeaconCfg = new(clparams.BeaconChainConfig)
|
||||
if *cfg.BeaconCfg, err = clparams.CustomConfig(ctx.String(sentinelflags.BeaconConfigFlag.Name)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ctx.String(sentinelflags.GenesisSSZFlag.Name) == "" {
|
||||
return nil, fmt.Errorf("no genesis file provided")
|
||||
}
|
||||
cfg.GenesisCfg = new(clparams.GenesisConfig)
|
||||
|
||||
}
|
||||
cfg.ServerAddr = fmt.Sprintf("%s:%d", ctx.String(sentinelflags.SentinelServerAddr.Name), ctx.Int(sentinelflags.SentinelServerPort.Name))
|
||||
cfg.ServerProtocol = "tcp"
|
||||
|
||||
cfg.Port = uint(ctx.Int(sentinelflags.SentinelDiscoveryPort.Name))
|
||||
cfg.Addr = ctx.String(sentinelflags.SentinelDiscoveryAddr.Name)
|
||||
|
||||
cfg.LogLvl = ctx.Uint(logging.LogVerbosityFlag.Name)
|
||||
if cfg.LogLvl == uint(log.LvlInfo) || cfg.LogLvl == 0 {
|
||||
cfg.LogLvl = uint(log.LvlDebug)
|
||||
}
|
||||
cfg.NoDiscovery = ctx.Bool(sentinelflags.NoDiscovery.Name)
|
||||
cfg.LocalDiscovery = ctx.Bool(sentinelflags.LocalDiscovery.Name)
|
||||
|
||||
// Process bootnodes
|
||||
if ctx.String(sentinelflags.BootnodesFlag.Name) != "" {
|
||||
cfg.NetworkCfg.BootNodes = common.CliString2Array(ctx.String(sentinelflags.BootnodesFlag.Name))
|
||||
}
|
||||
if ctx.String(sentinelflags.SentinelStaticPeersFlag.Name) != "" {
|
||||
cfg.NetworkCfg.StaticPeers = common.CliString2Array(ctx.String(sentinelflags.SentinelStaticPeersFlag.Name))
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
1
cmd/sentinel/sentinelcli/flags/defaultFlags.go
Normal file
1
cmd/sentinel/sentinelcli/flags/defaultFlags.go
Normal file
@ -0,0 +1 @@
|
||||
package flags
|
1
cmd/sentinel/sentinelcli/flags/flags.go
Normal file
1
cmd/sentinel/sentinelcli/flags/flags.go
Normal file
@ -0,0 +1 @@
|
||||
package flags
|
79
cmd/sentinel/sentinelflags/flags.go
Normal file
79
cmd/sentinel/sentinelflags/flags.go
Normal file
@ -0,0 +1,79 @@
|
||||
package sentinelflags
|
||||
|
||||
import (
|
||||
"github.com/ledgerwatch/erigon/cmd/utils"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var CliFlags = []cli.Flag{
|
||||
&utils.ChainFlag,
|
||||
|
||||
&SentinelDiscoveryPort,
|
||||
&SentinelDiscoveryAddr,
|
||||
&SentinelServerPort,
|
||||
&SentinelServerAddr,
|
||||
&SentinelTcpPort,
|
||||
&NoDiscovery,
|
||||
&BootnodesFlag,
|
||||
&BeaconConfigFlag,
|
||||
&GenesisSSZFlag,
|
||||
&SentinelStaticPeersFlag,
|
||||
}
|
||||
|
||||
var (
|
||||
SentinelDiscoveryPort = cli.IntFlag{
|
||||
Name: "discovery.port",
|
||||
Usage: "sets the lightclient port",
|
||||
Value: 4000,
|
||||
}
|
||||
SentinelDiscoveryAddr = cli.StringFlag{
|
||||
Name: "discovery.addr",
|
||||
Usage: "sets the lightclient discovery addr",
|
||||
Value: "127.0.0.1",
|
||||
}
|
||||
SentinelTcpPort = cli.UintFlag{
|
||||
Name: "sentinel.tcp.port",
|
||||
Usage: "sets lightclient tcp port",
|
||||
Value: 4001,
|
||||
}
|
||||
SentinelServerPort = cli.IntFlag{
|
||||
Name: "sentinel.port",
|
||||
Usage: "sets the lightclient server port",
|
||||
Value: 7777,
|
||||
}
|
||||
SentinelServerAddr = cli.StringFlag{
|
||||
Name: "sentinel.addr",
|
||||
Usage: "sets the lightclient server host addr",
|
||||
Value: "localhost",
|
||||
}
|
||||
NoDiscovery = cli.BoolFlag{
|
||||
Name: "no-discovery",
|
||||
Usage: "turn off or on the lightclient finding peers",
|
||||
Value: false,
|
||||
}
|
||||
LocalDiscovery = cli.BoolFlag{
|
||||
Name: "local-discovery",
|
||||
Usage: "enable to also attempt to find peers over private ips. turning this on may cause issues with hosts such as hetzner",
|
||||
Value: false,
|
||||
}
|
||||
BootnodesFlag = cli.StringFlag{
|
||||
Name: "sentinel.bootnodes",
|
||||
Usage: "Comma separated enode URLs for P2P discovery bootstrap",
|
||||
Value: "",
|
||||
}
|
||||
BeaconConfigFlag = cli.StringFlag{
|
||||
Name: "beacon-config",
|
||||
Usage: "Path to beacon config",
|
||||
Value: "",
|
||||
}
|
||||
GenesisSSZFlag = cli.StringFlag{
|
||||
Name: "genesis-ssz",
|
||||
Usage: "Path to genesis ssz",
|
||||
Value: "",
|
||||
}
|
||||
SentinelStaticPeersFlag = cli.StringFlag{
|
||||
Name: "sentinel.staticpeers",
|
||||
Usage: "connect to comma-separated Consensus static peers",
|
||||
Value: "",
|
||||
}
|
||||
)
|
@ -97,7 +97,7 @@ var (
|
||||
}
|
||||
ChainFlag = cli.StringFlag{
|
||||
Name: "chain",
|
||||
Usage: "Name of the testnet to join",
|
||||
Usage: "name of the network to join",
|
||||
Value: networkname.MainnetChainName,
|
||||
}
|
||||
IdentityFlag = cli.StringFlag{
|
||||
|
@ -39,12 +39,35 @@ func MakeApp(name string, action cli.ActionFunc, cliFlags []cli.Flag) *cli.App {
|
||||
cli.ShowAppHelpAndExit(context, 1)
|
||||
}
|
||||
|
||||
// handle case: config flag
|
||||
configFilePath := context.String(utils.ConfigFlag.Name)
|
||||
if configFilePath != "" {
|
||||
if err := cli2.SetFlagsFromConfigFile(context, configFilePath); err != nil {
|
||||
log.Error("failed setting config flags from yaml/toml file", "err", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// run default action
|
||||
return action(context)
|
||||
}
|
||||
app.Flags = append(cliFlags, debug.Flags...) // debug flags are required
|
||||
app.Flags = append(app.Flags, utils.MetricFlags...)
|
||||
app.Flags = append(app.Flags, logging.Flags...)
|
||||
app.Flags = append(app.Flags, &utils.ConfigFlag)
|
||||
|
||||
// remove exact duplicate flags, keeping only the first one. this will allow easier composition later down the line
|
||||
allFlags := app.Flags
|
||||
newFlags := make([]cli.Flag, 0, len(allFlags))
|
||||
seen := map[string]struct{}{}
|
||||
for _, vv := range allFlags {
|
||||
v := vv
|
||||
if _, ok := seen[v.String()]; ok {
|
||||
continue
|
||||
}
|
||||
newFlags = append(newFlags, v)
|
||||
}
|
||||
app.Flags = newFlags
|
||||
|
||||
app.After = func(ctx *cli.Context) error {
|
||||
debug.Exit()
|
||||
|
66
turbo/cli/config_file.go
Normal file
66
turbo/cli/config_file.go
Normal file
@ -0,0 +1,66 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/pelletier/go-toml"
|
||||
"github.com/urfave/cli/v2"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func SetFlagsFromConfigFile(ctx *cli.Context, filePath string) error {
|
||||
fileExtension := filepath.Ext(filePath)
|
||||
|
||||
fileConfig := make(map[string]interface{})
|
||||
|
||||
if fileExtension == ".yml" || fileExtension == ".yaml" {
|
||||
yamlFile, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = yaml.Unmarshal(yamlFile, fileConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if fileExtension == ".toml" {
|
||||
tomlFile, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = toml.Unmarshal(tomlFile, &fileConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return errors.New("config files only accepted are .yaml and .toml")
|
||||
}
|
||||
// sets global flags to value in yaml/toml file
|
||||
for key, value := range fileConfig {
|
||||
if !ctx.IsSet(key) {
|
||||
if reflect.ValueOf(value).Kind() == reflect.Slice {
|
||||
sliceInterface := value.([]interface{})
|
||||
s := make([]string, len(sliceInterface))
|
||||
for i, v := range sliceInterface {
|
||||
s[i] = fmt.Sprintf("%v", v)
|
||||
}
|
||||
err := ctx.Set(key, strings.Join(s, ","))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed setting %s flag with values=%s error=%s", key, s, err)
|
||||
}
|
||||
} else {
|
||||
err := ctx.Set(key, fmt.Sprintf("%v", value))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed setting %s flag with value=%v error=%s", key, value, err)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -155,8 +155,6 @@ var DefaultFlags = []cli.Flag{
|
||||
&utils.EthStatsURLFlag,
|
||||
&utils.OverrideCancunFlag,
|
||||
|
||||
&utils.ConfigFlag,
|
||||
|
||||
&utils.LightClientDiscoveryAddrFlag,
|
||||
&utils.LightClientDiscoveryPortFlag,
|
||||
&utils.LightClientDiscoveryTCPPortFlag,
|
||||
|
Loading…
Reference in New Issue
Block a user