From 9015b958a1753f3bd174235c8ddb146109121417 Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Sun, 1 Nov 2015 14:04:59 +0100 Subject: [PATCH] cmd, eth: setup made easy --- cmd/utils/flags.go | 1 - eth/backend.go | 149 ++---------------------------- eth/backend_test.go | 20 +++++ eth/config.go | 215 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 241 insertions(+), 144 deletions(-) create mode 100644 eth/config.go diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index d741d0544f..a7474cd630 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -450,7 +450,6 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config { Discovery: !ctx.GlobalBool(NoDiscoverFlag.Name), NodeKey: MakeNodeKey(ctx), Shh: ctx.GlobalBool(WhisperEnabledFlag.Name), - Dial: true, BootNodes: ctx.GlobalString(BootnodesFlag.Name), GasPrice: common.String2Big(ctx.GlobalString(GasPriceFlag.Name)), GpoMinGasPrice: common.String2Big(ctx.GlobalString(GpoMinGasPriceFlag.Name)), diff --git a/eth/backend.go b/eth/backend.go index 9eb211e31f..dc2a2fe3d5 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -19,10 +19,7 @@ package eth import ( "bytes" - "crypto/ecdsa" - "encoding/json" "fmt" - "io/ioutil" "math/big" "os" "path/filepath" @@ -40,7 +37,6 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" @@ -49,7 +45,6 @@ import ( "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" - "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/whisper" ) @@ -86,143 +81,6 @@ var ( trustedNodes = "trusted-nodes.json" // Path within to search for the trusted node list ) -type Config struct { - DevMode bool - TestNet bool - - Name string - NetworkId int - GenesisFile string - GenesisBlock *types.Block // used by block tests - FastSync bool - Olympic bool - - BlockChainVersion int - SkipBcVersionCheck bool // e.g. blockchain export - DatabaseCache int - - DataDir string - LogFile string - Verbosity int - VmDebug bool - NatSpec bool - DocRoot string - AutoDAG bool - PowTest bool - ExtraData []byte - - MaxPeers int - MaxPendingPeers int - Discovery bool - Port string - - // Space-separated list of discovery node URLs - BootNodes string - - // This key is used to identify the node on the network. - // If nil, an ephemeral key is used. - NodeKey *ecdsa.PrivateKey - - NAT nat.Interface - Shh bool - Dial bool - - Etherbase common.Address - GasPrice *big.Int - MinerThreads int - AccountManager *accounts.Manager - SolcPath string - - GpoMinGasPrice *big.Int - GpoMaxGasPrice *big.Int - GpoFullBlockRatio int - GpobaseStepDown int - GpobaseStepUp int - GpobaseCorrectionFactor int - - // NewDB is used to create databases. - // If nil, the default is to create leveldb databases on disk. - NewDB func(path string) (ethdb.Database, error) -} - -func (cfg *Config) parseBootNodes() []*discover.Node { - if cfg.BootNodes == "" { - if cfg.TestNet { - return defaultTestNetBootNodes - } - - return defaultBootNodes - } - var ns []*discover.Node - for _, url := range strings.Split(cfg.BootNodes, " ") { - if url == "" { - continue - } - n, err := discover.ParseNode(url) - if err != nil { - glog.V(logger.Error).Infof("Bootstrap URL %s: %v\n", url, err) - continue - } - ns = append(ns, n) - } - return ns -} - -// parseNodes parses a list of discovery node URLs loaded from a .json file. -func (cfg *Config) parseNodes(file string) []*discover.Node { - // Short circuit if no node config is present - path := filepath.Join(cfg.DataDir, file) - if _, err := os.Stat(path); err != nil { - return nil - } - // Load the nodes from the config file - blob, err := ioutil.ReadFile(path) - if err != nil { - glog.V(logger.Error).Infof("Failed to access nodes: %v", err) - return nil - } - nodelist := []string{} - if err := json.Unmarshal(blob, &nodelist); err != nil { - glog.V(logger.Error).Infof("Failed to load nodes: %v", err) - return nil - } - // Interpret the list as a discovery node array - var nodes []*discover.Node - for _, url := range nodelist { - if url == "" { - continue - } - node, err := discover.ParseNode(url) - if err != nil { - glog.V(logger.Error).Infof("Node URL %s: %v\n", url, err) - continue - } - nodes = append(nodes, node) - } - return nodes -} - -func (cfg *Config) nodeKey() (*ecdsa.PrivateKey, error) { - // use explicit key from command line args if set - if cfg.NodeKey != nil { - return cfg.NodeKey, nil - } - // use persistent key if present - keyfile := filepath.Join(cfg.DataDir, "nodekey") - key, err := crypto.LoadECDSA(keyfile) - if err == nil { - return key, nil - } - // no persistent key, generate and store a new one - if key, err = crypto.GenerateKey(); err != nil { - return nil, fmt.Errorf("could not generate server key: %v", err) - } - if err := crypto.SaveECDSA(keyfile, key); err != nil { - glog.V(logger.Error).Infoln("could not persist nodekey: ", err) - } - return key, nil -} - type Ethereum struct { // Channel for shutting down the ethereum shutdownChan chan bool @@ -272,6 +130,11 @@ type Ethereum struct { } func New(config *Config) (*Ethereum, error) { + if config == nil { + config = new(Config) + } + MakeConfig(config) + logger.New(config.DataDir, config.LogFile, config.Verbosity) // Let the database take 3/4 of the max open files (TODO figure out a way to get the actual limit of the open files) @@ -452,7 +315,7 @@ func New(config *Config) (*Ethereum, error) { Discovery: config.Discovery, Protocols: protocols, NAT: config.NAT, - NoDial: !config.Dial, + NoDial: config.NoDial, BootstrapNodes: config.parseBootNodes(), StaticNodes: config.parseNodes(staticNodes), TrustedNodes: config.parseNodes(trustedNodes), diff --git a/eth/backend_test.go b/eth/backend_test.go index 0379fc843a..1b16902c13 100644 --- a/eth/backend_test.go +++ b/eth/backend_test.go @@ -1,7 +1,10 @@ package eth import ( + "log" "math/big" + "os" + "os/signal" "testing" "github.com/ethereum/go-ethereum/common" @@ -65,3 +68,20 @@ func TestMipmapUpgrade(t *testing.T) { t.Error("setting-mipmap-version not written to database") } } + +func ExampleNew() { + // Setup ethereum stack; initialising the blockchain, transaction pool + // and network stack. + ethereum, err := New(&Config{Name: "Ghost"}) + if err != nil { + log.Fatalln(err) + } + // start all services + ethereum.Start() + + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt, os.Kill) + <-c + // stop all services + ethereum.Stop() +} diff --git a/eth/config.go b/eth/config.go new file mode 100644 index 0000000000..80002b1d2f --- /dev/null +++ b/eth/config.go @@ -0,0 +1,215 @@ +package eth + +import ( + "crypto/ecdsa" + "encoding/json" + "fmt" + "io/ioutil" + "math/big" + "os" + "path/filepath" + "strings" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/nat" +) + +// Config is the configuration object which holds information about the various +// sub system and ethereum's environment and settings. +type Config struct { + DevMode bool // Developer mode + TestNet bool // Testnet mode + + Name string // Name of the instance (visible through p2p) + NetworkId int // The network id used for p2p + GenesisFile string // genesis file for initialising the genesis block + GenesisBlock *types.Block // used by block tests + FastSync bool // enble fast sync + Olympic bool // enable olympic settings + + BlockChainVersion int // version of the block chain in database + SkipBcVersionCheck bool // e.g. blockchain export + DatabaseCache int // Max cache for leveldb + + DataDir string // datadir containing leveldb, node settings, etc. + LogFile string // file to which logs are written + Verbosity int // the level of verbosity + VmDebug bool // log debug output during vm execution + NatSpec bool // enable natspec + DocRoot string // documentation root for natspec + AutoDAG bool // pre-generate dags + PowTest bool // enabled pow test + ExtraData []byte // default extra data to be used for the miner + + MaxPeers int // maximum amount of peers + MaxPendingPeers int // maximum amount of pending peers + Discovery bool // enable discovery + Port string // port to be used for p2p + + BootNodes string // Space-separated list of discovery node URLs + + // This key is used to identify the node on the network. + // If nil, an ephemeral key is used. + NodeKey *ecdsa.PrivateKey + + NAT nat.Interface // NAT interface + Shh bool // enable shh + NoDial bool // disable outgoing dials + NoConn bool // disable peers + + AccountManager *accounts.Manager // account manager + LightKDF bool // enables light KDF + + Etherbase common.Address // default coinbase + GasPrice *big.Int // minimum acceptable gas price (tx relay, mining) + MinerThreads int // amount of default miner threads to be used during mining + SolcPath string // path to solidity executable + + GpoMinGasPrice *big.Int // GPO minimum gas price + GpoMaxGasPrice *big.Int // GPO maximum gas price + GpoFullBlockRatio int + GpobaseStepDown int + GpobaseStepUp int + GpobaseCorrectionFactor int + + // NewDB is used to create databases. + // If nil, the default is to create leveldb databases on disk. + NewDB func(path string) (ethdb.Database, error) +} + +// MakeConfig sets missing default values +func MakeConfig(cfg *Config) { + if cfg.BlockChainVersion < 3 { + cfg.BlockChainVersion = 3 + } + if len(cfg.Name) == 0 { + cfg.Name = "Custom-Ethereum-Client" + } + if len(cfg.DataDir) == 0 { + cfg.DataDir = common.DefaultDataDir() + } + if cfg.MaxPeers == 0 && !cfg.NoConn { + cfg.MaxPeers = 25 + } + if len(cfg.Port) == 0 { + cfg.Port = "0" // auto + } + if cfg.TestNet && cfg.NetworkId == 0 { + cfg.NetworkId = 2 + } + if cfg.NAT == nil { + cfg.NAT, _ = nat.Parse("any") + } + if cfg.GasPrice == nil { + cfg.GasPrice = new(big.Int).Mul(big.NewInt(10), common.Szabo) + } + if cfg.GpoMinGasPrice == nil { + cfg.GpoMinGasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon) + } + if cfg.GpoMaxGasPrice == nil { + cfg.GpoMaxGasPrice = new(big.Int).Mul(big.NewInt(500), common.Shannon) + } + if cfg.GpoFullBlockRatio == 0 && cfg.GpobaseStepDown == 0 && cfg.GpobaseStepUp == 0 && cfg.GpobaseCorrectionFactor == 0 { + cfg.GpoFullBlockRatio = 80 + cfg.GpobaseStepDown = 10 + cfg.GpobaseStepUp = 100 + cfg.GpobaseCorrectionFactor = 110 + } + if cfg.AccountManager == nil { + scryptN := crypto.StandardScryptN + scryptP := crypto.StandardScryptP + if cfg.LightKDF { + scryptN = crypto.LightScryptN + scryptP = crypto.LightScryptP + } + cfg.AccountManager = accounts.NewManager(crypto.NewKeyStorePassphrase(filepath.Join(cfg.DataDir, "keystore"), scryptN, scryptP)) + } + glog.SetV(cfg.Verbosity) + glog.CopyStandardLogTo("INFO") + glog.SetToStderr(true) +} + +func (cfg *Config) parseBootNodes() []*discover.Node { + if cfg.BootNodes == "" { + if cfg.TestNet { + return defaultTestNetBootNodes + } + + return defaultBootNodes + } + var ns []*discover.Node + for _, url := range strings.Split(cfg.BootNodes, " ") { + if url == "" { + continue + } + n, err := discover.ParseNode(url) + if err != nil { + glog.V(logger.Error).Infof("Bootstrap URL %s: %v\n", url, err) + continue + } + ns = append(ns, n) + } + return ns +} + +// parseNodes parses a list of discovery node URLs loaded from a .json file. +func (cfg *Config) parseNodes(file string) []*discover.Node { + // Short circuit if no node config is present + path := filepath.Join(cfg.DataDir, file) + if _, err := os.Stat(path); err != nil { + return nil + } + // Load the nodes from the config file + blob, err := ioutil.ReadFile(path) + if err != nil { + glog.V(logger.Error).Infof("Failed to access nodes: %v", err) + return nil + } + nodelist := []string{} + if err := json.Unmarshal(blob, &nodelist); err != nil { + glog.V(logger.Error).Infof("Failed to load nodes: %v", err) + return nil + } + // Interpret the list as a discovery node array + var nodes []*discover.Node + for _, url := range nodelist { + if url == "" { + continue + } + node, err := discover.ParseNode(url) + if err != nil { + glog.V(logger.Error).Infof("Node URL %s: %v\n", url, err) + continue + } + nodes = append(nodes, node) + } + return nodes +} + +func (cfg *Config) nodeKey() (*ecdsa.PrivateKey, error) { + // use explicit key from command line args if set + if cfg.NodeKey != nil { + return cfg.NodeKey, nil + } + // use persistent key if present + keyfile := filepath.Join(cfg.DataDir, "nodekey") + key, err := crypto.LoadECDSA(keyfile) + if err == nil { + return key, nil + } + // no persistent key, generate and store a new one + if key, err = crypto.GenerateKey(); err != nil { + return nil, fmt.Errorf("could not generate server key: %v", err) + } + if err := crypto.SaveECDSA(keyfile, key); err != nil { + glog.V(logger.Error).Infoln("could not persist nodekey: ", err) + } + return key, nil +}