github.com/shrimpyuk/bor@v0.2.15-0.20220224151350-fb4ec6020bae/internal/cli/server/config.go (about)

     1  package server
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"math"
     7  	"math/big"
     8  	"os"
     9  	"path/filepath"
    10  	"runtime"
    11  	"strconv"
    12  	"strings"
    13  	"time"
    14  
    15  	godebug "runtime/debug"
    16  
    17  	"github.com/ethereum/go-ethereum/accounts"
    18  	"github.com/ethereum/go-ethereum/accounts/keystore"
    19  	"github.com/ethereum/go-ethereum/common"
    20  	"github.com/ethereum/go-ethereum/common/fdlimit"
    21  	"github.com/ethereum/go-ethereum/eth/downloader"
    22  	"github.com/ethereum/go-ethereum/eth/ethconfig"
    23  	"github.com/ethereum/go-ethereum/eth/gasprice"
    24  	"github.com/ethereum/go-ethereum/internal/cli/server/chains"
    25  	"github.com/ethereum/go-ethereum/log"
    26  	"github.com/ethereum/go-ethereum/node"
    27  	"github.com/ethereum/go-ethereum/p2p"
    28  	"github.com/ethereum/go-ethereum/p2p/enode"
    29  	"github.com/ethereum/go-ethereum/p2p/nat"
    30  	"github.com/ethereum/go-ethereum/params"
    31  	"github.com/hashicorp/hcl/v2/hclsimple"
    32  	"github.com/imdario/mergo"
    33  	"github.com/mitchellh/go-homedir"
    34  	gopsutil "github.com/shirou/gopsutil/mem"
    35  )
    36  
    37  type Config struct {
    38  	chain *chains.Chain
    39  
    40  	// Chain is the chain to sync with
    41  	Chain string `hcl:"chain,optional"`
    42  
    43  	// Name, or identity of the node
    44  	Name string `hcl:"name,optional"`
    45  
    46  	// Whitelist is a list of required (block number, hash) pairs to accept
    47  	Whitelist map[string]string `hcl:"whitelist,optional"`
    48  
    49  	// LogLevel is the level of the logs to put out
    50  	LogLevel string `hcl:"log-level,optional"`
    51  
    52  	// DataDir is the directory to store the state in
    53  	DataDir string `hcl:"data-dir,optional"`
    54  
    55  	// SyncMode selects the sync protocol
    56  	SyncMode string `hcl:"sync-mode,optional"`
    57  
    58  	// GcMode selects the garbage collection mode for the trie
    59  	GcMode string `hcl:"gc-mode,optional"`
    60  
    61  	// XXX
    62  	Snapshot bool `hcl:"snapshot,optional"`
    63  
    64  	// Ethstats is the address of the ethstats server to send telemetry
    65  	Ethstats string `hcl:"ethstats,optional"`
    66  
    67  	// P2P has the p2p network related settings
    68  	P2P *P2PConfig `hcl:"p2p,block"`
    69  
    70  	// Heimdall has the heimdall connection related settings
    71  	Heimdall *HeimdallConfig `hcl:"heimdall,block"`
    72  
    73  	// TxPool has the transaction pool related settings
    74  	TxPool *TxPoolConfig `hcl:"txpool,block"`
    75  
    76  	// Sealer has the validator related settings
    77  	Sealer *SealerConfig `hcl:"sealer,block"`
    78  
    79  	// JsonRPC has the json-rpc related settings
    80  	JsonRPC *JsonRPCConfig `hcl:"jsonrpc,block"`
    81  
    82  	// Gpo has the gas price oracle related settings
    83  	Gpo *GpoConfig `hcl:"gpo,block"`
    84  
    85  	// Telemetry has the telemetry related settings
    86  	Telemetry *TelemetryConfig `hcl:"telemetry,block"`
    87  
    88  	// Cache has the cache related settings
    89  	Cache *CacheConfig `hcl:"cache,block"`
    90  
    91  	// Account has the validator account related settings
    92  	Accounts *AccountsConfig `hcl:"accounts,block"`
    93  
    94  	// GRPC has the grpc server related settings
    95  	GRPC *GRPCConfig
    96  
    97  	// Developer has the developer mode related settings
    98  	Developer *DeveloperConfig
    99  }
   100  
   101  type P2PConfig struct {
   102  	// MaxPeers sets the maximum number of connected peers
   103  	MaxPeers uint64 `hcl:"max-peers,optional"`
   104  
   105  	// MaxPendPeers sets the maximum number of pending connected peers
   106  	MaxPendPeers uint64 `hcl:"max-pend-peers,optional"`
   107  
   108  	// Bind is the bind address
   109  	Bind string `hcl:"bind,optional"`
   110  
   111  	// Port is the port number
   112  	Port uint64 `hcl:"port,optional"`
   113  
   114  	// NoDiscover is used to disable discovery
   115  	NoDiscover bool `hcl:"no-discover,optional"`
   116  
   117  	// NAT it used to set NAT options
   118  	NAT string `hcl:"nat,optional"`
   119  
   120  	// Discovery has the p2p discovery related settings
   121  	Discovery *P2PDiscovery `hcl:"discovery,block"`
   122  }
   123  
   124  type P2PDiscovery struct {
   125  	// V5Enabled is used to enable disc v5 discovery mode
   126  	V5Enabled bool `hcl:"v5-enabled,optional"`
   127  
   128  	// Bootnodes is the list of initial bootnodes
   129  	Bootnodes []string `hcl:"bootnodes,optional"`
   130  
   131  	// BootnodesV4 is the list of initial v4 bootnodes
   132  	BootnodesV4 []string `hcl:"bootnodesv4,optional"`
   133  
   134  	// BootnodesV5 is the list of initial v5 bootnodes
   135  	BootnodesV5 []string `hcl:"bootnodesv5,optional"`
   136  
   137  	// StaticNodes is the list of static nodes
   138  	StaticNodes []string `hcl:"static-nodes,optional"`
   139  
   140  	// TrustedNodes is the list of trusted nodes
   141  	TrustedNodes []string `hcl:"trusted-nodes,optional"`
   142  
   143  	// DNS is the list of enrtree:// URLs which will be queried for nodes to connect to
   144  	DNS []string `hcl:"dns,optional"`
   145  }
   146  
   147  type HeimdallConfig struct {
   148  	// URL is the url of the heimdall server
   149  	URL string `hcl:"url,optional"`
   150  
   151  	// Without is used to disable remote heimdall during testing
   152  	Without bool `hcl:"without,optional"`
   153  }
   154  
   155  type TxPoolConfig struct {
   156  	// Locals are the addresses that should be treated by default as local
   157  	Locals []string `hcl:"locals,optional"`
   158  
   159  	// NoLocals enables whether local transaction handling should be disabled
   160  	NoLocals bool `hcl:"no-locals,optional"`
   161  
   162  	// Journal is the path to store local transactions to survive node restarts
   163  	Journal string `hcl:"journal,optional"`
   164  
   165  	// Rejournal is the time interval to regenerate the local transaction journal
   166  	Rejournal    time.Duration
   167  	RejournalRaw string `hcl:"rejournal,optional"`
   168  
   169  	// PriceLimit is the minimum gas price to enforce for acceptance into the pool
   170  	PriceLimit uint64 `hcl:"price-limit,optional"`
   171  
   172  	// PriceBump is the minimum price bump percentage to replace an already existing transaction (nonce)
   173  	PriceBump uint64 `hcl:"price-bump,optional"`
   174  
   175  	// AccountSlots is the number of executable transaction slots guaranteed per account
   176  	AccountSlots uint64 `hcl:"account-slots,optional"`
   177  
   178  	// GlobalSlots is the maximum number of executable transaction slots for all accounts
   179  	GlobalSlots uint64 `hcl:"global-slots,optional"`
   180  
   181  	// AccountQueue is the maximum number of non-executable transaction slots permitted per account
   182  	AccountQueue uint64 `hcl:"account-queue,optional"`
   183  
   184  	// GlobalQueueis the maximum number of non-executable transaction slots for all accounts
   185  	GlobalQueue uint64 `hcl:"global-queue,optional"`
   186  
   187  	// Lifetime is the maximum amount of time non-executable transaction are queued
   188  	LifeTime    time.Duration
   189  	LifeTimeRaw string `hcl:"lifetime,optional"`
   190  }
   191  
   192  type SealerConfig struct {
   193  	// Enabled is used to enable validator mode
   194  	Enabled bool `hcl:"enabled,optional"`
   195  
   196  	// Etherbase is the address of the validator
   197  	Etherbase string `hcl:"etherbase,optional"`
   198  
   199  	// ExtraData is the block extra data set by the miner
   200  	ExtraData string `hcl:"extra-data,optional"`
   201  
   202  	// GasCeil is the target gas ceiling for mined blocks.
   203  	GasCeil uint64 `hcl:"gas-ceil,optional"`
   204  
   205  	// GasPrice is the minimum gas price for mining a transaction
   206  	GasPrice    *big.Int
   207  	GasPriceRaw string `hcl:"gas-price,optional"`
   208  }
   209  
   210  type JsonRPCConfig struct {
   211  	// IPCDisable enables whether ipc is enabled or not
   212  	IPCDisable bool `hcl:"ipc-disable,optional"`
   213  
   214  	// IPCPath is the path of the ipc endpoint
   215  	IPCPath string `hcl:"ipc-path,optional"`
   216  
   217  	// VHost is the list of valid virtual hosts
   218  	VHost []string `hcl:"vhost,optional"`
   219  
   220  	// Cors is the list of Cors endpoints
   221  	Cors []string `hcl:"cors,optional"`
   222  
   223  	// GasCap is the global gas cap for eth-call variants.
   224  	GasCap uint64 `hcl:"gas-cap,optional"`
   225  
   226  	// TxFeeCap is the global transaction fee cap for send-transaction variants
   227  	TxFeeCap float64 `hcl:"tx-fee-cap,optional"`
   228  
   229  	// Http has the json-rpc http related settings
   230  	Http *APIConfig `hcl:"http,block"`
   231  
   232  	// Http has the json-rpc websocket related settings
   233  	Ws *APIConfig `hcl:"ws,block"`
   234  
   235  	// Http has the json-rpc graphql related settings
   236  	Graphql *APIConfig `hcl:"graphql,block"`
   237  }
   238  
   239  type GRPCConfig struct {
   240  	// Addr is the bind address for the grpc rpc server
   241  	Addr string
   242  }
   243  
   244  type APIConfig struct {
   245  	// Enabled selects whether the api is enabled
   246  	Enabled bool `hcl:"enabled,optional"`
   247  
   248  	// Port is the port number for this api
   249  	Port uint64 `hcl:"port,optional"`
   250  
   251  	// Prefix is the http prefix to expose this api
   252  	Prefix string `hcl:"prefix,optional"`
   253  
   254  	// Host is the address to bind the api
   255  	Host string `hcl:"host,optional"`
   256  
   257  	// Modules is the list of enabled api modules
   258  	Modules []string `hcl:"modules,optional"`
   259  }
   260  
   261  type GpoConfig struct {
   262  	// Blocks is the number of blocks to track to compute the price oracle
   263  	Blocks uint64 `hcl:"blocks,optional"`
   264  
   265  	// Percentile sets the weights to new blocks
   266  	Percentile uint64 `hcl:"percentile,optional"`
   267  
   268  	// MaxPrice is an upper bound gas price
   269  	MaxPrice    *big.Int
   270  	MaxPriceRaw string `hcl:"max-price,optional"`
   271  
   272  	// IgnorePrice is a lower bound gas price
   273  	IgnorePrice    *big.Int
   274  	IgnorePriceRaw string `hcl:"ignore-price,optional"`
   275  }
   276  
   277  type TelemetryConfig struct {
   278  	// Enabled enables metrics
   279  	Enabled bool `hcl:"enabled,optional"`
   280  
   281  	// Expensive enables expensive metrics
   282  	Expensive bool `hcl:"expensive,optional"`
   283  
   284  	// InfluxDB has the influxdb related settings
   285  	InfluxDB *InfluxDBConfig `hcl:"influx,block"`
   286  
   287  	// Prometheus Address
   288  	PrometheusAddr string `hcl:"prometheus-addr,optional"`
   289  
   290  	// Open collector endpoint
   291  	OpenCollectorEndpoint string `hcl:"opencollector-endpoint,optional"`
   292  }
   293  
   294  type InfluxDBConfig struct {
   295  	// V1Enabled enables influx v1 mode
   296  	V1Enabled bool `hcl:"v1-enabled,optional"`
   297  
   298  	// Endpoint is the url endpoint of the influxdb service
   299  	Endpoint string `hcl:"endpoint,optional"`
   300  
   301  	// Database is the name of the database in Influxdb to store the metrics.
   302  	Database string `hcl:"database,optional"`
   303  
   304  	// Enabled is the username to authorize access to Influxdb
   305  	Username string `hcl:"username,optional"`
   306  
   307  	// Password is the password to authorize access to Influxdb
   308  	Password string `hcl:"password,optional"`
   309  
   310  	// Tags are tags attaches to all generated metrics
   311  	Tags map[string]string `hcl:"tags,optional"`
   312  
   313  	// Enabled enables influx v2 mode
   314  	V2Enabled bool `hcl:"v2-enabled,optional"`
   315  
   316  	// Token is the token to authorize access to Influxdb V2.
   317  	Token string `hcl:"token,optional"`
   318  
   319  	// Bucket is the bucket to store metrics in Influxdb V2.
   320  	Bucket string `hcl:"bucket,optional"`
   321  
   322  	// Organization is the name of the organization for Influxdb V2.
   323  	Organization string `hcl:"organization,optional"`
   324  }
   325  
   326  type CacheConfig struct {
   327  	// Cache is the amount of cache of the node
   328  	Cache uint64 `hcl:"cache,optional"`
   329  
   330  	// PercGc is percentage of cache used for garbage collection
   331  	PercGc uint64 `hcl:"perc-gc,optional"`
   332  
   333  	// PercSnapshot is percentage of cache used for snapshots
   334  	PercSnapshot uint64 `hcl:"perc-snapshot,optional"`
   335  
   336  	// PercDatabase is percentage of cache used for the database
   337  	PercDatabase uint64 `hcl:"perc-database,optional"`
   338  
   339  	// PercTrie is percentage of cache used for the trie
   340  	PercTrie uint64 `hcl:"perc-trie,optional"`
   341  
   342  	// Journal is the disk journal directory for trie cache to survive node restarts
   343  	Journal string `hcl:"journal,optional"`
   344  
   345  	// Rejournal is the time interval to regenerate the journal for clean cache
   346  	Rejournal    time.Duration
   347  	RejournalRaw string `hcl:"rejournal,optional"`
   348  
   349  	// NoPrefetch is used to disable prefetch of tries
   350  	NoPrefetch bool `hcl:"no-prefetch,optional"`
   351  
   352  	// Preimages is used to enable the track of hash preimages
   353  	Preimages bool `hcl:"preimages,optional"`
   354  
   355  	// TxLookupLimit sets the maximum number of blocks from head whose tx indices are reserved.
   356  	TxLookupLimit uint64 `hcl:"tx-lookup-limit,optional"`
   357  }
   358  
   359  type AccountsConfig struct {
   360  	// Unlock is the list of addresses to unlock in the node
   361  	Unlock []string `hcl:"unlock,optional"`
   362  
   363  	// PasswordFile is the file where the account passwords are stored
   364  	PasswordFile string `hcl:"password-file,optional"`
   365  
   366  	// AllowInsecureUnlock allows user to unlock accounts in unsafe http environment.
   367  	AllowInsecureUnlock bool `hcl:"allow-insecure-unlock,optional"`
   368  
   369  	// UseLightweightKDF enables a faster but less secure encryption of accounts
   370  	UseLightweightKDF bool `hcl:"use-lightweight-kdf,optional"`
   371  }
   372  
   373  type DeveloperConfig struct {
   374  	// Enabled enables the developer mode
   375  	Enabled bool `hcl:"dev,optional"`
   376  
   377  	// Period is the block period to use in developer mode
   378  	Period uint64 `hcl:"period,optional"`
   379  }
   380  
   381  func DefaultConfig() *Config {
   382  	return &Config{
   383  		Chain:     "mainnet",
   384  		Name:      Hostname(),
   385  		Whitelist: map[string]string{},
   386  		LogLevel:  "INFO",
   387  		DataDir:   defaultDataDir(),
   388  		P2P: &P2PConfig{
   389  			MaxPeers:     30,
   390  			MaxPendPeers: 50,
   391  			Bind:         "0.0.0.0",
   392  			Port:         30303,
   393  			NoDiscover:   false,
   394  			NAT:          "any",
   395  			Discovery: &P2PDiscovery{
   396  				V5Enabled:    false,
   397  				Bootnodes:    []string{},
   398  				BootnodesV4:  []string{},
   399  				BootnodesV5:  []string{},
   400  				StaticNodes:  []string{},
   401  				TrustedNodes: []string{},
   402  				DNS:          []string{},
   403  			},
   404  		},
   405  		Heimdall: &HeimdallConfig{
   406  			URL:     "http://localhost:1317",
   407  			Without: false,
   408  		},
   409  		SyncMode: "full",
   410  		GcMode:   "full",
   411  		Snapshot: true,
   412  		TxPool: &TxPoolConfig{
   413  			Locals:       []string{},
   414  			NoLocals:     false,
   415  			Journal:      "",
   416  			Rejournal:    time.Duration(1 * time.Hour),
   417  			PriceLimit:   1,
   418  			PriceBump:    10,
   419  			AccountSlots: 16,
   420  			GlobalSlots:  4096,
   421  			AccountQueue: 64,
   422  			GlobalQueue:  1024,
   423  			LifeTime:     time.Duration(3 * time.Hour),
   424  		},
   425  		Sealer: &SealerConfig{
   426  			Enabled:   false,
   427  			Etherbase: "",
   428  			GasCeil:   8000000,
   429  			GasPrice:  big.NewInt(params.GWei),
   430  			ExtraData: "",
   431  		},
   432  		Gpo: &GpoConfig{
   433  			Blocks:      20,
   434  			Percentile:  60,
   435  			MaxPrice:    gasprice.DefaultMaxPrice,
   436  			IgnorePrice: gasprice.DefaultIgnorePrice,
   437  		},
   438  		JsonRPC: &JsonRPCConfig{
   439  			IPCDisable: false,
   440  			IPCPath:    "",
   441  			Cors:       []string{"*"},
   442  			VHost:      []string{"*"},
   443  			GasCap:     ethconfig.Defaults.RPCGasCap,
   444  			TxFeeCap:   ethconfig.Defaults.RPCTxFeeCap,
   445  			Http: &APIConfig{
   446  				Enabled: false,
   447  				Port:    8545,
   448  				Prefix:  "",
   449  				Host:    "localhost",
   450  				Modules: []string{"web3", "net"},
   451  			},
   452  			Ws: &APIConfig{
   453  				Enabled: false,
   454  				Port:    8546,
   455  				Prefix:  "",
   456  				Host:    "localhost",
   457  				Modules: []string{"web3", "net"},
   458  			},
   459  			Graphql: &APIConfig{
   460  				Enabled: false,
   461  			},
   462  		},
   463  		Ethstats: "",
   464  		Telemetry: &TelemetryConfig{
   465  			Enabled:               false,
   466  			Expensive:             false,
   467  			PrometheusAddr:        "",
   468  			OpenCollectorEndpoint: "",
   469  			InfluxDB: &InfluxDBConfig{
   470  				V1Enabled:    false,
   471  				Endpoint:     "",
   472  				Database:     "",
   473  				Username:     "",
   474  				Password:     "",
   475  				Tags:         map[string]string{},
   476  				V2Enabled:    false,
   477  				Token:        "",
   478  				Bucket:       "",
   479  				Organization: "",
   480  			},
   481  		},
   482  		Cache: &CacheConfig{
   483  			Cache:         1024,
   484  			PercDatabase:  50,
   485  			PercTrie:      15,
   486  			PercGc:        25,
   487  			PercSnapshot:  10,
   488  			Journal:       "triecache",
   489  			Rejournal:     60 * time.Minute,
   490  			NoPrefetch:    false,
   491  			Preimages:     false,
   492  			TxLookupLimit: 2350000,
   493  		},
   494  		Accounts: &AccountsConfig{
   495  			Unlock:              []string{},
   496  			PasswordFile:        "",
   497  			AllowInsecureUnlock: false,
   498  			UseLightweightKDF:   false,
   499  		},
   500  		GRPC: &GRPCConfig{
   501  			Addr: ":3131",
   502  		},
   503  		Developer: &DeveloperConfig{
   504  			Enabled: false,
   505  			Period:  0,
   506  		},
   507  	}
   508  }
   509  
   510  func (c *Config) fillBigInt() error {
   511  	tds := []struct {
   512  		path string
   513  		td   **big.Int
   514  		str  *string
   515  	}{
   516  		{"gpo.maxprice", &c.Gpo.MaxPrice, &c.Gpo.MaxPriceRaw},
   517  		{"gpo.ignoreprice", &c.Gpo.IgnorePrice, &c.Gpo.IgnorePriceRaw},
   518  		{"sealer.gasprice", &c.Sealer.GasPrice, &c.Sealer.GasPriceRaw},
   519  	}
   520  
   521  	for _, x := range tds {
   522  		if *x.str != "" {
   523  			b := new(big.Int)
   524  
   525  			var ok bool
   526  			if strings.HasPrefix(*x.str, "0x") {
   527  				b, ok = b.SetString((*x.str)[2:], 16)
   528  			} else {
   529  				b, ok = b.SetString(*x.str, 10)
   530  			}
   531  			if !ok {
   532  				return fmt.Errorf("%s can't parse big int %s", x.path, *x.str)
   533  			}
   534  			*x.str = ""
   535  			*x.td = b
   536  		}
   537  	}
   538  	return nil
   539  }
   540  
   541  func (c *Config) fillTimeDurations() error {
   542  	tds := []struct {
   543  		path string
   544  		td   *time.Duration
   545  		str  *string
   546  	}{
   547  		{"txpool.lifetime", &c.TxPool.LifeTime, &c.TxPool.LifeTimeRaw},
   548  		{"txpool.rejournal", &c.TxPool.Rejournal, &c.TxPool.RejournalRaw},
   549  		{"cache.rejournal", &c.Cache.Rejournal, &c.Cache.RejournalRaw},
   550  	}
   551  
   552  	for _, x := range tds {
   553  		if x.td != nil && x.str != nil && *x.str != "" {
   554  			d, err := time.ParseDuration(*x.str)
   555  			if err != nil {
   556  				return fmt.Errorf("%s can't parse time duration %s", x.path, *x.str)
   557  			}
   558  			*x.str = ""
   559  			*x.td = d
   560  		}
   561  	}
   562  	return nil
   563  }
   564  
   565  func readConfigFile(path string) (*Config, error) {
   566  	ext := filepath.Ext(path)
   567  	if ext == ".toml" {
   568  		// read file and apply the legacy config
   569  		data, err := ioutil.ReadFile(path)
   570  		if err != nil {
   571  			return nil, err
   572  		}
   573  		return readLegacyConfig(data)
   574  	}
   575  
   576  	config := &Config{
   577  		TxPool: &TxPoolConfig{},
   578  		Cache:  &CacheConfig{},
   579  		Sealer: &SealerConfig{},
   580  	}
   581  	if err := hclsimple.DecodeFile(path, nil, config); err != nil {
   582  		return nil, fmt.Errorf("failed to decode config file '%s': %v", path, err)
   583  	}
   584  	if err := config.fillBigInt(); err != nil {
   585  		return nil, err
   586  	}
   587  	if err := config.fillTimeDurations(); err != nil {
   588  		return nil, err
   589  	}
   590  	return config, nil
   591  }
   592  
   593  func (c *Config) loadChain() error {
   594  	if c.Developer.Enabled {
   595  		return nil
   596  	}
   597  	chain, ok := chains.GetChain(c.Chain)
   598  	if !ok {
   599  		return fmt.Errorf("chain '%s' not found", c.Chain)
   600  	}
   601  	c.chain = chain
   602  
   603  	// preload some default values that depend on the chain file
   604  	if c.P2P.Discovery.DNS == nil {
   605  		c.P2P.Discovery.DNS = c.chain.DNS
   606  	}
   607  
   608  	// depending on the chain we have different cache values
   609  	if c.Chain == "mainnet" {
   610  		c.Cache.Cache = 4096
   611  	} else {
   612  		c.Cache.Cache = 1024
   613  	}
   614  	return nil
   615  }
   616  
   617  func (c *Config) buildEth(stack *node.Node) (*ethconfig.Config, error) {
   618  	dbHandles, err := makeDatabaseHandles()
   619  	if err != nil {
   620  		return nil, err
   621  	}
   622  	n := ethconfig.Defaults
   623  
   624  	// only update for non-developer mode as we don't yet
   625  	// have the chain object for it.
   626  	if !c.Developer.Enabled {
   627  		n.NetworkId = c.chain.NetworkId
   628  		n.Genesis = c.chain.Genesis
   629  	}
   630  	n.HeimdallURL = c.Heimdall.URL
   631  	n.WithoutHeimdall = c.Heimdall.Without
   632  
   633  	// gas price oracle
   634  	{
   635  		n.GPO.Blocks = int(c.Gpo.Blocks)
   636  		n.GPO.Percentile = int(c.Gpo.Percentile)
   637  		n.GPO.MaxPrice = c.Gpo.MaxPrice
   638  		n.GPO.IgnorePrice = c.Gpo.IgnorePrice
   639  	}
   640  
   641  	// txpool options
   642  	{
   643  		n.TxPool.NoLocals = c.TxPool.NoLocals
   644  		n.TxPool.Journal = c.TxPool.Journal
   645  		n.TxPool.Rejournal = c.TxPool.Rejournal
   646  		n.TxPool.PriceLimit = c.TxPool.PriceLimit
   647  		n.TxPool.PriceBump = c.TxPool.PriceBump
   648  		n.TxPool.AccountSlots = c.TxPool.AccountSlots
   649  		n.TxPool.GlobalSlots = c.TxPool.GlobalSlots
   650  		n.TxPool.AccountQueue = c.TxPool.AccountQueue
   651  		n.TxPool.GlobalQueue = c.TxPool.GlobalQueue
   652  		n.TxPool.Lifetime = c.TxPool.LifeTime
   653  	}
   654  
   655  	// miner options
   656  	{
   657  		n.Miner.GasPrice = c.Sealer.GasPrice
   658  		n.Miner.GasCeil = c.Sealer.GasCeil
   659  		n.Miner.ExtraData = []byte(c.Sealer.ExtraData)
   660  
   661  		if etherbase := c.Sealer.Etherbase; etherbase != "" {
   662  			if !common.IsHexAddress(etherbase) {
   663  				return nil, fmt.Errorf("etherbase is not an address: %s", etherbase)
   664  			}
   665  			n.Miner.Etherbase = common.HexToAddress(etherbase)
   666  		}
   667  	}
   668  
   669  	// update for developer mode
   670  	if c.Developer.Enabled {
   671  		// Get a keystore
   672  		var ks *keystore.KeyStore
   673  		if keystores := stack.AccountManager().Backends(keystore.KeyStoreType); len(keystores) > 0 {
   674  			ks = keystores[0].(*keystore.KeyStore)
   675  		}
   676  
   677  		// Create new developer account or reuse existing one
   678  		var (
   679  			developer  accounts.Account
   680  			passphrase string
   681  			err        error
   682  		)
   683  		// etherbase has been set above, configuring the miner address from command line flags.
   684  		if n.Miner.Etherbase != (common.Address{}) {
   685  			developer = accounts.Account{Address: n.Miner.Etherbase}
   686  		} else if accs := ks.Accounts(); len(accs) > 0 {
   687  			developer = ks.Accounts()[0]
   688  		} else {
   689  			developer, err = ks.NewAccount(passphrase)
   690  			if err != nil {
   691  				return nil, fmt.Errorf("failed to create developer account: %v", err)
   692  			}
   693  		}
   694  		if err := ks.Unlock(developer, passphrase); err != nil {
   695  			return nil, fmt.Errorf("failed to unlock developer account: %v", err)
   696  		}
   697  		log.Info("Using developer account", "address", developer.Address)
   698  
   699  		// get developer mode chain config
   700  		c.chain = chains.GetDeveloperChain(c.Developer.Period, developer.Address)
   701  
   702  		// update the parameters
   703  		n.NetworkId = c.chain.NetworkId
   704  		n.Genesis = c.chain.Genesis
   705  
   706  		// Update cache
   707  		c.Cache.Cache = 1024
   708  
   709  		// Update sync mode
   710  		c.SyncMode = "full"
   711  
   712  		// update miner gas price
   713  		if n.Miner.GasPrice == nil {
   714  			n.Miner.GasPrice = big.NewInt(1)
   715  		}
   716  	}
   717  
   718  	// discovery (this params should be in node.Config)
   719  	{
   720  		n.EthDiscoveryURLs = c.P2P.Discovery.DNS
   721  		n.SnapDiscoveryURLs = c.P2P.Discovery.DNS
   722  	}
   723  
   724  	// whitelist
   725  	{
   726  		n.Whitelist = map[uint64]common.Hash{}
   727  		for k, v := range c.Whitelist {
   728  			number, err := strconv.ParseUint(k, 0, 64)
   729  			if err != nil {
   730  				return nil, fmt.Errorf("invalid whitelist block number %s: %v", k, err)
   731  			}
   732  			var hash common.Hash
   733  			if err = hash.UnmarshalText([]byte(v)); err != nil {
   734  				return nil, fmt.Errorf("invalid whitelist hash %s: %v", v, err)
   735  			}
   736  			n.Whitelist[number] = hash
   737  		}
   738  	}
   739  
   740  	// cache
   741  	{
   742  		cache := c.Cache.Cache
   743  		calcPerc := func(val uint64) int {
   744  			return int(cache * (val) / 100)
   745  		}
   746  
   747  		// Cap the cache allowance
   748  		mem, err := gopsutil.VirtualMemory()
   749  		if err == nil {
   750  			if 32<<(^uintptr(0)>>63) == 32 && mem.Total > 2*1024*1024*1024 {
   751  				log.Warn("Lowering memory allowance on 32bit arch", "available", mem.Total/1024/1024, "addressable", 2*1024)
   752  				mem.Total = 2 * 1024 * 1024 * 1024
   753  			}
   754  			allowance := uint64(mem.Total / 1024 / 1024 / 3)
   755  			if cache > allowance {
   756  				log.Warn("Sanitizing cache to Go's GC limits", "provided", cache, "updated", allowance)
   757  				cache = allowance
   758  			}
   759  		}
   760  		// Tune the garbage collector
   761  		gogc := math.Max(20, math.Min(100, 100/(float64(cache)/1024)))
   762  
   763  		log.Debug("Sanitizing Go's GC trigger", "percent", int(gogc))
   764  		godebug.SetGCPercent(int(gogc))
   765  
   766  		n.TrieCleanCacheJournal = c.Cache.Journal
   767  		n.TrieCleanCacheRejournal = c.Cache.Rejournal
   768  		n.DatabaseCache = calcPerc(c.Cache.PercDatabase)
   769  		n.SnapshotCache = calcPerc(c.Cache.PercSnapshot)
   770  		n.TrieCleanCache = calcPerc(c.Cache.PercTrie)
   771  		n.TrieDirtyCache = calcPerc(c.Cache.PercGc)
   772  		n.NoPrefetch = c.Cache.NoPrefetch
   773  		n.Preimages = c.Cache.Preimages
   774  		n.TxLookupLimit = c.Cache.TxLookupLimit
   775  	}
   776  
   777  	n.RPCGasCap = c.JsonRPC.GasCap
   778  	if n.RPCGasCap != 0 {
   779  		log.Info("Set global gas cap", "cap", n.RPCGasCap)
   780  	} else {
   781  		log.Info("Global gas cap disabled")
   782  	}
   783  	n.RPCTxFeeCap = c.JsonRPC.TxFeeCap
   784  
   785  	// sync mode. It can either be "fast", "full" or "snap". We disable
   786  	// for now the "light" mode.
   787  	switch c.SyncMode {
   788  	case "fast":
   789  		n.SyncMode = downloader.FastSync
   790  	case "full":
   791  		n.SyncMode = downloader.FullSync
   792  	case "snap":
   793  		n.SyncMode = downloader.SnapSync
   794  	default:
   795  		return nil, fmt.Errorf("sync mode '%s' not found", c.SyncMode)
   796  	}
   797  
   798  	// archive mode. It can either be "archive" or "full".
   799  	switch c.GcMode {
   800  	case "full":
   801  		n.NoPruning = false
   802  	case "archive":
   803  		n.NoPruning = true
   804  		if !n.Preimages {
   805  			n.Preimages = true
   806  			log.Info("Enabling recording of key preimages since archive mode is used")
   807  		}
   808  	default:
   809  		return nil, fmt.Errorf("gcmode '%s' not found", c.GcMode)
   810  	}
   811  
   812  	// snapshot disable check
   813  	if c.Snapshot {
   814  		if n.SyncMode == downloader.SnapSync {
   815  			log.Info("Snap sync requested, enabling --snapshot")
   816  		} else {
   817  			// disable snapshot
   818  			n.TrieCleanCache += n.SnapshotCache
   819  			n.SnapshotCache = 0
   820  		}
   821  	}
   822  
   823  	n.DatabaseHandles = dbHandles
   824  	return &n, nil
   825  }
   826  
   827  var (
   828  	clientIdentifier = "bor"
   829  	gitCommit        = "" // Git SHA1 commit hash of the release (set via linker flags)
   830  	gitDate          = "" // Git commit date YYYYMMDD of the release (set via linker flags)
   831  )
   832  
   833  func (c *Config) buildNode() (*node.Config, error) {
   834  	ipcPath := ""
   835  	if !c.JsonRPC.IPCDisable {
   836  		ipcPath = clientIdentifier + ".ipc"
   837  		if c.JsonRPC.IPCPath != "" {
   838  			ipcPath = c.JsonRPC.IPCPath
   839  		}
   840  	}
   841  
   842  	cfg := &node.Config{
   843  		Name:                  clientIdentifier,
   844  		DataDir:               c.DataDir,
   845  		UseLightweightKDF:     c.Accounts.UseLightweightKDF,
   846  		InsecureUnlockAllowed: c.Accounts.AllowInsecureUnlock,
   847  		Version:               params.VersionWithCommit(gitCommit, gitDate),
   848  		IPCPath:               ipcPath,
   849  		P2P: p2p.Config{
   850  			MaxPeers:        int(c.P2P.MaxPeers),
   851  			MaxPendingPeers: int(c.P2P.MaxPendPeers),
   852  			ListenAddr:      c.P2P.Bind + ":" + strconv.Itoa(int(c.P2P.Port)),
   853  			DiscoveryV5:     c.P2P.Discovery.V5Enabled,
   854  		},
   855  		HTTPModules:         c.JsonRPC.Http.Modules,
   856  		HTTPCors:            c.JsonRPC.Cors,
   857  		HTTPVirtualHosts:    c.JsonRPC.VHost,
   858  		HTTPPathPrefix:      c.JsonRPC.Http.Prefix,
   859  		WSModules:           c.JsonRPC.Ws.Modules,
   860  		WSOrigins:           c.JsonRPC.Cors,
   861  		WSPathPrefix:        c.JsonRPC.Ws.Prefix,
   862  		GraphQLCors:         c.JsonRPC.Cors,
   863  		GraphQLVirtualHosts: c.JsonRPC.VHost,
   864  	}
   865  
   866  	// dev mode
   867  	if c.Developer.Enabled {
   868  		cfg.UseLightweightKDF = true
   869  
   870  		// disable p2p networking
   871  		c.P2P.NoDiscover = true
   872  		cfg.P2P.ListenAddr = ""
   873  		cfg.P2P.NoDial = true
   874  		cfg.P2P.DiscoveryV5 = false
   875  	}
   876  
   877  	// enable jsonrpc endpoints
   878  	{
   879  		if c.JsonRPC.Http.Enabled {
   880  			cfg.HTTPHost = c.JsonRPC.Http.Host
   881  			cfg.HTTPPort = int(c.JsonRPC.Http.Port)
   882  		}
   883  		if c.JsonRPC.Ws.Enabled {
   884  			cfg.WSHost = c.JsonRPC.Ws.Host
   885  			cfg.WSPort = int(c.JsonRPC.Ws.Port)
   886  		}
   887  	}
   888  
   889  	natif, err := nat.Parse(c.P2P.NAT)
   890  	if err != nil {
   891  		return nil, fmt.Errorf("wrong 'nat' flag: %v", err)
   892  	}
   893  	cfg.P2P.NAT = natif
   894  
   895  	// only check for non-developer modes
   896  	if !c.Developer.Enabled {
   897  		// Discovery
   898  		// if no bootnodes are defined, use the ones from the chain file.
   899  		bootnodes := c.P2P.Discovery.Bootnodes
   900  		if len(bootnodes) == 0 {
   901  			bootnodes = c.chain.Bootnodes
   902  		}
   903  		if cfg.P2P.BootstrapNodes, err = parseBootnodes(bootnodes); err != nil {
   904  			return nil, err
   905  		}
   906  		if cfg.P2P.BootstrapNodesV5, err = parseBootnodes(c.P2P.Discovery.BootnodesV5); err != nil {
   907  			return nil, err
   908  		}
   909  		if cfg.P2P.StaticNodes, err = parseBootnodes(c.P2P.Discovery.StaticNodes); err != nil {
   910  			return nil, err
   911  		}
   912  		if cfg.P2P.TrustedNodes, err = parseBootnodes(c.P2P.Discovery.TrustedNodes); err != nil {
   913  			return nil, err
   914  		}
   915  	}
   916  
   917  	if c.P2P.NoDiscover {
   918  		// Disable networking, for now, we will not even allow incomming connections
   919  		cfg.P2P.MaxPeers = 0
   920  		cfg.P2P.NoDiscovery = true
   921  	}
   922  	return cfg, nil
   923  }
   924  
   925  func (c *Config) Merge(cc ...*Config) error {
   926  	for _, elem := range cc {
   927  		if err := mergo.Merge(c, elem, mergo.WithOverride, mergo.WithAppendSlice); err != nil {
   928  			return fmt.Errorf("failed to merge configurations: %v", err)
   929  		}
   930  	}
   931  	return nil
   932  }
   933  
   934  func makeDatabaseHandles() (int, error) {
   935  	limit, err := fdlimit.Maximum()
   936  	if err != nil {
   937  		return -1, err
   938  	}
   939  	raised, err := fdlimit.Raise(uint64(limit))
   940  	if err != nil {
   941  		return -1, err
   942  	}
   943  	return int(raised / 2), nil
   944  }
   945  
   946  func parseBootnodes(urls []string) ([]*enode.Node, error) {
   947  	dst := []*enode.Node{}
   948  	for _, url := range urls {
   949  		if url != "" {
   950  			node, err := enode.Parse(enode.ValidSchemes, url)
   951  			if err != nil {
   952  				return nil, fmt.Errorf("invalid bootstrap url '%s': %v", url, err)
   953  			}
   954  			dst = append(dst, node)
   955  		}
   956  	}
   957  	return dst, nil
   958  }
   959  
   960  func defaultDataDir() string {
   961  	// Try to place the data folder in the user's home dir
   962  	home, _ := homedir.Dir()
   963  	if home == "" {
   964  		// we cannot guess a stable location
   965  		return ""
   966  	}
   967  	switch runtime.GOOS {
   968  	case "darwin":
   969  		return filepath.Join(home, "Library", "Bor")
   970  	case "windows":
   971  		appdata := os.Getenv("LOCALAPPDATA")
   972  		if appdata == "" {
   973  			// Windows XP and below don't have LocalAppData.
   974  			panic("environment variable LocalAppData is undefined")
   975  		}
   976  		return filepath.Join(appdata, "Bor")
   977  	default:
   978  		return filepath.Join(home, ".bor")
   979  	}
   980  }
   981  
   982  func Hostname() string {
   983  	hostname, err := os.Hostname()
   984  	if err != nil {
   985  		return "bor"
   986  	}
   987  	return hostname
   988  }