mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 06:06:44 +00:00
1577 lines
51 KiB
Go
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
|
|
}
|