gecko/main/params.go

438 lines
14 KiB
Go

// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package main
import (
"errors"
"flag"
"fmt"
"net"
"os"
"path"
"path/filepath"
"strings"
"github.com/ava-labs/gecko/database/leveldb"
"github.com/ava-labs/gecko/database/memdb"
"github.com/ava-labs/gecko/genesis"
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/nat"
"github.com/ava-labs/gecko/node"
"github.com/ava-labs/gecko/snow/networking/router"
"github.com/ava-labs/gecko/staking"
"github.com/ava-labs/gecko/utils"
"github.com/ava-labs/gecko/utils/formatting"
"github.com/ava-labs/gecko/utils/hashing"
"github.com/ava-labs/gecko/utils/logging"
"github.com/ava-labs/gecko/utils/random"
"github.com/ava-labs/gecko/utils/wrappers"
)
const (
dbVersion = "v0.5.0"
)
// Results of parsing the CLI
var (
Config = node.Config{}
Err error
defaultNetworkName = genesis.TestnetName
homeDir = os.ExpandEnv("$HOME")
defaultDbDir = filepath.Join(homeDir, ".gecko", "db")
defaultStakingKeyPath = filepath.Join(homeDir, ".gecko", "staking", "staker.key")
defaultStakingCertPath = filepath.Join(homeDir, ".gecko", "staking", "staker.crt")
defaultPluginDirs = []string{
filepath.Join(".", "build", "plugins"),
filepath.Join(".", "plugins"),
filepath.Join("/", "usr", "local", "lib", "gecko"),
filepath.Join(homeDir, ".gecko", "plugins"),
}
)
var (
errBootstrapMismatch = errors.New("more bootstrap IDs provided than bootstrap IPs")
errStakingRequiresTLS = errors.New("if staking is enabled, network TLS must also be enabled")
)
// GetIPs returns the default IPs for each network
func GetIPs(networkID uint32) []string {
switch networkID {
case genesis.DenaliID:
return []string{
"18.188.121.35:21001",
"3.133.83.66:21001",
"3.15.206.239:21001",
"18.224.140.156:21001",
"3.133.131.39:21001",
"18.191.29.54:21001",
"18.224.172.110:21001",
"18.223.211.203:21001",
"18.216.130.143:21001",
"18.223.184.147:21001",
"52.15.48.84:21001",
"18.189.194.220:21001",
"18.223.119.104:21001",
"3.133.155.41:21001",
"13.58.170.174:21001",
"3.21.245.246:21001",
"52.15.190.149:21001",
"18.188.95.241:21001",
"3.12.197.248:21001",
"3.17.39.236:21001",
}
case genesis.CascadeID:
return []string{
"3.227.207.132:21001",
"34.207.133.167:21001",
"54.162.71.9:21001",
"54.197.215.186:21001",
"18.234.153.22:21001",
}
default:
return nil
}
}
// GetIDs returns the default IDs for each network
func GetIDs(networkID uint32) []string {
switch networkID {
case genesis.DenaliID:
return []string{
"NpagUxt6KQiwPch9Sd4osv8kD1TZnkjdk",
"2m38qc95mhHXtrhjyGbe7r2NhniqHHJRB",
"LQwRLm4cbJ7T2kxcxp4uXCU5XD8DFrE1C",
"hArafGhY2HFTbwaaVh1CSCUCUCiJ2Vfb",
"4QBwET5o8kUhvt9xArhir4d3R25CtmZho",
"HGZ8ae74J3odT8ESreAdCtdnvWG1J4X5n",
"4KXitMCoE9p2BHA6VzXtaTxLoEjNDo2Pt",
"JyE4P8f4cTryNV8DCz2M81bMtGhFFHexG",
"EzGaipqomyK9UKx9DBHV6Ky3y68hoknrF",
"CYKruAjwH1BmV3m37sXNuprbr7dGQuJwG",
"LegbVf6qaMKcsXPnLStkdc1JVktmmiDxy",
"FesGqwKq7z5nPFHa5iwZctHE5EZV9Lpdq",
"BFa1padLXBj7VHa2JYvYGzcTBPQGjPhUy",
"4B4rc5vdD1758JSBYL1xyvE5NHGzz6xzH",
"EDESh4DfZFC15i613pMtWniQ9arbBZRnL",
"CZmZ9xpCzkWqjAyS7L4htzh5Lg6kf1k18",
"CTtkcXvVdhpNp6f97LEUXPwsRD3A2ZHqP",
"84KbQHSDnojroCVY7vQ7u9Tx7pUonPaS",
"JjvzhxnLHLUQ5HjVRkvG827ivbLXPwA9u",
"4CWTbdvgXHY1CLXqQNAp22nJDo5nAmts6",
}
case genesis.CascadeID:
return []string{
"NX4zVkuiRJZYe6Nzzav7GXN3TakUet3Co",
"CMsa8cMw4eib1Hb8GG4xiUKAq5eE1BwUX",
"DsMP6jLhi1MkDVc3qx9xx9AAZWx8e87Jd",
"N86eodVZja3GEyZJTo3DFUPGpxEEvjGHs",
"EkKeGSLUbHrrtuayBtbwgWDRUiAziC3ao",
}
default:
return nil
}
}
// GetDefaultBootstraps returns the default bootstraps this node should connect
// to
func GetDefaultBootstraps(networkID uint32, count int) ([]string, []string) {
ips := GetIPs(networkID)
ids := GetIDs(networkID)
if numIPs := len(ips); numIPs < count {
count = numIPs
}
sampledIPs := make([]string, 0, count)
sampledIDs := make([]string, 0, count)
sampler := random.Uniform{N: len(ips)}
for i := 0; i < count; i++ {
i := sampler.Sample()
sampledIPs = append(sampledIPs, ips[i])
sampledIDs = append(sampledIDs, ids[i])
}
return sampledIPs, sampledIDs
}
// Parse the CLI arguments
func init() {
errs := &wrappers.Errs{}
defer func() { Err = errs.Err }()
loggingConfig, err := logging.DefaultConfig()
if errs.Add(err); errs.Errored() {
return
}
fs := flag.NewFlagSet("gecko", flag.ContinueOnError)
// If this is true, print the version and quit.
version := fs.Bool("version", false, "If true, print version and quit")
// NetworkID:
networkName := fs.String("network-id", defaultNetworkName, "Network ID this node will connect to")
// Ava fees:
fs.Uint64Var(&Config.AvaTxFee, "ava-tx-fee", 0, "Ava transaction fee, in $nAva")
// Assertions:
fs.BoolVar(&loggingConfig.Assertions, "assertions-enabled", true, "Turn on assertion execution")
// Crypto:
fs.BoolVar(&Config.EnableCrypto, "signature-verification-enabled", true, "Turn on signature verification")
// Database:
db := fs.Bool("db-enabled", true, "Turn on persistent storage")
dbDir := fs.String("db-dir", defaultDbDir, "Database directory for Ava state")
// IP:
consensusIP := fs.String("public-ip", "", "Public IP of this node")
// HTTP Server:
httpHost := fs.String("http-host", "127.0.0.1", "Address of the HTTP server")
httpPort := fs.Uint("http-port", 9650, "Port of the HTTP server")
fs.BoolVar(&Config.EnableHTTPS, "http-tls-enabled", false, "Upgrade the HTTP server to HTTPs")
fs.StringVar(&Config.HTTPSKeyFile, "http-tls-key-file", "", "TLS private key file for the HTTPs server")
fs.StringVar(&Config.HTTPSCertFile, "http-tls-cert-file", "", "TLS certificate file for the HTTPs server")
// Bootstrapping:
bootstrapIPs := fs.String("bootstrap-ips", "default", "Comma separated list of bootstrap peer ips to connect to. Example: 127.0.0.1:9630,127.0.0.1:9631")
bootstrapIDs := fs.String("bootstrap-ids", "default", "Comma separated list of bootstrap peer ids to connect to. Example: JR4dVmy6ffUGAKCBDkyCbeZbyHQBeDsET,8CrVPQZ4VSqgL8zTdvL14G8HqAfrBr4z")
// Staking:
consensusPort := fs.Uint("staking-port", 9651, "Port of the consensus server")
// TODO - keeping same flag for backwards compatibility, should be changed to "staking-enabled"
fs.BoolVar(&Config.EnableStaking, "staking-tls-enabled", true, "Enable staking. If enabled, Network TLS is required.")
fs.BoolVar(&Config.EnableP2PTLS, "p2p-tls-enabled", true, "Require TLS to authenticate network communication")
fs.StringVar(&Config.StakingKeyFile, "staking-tls-key-file", defaultStakingKeyPath, "TLS private key for staking")
fs.StringVar(&Config.StakingCertFile, "staking-tls-cert-file", defaultStakingCertPath, "TLS certificate for staking")
// Plugins:
fs.StringVar(&Config.PluginDir, "plugin-dir", defaultPluginDirs[0], "Plugin directory for Ava VMs")
// Logging:
logsDir := fs.String("log-dir", "", "Logging directory for Ava")
logLevel := fs.String("log-level", "info", "The log level. Should be one of {verbo, debug, info, warn, error, fatal, off}")
logDisplayLevel := fs.String("log-display-level", "", "The log display level. If left blank, will inherit the value of log-level. Otherwise, should be one of {verbo, debug, info, warn, error, fatal, off}")
fs.IntVar(&Config.ConsensusParams.K, "snow-sample-size", 5, "Number of nodes to query for each network poll")
fs.IntVar(&Config.ConsensusParams.Alpha, "snow-quorum-size", 4, "Alpha value to use for required number positive results")
fs.IntVar(&Config.ConsensusParams.BetaVirtuous, "snow-virtuous-commit-threshold", 20, "Beta value to use for virtuous transactions")
fs.IntVar(&Config.ConsensusParams.BetaRogue, "snow-rogue-commit-threshold", 30, "Beta value to use for rogue transactions")
fs.IntVar(&Config.ConsensusParams.Parents, "snow-avalanche-num-parents", 5, "Number of vertexes for reference from each new vertex")
fs.IntVar(&Config.ConsensusParams.BatchSize, "snow-avalanche-batch-size", 30, "Number of operations to batch in each new vertex")
fs.IntVar(&Config.ConsensusParams.ConcurrentRepolls, "snow-concurrent-repolls", 1, "Minimum number of concurrent polls for finalizing consensus")
// Enable/Disable APIs:
fs.BoolVar(&Config.AdminAPIEnabled, "api-admin-enabled", false, "If true, this node exposes the Admin API")
fs.BoolVar(&Config.InfoAPIEnabled, "api-info-enabled", true, "If true, this node exposes the Info API")
fs.BoolVar(&Config.KeystoreAPIEnabled, "api-keystore-enabled", true, "If true, this node exposes the Keystore API")
fs.BoolVar(&Config.MetricsAPIEnabled, "api-metrics-enabled", true, "If true, this node exposes the Metrics API")
fs.BoolVar(&Config.HealthAPIEnabled, "api-health-enabled", true, "If true, this node exposes the Health API")
fs.BoolVar(&Config.IPCEnabled, "api-ipcs-enabled", false, "If true, IPCs can be opened")
// Throughput Server
throughputPort := fs.Uint("xput-server-port", 9652, "Port of the deprecated throughput test server")
fs.BoolVar(&Config.ThroughputServerEnabled, "xput-server-enabled", false, "If true, throughput test server is created")
ferr := fs.Parse(os.Args[1:])
if *version { // If --version used, print version and exit
networkID, err := genesis.NetworkID(defaultNetworkName)
if errs.Add(err); err != nil {
return
}
networkGeneration := genesis.NetworkName(networkID)
fmt.Printf(
"%s [database=%s, network=%s/%s]\n",
node.Version, dbVersion, defaultNetworkName, networkGeneration,
)
os.Exit(0)
}
if ferr == flag.ErrHelp {
// display usage/help text and exit successfully
os.Exit(0)
}
if ferr != nil {
// other type of error occurred when parsing args
os.Exit(2)
}
networkID, err := genesis.NetworkID(*networkName)
if errs.Add(err); err != nil {
return
}
Config.NetworkID = networkID
// DB:
if *db {
*dbDir = os.ExpandEnv(*dbDir) // parse any env variables
dbPath := path.Join(*dbDir, genesis.NetworkName(Config.NetworkID), dbVersion)
db, err := leveldb.New(dbPath, 0, 0, 0)
if err != nil {
errs.Add(fmt.Errorf("couldn't create db at %s: %w", dbPath, err))
return
}
Config.DB = db
} else {
Config.DB = memdb.New()
}
var ip net.IP
// If public IP is not specified, get it using shell command dig
if *consensusIP == "" {
Config.Nat = nat.GetRouter()
ip, err = Config.Nat.ExternalIP()
if err != nil {
ip = net.IPv4zero // Couldn't get my IP...set to 0.0.0.0
}
} else {
Config.Nat = nat.NewNoRouter()
ip = net.ParseIP(*consensusIP)
}
if ip == nil {
errs.Add(fmt.Errorf("Invalid IP Address %s", *consensusIP))
return
}
Config.StakingIP = utils.IPDesc{
IP: ip,
Port: uint16(*consensusPort),
}
Config.StakingLocalPort = uint16(*consensusPort)
defaultBootstrapIPs, defaultBootstrapIDs := GetDefaultBootstraps(networkID, 5)
// Bootstrapping:
if *bootstrapIPs == "default" {
*bootstrapIPs = strings.Join(defaultBootstrapIPs, ",")
}
for _, ip := range strings.Split(*bootstrapIPs, ",") {
if ip != "" {
addr, err := utils.ToIPDesc(ip)
if err != nil {
errs.Add(fmt.Errorf("couldn't parse ip: %w", err))
return
}
Config.BootstrapPeers = append(Config.BootstrapPeers, &node.Peer{
IP: addr,
})
}
}
if *bootstrapIDs == "default" {
if *bootstrapIPs == "" {
*bootstrapIDs = ""
} else {
*bootstrapIDs = strings.Join(defaultBootstrapIDs, ",")
}
}
if Config.EnableStaking && !Config.EnableP2PTLS {
errs.Add(errStakingRequiresTLS)
return
}
if Config.EnableP2PTLS {
i := 0
cb58 := formatting.CB58{}
for _, id := range strings.Split(*bootstrapIDs, ",") {
if id != "" {
err = cb58.FromString(id)
if err != nil {
errs.Add(fmt.Errorf("couldn't parse bootstrap peer id to bytes: %w", err))
return
}
peerID, err := ids.ToShortID(cb58.Bytes)
if err != nil {
errs.Add(fmt.Errorf("couldn't parse bootstrap peer id: %w", err))
return
}
if len(Config.BootstrapPeers) <= i {
errs.Add(errBootstrapMismatch)
return
}
Config.BootstrapPeers[i].ID = peerID
i++
}
}
if len(Config.BootstrapPeers) != i {
errs.Add(fmt.Errorf("More bootstrap IPs, %d, provided than bootstrap IDs, %d", len(Config.BootstrapPeers), i))
return
}
} else {
for _, peer := range Config.BootstrapPeers {
peer.ID = ids.NewShortID(hashing.ComputeHash160Array([]byte(peer.IP.String())))
}
}
// Plugins
if _, err := os.Stat(Config.PluginDir); os.IsNotExist(err) {
for _, dir := range defaultPluginDirs {
if _, err := os.Stat(dir); !os.IsNotExist(err) {
Config.PluginDir = dir
break
}
}
}
// Staking
Config.StakingCertFile = os.ExpandEnv(Config.StakingCertFile) // parse any env variable
Config.StakingKeyFile = os.ExpandEnv(Config.StakingKeyFile)
switch {
// If staking key/cert locations are specified but not found, error
case Config.StakingKeyFile != defaultStakingKeyPath || Config.StakingCertFile != defaultStakingCertPath:
if _, err := os.Stat(Config.StakingKeyFile); os.IsNotExist(err) {
errs.Add(fmt.Errorf("couldn't find staking key at %s", Config.StakingKeyFile))
return
} else if _, err := os.Stat(Config.StakingCertFile); os.IsNotExist(err) {
errs.Add(fmt.Errorf("couldn't find staking certificate at %s", Config.StakingCertFile))
return
}
default:
// Only creates staking key/cert if [stakingKeyPath] doesn't exist
if err := staking.GenerateStakingKeyCert(Config.StakingKeyFile, Config.StakingCertFile); err != nil {
errs.Add(fmt.Errorf("couldn't generate staking key/cert: %w", err))
return
}
}
// HTTP:
Config.HTTPHost = *httpHost
Config.HTTPPort = uint16(*httpPort)
// Logging:
if *logsDir != "" {
loggingConfig.Directory = *logsDir
}
logFileLevel, err := logging.ToLevel(*logLevel)
if errs.Add(err); err != nil {
return
}
loggingConfig.LogLevel = logFileLevel
if *logDisplayLevel == "" {
*logDisplayLevel = *logLevel
}
displayLevel, err := logging.ToLevel(*logDisplayLevel)
if errs.Add(err); err != nil {
return
}
loggingConfig.DisplayLevel = displayLevel
Config.LoggingConfig = loggingConfig
// Throughput:
Config.ThroughputPort = uint16(*throughputPort)
// Router used for consensus
Config.ConsensusRouter = &router.ChainRouter{}
}