2015-07-07 03:54:22 +03:00
// Copyright 2015 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
2015-07-22 19:48:40 +03:00
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2015-07-07 03:54:22 +03:00
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
2015-07-22 19:48:40 +03:00
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
2015-07-07 03:54:22 +03:00
2015-03-06 04:00:41 +02:00
package utils
import (
"crypto/ecdsa"
2015-04-20 18:45:37 +03:00
"fmt"
2015-11-17 18:33:25 +02:00
"io/ioutil"
2015-10-07 18:21:13 +03:00
"math"
2015-05-09 13:00:51 +03:00
"math/big"
2016-03-24 14:06:10 +02:00
"math/rand"
2015-03-10 00:00:27 +02:00
"os"
2015-05-12 15:24:11 +03:00
"path/filepath"
2015-05-12 16:20:53 +03:00
"runtime"
2015-07-07 11:32:05 +03:00
"strconv"
2015-11-17 18:33:25 +02:00
"strings"
2016-03-24 14:06:10 +02:00
"time"
2015-05-12 15:24:11 +03:00
2015-04-13 11:13:52 +03:00
"github.com/ethereum/ethash"
2015-03-07 13:39:52 +02:00
"github.com/ethereum/go-ethereum/accounts"
2015-03-18 09:44:58 +02:00
"github.com/ethereum/go-ethereum/common"
2015-03-06 04:00:41 +02:00
"github.com/ethereum/go-ethereum/core"
2015-11-17 18:33:25 +02:00
"github.com/ethereum/go-ethereum/core/state"
2015-03-06 04:00:41 +02:00
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
2015-04-03 18:09:01 +03:00
"github.com/ethereum/go-ethereum/logger/glog"
2015-08-07 10:56:49 +03:00
"github.com/ethereum/go-ethereum/metrics"
2015-11-17 18:33:25 +02:00
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p/discover"
2015-03-06 04:00:41 +02:00
"github.com/ethereum/go-ethereum/p2p/nat"
2015-10-07 18:21:13 +03:00
"github.com/ethereum/go-ethereum/params"
2016-04-21 12:14:57 +03:00
"github.com/ethereum/go-ethereum/pow"
2015-12-16 11:58:01 +02:00
"github.com/ethereum/go-ethereum/rpc"
2015-11-17 18:33:25 +02:00
"github.com/ethereum/go-ethereum/whisper"
2016-06-09 12:44:42 +03:00
"gopkg.in/urfave/cli.v1"
2015-03-06 04:00:41 +02:00
)
2015-03-10 17:44:48 +02:00
func init ( ) {
cli . AppHelpTemplate = ` { { . Name } } { { if . Flags } } [ global options ] { { end } } command { { if . Flags } } [ command options ] { { end } } [ arguments ... ]
VERSION :
{ { . Version } }
COMMANDS :
{ { range . Commands } } { { . Name } } { { with . ShortName } } , { { . } } { { end } } { { "\t" } } { { . Usage } }
{ { end } } { { if . Flags } }
GLOBAL OPTIONS :
{ { range . Flags } } { { . } }
{ { end } } { { end } }
`
cli . CommandHelpTemplate = ` { { . Name } } { { if . Subcommands } } command { { end } } { { if . Flags } } [ command options ] { { end } } [ arguments ... ]
{ { if . Description } } { { . Description } }
{ { end } } { { if . Subcommands } }
SUBCOMMANDS :
{ { range . Subcommands } } { { . Name } } { { with . ShortName } } , { { . } } { { end } } { { "\t" } } { { . Usage } }
{ { end } } { { end } } { { if . Flags } }
OPTIONS :
{ { range . Flags } } { { . } }
{ { end } } { { end } }
`
}
2015-03-09 23:51:50 +02:00
// NewApp creates an app with sane defaults.
2016-09-05 14:08:41 +03:00
func NewApp ( gitCommit , usage string ) * cli . App {
2015-03-09 23:51:50 +02:00
app := cli . NewApp ( )
2015-05-12 15:24:11 +03:00
app . Name = filepath . Base ( os . Args [ 0 ] )
2015-03-09 23:51:50 +02:00
app . Author = ""
2015-03-26 02:03:03 +02:00
//app.Authors = nil
2015-03-09 23:51:50 +02:00
app . Email = ""
2016-09-05 14:08:41 +03:00
app . Version = Version
if gitCommit != "" {
app . Version += "-" + gitCommit [ : 8 ]
}
2015-03-09 23:51:50 +02:00
app . Usage = usage
return app
}
2015-03-06 04:00:41 +02:00
// These are all the command line flags we support.
// If you add to this list, please remember to include the
// flag in the appropriate command definition.
//
// The flags are defined here so their names and help texts
// are the same for all commands.
var (
// General settings
2015-04-08 16:43:55 +03:00
DataDirFlag = DirectoryFlag {
2015-03-06 04:00:41 +02:00
Name : "datadir" ,
2015-10-29 20:53:24 +03:00
Usage : "Data directory for the databases and keystore" ,
2016-09-16 12:53:50 +03:00
Value : DirectoryString { node . DefaultDataDir ( ) } ,
2015-03-06 04:00:41 +02:00
}
2016-03-08 00:38:56 +02:00
KeyStoreDirFlag = DirectoryFlag {
Name : "keystore" ,
Usage : "Directory for the keystore (default = inside the datadir)" ,
}
2015-03-18 09:44:58 +02:00
NetworkIdFlag = cli . IntFlag {
Name : "networkid" ,
2015-10-29 20:53:24 +03:00
Usage : "Network identifier (integer, 0=Olympic, 1=Frontier, 2=Morden)" ,
2015-03-18 09:44:58 +02:00
Value : eth . NetworkId ,
}
2015-10-29 20:53:24 +03:00
OlympicFlag = cli . BoolFlag {
Name : "olympic" ,
Usage : "Olympic network: pre-configured pre-release test network" ,
2015-06-08 13:12:13 +03:00
}
2015-10-29 20:53:24 +03:00
TestNetFlag = cli . BoolFlag {
Name : "testnet" ,
Usage : "Morden network: pre-configured test network with modified starting nonces (replay protection)" ,
2015-07-10 15:29:40 +03:00
}
2015-09-06 16:46:54 +03:00
DevModeFlag = cli . BoolFlag {
Name : "dev" ,
2015-10-29 20:53:24 +03:00
Usage : "Developer mode: pre-configured private network with several debugging flags" ,
2015-09-06 16:46:54 +03:00
}
2015-04-19 00:53:30 +03:00
IdentityFlag = cli . StringFlag {
Name : "identity" ,
2015-04-22 02:41:34 +03:00
Usage : "Custom node name" ,
2015-04-19 00:53:30 +03:00
}
2015-04-08 14:22:31 +03:00
NatspecEnabledFlag = cli . BoolFlag {
Name : "natspec" ,
Usage : "Enable NatSpec confirmation notice" ,
}
2015-10-27 00:24:09 +03:00
DocRootFlag = DirectoryFlag {
Name : "docroot" ,
Usage : "Document Root for HTTPClient file scheme" ,
2016-09-16 12:53:50 +03:00
Value : DirectoryString { homeDir ( ) } ,
2015-10-27 00:24:09 +03:00
}
2015-10-09 18:36:31 +03:00
FastSyncFlag = cli . BoolFlag {
Name : "fast" ,
2015-11-10 15:47:19 +02:00
Usage : "Enable fast syncing through state downloads" ,
2015-07-02 19:55:18 +03:00
}
2015-10-23 17:49:36 +03:00
LightKDFFlag = cli . BoolFlag {
Name : "lightkdf" ,
2015-11-10 15:47:19 +02:00
Usage : "Reduce key-derivation RAM & CPU usage at some expense of KDF strength" ,
2015-10-23 17:49:36 +03:00
}
2016-10-19 14:55:13 +03:00
// Performance tuning settings
CacheFlag = cli . IntFlag {
Name : "cache" ,
Usage : "Megabytes of memory allocated to internal caching (min 16MB / database forced)" ,
Value : 128 ,
}
TrieCacheGenFlag = cli . IntFlag {
Name : "trie-cache-gens" ,
Usage : "Number of trie node generations to keep in memory" ,
Value : int ( state . MaxTrieCacheGen ) ,
}
2016-07-07 16:04:34 +03:00
// Fork settings
SupportDAOFork = cli . BoolFlag {
Name : "support-dao-fork" ,
Usage : "Updates the chain rules to support the DAO hard-fork" ,
}
OpposeDAOFork = cli . BoolFlag {
Name : "oppose-dao-fork" ,
Usage : "Updates the chain rules to oppose the DAO hard-fork" ,
}
2015-10-29 20:53:24 +03:00
// Miner settings
2015-06-12 08:45:23 +03:00
// TODO: refactor CPU vs GPU mining flags
2015-10-29 20:53:24 +03:00
MiningEnabledFlag = cli . BoolFlag {
Name : "mine" ,
Usage : "Enable mining" ,
2015-06-12 08:45:23 +03:00
}
2015-03-06 04:00:41 +02:00
MinerThreadsFlag = cli . IntFlag {
Name : "minerthreads" ,
2015-10-29 20:53:24 +03:00
Usage : "Number of CPU threads to use for mining" ,
2015-03-06 04:00:41 +02:00
Value : runtime . NumCPU ( ) ,
}
2015-10-29 20:53:24 +03:00
MiningGPUFlag = cli . StringFlag {
Name : "minergpus" ,
Usage : "List of GPUs to use for mining (e.g. '0,1' will use the first two GPUs found)" ,
2015-03-06 04:00:41 +02:00
}
2016-03-02 00:32:43 +02:00
TargetGasLimitFlag = cli . StringFlag {
Name : "targetgaslimit" ,
Usage : "Target gas limit sets the artificial target gas floor for the blocks to mine" ,
Value : params . GenesisGasLimit . String ( ) ,
}
2015-05-20 18:56:17 +03:00
AutoDAGFlag = cli . BoolFlag {
Name : "autodag" ,
Usage : "Enable automatic DAG pregeneration" ,
}
2015-03-26 23:49:22 +02:00
EtherbaseFlag = cli . StringFlag {
2015-03-27 13:14:00 +02:00
Name : "etherbase" ,
2015-10-29 20:53:24 +03:00
Usage : "Public address for block mining rewards (default = first account created)" ,
2015-07-07 11:32:05 +03:00
Value : "0" ,
2015-03-26 23:49:22 +02:00
}
2015-05-09 13:00:51 +03:00
GasPriceFlag = cli . StringFlag {
Name : "gasprice" ,
2015-10-29 20:53:24 +03:00
Usage : "Minimal gas price to accept for mining a transactions" ,
2016-02-29 15:15:18 +02:00
Value : new ( big . Int ) . Mul ( big . NewInt ( 20 ) , common . Shannon ) . String ( ) ,
2015-05-09 13:00:51 +03:00
}
2015-09-22 11:34:58 +03:00
ExtraDataFlag = cli . StringFlag {
Name : "extradata" ,
2015-10-29 20:53:24 +03:00
Usage : "Block extra data set by the miner (default = client version)" ,
2015-09-22 11:34:58 +03:00
}
2015-10-29 20:53:24 +03:00
// Account settings
2015-03-18 09:44:58 +02:00
UnlockedAccountFlag = cli . StringFlag {
Name : "unlock" ,
2015-11-17 18:33:25 +02:00
Usage : "Comma separated list of accounts to unlock" ,
2015-03-23 15:00:06 +02:00
Value : "" ,
}
PasswordFileFlag = cli . StringFlag {
Name : "password" ,
2015-11-17 18:33:25 +02:00
Usage : "Password file to use for non-inteactive password input" ,
2015-03-23 15:00:06 +02:00
Value : "" ,
2015-03-18 09:44:58 +02:00
}
2015-03-06 04:00:41 +02:00
2015-07-18 00:09:36 +03:00
VMForceJitFlag = cli . BoolFlag {
Name : "forcejit" ,
Usage : "Force the JIT VM to take precedence" ,
}
VMJitCacheFlag = cli . IntFlag {
Name : "jitcache" ,
Usage : "Amount of cached JIT VM programs" ,
Value : 64 ,
}
VMEnableJitFlag = cli . BoolFlag {
Name : "jitvm" ,
Usage : "Enable the JIT VM" ,
}
2015-03-18 09:44:58 +02:00
// logging and debug settings
2015-06-29 16:11:01 +03:00
MetricsEnabledFlag = cli . BoolFlag {
Name : metrics . MetricsEnabledFlag ,
2015-10-29 20:53:24 +03:00
Usage : "Enable metrics collection and reporting" ,
2015-06-29 16:11:01 +03:00
}
2016-04-21 12:14:57 +03:00
FakePoWFlag = cli . BoolFlag {
Name : "fakepow" ,
Usage : "Disables proof-of-work verification" ,
}
2015-03-06 04:00:41 +02:00
// RPC settings
RPCEnabledFlag = cli . BoolFlag {
Name : "rpc" ,
2015-10-29 20:53:24 +03:00
Usage : "Enable the HTTP-RPC server" ,
2015-03-06 04:00:41 +02:00
}
RPCListenAddrFlag = cli . StringFlag {
Name : "rpcaddr" ,
2015-10-29 20:53:24 +03:00
Usage : "HTTP-RPC server listening interface" ,
2016-09-16 12:53:50 +03:00
Value : node . DefaultHTTPHost ,
2015-03-06 04:00:41 +02:00
}
RPCPortFlag = cli . IntFlag {
Name : "rpcport" ,
2015-10-29 20:53:24 +03:00
Usage : "HTTP-RPC server listening port" ,
2016-09-16 12:53:50 +03:00
Value : node . DefaultHTTPPort ,
2015-03-06 04:00:41 +02:00
}
2015-03-29 22:21:14 +03:00
RPCCORSDomainFlag = cli . StringFlag {
Name : "rpccorsdomain" ,
2016-02-24 12:19:00 +02:00
Usage : "Comma separated list of domains from which to accept cross origin requests (browser enforced)" ,
2015-03-29 22:21:14 +03:00
Value : "" ,
}
2015-12-16 11:58:01 +02:00
RPCApiFlag = cli . StringFlag {
2015-06-16 14:30:53 +03:00
Name : "rpcapi" ,
2015-10-29 20:53:24 +03:00
Usage : "API's offered over the HTTP-RPC interface" ,
2016-02-09 13:24:42 +02:00
Value : rpc . DefaultHTTPApis ,
2015-06-16 14:30:53 +03:00
}
2015-06-08 12:01:02 +03:00
IPCDisabledFlag = cli . BoolFlag {
Name : "ipcdisable" ,
Usage : "Disable the IPC-RPC server" ,
}
IPCApiFlag = cli . StringFlag {
Name : "ipcapi" ,
2016-09-16 12:53:50 +03:00
Usage : "APIs offered over the IPC-RPC interface" ,
2016-02-09 13:24:42 +02:00
Value : rpc . DefaultIPCApis ,
2015-06-08 12:01:02 +03:00
}
IPCPathFlag = DirectoryFlag {
Name : "ipcpath" ,
2016-02-02 19:06:43 +02:00
Usage : "Filename for IPC socket/pipe within the datadir (explicit paths escape it)" ,
2016-09-16 12:53:50 +03:00
Value : DirectoryString { "geth.ipc" } ,
2015-06-08 12:01:02 +03:00
}
2015-12-16 11:58:01 +02:00
WSEnabledFlag = cli . BoolFlag {
2016-01-26 15:39:21 +02:00
Name : "ws" ,
2015-12-16 11:58:01 +02:00
Usage : "Enable the WS-RPC server" ,
}
WSListenAddrFlag = cli . StringFlag {
Name : "wsaddr" ,
Usage : "WS-RPC server listening interface" ,
2016-09-16 12:53:50 +03:00
Value : node . DefaultWSHost ,
2015-12-16 11:58:01 +02:00
}
WSPortFlag = cli . IntFlag {
Name : "wsport" ,
Usage : "WS-RPC server listening port" ,
2016-09-16 12:53:50 +03:00
Value : node . DefaultWSPort ,
2015-12-16 11:58:01 +02:00
}
WSApiFlag = cli . StringFlag {
Name : "wsapi" ,
Usage : "API's offered over the WS-RPC interface" ,
2016-02-09 13:24:42 +02:00
Value : rpc . DefaultHTTPApis ,
2015-12-16 11:58:01 +02:00
}
2016-03-14 10:38:54 +02:00
WSAllowedOriginsFlag = cli . StringFlag {
Name : "wsorigins" ,
Usage : "Origins from which to accept websockets requests" ,
2015-12-16 11:58:01 +02:00
Value : "" ,
2015-10-15 17:07:19 +03:00
}
2015-06-19 15:04:18 +03:00
ExecFlag = cli . StringFlag {
Name : "exec" ,
2015-10-29 20:53:24 +03:00
Usage : "Execute JavaScript statement (only in combination with console/attach)" ,
2015-06-19 15:04:18 +03:00
}
2016-05-06 12:40:23 +03:00
PreloadJSFlag = cli . StringFlag {
2016-04-07 14:48:24 +03:00
Name : "preload" ,
Usage : "Comma separated list of JavaScript files to preload into the console" ,
}
2016-01-26 15:39:21 +02:00
2015-03-06 04:00:41 +02:00
// Network Settings
MaxPeersFlag = cli . IntFlag {
Name : "maxpeers" ,
2015-04-22 02:41:34 +03:00
Usage : "Maximum number of network peers (network disabled if set to 0)" ,
2015-05-08 17:01:31 +03:00
Value : 25 ,
2015-03-06 04:00:41 +02:00
}
2015-05-04 17:35:49 +03:00
MaxPendingPeersFlag = cli . IntFlag {
Name : "maxpendpeers" ,
Usage : "Maximum number of pending connection attempts (defaults used if set to 0)" ,
Value : 0 ,
}
2015-03-06 04:00:41 +02:00
ListenPortFlag = cli . IntFlag {
Name : "port" ,
Usage : "Network listening port" ,
Value : 30303 ,
}
BootnodesFlag = cli . StringFlag {
Name : "bootnodes" ,
2015-11-17 18:33:25 +02:00
Usage : "Comma separated enode URLs for P2P discovery bootstrap" ,
2015-03-06 04:00:41 +02:00
Value : "" ,
}
NodeKeyFileFlag = cli . StringFlag {
Name : "nodekey" ,
Usage : "P2P node key file" ,
}
NodeKeyHexFlag = cli . StringFlag {
Name : "nodekeyhex" ,
Usage : "P2P node key as hex (for testing)" ,
}
NATFlag = cli . StringFlag {
Name : "nat" ,
2015-04-22 02:41:34 +03:00
Usage : "NAT port mapping mechanism (any|none|upnp|pmp|extip:<IP>)" ,
2015-03-06 04:00:41 +02:00
Value : "any" ,
}
2015-05-26 19:07:24 +03:00
NoDiscoverFlag = cli . BoolFlag {
Name : "nodiscover" ,
Usage : "Disables the peer discovery mechanism (manual peer addition)" ,
}
2015-04-20 18:45:37 +03:00
WhisperEnabledFlag = cli . BoolFlag {
Name : "shh" ,
2015-10-29 20:53:24 +03:00
Usage : "Enable Whisper" ,
2015-04-20 18:45:37 +03:00
}
2015-04-23 01:11:11 +03:00
// ATM the url is left to the user and deployment to
2015-03-15 08:31:40 +02:00
JSpathFlag = cli . StringFlag {
Name : "jspath" ,
2016-02-05 16:32:00 +02:00
Usage : "JavaScript root path for `loadScript` and document root for `admin.httpGet`" ,
2015-03-15 08:31:40 +02:00
Value : "." ,
}
2015-04-23 01:11:11 +03:00
SolcPathFlag = cli . StringFlag {
Name : "solc" ,
2015-10-29 20:53:24 +03:00
Usage : "Solidity compiler command to be used" ,
2015-04-23 01:11:11 +03:00
Value : "solc" ,
}
2015-10-29 20:53:24 +03:00
// Gas price oracle settings
2015-05-26 15:17:43 +03:00
GpoMinGasPriceFlag = cli . StringFlag {
Name : "gpomin" ,
Usage : "Minimum suggested gas price" ,
2016-02-29 15:15:18 +02:00
Value : new ( big . Int ) . Mul ( big . NewInt ( 20 ) , common . Shannon ) . String ( ) ,
2015-05-26 15:17:43 +03:00
}
GpoMaxGasPriceFlag = cli . StringFlag {
Name : "gpomax" ,
Usage : "Maximum suggested gas price" ,
2015-08-03 03:46:34 +03:00
Value : new ( big . Int ) . Mul ( big . NewInt ( 500 ) , common . Shannon ) . String ( ) ,
2015-05-26 15:17:43 +03:00
}
GpoFullBlockRatioFlag = cli . IntFlag {
Name : "gpofull" ,
Usage : "Full block threshold for gas price calculation (%)" ,
Value : 80 ,
}
GpobaseStepDownFlag = cli . IntFlag {
Name : "gpobasedown" ,
Usage : "Suggested gas price base step down ratio (1/1000)" ,
Value : 10 ,
}
GpobaseStepUpFlag = cli . IntFlag {
Name : "gpobaseup" ,
Usage : "Suggested gas price base step up ratio (1/1000)" ,
Value : 100 ,
}
GpobaseCorrectionFactorFlag = cli . IntFlag {
Name : "gpobasecf" ,
Usage : "Suggested gas price base correction factor (%)" ,
Value : 110 ,
}
2015-03-06 04:00:41 +02:00
)
2016-08-18 14:28:17 +03:00
// MakeDataDir retrieves the currently requested data directory, terminating
2015-11-17 18:33:25 +02:00
// if none (or the empty string) is specified. If the node is starting a testnet,
// the a subdirectory of the specified datadir will be used.
2016-08-18 14:28:17 +03:00
func MakeDataDir ( ctx * cli . Context ) string {
2015-11-17 18:33:25 +02:00
if path := ctx . GlobalString ( DataDirFlag . Name ) ; path != "" {
2016-08-18 14:28:17 +03:00
// TODO: choose a different location outside of the regular datadir.
2015-11-17 18:33:25 +02:00
if ctx . GlobalBool ( TestNetFlag . Name ) {
2016-08-18 14:28:17 +03:00
return filepath . Join ( path , "testnet" )
2015-11-17 18:33:25 +02:00
}
return path
2015-03-06 04:00:41 +02:00
}
2015-11-17 18:33:25 +02:00
Fatalf ( "Cannot determine default data directory, please set manually (--datadir)" )
return ""
2015-03-06 04:00:41 +02:00
}
2016-02-09 13:24:42 +02:00
// MakeIPCPath creates an IPC path configuration from the set command line flags,
2016-02-02 19:06:43 +02:00
// returning an empty string if IPC was explicitly disabled, or the set path.
2016-02-09 13:24:42 +02:00
func MakeIPCPath ( ctx * cli . Context ) string {
2016-02-02 19:06:43 +02:00
if ctx . GlobalBool ( IPCDisabledFlag . Name ) {
return ""
}
return ctx . GlobalString ( IPCPathFlag . Name )
}
2015-11-17 18:33:25 +02:00
// MakeNodeKey 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 MakeNodeKey ( ctx * cli . Context ) * ecdsa . PrivateKey {
var (
hex = ctx . GlobalString ( NodeKeyHexFlag . Name )
file = ctx . GlobalString ( NodeKeyFileFlag . Name )
key * ecdsa . PrivateKey
err error
)
2015-03-06 04:00:41 +02:00
switch {
case file != "" && hex != "" :
Fatalf ( "Options %q and %q are mutually exclusive" , NodeKeyFileFlag . Name , NodeKeyHexFlag . Name )
2015-11-17 18:33:25 +02:00
2015-03-06 04:00:41 +02:00
case file != "" :
if key , err = crypto . LoadECDSA ( file ) ; err != nil {
Fatalf ( "Option %q: %v" , NodeKeyFileFlag . Name , err )
}
2015-11-17 18:33:25 +02:00
2015-03-06 04:00:41 +02:00
case hex != "" :
if key , err = crypto . HexToECDSA ( hex ) ; err != nil {
Fatalf ( "Option %q: %v" , NodeKeyHexFlag . Name , err )
}
}
return key
}
2016-08-18 14:28:17 +03:00
// makeNodeUserIdent creates the user identifier from CLI flags.
func makeNodeUserIdent ( ctx * cli . Context ) string {
var comps [ ] string
2015-11-17 18:33:25 +02:00
if identity := ctx . GlobalString ( IdentityFlag . Name ) ; len ( identity ) > 0 {
2016-08-18 14:28:17 +03:00
comps = append ( comps , identity )
2015-11-17 18:33:25 +02:00
}
if ctx . GlobalBool ( VMEnableJitFlag . Name ) {
2016-08-18 14:28:17 +03:00
comps = append ( comps , "JIT" )
2015-04-19 00:53:30 +03:00
}
2016-08-18 14:28:17 +03:00
return strings . Join ( comps , "/" )
2015-11-17 18:33:25 +02:00
}
// MakeBootstrapNodes creates a list of bootstrap nodes from the command line
// flags, reverting to pre-configured ones if none have been specified.
func MakeBootstrapNodes ( ctx * cli . Context ) [ ] * discover . Node {
// Return pre-configured nodes if none were manually requested
if ! ctx . GlobalIsSet ( BootnodesFlag . Name ) {
if ctx . GlobalBool ( TestNetFlag . Name ) {
return TestNetBootNodes
}
return FrontierBootNodes
}
// Otherwise parse and use the CLI bootstrap nodes
bootnodes := [ ] * discover . Node { }
for _ , url := range strings . Split ( ctx . GlobalString ( BootnodesFlag . Name ) , "," ) {
node , err := discover . ParseNode ( url )
if err != nil {
glog . V ( logger . Error ) . Infof ( "Bootstrap URL %s: %v\n" , url , err )
continue
}
bootnodes = append ( bootnodes , node )
}
return bootnodes
}
// MakeListenAddress creates a TCP listening address string from set command
// line flags.
func MakeListenAddress ( ctx * cli . Context ) string {
return fmt . Sprintf ( ":%d" , ctx . GlobalInt ( ListenPortFlag . Name ) )
}
// MakeNAT creates a port mapper from set command line flags.
func MakeNAT ( ctx * cli . Context ) nat . Interface {
natif , err := nat . Parse ( ctx . GlobalString ( NATFlag . Name ) )
2015-07-07 13:53:36 +03:00
if err != nil {
2015-11-17 18:33:25 +02:00
Fatalf ( "Option %s: %v" , NATFlag . Name , err )
}
return natif
}
2016-04-14 17:18:35 +03:00
// MakeRPCModules splits input separated by a comma and trims excessive white
// space from the substrings.
func MakeRPCModules ( input string ) [ ] string {
result := strings . Split ( input , "," )
for i , r := range result {
result [ i ] = strings . TrimSpace ( r )
}
return result
}
2016-02-09 13:24:42 +02:00
// MakeHTTPRpcHost creates the HTTP RPC listener interface string from the set
2016-02-05 13:45:36 +02:00
// command line flags, returning empty if the HTTP endpoint is disabled.
2016-02-09 13:24:42 +02:00
func MakeHTTPRpcHost ( ctx * cli . Context ) string {
2016-02-05 13:45:36 +02:00
if ! ctx . GlobalBool ( RPCEnabledFlag . Name ) {
return ""
}
return ctx . GlobalString ( RPCListenAddrFlag . Name )
}
2016-02-09 13:24:42 +02:00
// MakeWSRpcHost creates the WebSocket RPC listener interface string from the set
2016-02-05 15:08:48 +02:00
// command line flags, returning empty if the HTTP endpoint is disabled.
2016-02-09 13:24:42 +02:00
func MakeWSRpcHost ( ctx * cli . Context ) string {
2016-02-05 15:08:48 +02:00
if ! ctx . GlobalBool ( WSEnabledFlag . Name ) {
return ""
}
return ctx . GlobalString ( WSListenAddrFlag . Name )
}
2016-02-19 14:29:19 +02:00
// MakeDatabaseHandles raises out the number of allowed file handles per process
// for Geth and returns half of the allowance to assign to the database.
func MakeDatabaseHandles ( ) int {
if err := raiseFdLimit ( 2048 ) ; err != nil {
Fatalf ( "Failed to raise file descriptor allowance: %v" , err )
}
limit , err := getFdLimit ( )
if err != nil {
Fatalf ( "Failed to retrieve file descriptor allowance: %v" , err )
}
if limit > 2048 { // cap database file descriptors even if more is available
limit = 2048
}
return limit / 2 // Leave half for networking and other stuff
}
2015-11-17 18:33:25 +02:00
// MakeAddress converts an account specified directly as a hex encoded string or
// a key index in the key store to an internal account representation.
2016-03-03 02:09:16 +02:00
func MakeAddress ( accman * accounts . Manager , account string ) ( accounts . Account , error ) {
2015-11-17 18:33:25 +02:00
// If the specified account is a valid address, return it
if common . IsHexAddress ( account ) {
2016-03-03 02:09:16 +02:00
return accounts . Account { Address : common . HexToAddress ( account ) } , nil
2015-11-17 18:33:25 +02:00
}
// Otherwise try to interpret the account as a keystore index
index , err := strconv . Atoi ( account )
if err != nil {
2016-03-03 02:09:16 +02:00
return accounts . Account { } , fmt . Errorf ( "invalid account address or index %q" , account )
2015-11-17 18:33:25 +02:00
}
2016-03-03 02:09:16 +02:00
return accman . AccountByIndex ( index )
2015-11-17 18:33:25 +02:00
}
// MakeEtherbase retrieves the etherbase either from the directly specified
// command line flags or from the keystore if CLI indexed.
func MakeEtherbase ( accman * accounts . Manager , ctx * cli . Context ) common . Address {
2016-03-03 02:09:16 +02:00
accounts := accman . Accounts ( )
2015-12-01 12:20:49 +02:00
if ! ctx . GlobalIsSet ( EtherbaseFlag . Name ) && len ( accounts ) == 0 {
2015-07-07 13:53:36 +03:00
glog . V ( logger . Error ) . Infoln ( "WARNING: No etherbase set and no accounts found as default" )
2015-11-17 18:33:25 +02:00
return common . Address { }
}
2015-12-01 12:20:49 +02:00
etherbase := ctx . GlobalString ( EtherbaseFlag . Name )
if etherbase == "" {
return common . Address { }
2015-11-17 18:33:25 +02:00
}
2015-12-01 12:20:49 +02:00
// If the specified etherbase is a valid address, return it
2016-03-03 02:09:16 +02:00
account , err := MakeAddress ( accman , etherbase )
2015-11-17 18:33:25 +02:00
if err != nil {
2015-12-01 12:20:49 +02:00
Fatalf ( "Option %q: %v" , EtherbaseFlag . Name , err )
2015-11-17 18:33:25 +02:00
}
2016-03-03 02:09:16 +02:00
return account . Address
2015-11-17 18:33:25 +02:00
}
// MakeMinerExtra resolves extradata for the miner from the set command line flags
// or returns a default one composed on the client, runtime and OS metadata.
func MakeMinerExtra ( extra [ ] byte , ctx * cli . Context ) [ ] byte {
if ctx . GlobalIsSet ( ExtraDataFlag . Name ) {
return [ ] byte ( ctx . GlobalString ( ExtraDataFlag . Name ) )
2015-07-07 13:53:36 +03:00
}
2015-11-17 18:33:25 +02:00
return extra
}
2016-03-31 00:20:06 +03:00
// MakePasswordList reads password lines from the file specified by --password.
2015-11-17 18:33:25 +02:00
func MakePasswordList ( ctx * cli . Context ) [ ] string {
2016-03-31 00:20:06 +03:00
path := ctx . GlobalString ( PasswordFileFlag . Name )
if path == "" {
return nil
}
text , err := ioutil . ReadFile ( path )
if err != nil {
Fatalf ( "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" )
2015-11-17 18:33:25 +02:00
}
2016-03-31 00:20:06 +03:00
return lines
2015-11-17 18:33:25 +02:00
}
2016-08-15 19:38:32 +03:00
// MakeNode configures a node with no services from command line flags.
2016-09-05 14:08:41 +03:00
func MakeNode ( ctx * cli . Context , name , gitCommit string ) * node . Node {
vsn := Version
if gitCommit != "" {
vsn += "-" + gitCommit [ : 8 ]
}
2016-08-15 19:38:32 +03:00
config := & node . Config {
2016-08-18 14:28:17 +03:00
DataDir : MakeDataDir ( ctx ) ,
2016-08-15 19:38:32 +03:00
KeyStoreDir : ctx . GlobalString ( KeyStoreDirFlag . Name ) ,
UseLightweightKDF : ctx . GlobalBool ( LightKDFFlag . Name ) ,
PrivateKey : MakeNodeKey ( ctx ) ,
2016-08-18 14:28:17 +03:00
Name : name ,
Version : vsn ,
UserIdent : makeNodeUserIdent ( ctx ) ,
2016-08-15 19:38:32 +03:00
NoDiscovery : ctx . GlobalBool ( NoDiscoverFlag . Name ) ,
BootstrapNodes : MakeBootstrapNodes ( ctx ) ,
ListenAddr : MakeListenAddress ( ctx ) ,
NAT : MakeNAT ( ctx ) ,
MaxPeers : ctx . GlobalInt ( MaxPeersFlag . Name ) ,
MaxPendingPeers : ctx . GlobalInt ( MaxPendingPeersFlag . Name ) ,
IPCPath : MakeIPCPath ( ctx ) ,
HTTPHost : MakeHTTPRpcHost ( ctx ) ,
HTTPPort : ctx . GlobalInt ( RPCPortFlag . Name ) ,
HTTPCors : ctx . GlobalString ( RPCCORSDomainFlag . Name ) ,
HTTPModules : MakeRPCModules ( ctx . GlobalString ( RPCApiFlag . Name ) ) ,
WSHost : MakeWSRpcHost ( ctx ) ,
WSPort : ctx . GlobalInt ( WSPortFlag . Name ) ,
WSOrigins : ctx . GlobalString ( WSAllowedOriginsFlag . Name ) ,
WSModules : MakeRPCModules ( ctx . GlobalString ( WSApiFlag . Name ) ) ,
}
if ctx . GlobalBool ( DevModeFlag . Name ) {
if ! ctx . GlobalIsSet ( DataDirFlag . Name ) {
config . DataDir = filepath . Join ( os . TempDir ( ) , "/ethereum_dev_mode" )
}
// --dev mode does not need p2p networking.
config . MaxPeers = 0
config . ListenAddr = ":0"
}
stack , err := node . New ( config )
if err != nil {
Fatalf ( "Failed to create the protocol stack: %v" , err )
}
return stack
}
// RegisterEthService configures eth.Ethereum from command line flags and adds it to the
// given node.
2016-09-05 14:08:41 +03:00
func RegisterEthService ( ctx * cli . Context , stack * node . Node , extra [ ] byte ) {
2015-11-17 18:33:25 +02:00
// Avoid conflicting network flags
networks , netFlags := 0 , [ ] cli . BoolFlag { DevModeFlag , TestNetFlag , OlympicFlag }
for _ , flag := range netFlags {
if ctx . GlobalBool ( flag . Name ) {
networks ++
}
}
if networks > 1 {
Fatalf ( "The %v flags are mutually exclusive" , netFlags )
}
2016-03-24 14:06:10 +02:00
// initialise new random number generator
rand := rand . New ( rand . NewSource ( time . Now ( ) . UnixNano ( ) ) )
// get enabled jit flag
jitEnabled := ctx . GlobalBool ( VMEnableJitFlag . Name )
// if the jit is not enabled enable it for 10 pct of the people
if ! jitEnabled && rand . Float64 ( ) < 0.1 {
jitEnabled = true
glog . V ( logger . Info ) . Infoln ( "You're one of the lucky few that will try out the JIT VM (random). If you get a consensus failure please be so kind to report this incident with the block hash that failed. You can switch to the regular VM by setting --jitvm=false" )
}
2015-11-17 18:33:25 +02:00
ethConf := & eth . Config {
2016-08-15 19:38:32 +03:00
Etherbase : MakeEtherbase ( stack . AccountManager ( ) , ctx ) ,
2016-08-18 14:28:17 +03:00
ChainConfig : MakeChainConfig ( ctx , stack ) ,
2015-10-09 18:36:31 +03:00
FastSync : ctx . GlobalBool ( FastSyncFlag . Name ) ,
2015-07-22 13:46:20 +03:00
DatabaseCache : ctx . GlobalInt ( CacheFlag . Name ) ,
2016-02-19 14:29:19 +02:00
DatabaseHandles : MakeDatabaseHandles ( ) ,
2015-05-26 15:17:43 +03:00
NetworkId : ctx . GlobalInt ( NetworkIdFlag . Name ) ,
MinerThreads : ctx . GlobalInt ( MinerThreadsFlag . Name ) ,
2015-11-17 18:33:25 +02:00
ExtraData : MakeMinerExtra ( extra , ctx ) ,
2015-05-26 15:17:43 +03:00
NatSpec : ctx . GlobalBool ( NatspecEnabledFlag . Name ) ,
2015-10-27 00:24:09 +03:00
DocRoot : ctx . GlobalString ( DocRootFlag . Name ) ,
2016-03-24 14:06:10 +02:00
EnableJit : jitEnabled ,
2016-03-19 19:07:09 +02:00
ForceJit : ctx . GlobalBool ( VMForceJitFlag . Name ) ,
2015-05-26 15:17:43 +03:00
GasPrice : common . String2Big ( ctx . GlobalString ( GasPriceFlag . Name ) ) ,
GpoMinGasPrice : common . String2Big ( ctx . GlobalString ( GpoMinGasPriceFlag . Name ) ) ,
GpoMaxGasPrice : common . String2Big ( ctx . GlobalString ( GpoMaxGasPriceFlag . Name ) ) ,
GpoFullBlockRatio : ctx . GlobalInt ( GpoFullBlockRatioFlag . Name ) ,
GpobaseStepDown : ctx . GlobalInt ( GpobaseStepDownFlag . Name ) ,
GpobaseStepUp : ctx . GlobalInt ( GpobaseStepUpFlag . Name ) ,
GpobaseCorrectionFactor : ctx . GlobalInt ( GpobaseCorrectionFactorFlag . Name ) ,
SolcPath : ctx . GlobalString ( SolcPathFlag . Name ) ,
AutoDAG : ctx . GlobalBool ( AutoDAGFlag . Name ) || ctx . GlobalBool ( MiningEnabledFlag . Name ) ,
2015-03-13 19:30:45 +02:00
}
2015-09-06 16:46:54 +03:00
2015-11-17 18:33:25 +02:00
// Override any default configs in dev mode or the test net
switch {
case ctx . GlobalBool ( OlympicFlag . Name ) :
if ! ctx . GlobalIsSet ( NetworkIdFlag . Name ) {
ethConf . NetworkId = 1
}
2016-07-07 16:04:34 +03:00
ethConf . Genesis = core . OlympicGenesisBlock ( )
2015-10-05 14:01:34 +03:00
2015-11-17 18:33:25 +02:00
case ctx . GlobalBool ( TestNetFlag . Name ) :
if ! ctx . GlobalIsSet ( NetworkIdFlag . Name ) {
ethConf . NetworkId = 2
}
2016-07-07 16:04:34 +03:00
ethConf . Genesis = core . TestNetGenesisBlock ( )
2015-11-17 18:33:25 +02:00
state . StartingNonce = 1048576 // (2**20)
2015-10-05 14:01:34 +03:00
2015-11-17 18:33:25 +02:00
case ctx . GlobalBool ( DevModeFlag . Name ) :
2016-07-07 16:04:34 +03:00
ethConf . Genesis = core . OlympicGenesisBlock ( )
2015-11-17 18:33:25 +02:00
if ! ctx . GlobalIsSet ( GasPriceFlag . Name ) {
ethConf . GasPrice = new ( big . Int )
2015-09-06 16:46:54 +03:00
}
2015-11-17 18:33:25 +02:00
ethConf . PowTest = true
2015-09-06 16:46:54 +03:00
}
2016-10-19 14:55:13 +03:00
// Override any global options pertaining to the Ethereum protocol
if gen := ctx . GlobalInt ( TrieCacheGenFlag . Name ) ; gen > 0 {
state . MaxTrieCacheGen = uint16 ( gen )
}
2015-12-16 05:26:23 +02:00
2015-11-26 18:35:44 +02:00
if err := stack . Register ( func ( ctx * node . ServiceContext ) ( node . Service , error ) {
2015-11-17 18:33:25 +02:00
return eth . New ( ctx , ethConf )
} ) ; err != nil {
Fatalf ( "Failed to register the Ethereum service: %v" , err )
}
2016-08-15 19:38:32 +03:00
}
// RegisterShhService configures whisper and adds it to the given node.
func RegisterShhService ( stack * node . Node ) {
if err := stack . Register ( func ( * node . ServiceContext ) ( node . Service , error ) { return whisper . New ( ) , nil } ) ; err != nil {
Fatalf ( "Failed to register the Whisper service: %v" , err )
}
2015-03-06 04:00:41 +02:00
}
2015-10-07 18:21:13 +03:00
// SetupNetwork configures the system for either the main net or some test network.
func SetupNetwork ( ctx * cli . Context ) {
switch {
case ctx . GlobalBool ( OlympicFlag . Name ) :
params . DurationLimit = big . NewInt ( 8 )
params . GenesisGasLimit = big . NewInt ( 3141592 )
params . MinGasLimit = big . NewInt ( 125000 )
params . MaximumExtraDataSize = big . NewInt ( 1024 )
NetworkIdFlag . Value = 0
core . BlockReward = big . NewInt ( 1.5e+18 )
core . ExpDiffPeriod = big . NewInt ( math . MaxInt64 )
}
2016-03-02 00:32:43 +02:00
params . TargetGasLimit = common . String2Big ( ctx . GlobalString ( TargetGasLimitFlag . Name ) )
2015-10-07 18:21:13 +03:00
}
2016-08-18 14:28:17 +03:00
// MakeChainConfig reads the chain configuration from the database in ctx.Datadir.
func MakeChainConfig ( ctx * cli . Context , stack * node . Node ) * core . ChainConfig {
db := MakeChainDatabase ( ctx , stack )
2016-03-02 00:32:43 +02:00
defer db . Close ( )
2016-08-18 14:28:17 +03:00
return MakeChainConfigFromDb ( ctx , db )
2016-04-07 12:25:28 +03:00
}
2016-08-18 14:28:17 +03:00
// MakeChainConfigFromDb reads the chain configuration from the given database.
func MakeChainConfigFromDb ( ctx * cli . Context , db ethdb . Database ) * core . ChainConfig {
2016-07-07 16:04:34 +03:00
// If the chain is already initialized, use any existing chain configs
2016-07-08 18:48:17 +03:00
config := new ( core . ChainConfig )
2016-07-16 12:14:20 +03:00
genesis := core . GetBlock ( db , core . GetCanonicalHash ( db , 0 ) , 0 )
if genesis != nil {
2016-04-01 22:54:51 +03:00
storedConfig , err := core . GetChainConfig ( db , genesis . Hash ( ) )
2016-07-08 18:48:17 +03:00
switch err {
case nil :
config = storedConfig
case core . ChainConfigNotFoundErr :
// No configs found, use empty, will populate below
default :
2016-03-02 00:32:43 +02:00
Fatalf ( "Could not make chain configuration: %v" , err )
}
}
2016-07-08 18:48:17 +03:00
// Set any missing fields due to them being unset or system upgrade
if config . HomesteadBlock == nil {
if ctx . GlobalBool ( TestNetFlag . Name ) {
2016-07-14 11:22:58 +03:00
config . HomesteadBlock = params . TestNetHomesteadBlock
2016-07-08 18:48:17 +03:00
} else {
2016-07-14 11:22:58 +03:00
config . HomesteadBlock = params . MainNetHomesteadBlock
2016-07-08 18:48:17 +03:00
}
2016-07-07 16:04:34 +03:00
}
2016-07-08 18:48:17 +03:00
if config . DAOForkBlock == nil {
if ctx . GlobalBool ( TestNetFlag . Name ) {
2016-07-14 11:22:58 +03:00
config . DAOForkBlock = params . TestNetDAOForkBlock
2016-07-08 18:48:17 +03:00
} else {
2016-07-14 11:22:58 +03:00
config . DAOForkBlock = params . MainNetDAOForkBlock
2016-07-08 18:48:17 +03:00
}
2016-07-14 11:22:58 +03:00
config . DAOForkSupport = true
2016-07-08 18:48:17 +03:00
}
2016-10-08 01:23:45 +03:00
if config . HomesteadGasRepriceBlock == nil {
if ctx . GlobalBool ( TestNetFlag . Name ) {
config . HomesteadGasRepriceBlock = params . TestNetHomesteadGasRepriceBlock
} else {
config . HomesteadGasRepriceBlock = params . MainNetHomesteadGasRepriceBlock
}
}
2016-07-08 18:48:17 +03:00
// Force override any existing configs if explicitly requested
2016-07-07 16:04:34 +03:00
switch {
case ctx . GlobalBool ( SupportDAOFork . Name ) :
2016-07-08 18:48:17 +03:00
config . DAOForkSupport = true
2016-07-07 16:04:34 +03:00
case ctx . GlobalBool ( OpposeDAOFork . Name ) :
2016-07-08 18:48:17 +03:00
config . DAOForkSupport = false
2016-04-01 22:54:51 +03:00
}
2016-07-08 18:48:17 +03:00
return config
2015-07-18 00:09:36 +03:00
}
2016-03-02 00:32:43 +02:00
// MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails.
2016-08-18 14:28:17 +03:00
func MakeChainDatabase ( ctx * cli . Context , stack * node . Node ) ethdb . Database {
2016-03-02 00:32:43 +02:00
var (
cache = ctx . GlobalInt ( CacheFlag . Name )
handles = MakeDatabaseHandles ( )
)
2015-07-22 13:46:20 +03:00
2016-08-18 14:28:17 +03:00
chainDb , err := stack . OpenDatabase ( "chaindata" , cache , handles )
2016-03-02 00:32:43 +02:00
if err != nil {
2015-04-13 11:13:52 +03:00
Fatalf ( "Could not open database: %v" , err )
}
2016-03-02 00:32:43 +02:00
return chainDb
}
// MakeChain creates a chain manager from set command line flags.
2016-08-18 14:28:17 +03:00
func MakeChain ( ctx * cli . Context , stack * node . Node ) ( chain * core . BlockChain , chainDb ethdb . Database ) {
2016-03-02 00:32:43 +02:00
var err error
2016-08-18 14:28:17 +03:00
chainDb = MakeChainDatabase ( ctx , stack )
2016-03-02 00:32:43 +02:00
2015-08-03 18:48:24 +03:00
if ctx . GlobalBool ( OlympicFlag . Name ) {
2015-11-17 18:33:25 +02:00
_ , err := core . WriteTestNetGenesisBlock ( chainDb )
2015-08-03 18:48:24 +03:00
if err != nil {
glog . Fatalln ( err )
}
}
2016-08-18 14:28:17 +03:00
chainConfig := MakeChainConfigFromDb ( ctx , chainDb )
2016-03-02 00:32:43 +02:00
2016-04-21 12:14:57 +03:00
pow := pow . PoW ( core . FakePow { } )
if ! ctx . GlobalBool ( FakePoWFlag . Name ) {
pow = ethash . New ( )
}
chain , err = core . NewBlockChain ( chainDb , chainConfig , pow , new ( event . TypeMux ) )
2015-06-08 13:12:13 +03:00
if err != nil {
Fatalf ( "Could not start chainmanager: %v" , err )
}
2015-08-06 20:57:39 +03:00
return chain , chainDb
2015-03-06 04:00:41 +02:00
}
2016-05-06 12:40:23 +03:00
// MakeConsolePreloads retrieves the absolute paths for the console JavaScript
// scripts to preload before starting.
func MakeConsolePreloads ( ctx * cli . Context ) [ ] string {
// Skip preloading if there's nothing to preload
if ctx . GlobalString ( PreloadJSFlag . Name ) == "" {
return nil
}
// Otherwise resolve absolute paths and return them
preloads := [ ] string { }
assets := ctx . GlobalString ( JSpathFlag . Name )
for _ , file := range strings . Split ( ctx . GlobalString ( PreloadJSFlag . Name ) , "," ) {
preloads = append ( preloads , common . AbsolutePath ( assets , strings . TrimSpace ( file ) ) )
}
return preloads
}