// Copyright 2016 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . // Contains all the wrappers from the node package to support client side node // management on mobile platforms. package geth import ( "fmt" "math/big" "path/filepath" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethstats" "github.com/ethereum/go-ethereum/les" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/whisper/whisperv2" ) // NodeConfig represents the collection of configuration values to fine tune the Geth // node embedded into a mobile process. The available values are a subset of the // entire API provided by go-ethereum to reduce the maintenance surface and dev // complexity. type NodeConfig struct { // Bootstrap nodes used to establish connectivity with the rest of the network. BootstrapNodes *Enodes // MaxPeers is the maximum number of peers that can be connected. If this is // set to zero, then only the configured static and trusted peers can connect. MaxPeers int // EthereumEnabled specifies whether the node should run the Ethereum protocol. EthereumEnabled bool // EthereumNetworkID is the network identifier used by the Ethereum protocol to // decide if remote peers should be accepted or not. EthereumNetworkID int // EthereumChainConfig is the default parameters of the blockchain to use. If no // configuration is specified, it defaults to the main network. EthereumChainConfig *ChainConfig // EthereumGenesis is the genesis JSON to use to seed the blockchain with. An // empty genesis state is equivalent to using the mainnet's state. EthereumGenesis string // EthereumDatabaseCache is the system memory in MB to allocate for database caching. // A minimum of 16MB is always reserved. EthereumDatabaseCache int // EthereumNetStats is a netstats connection string to use to report various // chain, transaction and node stats to a monitoring server. // // It has the form "nodename:secret@host:port" EthereumNetStats string // WhisperEnabled specifies whether the node should run the Whisper protocol. WhisperEnabled bool } // defaultNodeConfig contains the default node configuration values to use if all // or some fields are missing from the user's specified list. var defaultNodeConfig = &NodeConfig{ BootstrapNodes: FoundationBootnodes(), MaxPeers: 25, EthereumEnabled: true, EthereumNetworkID: 1, EthereumChainConfig: MainnetChainConfig(), EthereumDatabaseCache: 16, } // NewNodeConfig creates a new node option set, initialized to the default values. func NewNodeConfig() *NodeConfig { config := *defaultNodeConfig return &config } // Node represents a Geth Ethereum node instance. type Node struct { node *node.Node } // NewNode creates and configures a new Geth node. func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) { // If no or partial configurations were specified, use defaults if config == nil { config = NewNodeConfig() } if config.MaxPeers == 0 { config.MaxPeers = defaultNodeConfig.MaxPeers } if config.BootstrapNodes == nil || config.BootstrapNodes.Size() == 0 { config.BootstrapNodes = defaultNodeConfig.BootstrapNodes } // Create the empty networking stack nodeConf := &node.Config{ Name: clientIdentifier, Version: params.Version, DataDir: datadir, KeyStoreDir: filepath.Join(datadir, "keystore"), // Mobile should never use internal keystores! NoDiscovery: true, DiscoveryV5: true, DiscoveryV5Addr: ":0", BootstrapNodesV5: config.BootstrapNodes.nodes, ListenAddr: ":0", NAT: nat.Any(), MaxPeers: config.MaxPeers, } rawStack, err := node.New(nodeConf) if err != nil { return nil, err } // Register the Ethereum protocol if requested if config.EthereumEnabled { ethConf := ð.Config{ ChainConfig: ¶ms.ChainConfig{ ChainId: big.NewInt(config.EthereumChainConfig.ChainID), HomesteadBlock: big.NewInt(config.EthereumChainConfig.HomesteadBlock), DAOForkBlock: big.NewInt(config.EthereumChainConfig.DAOForkBlock), DAOForkSupport: config.EthereumChainConfig.DAOForkSupport, EIP150Block: big.NewInt(config.EthereumChainConfig.EIP150Block), EIP150Hash: config.EthereumChainConfig.EIP150Hash.hash, EIP155Block: big.NewInt(config.EthereumChainConfig.EIP155Block), EIP158Block: big.NewInt(config.EthereumChainConfig.EIP158Block), }, Genesis: config.EthereumGenesis, LightMode: true, DatabaseCache: config.EthereumDatabaseCache, NetworkId: config.EthereumNetworkID, GasPrice: new(big.Int).Mul(big.NewInt(20), common.Shannon), GpoMinGasPrice: new(big.Int).Mul(big.NewInt(20), common.Shannon), GpoMaxGasPrice: new(big.Int).Mul(big.NewInt(500), common.Shannon), GpoFullBlockRatio: 80, GpobaseStepDown: 10, GpobaseStepUp: 100, GpobaseCorrectionFactor: 110, } if err := rawStack.Register(func(ctx *node.ServiceContext) (node.Service, error) { return les.New(ctx, ethConf) }); err != nil { return nil, fmt.Errorf("ethereum init: %v", err) } // If netstats reporting is requested, do it if config.EthereumNetStats != "" { if err := rawStack.Register(func(ctx *node.ServiceContext) (node.Service, error) { var lesServ *les.LightEthereum ctx.Service(&lesServ) return ethstats.New(config.EthereumNetStats, nil, lesServ) }); err != nil { return nil, fmt.Errorf("netstats init: %v", err) } } } // Register the Whisper protocol if requested if config.WhisperEnabled { if err := rawStack.Register(func(*node.ServiceContext) (node.Service, error) { return whisperv2.New(), nil }); err != nil { return nil, fmt.Errorf("whisper init: %v", err) } } return &Node{rawStack}, nil } // Start creates a live P2P node and starts running it. func (n *Node) Start() error { return n.node.Start() } // Stop terminates a running node along with all it's services. In the node was // not started, an error is returned. func (n *Node) Stop() error { return n.node.Stop() } // GetEthereumClient retrieves a client to access the Ethereum subsystem. func (n *Node) GetEthereumClient() (client *EthereumClient, _ error) { rpc, err := n.node.Attach() if err != nil { return nil, err } return &EthereumClient{ethclient.NewClient(rpc)}, nil } // GetNodeInfo gathers and returns a collection of metadata known about the host. func (n *Node) GetNodeInfo() *NodeInfo { return &NodeInfo{n.node.Server().NodeInfo()} } // GetPeersInfo returns an array of metadata objects describing connected peers. func (n *Node) GetPeersInfo() *PeerInfos { return &PeerInfos{n.node.Server().PeersInfo()} }