go-ethereum/command/server/config.go
2021-10-10 15:06:46 +02:00

342 lines
7.6 KiB
Go

package server
import (
"crypto/ecdsa"
"fmt"
"io/ioutil"
"math/big"
"os"
"path/filepath"
"strconv"
"time"
"github.com/ethereum/go-ethereum/command/server/chains"
"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/node"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/params"
"github.com/imdario/mergo"
)
func stringPtr(s string) *string {
return &s
}
func uint64Ptr(i uint64) *uint64 {
return &i
}
func boolPtr(b bool) *bool {
return &b
}
func durPtr(d time.Duration) *time.Duration {
return &d
}
type Config struct {
chain *chains.Chain
Chain *string
Debug *bool
LogLevel *string
DataDir *string
P2P *P2PConfig
SyncMode *string
EthStats *string
TxPool *TxPoolConfig
Sealer *SealerConfig
JsonRPC *JsonRPCConfig
}
type P2PConfig struct {
MaxPeers *uint64
Bind *string
Port *uint64
NoDiscover *bool
V5Disc *bool
Discovery *P2PDiscovery
}
type P2PDiscovery struct {
Bootnodes []string
BootnodesV4 []string
BootnodesV5 []string
StaticNodes []string
TrustedNodes []string
DNS []string
}
type TxPoolConfig struct {
Locals []string
NoLocals *bool
Journal *string
Rejournal *time.Duration
PriceLimit *uint64
PriceBump *uint64
AccountSlots *uint64
GlobalSlots *uint64
AccountQueue *uint64
GlobalQueue *uint64
LifeTime *time.Duration
}
type SealerConfig struct {
Enabled *bool
Etherbase *string
ExtraData *string
GasCeil *uint64
GasPrice *big.Int
}
type JsonRPCConfig struct {
}
func DefaultConfig() *Config {
return &Config{
Chain: stringPtr("mainnet"),
Debug: boolPtr(false),
LogLevel: stringPtr("INFO"),
DataDir: stringPtr(""),
P2P: &P2PConfig{
MaxPeers: uint64Ptr(30),
Bind: stringPtr("0.0.0.0"),
Port: uint64Ptr(30303),
NoDiscover: boolPtr(false),
V5Disc: boolPtr(false),
Discovery: &P2PDiscovery{
Bootnodes: []string{},
BootnodesV4: []string{},
BootnodesV5: []string{},
StaticNodes: []string{},
TrustedNodes: []string{},
DNS: []string{},
},
},
SyncMode: stringPtr("fast"),
EthStats: stringPtr(""),
TxPool: &TxPoolConfig{
Locals: []string{},
NoLocals: boolPtr(false),
Journal: stringPtr(""),
Rejournal: durPtr(1 * time.Hour),
PriceLimit: uint64Ptr(1),
PriceBump: uint64Ptr(10),
AccountSlots: uint64Ptr(16),
GlobalSlots: uint64Ptr(4096),
AccountQueue: uint64Ptr(64),
GlobalQueue: uint64Ptr(1024),
LifeTime: durPtr(3 * time.Hour),
},
Sealer: &SealerConfig{
Enabled: boolPtr(false),
GasCeil: uint64Ptr(8000000),
GasPrice: big.NewInt(params.GWei),
},
}
}
func readConfigFile(path string) (*Config, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
// TODO: Use hcl as config format
ext := filepath.Ext(path)
switch ext {
case ".toml":
return readLegacyConfig(data)
default:
return nil, fmt.Errorf("file path extension '%s' not found", ext)
}
}
func (c *Config) loadChain() error {
chain, ok := chains.GetChain(*c.Chain)
if !ok {
return fmt.Errorf("chain '%s' not found", *c.Chain)
}
c.chain = chain
// preload some default values that are on the chain file
if c.P2P.Discovery.Bootnodes == nil {
c.P2P.Discovery.Bootnodes = c.chain.Bootnodes
}
if c.P2P.Discovery.DNS == nil {
c.P2P.Discovery.DNS = c.chain.DNS
}
return nil
}
func (c *Config) buildEth() (*ethconfig.Config, error) {
dbHandles, err := makeDatabaseHandles()
if err != nil {
return nil, err
}
n := ethconfig.Defaults
n.NetworkId = c.chain.NetworkId
n.Genesis = c.chain.Genesis
// txpool options
{
cfg := n.TxPool
cfg.NoLocals = *c.TxPool.NoLocals
cfg.Journal = *c.TxPool.Journal
cfg.Rejournal = *c.TxPool.Rejournal
cfg.PriceLimit = *c.TxPool.PriceLimit
cfg.PriceBump = *c.TxPool.PriceBump
cfg.AccountSlots = *c.TxPool.AccountSlots
cfg.GlobalSlots = *c.TxPool.GlobalSlots
cfg.AccountQueue = *c.TxPool.AccountQueue
cfg.GlobalQueue = *c.TxPool.GlobalQueue
cfg.Lifetime = *c.TxPool.LifeTime
}
// miner options
{
cfg := n.Miner
cfg.Etherbase = common.HexToAddress(*c.Sealer.Etherbase)
cfg.GasPrice = c.Sealer.GasPrice
cfg.GasCeil = *c.Sealer.GasCeil
}
// discovery (this params should be in node.Config)
{
n.EthDiscoveryURLs = c.P2P.Discovery.DNS
n.SnapDiscoveryURLs = c.P2P.Discovery.DNS
}
var syncMode downloader.SyncMode
switch *c.SyncMode {
case "fast":
syncMode = downloader.FastSync
default:
return nil, fmt.Errorf("sync mode '%s' not found", syncMode)
}
n.SyncMode = syncMode
n.DatabaseHandles = dbHandles
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)
)
func (c *Config) buildNode() (*node.Config, error) {
cfg := &node.Config{
Name: clientIdentifier,
DataDir: *c.DataDir,
Version: params.VersionWithCommit(gitCommit, gitDate),
IPCPath: clientIdentifier + ".ipc",
P2P: p2p.Config{
MaxPeers: int(*c.P2P.MaxPeers),
ListenAddr: *c.P2P.Bind + ":" + strconv.Itoa(int(*c.P2P.Port)),
},
/*
HTTPHost: *c.BindAddr,
HTTPPort: int(*c.Ports.HTTP),
HTTPVirtualHosts: []string{"*"},
WSHost: *c.BindAddr,
WSPort: int(*c.Ports.Websocket),
*/
}
// setup private key for DevP2P if not found
devP2PPrivKey, err := readDevP2PKey(*c.DataDir)
if err != nil {
return nil, err
}
cfg.P2P.PrivateKey = devP2PPrivKey
// Discovery
if cfg.P2P.BootstrapNodes, err = parseBootnodes(c.P2P.Discovery.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 cfg.P2P.TrustedNodes, err = parseBootnodes(c.P2P.Discovery.TrustedNodes); err != nil {
return nil, err
}
if *c.P2P.NoDiscover {
// Disable networking, for now, we will not even allow incomming connections
cfg.P2P.MaxPeers = 0
cfg.P2P.NoDiscovery = true
}
return cfg, nil
}
func (c *Config) Merge(cc ...*Config) error {
for _, elem := range cc {
if err := mergo.Merge(&c, elem); err != nil {
return err
}
}
return nil
}
func makeDatabaseHandles() (int, error) {
limit, err := fdlimit.Maximum()
if err != nil {
return -1, err
}
raised, err := fdlimit.Raise(uint64(limit))
if err != nil {
return -1, err
}
return int(raised / 2), nil
}
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
}
const devP2PKeyPath = "devp2p.key"
func readDevP2PKey(dataDir string) (*ecdsa.PrivateKey, error) {
path := filepath.Join(dataDir, devP2PKeyPath)
_, err := os.Stat(path)
if err != nil && !os.IsNotExist(err) {
return nil, fmt.Errorf("failed to stat (%s): %v", path, err)
}
if os.IsNotExist(err) {
priv, err := crypto.GenerateKey()
if err != nil {
return nil, err
}
if err := crypto.SaveECDSA(path, priv); err != nil {
return nil, err
}
return priv, nil
}
// exists
priv, err := crypto.LoadECDSA(path)
if err != nil {
return nil, err
}
return priv, nil
}