go-ethereum/internal/cli/server/config.go
2025-03-21 15:22:27 +05:30

1577 lines
51 KiB
Go

package server
import (
"crypto/ecdsa"
"fmt"
"math"
"math/big"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
godebug "runtime/debug"
"github.com/hashicorp/hcl/v2/hclsimple"
"github.com/imdario/mergo"
"github.com/mitchellh/go-homedir"
gopsutil "github.com/shirou/gopsutil/mem"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/fdlimit"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/internal/cli/server/chains"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/p2p/netutil"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
)
type Config struct {
chain *chains.Chain
// Chain is the chain to sync with
Chain string `hcl:"chain,optional" toml:"chain,optional"`
// Identity of the node
Identity string `hcl:"identity,optional" toml:"identity,optional"`
// RequiredBlocks is a list of required (block number, hash) pairs to accept
RequiredBlocks map[string]string `hcl:"eth.requiredblocks,optional" toml:"eth.requiredblocks,optional"`
// Verbosity is the level of the logs to put out
Verbosity int `hcl:"verbosity,optional" toml:"verbosity,optional"`
// LogLevel is the level of the logs to put out
LogLevel string `hcl:"log-level,optional" toml:"log-level,optional"`
// Record information useful for VM and contract debugging
EnablePreimageRecording bool `hcl:"vmdebug,optional" toml:"vmdebug,optional"`
// DataDir is the directory to store the state in
DataDir string `hcl:"datadir,optional" toml:"datadir,optional"`
// Ancient is the directory to store the state in
Ancient string `hcl:"ancient,optional" toml:"ancient,optional"`
// DBEngine is used to select leveldb or pebble as database
DBEngine string `hcl:"db.engine,optional" toml:"db.engine,optional"`
// KeyStoreDir is the directory to store keystores
KeyStoreDir string `hcl:"keystore,optional" toml:"keystore,optional"`
// Maximum number of messages in a batch (default=100, use 0 for no limits)
RPCBatchLimit uint64 `hcl:"rpc.batchlimit,optional" toml:"rpc.batchlimit,optional"`
// Maximum size (in bytes) a result of an rpc request could have (default=100000, use 0 for no limits)
RPCReturnDataLimit uint64 `hcl:"rpc.returndatalimit,optional" toml:"rpc.returndatalimit,optional"`
// SyncMode selects the sync protocol
SyncMode string `hcl:"syncmode,optional" toml:"syncmode,optional"`
// GcMode selects the garbage collection mode for the trie
GcMode string `hcl:"gcmode,optional" toml:"gcmode,optional"`
// state.scheme selects the Scheme to use for storing ethereum state ('hash' or 'path')
StateScheme string `hcl:"state.scheme,optional" toml:"state.scheme,optional"`
// Snapshot enables the snapshot database mode
Snapshot bool `hcl:"snapshot,optional" toml:"snapshot,optional"`
// BorLogs enables bor log retrieval
BorLogs bool `hcl:"bor.logs,optional" toml:"bor.logs,optional"`
// Ethstats is the address of the ethstats server to send telemetry
Ethstats string `hcl:"ethstats,optional" toml:"ethstats,optional"`
// Logging has the logging related settings
Logging *LoggingConfig `hcl:"log,block" toml:"log,block"`
// P2P has the p2p network related settings
P2P *P2PConfig `hcl:"p2p,block" toml:"p2p,block"`
// Heimdall has the heimdall connection related settings
Heimdall *HeimdallConfig `hcl:"heimdall,block" toml:"heimdall,block"`
// TxPool has the transaction pool related settings
TxPool *TxPoolConfig `hcl:"txpool,block" toml:"txpool,block"`
// Sealer has the validator related settings
Sealer *SealerConfig `hcl:"miner,block" toml:"miner,block"`
// JsonRPC has the json-rpc related settings
JsonRPC *JsonRPCConfig `hcl:"jsonrpc,block" toml:"jsonrpc,block"`
// Gpo has the gas price oracle related settings
Gpo *GpoConfig `hcl:"gpo,block" toml:"gpo,block"`
// Telemetry has the telemetry related settings
Telemetry *TelemetryConfig `hcl:"telemetry,block" toml:"telemetry,block"`
// Cache has the cache related settings
Cache *CacheConfig `hcl:"cache,block" toml:"cache,block"`
ExtraDB *ExtraDBConfig `hcl:"leveldb,block" toml:"leveldb,block"`
// Account has the validator account related settings
Accounts *AccountsConfig `hcl:"accounts,block" toml:"accounts,block"`
// GRPC has the grpc server related settings
GRPC *GRPCConfig `hcl:"grpc,block" toml:"grpc,block"`
// Developer has the developer mode related settings
Developer *DeveloperConfig `hcl:"developer,block" toml:"developer,block"`
// ParallelEVM has the parallel evm related settings
ParallelEVM *ParallelEVMConfig `hcl:"parallelevm,block" toml:"parallelevm,block"`
// Develop Fake Author mode to produce blocks without authorisation
DevFakeAuthor bool `hcl:"devfakeauthor,optional" toml:"devfakeauthor,optional"`
// Pprof has the pprof related settings
Pprof *PprofConfig `hcl:"pprof,block" toml:"pprof,block"`
}
type LoggingConfig struct {
// Per-module verbosity: comma-separated list of <pattern>=<level> (e.g. eth/*=5,p2p=4)
Vmodule string `hcl:"vmodule,optional" toml:"vmodule,optional"`
// Format logs with JSON
Json bool `hcl:"json,optional" toml:"json,optional"`
// Request a stack trace at a specific logging statement (e.g. "block.go:271")
Backtrace string `hcl:"backtrace,optional" toml:"backtrace,optional"`
// Prepends log messages with call-site location (file and line number)
Debug bool `hcl:"debug,optional" toml:"debug,optional"`
// EnableBlockTracking allows logging of information collected while tracking block lifecycle
EnableBlockTracking bool `hcl:"enable-block-tracking,optional" toml:"enable-block-tracking,optional"`
// TODO - implement this
// // Write execution trace to the given file
// Trace string `hcl:"trace,optional" toml:"trace,optional"`
}
type PprofConfig struct {
// Enableed enable the pprof HTTP server
Enabled bool `hcl:"pprof,optional" toml:"pprof,optional"`
// pprof HTTP server listening port
Port int `hcl:"port,optional" toml:"port,optional"`
// pprof HTTP server listening interface
Addr string `hcl:"addr,optional" toml:"addr,optional"`
// Turn on memory profiling with the given rate
MemProfileRate int `hcl:"memprofilerate,optional" toml:"memprofilerate,optional"`
// Turn on block profiling with the given rate
BlockProfileRate int `hcl:"blockprofilerate,optional" toml:"blockprofilerate,optional"`
// // Write CPU profile to the given file
// CPUProfile string `hcl:"cpuprofile,optional" toml:"cpuprofile,optional"`
}
type P2PConfig struct {
// MaxPeers sets the maximum number of connected peers
MaxPeers uint64 `hcl:"maxpeers,optional" toml:"maxpeers,optional"`
// MaxPendPeers sets the maximum number of pending connected peers
MaxPendPeers uint64 `hcl:"maxpendpeers,optional" toml:"maxpendpeers,optional"`
// Bind is the bind address
Bind string `hcl:"bind,optional" toml:"bind,optional"`
// Port is the port number
Port uint64 `hcl:"port,optional" toml:"port,optional"`
// NoDiscover is used to disable discovery
NoDiscover bool `hcl:"nodiscover,optional" toml:"nodiscover,optional"`
// NAT it used to set NAT options
NAT string `hcl:"nat,optional" toml:"nat,optional"`
// Connectivity can be restricted to certain IP networks.
// If this option is set to a non-nil value, only hosts which match one of the
// IP networks contained in the list are considered.
NetRestrict string `hcl:"netrestrict,optional" toml:"netrestrict,optional"`
// P2P node key file
NodeKey string `hcl:"nodekey,optional" toml:"nodekey,optional"`
// P2P node key as hex
NodeKeyHex string `hcl:"nodekeyhex,optional" toml:"nodekeyhex,optional"`
// Discovery has the p2p discovery related settings
Discovery *P2PDiscovery `hcl:"discovery,block" toml:"discovery,block"`
// TxArrivalWait sets the maximum duration the transaction fetcher will wait for
// an announced transaction to arrive before explicitly requesting it
TxArrivalWait time.Duration `hcl:"-,optional" toml:"-"`
TxArrivalWaitRaw string `hcl:"txarrivalwait,optional" toml:"txarrivalwait,optional"`
// TxAnnouncementOnly is used to only announce transactions to peers
TxAnnouncementOnly bool `hcl:"txannouncementonly,optional" toml:"txannouncementonly,optional"`
}
type P2PDiscovery struct {
// DiscoveryV4 specifies whether V4 discovery should be started.
DiscoveryV4 bool `hcl:"v4disc,optional" toml:"v4disc,optional"`
// DiscoveryV5 specifies whether the new topic-discovery based V5 discovery
// protocol should be started or not.
DiscoveryV5 bool `hcl:"v5disc,optional" toml:"v5disc,optional"`
// Bootnodes is the list of initial bootnodes
Bootnodes []string `hcl:"bootnodes,optional" toml:"bootnodes,optional"`
// BootnodesV4 is the list of initial v4 bootnodes
BootnodesV4 []string `hcl:"bootnodesv4,optional" toml:"bootnodesv4,optional"`
// BootnodesV5 is the list of initial v5 bootnodes
BootnodesV5 []string `hcl:"bootnodesv5,optional" toml:"bootnodesv5,optional"`
// StaticNodes is the list of static nodes
StaticNodes []string `hcl:"static-nodes,optional" toml:"static-nodes,optional"`
// TrustedNodes is the list of trusted nodes
TrustedNodes []string `hcl:"trusted-nodes,optional" toml:"trusted-nodes,optional"`
// DNS is the list of enrtree:// URLs which will be queried for nodes to connect to
DNS []string `hcl:"dns,optional" toml:"dns,optional"`
}
type HeimdallConfig struct {
// URL is the url of the heimdall server
URL string `hcl:"url,optional" toml:"url,optional"`
Timeout time.Duration `hcl:"timeout,optional" toml:"timeout,optional"`
// Without is used to disable remote heimdall during testing
Without bool `hcl:"bor.without,optional" toml:"bor.without,optional"`
// GRPCAddress is the address of the heimdall grpc server
GRPCAddress string `hcl:"grpc-address,optional" toml:"grpc-address,optional"`
// RunHeimdall is used to run heimdall as a child process
RunHeimdall bool `hcl:"bor.runheimdall,optional" toml:"bor.runheimdall,optional"`
// RunHeimdal args are the arguments to run heimdall with
RunHeimdallArgs string `hcl:"bor.runheimdallargs,optional" toml:"bor.runheimdallargs,optional"`
// UseHeimdallApp is used to fetch data from heimdall app when running heimdall as a child process
UseHeimdallApp bool `hcl:"bor.useheimdallapp,optional" toml:"bor.useheimdallapp,optional"`
}
type TxPoolConfig struct {
// Locals are the addresses that should be treated by default as local
Locals []string `hcl:"locals,optional" toml:"locals,optional"`
// NoLocals enables whether local transaction handling should be disabled
NoLocals bool `hcl:"nolocals,optional" toml:"nolocals,optional"`
// Journal is the path to store local transactions to survive node restarts
Journal string `hcl:"journal,optional" toml:"journal,optional"`
// Rejournal is the time interval to regenerate the local transaction journal
Rejournal time.Duration `hcl:"-,optional" toml:"-"`
RejournalRaw string `hcl:"rejournal,optional" toml:"rejournal,optional"`
// PriceLimit is the minimum gas price to enforce for acceptance into the pool
PriceLimit uint64 `hcl:"pricelimit,optional" toml:"pricelimit,optional"`
// PriceBump is the minimum price bump percentage to replace an already existing transaction (nonce)
PriceBump uint64 `hcl:"pricebump,optional" toml:"pricebump,optional"`
// AccountSlots is the number of executable transaction slots guaranteed per account
AccountSlots uint64 `hcl:"accountslots,optional" toml:"accountslots,optional"`
// GlobalSlots is the maximum number of executable transaction slots for all accounts
GlobalSlots uint64 `hcl:"globalslots,optional" toml:"globalslots,optional"`
// AccountQueue is the maximum number of non-executable transaction slots permitted per account
AccountQueue uint64 `hcl:"accountqueue,optional" toml:"accountqueue,optional"`
// GlobalQueueis the maximum number of non-executable transaction slots for all accounts
GlobalQueue uint64 `hcl:"globalqueue,optional" toml:"globalqueue,optional"`
// lifetime is the maximum amount of time non-executable transaction are queued
LifeTime time.Duration `hcl:"-,optional" toml:"-"`
LifeTimeRaw string `hcl:"lifetime,optional" toml:"lifetime,optional"`
}
type SealerConfig struct {
// Enabled is used to enable validator mode
Enabled bool `hcl:"mine,optional" toml:"mine,optional"`
// Etherbase is the address of the validator
Etherbase string `hcl:"etherbase,optional" toml:"etherbase,optional"`
// ExtraData is the block extra data set by the miner
ExtraData string `hcl:"extradata,optional" toml:"extradata,optional"`
// GasCeil is the target gas ceiling for mined blocks.
GasCeil uint64 `hcl:"gaslimit,optional" toml:"gaslimit,optional"`
// GasPrice is the minimum gas price for mining a transaction
GasPrice *big.Int `hcl:"-,optional" toml:"-"`
GasPriceRaw string `hcl:"gasprice,optional" toml:"gasprice,optional"`
// The time interval for miner to re-create mining work.
Recommit time.Duration `hcl:"-,optional" toml:"-"`
RecommitRaw string `hcl:"recommit,optional" toml:"recommit,optional"`
CommitInterruptFlag bool `hcl:"commitinterrupt,optional" toml:"commitinterrupt,optional"`
}
type JsonRPCConfig struct {
// IPCDisable enables whether ipc is enabled or not
IPCDisable bool `hcl:"ipcdisable,optional" toml:"ipcdisable,optional"`
// IPCPath is the path of the ipc endpoint
IPCPath string `hcl:"ipcpath,optional" toml:"ipcpath,optional"`
// GasCap is the global gas cap for eth-call variants.
GasCap uint64 `hcl:"gascap,optional" toml:"gascap,optional"`
// Sets a timeout used for eth_call (0=infinite)
RPCEVMTimeout time.Duration `hcl:"-,optional" toml:"-"`
RPCEVMTimeoutRaw string `hcl:"evmtimeout,optional" toml:"evmtimeout,optional"`
// TxFeeCap is the global transaction fee cap for send-transaction variants
TxFeeCap float64 `hcl:"txfeecap,optional" toml:"txfeecap,optional"`
// Http has the json-rpc http related settings
Http *APIConfig `hcl:"http,block" toml:"http,block"`
// Ws has the json-rpc websocket related settings
Ws *APIConfig `hcl:"ws,block" toml:"ws,block"`
// Graphql has the json-rpc graphql related settings
Graphql *APIConfig `hcl:"graphql,block" toml:"graphql,block"`
// AUTH RPC related settings
Auth *AUTHConfig `hcl:"auth,block" toml:"auth,block"`
HttpTimeout *HttpTimeouts `hcl:"timeouts,block" toml:"timeouts,block"`
AllowUnprotectedTxs bool `hcl:"allow-unprotected-txs,optional" toml:"allow-unprotected-txs,optional"`
// EnablePersonal enables the deprecated personal namespace.
EnablePersonal bool `hcl:"enabledeprecatedpersonal,optional" toml:"enabledeprecatedpersonal,optional"`
}
type AUTHConfig struct {
// JWTSecret is the hex-encoded jwt secret.
JWTSecret string `hcl:"jwtsecret,optional" toml:"jwtsecret,optional"`
// Addr is the listening address on which authenticated APIs are provided.
Addr string `hcl:"addr,optional" toml:"addr,optional"`
// Port is the port number on which authenticated APIs are provided.
Port uint64 `hcl:"port,optional" toml:"port,optional"`
// VHosts is the list of virtual hostnames which are allowed on incoming requests
// for the authenticated api. This is by default {'localhost'}.
VHosts []string `hcl:"vhosts,optional" toml:"vhosts,optional"`
}
type GRPCConfig struct {
// Addr is the bind address for the grpc rpc server
Addr string `hcl:"addr,optional" toml:"addr,optional"`
}
type APIConfig struct {
// Enabled selects whether the api is enabled
Enabled bool `hcl:"enabled,optional" toml:"enabled,optional"`
// Port is the port number for this api
Port uint64 `hcl:"port,optional" toml:"port,optional"`
// Prefix is the http prefix to expose this api
Prefix string `hcl:"prefix,optional" toml:"prefix,optional"`
// Host is the address to bind the api
Host string `hcl:"host,optional" toml:"host,optional"`
// API is the list of enabled api modules
API []string `hcl:"api,optional" toml:"api,optional"`
// VHost is the list of valid virtual hosts
VHost []string `hcl:"vhosts,optional" toml:"vhosts,optional"`
// Cors is the list of Cors endpoints
Cors []string `hcl:"corsdomain,optional" toml:"corsdomain,optional"`
// Origins is the list of endpoints to accept requests from (only consumed for websockets)
Origins []string `hcl:"origins,optional" toml:"origins,optional"`
// ExecutionPoolSize is max size of workers to be used for rpc execution
ExecutionPoolSize uint64 `hcl:"ep-size,optional" toml:"ep-size,optional"`
// ExecutionPoolRequestTimeout is timeout used by execution pool for rpc execution
ExecutionPoolRequestTimeout time.Duration `hcl:"-,optional" toml:"-"`
ExecutionPoolRequestTimeoutRaw string `hcl:"ep-requesttimeout,optional" toml:"ep-requesttimeout,optional"`
}
// Used from rpc.HTTPTimeouts
type HttpTimeouts struct {
// ReadTimeout is the maximum duration for reading the entire
// request, including the body.
//
// Because ReadTimeout does not let Handlers make per-request
// decisions on each request body's acceptable deadline or
// upload rate, most users will prefer to use
// ReadHeaderTimeout. It is valid to use them both.
ReadTimeout time.Duration `hcl:"-,optional" toml:"-"`
ReadTimeoutRaw string `hcl:"read,optional" toml:"read,optional"`
// WriteTimeout is the maximum duration before timing out
// writes of the response. It is reset whenever a new
// request's header is read. Like ReadTimeout, it does not
// let Handlers make decisions on a per-request basis.
WriteTimeout time.Duration `hcl:"-,optional" toml:"-"`
WriteTimeoutRaw string `hcl:"write,optional" toml:"write,optional"`
// IdleTimeout is the maximum amount of time to wait for the
// next request when keep-alives are enabled. If IdleTimeout
// is zero, the value of ReadTimeout is used. If both are
// zero, ReadHeaderTimeout is used.
IdleTimeout time.Duration `hcl:"-,optional" toml:"-"`
IdleTimeoutRaw string `hcl:"idle,optional" toml:"idle,optional"`
}
type GpoConfig struct {
// Blocks is the number of blocks to track to compute the price oracle
Blocks uint64 `hcl:"blocks,optional" toml:"blocks,optional"`
// Percentile sets the weights to new blocks
Percentile uint64 `hcl:"percentile,optional" toml:"percentile,optional"`
// Maximum header history of gasprice oracle
MaxHeaderHistory int `hcl:"maxheaderhistory,optional" toml:"maxheaderhistory,optional"`
// Maximum block history of gasprice oracle
MaxBlockHistory int `hcl:"maxblockhistory,optional" toml:"maxblockhistory,optional"`
// MaxPrice is an upper bound gas price
MaxPrice *big.Int `hcl:"-,optional" toml:"-"`
MaxPriceRaw string `hcl:"maxprice,optional" toml:"maxprice,optional"`
// IgnorePrice is a lower bound gas price
IgnorePrice *big.Int `hcl:"-,optional" toml:"-"`
IgnorePriceRaw string `hcl:"ignoreprice,optional" toml:"ignoreprice,optional"`
}
type TelemetryConfig struct {
// Enabled enables metrics
Enabled bool `hcl:"metrics,optional" toml:"metrics,optional"`
// Expensive enables expensive metrics
Expensive bool `hcl:"expensive,optional" toml:"expensive,optional"`
// InfluxDB has the influxdb related settings
InfluxDB *InfluxDBConfig `hcl:"influx,block" toml:"influx,block"`
// Prometheus Address
PrometheusAddr string `hcl:"prometheus-addr,optional" toml:"prometheus-addr,optional"`
// Open collector endpoint
OpenCollectorEndpoint string `hcl:"opencollector-endpoint,optional" toml:"opencollector-endpoint,optional"`
}
type InfluxDBConfig struct {
// V1Enabled enables influx v1 mode
V1Enabled bool `hcl:"influxdb,optional" toml:"influxdb,optional"`
// Endpoint is the url endpoint of the influxdb service
Endpoint string `hcl:"endpoint,optional" toml:"endpoint,optional"`
// Database is the name of the database in Influxdb to store the metrics.
Database string `hcl:"database,optional" toml:"database,optional"`
// Enabled is the username to authorize access to Influxdb
Username string `hcl:"username,optional" toml:"username,optional"`
// Password is the password to authorize access to Influxdb
Password string `hcl:"password,optional" toml:"password,optional"`
// Tags are tags attaches to all generated metrics
Tags map[string]string `hcl:"tags,optional" toml:"tags,optional"`
// Enabled enables influx v2 mode
V2Enabled bool `hcl:"influxdbv2,optional" toml:"influxdbv2,optional"`
// Token is the token to authorize access to Influxdb V2.
Token string `hcl:"token,optional" toml:"token,optional"`
// Bucket is the bucket to store metrics in Influxdb V2.
Bucket string `hcl:"bucket,optional" toml:"bucket,optional"`
// Organization is the name of the organization for Influxdb V2.
Organization string `hcl:"organization,optional" toml:"organization,optional"`
}
type CacheConfig struct {
// Cache is the amount of cache of the node
Cache uint64 `hcl:"cache,optional" toml:"cache,optional"`
// PercGc is percentage of cache used for garbage collection
PercGc uint64 `hcl:"gc,optional" toml:"gc,optional"`
// PercSnapshot is percentage of cache used for snapshots
PercSnapshot uint64 `hcl:"snapshot,optional" toml:"snapshot,optional"`
// PercDatabase is percentage of cache used for the database
PercDatabase uint64 `hcl:"database,optional" toml:"database,optional"`
// PercTrie is percentage of cache used for the trie
PercTrie uint64 `hcl:"trie,optional" toml:"trie,optional"`
// NoPrefetch is used to disable prefetch of tries
NoPrefetch bool `hcl:"noprefetch,optional" toml:"noprefetch,optional"`
// Preimages is used to enable the track of hash preimages
Preimages bool `hcl:"preimages,optional" toml:"preimages,optional"`
// TxLookupLimit sets the maximum number of blocks from head whose tx indices are reserved.
TxLookupLimit uint64 `hcl:"txlookuplimit,optional" toml:"txlookuplimit,optional"`
// Number of block states to keep in memory (default = 128)
TriesInMemory uint64 `hcl:"triesinmemory,optional" toml:"triesinmemory,optional"`
// This is the number of blocks for which logs will be cached in the filter system.
FilterLogCacheSize int `hcl:"blocklogs,optional" toml:"blocklogs,optional"`
// Time after which the Merkle Patricia Trie is stored to disc from memory
TrieTimeout time.Duration `hcl:"-,optional" toml:"-"`
TrieTimeoutRaw string `hcl:"timeout,optional" toml:"timeout,optional"`
// Raise the open file descriptor resource limit (default = system fd limit)
FDLimit int `hcl:"fdlimit,optional" toml:"fdlimit,optional"`
}
type ExtraDBConfig struct {
LevelDbCompactionTableSize uint64 `hcl:"compactiontablesize,optional" toml:"compactiontablesize,optional"`
LevelDbCompactionTableSizeMultiplier float64 `hcl:"compactiontablesizemultiplier,optional" toml:"compactiontablesizemultiplier,optional"`
LevelDbCompactionTotalSize uint64 `hcl:"compactiontotalsize,optional" toml:"compactiontotalsize,optional"`
LevelDbCompactionTotalSizeMultiplier float64 `hcl:"compactiontotalsizemultiplier,optional" toml:"compactiontotalsizemultiplier,optional"`
}
type AccountsConfig struct {
// Unlock is the list of addresses to unlock in the node
Unlock []string `hcl:"unlock,optional" toml:"unlock,optional"`
// PasswordFile is the file where the account passwords are stored
PasswordFile string `hcl:"password,optional" toml:"password,optional"`
// AllowInsecureUnlock allows user to unlock accounts in unsafe http environment.
AllowInsecureUnlock bool `hcl:"allow-insecure-unlock,optional" toml:"allow-insecure-unlock,optional"`
// UseLightweightKDF enables a faster but less secure encryption of accounts
UseLightweightKDF bool `hcl:"lightkdf,optional" toml:"lightkdf,optional"`
// DisableBorWallet disables the personal wallet endpoints
DisableBorWallet bool `hcl:"disable-bor-wallet,optional" toml:"disable-bor-wallet,optional"`
}
type DeveloperConfig struct {
// Enabled enables the developer mode
Enabled bool `hcl:"dev,optional" toml:"dev,optional"`
// Period is the block period to use in developer mode
Period uint64 `hcl:"period,optional" toml:"period,optional"`
// Initial block gas limit
GasLimit uint64 `hcl:"gaslimit,optional" toml:"gaslimit,optional"`
}
type ParallelEVMConfig struct {
Enable bool `hcl:"enable,optional" toml:"enable,optional"`
SpeculativeProcesses int `hcl:"procs,optional" toml:"procs,optional"`
Enforce bool `hcl:"enforce,optional" toml:"enforce,optional"`
}
func DefaultConfig() *Config {
return &Config{
Chain: "mainnet",
Identity: Hostname(),
RequiredBlocks: map[string]string{},
Verbosity: 3,
LogLevel: "",
EnablePreimageRecording: false,
DataDir: DefaultDataDir(),
Ancient: "",
DBEngine: "pebble",
KeyStoreDir: "",
Logging: &LoggingConfig{
Vmodule: "",
Json: false,
Backtrace: "",
Debug: false,
EnableBlockTracking: false,
},
RPCBatchLimit: 100,
RPCReturnDataLimit: 100000,
P2P: &P2PConfig{
MaxPeers: 50,
MaxPendPeers: 50,
Bind: "0.0.0.0",
Port: 30303,
NoDiscover: false,
NAT: "any",
NetRestrict: "",
TxArrivalWait: 500 * time.Millisecond,
TxAnnouncementOnly: false,
Discovery: &P2PDiscovery{
DiscoveryV4: true,
DiscoveryV5: true,
Bootnodes: []string{},
BootnodesV4: []string{},
BootnodesV5: []string{},
StaticNodes: []string{},
TrustedNodes: []string{},
DNS: []string{},
},
},
Heimdall: &HeimdallConfig{
URL: "http://localhost:1317",
Timeout: 5 * time.Second,
Without: false,
GRPCAddress: "",
},
SyncMode: "full",
GcMode: "full",
StateScheme: "path",
Snapshot: true,
BorLogs: false,
TxPool: &TxPoolConfig{
Locals: []string{},
NoLocals: false,
Journal: "transactions.rlp",
Rejournal: 1 * time.Hour,
PriceLimit: params.BorDefaultTxPoolPriceLimit, // bor's default
PriceBump: 10,
AccountSlots: 16,
GlobalSlots: 131072,
AccountQueue: 64,
GlobalQueue: 131072,
LifeTime: 3 * time.Hour,
},
Sealer: &SealerConfig{
Enabled: false,
Etherbase: "",
GasCeil: 30_000_000, // geth's default
GasPrice: big.NewInt(params.BorDefaultMinerGasPrice), // bor's default
ExtraData: "",
Recommit: 125 * time.Second,
CommitInterruptFlag: true,
},
Gpo: &GpoConfig{
Blocks: 20,
Percentile: 60,
MaxHeaderHistory: 1024,
MaxBlockHistory: 1024,
MaxPrice: gasprice.DefaultMaxPrice,
IgnorePrice: gasprice.DefaultIgnorePrice, // bor's default
},
JsonRPC: &JsonRPCConfig{
IPCDisable: false,
IPCPath: "",
GasCap: ethconfig.Defaults.RPCGasCap,
TxFeeCap: ethconfig.Defaults.RPCTxFeeCap,
RPCEVMTimeout: ethconfig.Defaults.RPCEVMTimeout,
AllowUnprotectedTxs: false,
EnablePersonal: false,
Http: &APIConfig{
Enabled: false,
Port: 8545,
Prefix: "",
Host: "localhost",
API: []string{"eth", "net", "web3", "txpool", "bor"},
Cors: []string{"localhost"},
VHost: []string{"localhost"},
ExecutionPoolSize: 40,
ExecutionPoolRequestTimeout: 0,
},
Ws: &APIConfig{
Enabled: false,
Port: 8546,
Prefix: "",
Host: "localhost",
API: []string{"net", "web3"},
Origins: []string{"localhost"},
ExecutionPoolSize: 40,
ExecutionPoolRequestTimeout: 0,
},
Graphql: &APIConfig{
Enabled: false,
Cors: []string{"localhost"},
VHost: []string{"localhost"},
},
HttpTimeout: &HttpTimeouts{
ReadTimeout: 10 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 120 * time.Second,
},
Auth: &AUTHConfig{
JWTSecret: "",
Port: node.DefaultAuthPort,
Addr: node.DefaultAuthHost,
VHosts: node.DefaultAuthVhosts,
},
},
Ethstats: "",
Telemetry: &TelemetryConfig{
Enabled: false,
Expensive: false,
PrometheusAddr: "127.0.0.1:7071",
OpenCollectorEndpoint: "",
InfluxDB: &InfluxDBConfig{
V1Enabled: false,
Endpoint: "",
Database: "",
Username: "",
Password: "",
Tags: map[string]string{},
V2Enabled: false,
Token: "",
Bucket: "",
Organization: "",
},
},
Cache: &CacheConfig{
Cache: 1024, // geth's default (suitable for mumbai)
PercDatabase: 50,
PercTrie: 15,
PercGc: 25,
PercSnapshot: 10,
NoPrefetch: false,
Preimages: false,
TxLookupLimit: 2350000,
TriesInMemory: 128,
FilterLogCacheSize: ethconfig.Defaults.FilterLogCacheSize,
TrieTimeout: 60 * time.Minute,
FDLimit: 0,
},
ExtraDB: &ExtraDBConfig{
// These are LevelDB defaults, specifying here for clarity in code and in logging.
// See: https://github.com/syndtr/goleveldb/blob/126854af5e6d8295ef8e8bee3040dd8380ae72e8/leveldb/opt/options.go
LevelDbCompactionTableSize: 2, // MiB
LevelDbCompactionTableSizeMultiplier: 1,
LevelDbCompactionTotalSize: 10, // MiB
LevelDbCompactionTotalSizeMultiplier: 10,
},
Accounts: &AccountsConfig{
Unlock: []string{},
PasswordFile: "",
AllowInsecureUnlock: false,
UseLightweightKDF: false,
DisableBorWallet: true,
},
GRPC: &GRPCConfig{
Addr: ":3131",
},
Developer: &DeveloperConfig{
Enabled: false,
Period: 0,
GasLimit: 11500000,
},
DevFakeAuthor: false,
Pprof: &PprofConfig{
Enabled: false,
Port: 6060,
Addr: "127.0.0.1",
MemProfileRate: 512 * 1024,
BlockProfileRate: 0,
// CPUProfile: "",
},
ParallelEVM: &ParallelEVMConfig{
Enable: true,
SpeculativeProcesses: 8,
Enforce: false,
},
}
}
func (c *Config) fillBigInt() error {
tds := []struct {
path string
td **big.Int
str *string
}{
{"gpo.maxprice", &c.Gpo.MaxPrice, &c.Gpo.MaxPriceRaw},
{"gpo.ignoreprice", &c.Gpo.IgnorePrice, &c.Gpo.IgnorePriceRaw},
{"miner.gasprice", &c.Sealer.GasPrice, &c.Sealer.GasPriceRaw},
}
for _, x := range tds {
if *x.str != "" {
b := new(big.Int)
var ok bool
if strings.HasPrefix(*x.str, "0x") {
b, ok = b.SetString((*x.str)[2:], 16)
} else {
b, ok = b.SetString(*x.str, 10)
}
if !ok {
return fmt.Errorf("%s can't parse big int %s", x.path, *x.str)
}
*x.str = ""
*x.td = b
}
}
return nil
}
func (c *Config) fillTimeDurations() error {
tds := []struct {
path string
td *time.Duration
str *string
}{
{"jsonrpc.evmtimeout", &c.JsonRPC.RPCEVMTimeout, &c.JsonRPC.RPCEVMTimeoutRaw},
{"miner.recommit", &c.Sealer.Recommit, &c.Sealer.RecommitRaw},
{"jsonrpc.timeouts.read", &c.JsonRPC.HttpTimeout.ReadTimeout, &c.JsonRPC.HttpTimeout.ReadTimeoutRaw},
{"jsonrpc.timeouts.write", &c.JsonRPC.HttpTimeout.WriteTimeout, &c.JsonRPC.HttpTimeout.WriteTimeoutRaw},
{"jsonrpc.timeouts.idle", &c.JsonRPC.HttpTimeout.IdleTimeout, &c.JsonRPC.HttpTimeout.IdleTimeoutRaw},
{"jsonrpc.ws.ep-requesttimeout", &c.JsonRPC.Ws.ExecutionPoolRequestTimeout, &c.JsonRPC.Ws.ExecutionPoolRequestTimeoutRaw},
{"jsonrpc.http.ep-requesttimeout", &c.JsonRPC.Http.ExecutionPoolRequestTimeout, &c.JsonRPC.Http.ExecutionPoolRequestTimeoutRaw},
{"txpool.lifetime", &c.TxPool.LifeTime, &c.TxPool.LifeTimeRaw},
{"txpool.rejournal", &c.TxPool.Rejournal, &c.TxPool.RejournalRaw},
{"cache.timeout", &c.Cache.TrieTimeout, &c.Cache.TrieTimeoutRaw},
{"p2p.txarrivalwait", &c.P2P.TxArrivalWait, &c.P2P.TxArrivalWaitRaw},
}
for _, x := range tds {
if x.td != nil && x.str != nil && *x.str != "" {
d, err := time.ParseDuration(*x.str)
if err != nil {
return fmt.Errorf("%s can't parse time duration %s", x.path, *x.str)
}
*x.str = ""
*x.td = d
}
}
return nil
}
func readConfigFile(path string) (*Config, error) {
ext := filepath.Ext(path)
if ext == ".toml" {
return readLegacyConfig(path)
}
config := &Config{
TxPool: &TxPoolConfig{},
Cache: &CacheConfig{},
Sealer: &SealerConfig{},
}
if err := hclsimple.DecodeFile(path, nil, config); err != nil {
return nil, fmt.Errorf("failed to decode config file '%s': %v", path, err)
}
if err := config.fillBigInt(); err != nil {
return nil, err
}
if err := config.fillTimeDurations(); err != nil {
return nil, err
}
return config, nil
}
func (c *Config) loadChain() error {
chain, err := chains.GetChain(c.Chain)
if err != nil {
return err
}
c.chain = chain
// preload some default values that depend on the chain file
if c.P2P.Discovery.DNS == nil {
c.P2P.Discovery.DNS = c.chain.DNS
}
return nil
}
//nolint:gocognit
func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (*ethconfig.Config, error) {
dbHandles, err := MakeDatabaseHandles(c.Cache.FDLimit)
if err != nil {
return nil, err
}
n := ethconfig.Defaults
// only update for non-developer mode as we don't yet
// have the chain object for it.
if !c.Developer.Enabled {
n.NetworkId = c.chain.NetworkId
n.Genesis = c.chain.Genesis
}
n.HeimdallURL = c.Heimdall.URL
n.HeimdallTimeout = c.Heimdall.Timeout
n.WithoutHeimdall = c.Heimdall.Without
n.HeimdallgRPCAddress = c.Heimdall.GRPCAddress
n.RunHeimdall = c.Heimdall.RunHeimdall
n.RunHeimdallArgs = c.Heimdall.RunHeimdallArgs
n.UseHeimdallApp = c.Heimdall.UseHeimdallApp
// Developer Fake Author for producing blocks without authorisation on bor consensus
n.DevFakeAuthor = c.DevFakeAuthor
// Developer Fake Author for producing blocks without authorisation on bor consensus
n.DevFakeAuthor = c.DevFakeAuthor
// gas price oracle
{
n.GPO.Blocks = int(c.Gpo.Blocks)
n.GPO.Percentile = int(c.Gpo.Percentile)
n.GPO.MaxHeaderHistory = uint64(c.Gpo.MaxHeaderHistory)
n.GPO.MaxBlockHistory = uint64(c.Gpo.MaxBlockHistory)
n.GPO.MaxPrice = c.Gpo.MaxPrice
n.GPO.IgnorePrice = c.Gpo.IgnorePrice
}
n.EnablePreimageRecording = c.EnablePreimageRecording
// txpool options
{
n.TxPool.NoLocals = c.TxPool.NoLocals
n.TxPool.Journal = c.TxPool.Journal
n.TxPool.Rejournal = c.TxPool.Rejournal
n.TxPool.PriceLimit = c.TxPool.PriceLimit
n.TxPool.PriceBump = c.TxPool.PriceBump
n.TxPool.AccountSlots = c.TxPool.AccountSlots
n.TxPool.GlobalSlots = c.TxPool.GlobalSlots
n.TxPool.AccountQueue = c.TxPool.AccountQueue
n.TxPool.GlobalQueue = c.TxPool.GlobalQueue
n.TxPool.Lifetime = c.TxPool.LifeTime
}
// miner options
{
n.Miner.Recommit = c.Sealer.Recommit
n.Miner.GasPrice = c.Sealer.GasPrice
n.Miner.GasCeil = c.Sealer.GasCeil
n.Miner.ExtraData = []byte(c.Sealer.ExtraData)
n.Miner.CommitInterruptFlag = c.Sealer.CommitInterruptFlag
if etherbase := c.Sealer.Etherbase; etherbase != "" {
if !common.IsHexAddress(etherbase) {
return nil, fmt.Errorf("etherbase is not an address: %s", etherbase)
}
n.Miner.Etherbase = common.HexToAddress(etherbase)
}
}
// unlock accounts
if len(c.Accounts.Unlock) > 0 {
if !stack.Config().InsecureUnlockAllowed && stack.Config().ExtRPCEnabled() {
return nil, fmt.Errorf("account unlock with HTTP access is forbidden")
}
ks := accountManager.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
passwords, err := MakePasswordListFromFile(c.Accounts.PasswordFile)
if err != nil {
return nil, err
}
if len(passwords) < len(c.Accounts.Unlock) {
return nil, fmt.Errorf("number of passwords provided (%v) is less than number of accounts (%v) to unlock",
len(passwords), len(c.Accounts.Unlock))
}
for i, account := range c.Accounts.Unlock {
unlockAccount(ks, account, i, passwords)
}
}
// update for developer mode
if c.Developer.Enabled {
// Get a keystore
var ks *keystore.KeyStore
if keystores := accountManager.Backends(keystore.KeyStoreType); len(keystores) > 0 {
ks = keystores[0].(*keystore.KeyStore)
}
// Create new developer account or reuse existing one
var (
developer accounts.Account
passphrase string
err error
)
// etherbase has been set above, configuring the miner address from command line flags.
if n.Miner.Etherbase != (common.Address{}) {
developer = accounts.Account{Address: n.Miner.Etherbase}
} else if accs := ks.Accounts(); len(accs) > 0 {
developer = ks.Accounts()[0]
} else {
developer, err = ks.NewAccount(passphrase)
if err != nil {
return nil, fmt.Errorf("failed to create developer account: %v", err)
}
}
if err := ks.Unlock(developer, passphrase); err != nil {
return nil, fmt.Errorf("failed to unlock developer account: %v", err)
}
log.Info("Using developer account", "address", developer.Address)
// Set the Etherbase
c.Sealer.Etherbase = developer.Address.Hex()
n.Miner.Etherbase = developer.Address
// get developer mode chain config
c.chain = chains.GetDeveloperChain(c.Developer.Period, c.Developer.GasLimit, developer.Address)
// update the parameters
n.NetworkId = c.chain.NetworkId
n.Genesis = c.chain.Genesis
// Update cache
c.Cache.Cache = 1024
// Update sync mode
c.SyncMode = "full"
// update miner gas price
if n.Miner.GasPrice == nil {
n.Miner.GasPrice = big.NewInt(1)
}
}
// discovery (this params should be in node.Config)
{
n.EthDiscoveryURLs = c.P2P.Discovery.DNS
n.SnapDiscoveryURLs = c.P2P.Discovery.DNS
}
// RequiredBlocks
{
n.RequiredBlocks = map[uint64]common.Hash{}
for k, v := range c.RequiredBlocks {
number, err := strconv.ParseUint(k, 0, 64)
if err != nil {
return nil, fmt.Errorf("invalid required block number %s: %v", k, err)
}
var hash common.Hash
if err = hash.UnmarshalText([]byte(v)); err != nil {
return nil, fmt.Errorf("invalid required block hash %s: %v", v, err)
}
n.RequiredBlocks[number] = hash
}
}
// cache
{
cache := c.Cache.Cache
calcPerc := func(val uint64) int {
return int(cache * (val) / 100)
}
// Cap the cache allowance
mem, err := gopsutil.VirtualMemory()
if err == nil {
if 32<<(^uintptr(0)>>63) == 32 && mem.Total > 2*1024*1024*1024 {
log.Warn("Lowering memory allowance on 32bit arch", "available", mem.Total/1024/1024, "addressable", 2*1024)
mem.Total = 2 * 1024 * 1024 * 1024
}
allowance := mem.Total / 1024 / 1024 / 3
if cache > allowance {
log.Warn("Sanitizing cache to Go's GC limits", "provided", cache, "updated", allowance)
cache = allowance
}
}
// Tune the garbage collector
gogc := math.Max(20, math.Min(100, 100/(float64(cache)/1024)))
log.Debug("Sanitizing Go's GC trigger", "percent", int(gogc))
godebug.SetGCPercent(int(gogc))
n.DatabaseCache = calcPerc(c.Cache.PercDatabase)
n.SnapshotCache = calcPerc(c.Cache.PercSnapshot)
n.TrieCleanCache = calcPerc(c.Cache.PercTrie)
n.TrieDirtyCache = calcPerc(c.Cache.PercGc)
n.NoPrefetch = c.Cache.NoPrefetch
n.Preimages = c.Cache.Preimages
n.TransactionHistory = c.Cache.TxLookupLimit
n.TrieTimeout = c.Cache.TrieTimeout
n.TriesInMemory = c.Cache.TriesInMemory
n.FilterLogCacheSize = c.Cache.FilterLogCacheSize
}
// LevelDB
{
n.LevelDbCompactionTableSize = c.ExtraDB.LevelDbCompactionTableSize
n.LevelDbCompactionTableSizeMultiplier = c.ExtraDB.LevelDbCompactionTableSizeMultiplier
n.LevelDbCompactionTotalSize = c.ExtraDB.LevelDbCompactionTotalSize
n.LevelDbCompactionTotalSizeMultiplier = c.ExtraDB.LevelDbCompactionTotalSizeMultiplier
}
n.RPCGasCap = c.JsonRPC.GasCap
if n.RPCGasCap != 0 {
log.Info("Set global gas cap", "cap", n.RPCGasCap)
} else {
log.Info("Global gas cap disabled")
}
n.RPCEVMTimeout = c.JsonRPC.RPCEVMTimeout
n.RPCTxFeeCap = c.JsonRPC.TxFeeCap
// sync mode. It can either be "fast", "full" or "snap". We disable
// for now the "light" mode.
switch c.SyncMode {
case "full":
n.SyncMode = downloader.FullSync
case "snap":
// n.SyncMode = downloader.SnapSync // TODO(snap): Uncomment when we have snap sync working
n.SyncMode = downloader.FullSync
log.Warn("Bor doesn't support Snap Sync yet, switching to Full Sync mode")
default:
return nil, fmt.Errorf("sync mode '%s' not found", c.SyncMode)
}
// archive mode. It can either be "archive" or "full".
switch c.GcMode {
case "full":
n.NoPruning = false
case "archive":
n.NoPruning = true
if !n.Preimages {
n.Preimages = true
log.Info("Enabling recording of key preimages since archive mode is used")
}
if c.StateScheme == "path" {
return nil, fmt.Errorf("path storage scheme is not supported in archive mode, please use hash instead")
}
default:
return nil, fmt.Errorf("gcmode '%s' not found", c.GcMode)
}
// statescheme "hash" or "path"
switch c.StateScheme {
case "path":
n.StateScheme = "path"
default:
n.StateScheme = "hash"
}
// snapshot disable check
if !c.Snapshot {
if n.SyncMode == downloader.SnapSync {
log.Info("Snap sync requested, enabling --snapshot")
} else {
// disable snapshot
n.TrieCleanCache += n.SnapshotCache
n.SnapshotCache = 0
}
}
n.BorLogs = c.BorLogs
n.DatabaseHandles = dbHandles
n.ParallelEVM.Enable = c.ParallelEVM.Enable
n.ParallelEVM.SpeculativeProcesses = c.ParallelEVM.SpeculativeProcesses
n.ParallelEVM.Enforce = c.ParallelEVM.Enforce
n.RPCReturnDataLimit = c.RPCReturnDataLimit
if c.Ancient != "" {
n.DatabaseFreezer = c.Ancient
}
n.EnableBlockTracking = c.Logging.EnableBlockTracking
return &n, nil
}
var (
clientIdentifier = "bor"
gitCommit = "" // Git SHA1 commit hash of the release (set via linker flags)
gitDate = "" // Git commit date YYYYMMDD of the release (set via linker flags)
)
// tries unlocking the specified account a few times.
func unlockAccount(ks *keystore.KeyStore, address string, i int, passwords []string) (accounts.Account, string) {
account, err := utils.MakeAddress(ks, address)
if err != nil {
utils.Fatalf("Could not list accounts: %v", err)
}
for trials := 0; trials < 3; trials++ {
prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3)
password := utils.GetPassPhraseWithList(prompt, false, i, passwords)
err = ks.Unlock(account, password)
if err == nil {
log.Info("Unlocked account", "address", account.Address.Hex())
return account, password
}
if err, ok := err.(*keystore.AmbiguousAddrError); ok {
log.Info("Unlocked account", "address", account.Address.Hex())
return ambiguousAddrRecovery(ks, err, password), password
}
if err != keystore.ErrDecrypt {
// No need to prompt again if the error is not decryption-related.
break
}
}
// All trials expended to unlock account, bail out
utils.Fatalf("Failed to unlock account %s (%v)", address, err)
return accounts.Account{}, ""
}
func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrError, auth string) accounts.Account {
log.Warn("Multiple key files exist for", "address", err.Addr)
for _, a := range err.Matches {
log.Info("Multiple keys", "file", a.URL.String())
}
log.Info("Testing your password against all of them...")
var match *accounts.Account
for _, a := range err.Matches {
if err := ks.Unlock(a, auth); err == nil {
// nolint: gosec, exportloopref
match = &a
break
}
}
if match == nil {
utils.Fatalf("None of the listed files could be unlocked.")
}
log.Info("Your password unlocked", "key", match.URL.String())
log.Warn("In order to avoid this warning, you need to remove the following duplicate key files:")
for _, a := range err.Matches {
if a != *match {
log.Warn("Duplicate", "key", a.URL.String())
}
}
return *match
}
// setNodeKey creates a node key from set command line flags, either loading it
// from a file or as a specified hex value. If neither flags were provided, this
// method returns nil and an emphemeral key is to be generated.
func getNodeKey(hex string, file string) *ecdsa.PrivateKey {
var (
key *ecdsa.PrivateKey
err error
)
switch {
case file != "" && hex != "":
utils.Fatalf("Options %q and %q are mutually exclusive", file, hex)
case file != "":
if key, err = crypto.LoadECDSA(file); err != nil {
utils.Fatalf("Option %q: %v", file, err)
}
return key
case hex != "":
if key, err = crypto.HexToECDSA(hex); err != nil {
utils.Fatalf("Option %q: %v", hex, err)
}
return key
}
return nil
}
func (c *Config) buildNode() (*node.Config, error) {
ipcPath := ""
if !c.JsonRPC.IPCDisable {
ipcPath = clientIdentifier + ".ipc"
if c.JsonRPC.IPCPath != "" {
ipcPath = c.JsonRPC.IPCPath
}
}
cfg := &node.Config{
Name: clientIdentifier,
DataDir: c.DataDir,
DBEngine: c.DBEngine,
KeyStoreDir: c.KeyStoreDir,
UseLightweightKDF: c.Accounts.UseLightweightKDF,
InsecureUnlockAllowed: c.Accounts.AllowInsecureUnlock,
Version: params.VersionWithCommit(gitCommit, gitDate),
IPCPath: ipcPath,
AllowUnprotectedTxs: c.JsonRPC.AllowUnprotectedTxs,
EnablePersonal: c.JsonRPC.EnablePersonal,
P2P: p2p.Config{
MaxPeers: int(c.P2P.MaxPeers),
MaxPendingPeers: int(c.P2P.MaxPendPeers),
ListenAddr: c.P2P.Bind + ":" + strconv.Itoa(int(c.P2P.Port)),
DiscoveryV4: c.P2P.Discovery.DiscoveryV4,
DiscoveryV5: c.P2P.Discovery.DiscoveryV5,
TxArrivalWait: c.P2P.TxArrivalWait,
TxAnnouncementOnly: c.P2P.TxAnnouncementOnly,
},
HTTPModules: c.JsonRPC.Http.API,
HTTPCors: c.JsonRPC.Http.Cors,
HTTPVirtualHosts: c.JsonRPC.Http.VHost,
HTTPPathPrefix: c.JsonRPC.Http.Prefix,
WSModules: c.JsonRPC.Ws.API,
WSOrigins: c.JsonRPC.Ws.Origins,
WSPathPrefix: c.JsonRPC.Ws.Prefix,
GraphQLCors: c.JsonRPC.Graphql.Cors,
GraphQLVirtualHosts: c.JsonRPC.Graphql.VHost,
HTTPTimeouts: rpc.HTTPTimeouts{
ReadTimeout: c.JsonRPC.HttpTimeout.ReadTimeout,
WriteTimeout: c.JsonRPC.HttpTimeout.WriteTimeout,
IdleTimeout: c.JsonRPC.HttpTimeout.IdleTimeout,
},
JWTSecret: c.JsonRPC.Auth.JWTSecret,
AuthPort: int(c.JsonRPC.Auth.Port),
AuthAddr: c.JsonRPC.Auth.Addr,
AuthVirtualHosts: c.JsonRPC.Auth.VHosts,
RPCBatchLimit: c.RPCBatchLimit,
WSJsonRPCExecutionPoolSize: c.JsonRPC.Ws.ExecutionPoolSize,
WSJsonRPCExecutionPoolRequestTimeout: c.JsonRPC.Ws.ExecutionPoolRequestTimeout,
HTTPJsonRPCExecutionPoolSize: c.JsonRPC.Http.ExecutionPoolSize,
HTTPJsonRPCExecutionPoolRequestTimeout: c.JsonRPC.Http.ExecutionPoolRequestTimeout,
}
if c.P2P.NetRestrict != "" {
list, err := netutil.ParseNetlist(c.P2P.NetRestrict)
if err != nil {
utils.Fatalf("Option %q: %v", c.P2P.NetRestrict, err)
}
cfg.P2P.NetRestrict = list
}
key := getNodeKey(c.P2P.NodeKeyHex, c.P2P.NodeKey)
if key != nil {
cfg.P2P.PrivateKey = key
}
// dev mode
if c.Developer.Enabled {
cfg.UseLightweightKDF = true
// disable p2p networking
c.P2P.NoDiscover = true
cfg.P2P.ListenAddr = ""
cfg.P2P.NoDial = true
cfg.P2P.DiscoveryV5 = false
// enable JsonRPC HTTP API
c.JsonRPC.Http.Enabled = true
cfg.HTTPModules = []string{"admin", "debug", "eth", "miner", "net", "personal", "txpool", "web3", "bor"}
}
// enable jsonrpc endpoints
{
if c.JsonRPC.Http.Enabled {
cfg.HTTPHost = c.JsonRPC.Http.Host
cfg.HTTPPort = int(c.JsonRPC.Http.Port)
}
if c.JsonRPC.Ws.Enabled {
cfg.WSHost = c.JsonRPC.Ws.Host
cfg.WSPort = int(c.JsonRPC.Ws.Port)
}
}
natif, err := nat.Parse(c.P2P.NAT)
if err != nil {
return nil, fmt.Errorf("wrong 'nat' flag: %v", err)
}
cfg.P2P.NAT = natif
// only check for non-developer modes
if !c.Developer.Enabled {
// Discovery
// Append the bootnodes defined with those hardcoded in the config file
bootnodes := c.P2P.Discovery.Bootnodes
if c.chain != nil {
bootnodes = append(bootnodes, c.chain.Bootnodes...)
}
if cfg.P2P.BootstrapNodes, err = parseBootnodes(bootnodes); err != nil {
return nil, err
}
if cfg.P2P.BootstrapNodesV5, err = parseBootnodes(c.P2P.Discovery.BootnodesV5); err != nil {
return nil, err
}
if cfg.P2P.StaticNodes, err = parseBootnodes(c.P2P.Discovery.StaticNodes); err != nil {
return nil, err
}
if len(cfg.P2P.StaticNodes) == 0 {
cfg.P2P.StaticNodes = cfg.StaticNodes()
}
if cfg.P2P.TrustedNodes, err = parseBootnodes(c.P2P.Discovery.TrustedNodes); err != nil {
return nil, err
}
if len(cfg.P2P.TrustedNodes) == 0 {
cfg.P2P.TrustedNodes = cfg.TrustedNodes()
}
}
if c.P2P.NoDiscover {
// Disable peer discovery
cfg.P2P.NoDiscovery = true
}
return cfg, nil
}
func (c *Config) Merge(cc ...*Config) error {
for _, elem := range cc {
if err := mergo.Merge(c, elem, mergo.WithOverwriteWithEmptyValue); err != nil {
return fmt.Errorf("failed to merge configurations: %v", err)
}
}
return nil
}
func MakeDatabaseHandles(max int) (int, error) {
limit, err := fdlimit.Maximum()
if err != nil {
return -1, err
}
switch {
case max == 0:
// User didn't specify a meaningful value, use system limits
case max < 128:
// User specified something unhealthy, just use system defaults
log.Error("File descriptor limit invalid (<128)", "had", max, "updated", limit)
case max > limit:
// User requested more than the OS allows, notify that we can't allocate it
log.Warn("Requested file descriptors denied by OS", "req", max, "limit", limit)
default:
// User limit is meaningful and within allowed range, use that
limit = max
}
raised, err := fdlimit.Raise(uint64(limit))
if err != nil {
return -1, err
}
return int(raised / 2), nil // Leave half for networking and other stuff
}
func parseBootnodes(urls []string) ([]*enode.Node, error) {
dst := []*enode.Node{}
for _, url := range urls {
if url != "" {
node, err := enode.Parse(enode.ValidSchemes, url)
if err != nil {
return nil, fmt.Errorf("invalid bootstrap url '%s': %v", url, err)
}
dst = append(dst, node)
}
}
return dst, nil
}
func DefaultDataDir() string {
// Try to place the data folder in the user's home dir
home, _ := homedir.Dir()
if home == "" {
// we cannot guess a stable location
return ""
}
switch runtime.GOOS {
case "darwin":
return filepath.Join(home, "Library", "Bor")
case "windows":
appdata := os.Getenv("LOCALAPPDATA")
if appdata == "" {
// Windows XP and below don't have LocalAppData.
panic("environment variable LocalAppData is undefined")
}
return filepath.Join(appdata, "Bor")
default:
return filepath.Join(home, ".bor")
}
}
func Hostname() string {
hostname, err := os.Hostname()
if err != nil {
return "bor"
}
return hostname
}
func MakePasswordListFromFile(path string) ([]string, error) {
text, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read password file: %v", err)
}
lines := strings.Split(string(text), "\n")
// Sanitise DOS line endings.
for i := range lines {
lines[i] = strings.TrimRight(lines[i], "\r")
}
return lines, nil
}