aboutsummaryrefslogtreecommitdiffstats
path: root/eth/backend.go
diff options
context:
space:
mode:
Diffstat (limited to 'eth/backend.go')
-rw-r--r--eth/backend.go239
1 files changed, 95 insertions, 144 deletions
diff --git a/eth/backend.go b/eth/backend.go
index ef951a6c2..03c2e38e5 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -20,14 +20,16 @@ package eth
import (
"errors"
"fmt"
- "math/big"
- "regexp"
- "strings"
+ "runtime"
"sync"
- "time"
+ "sync/atomic"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/consensus"
+ "github.com/ethereum/go-ethereum/consensus/clique"
+ "github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
@@ -42,69 +44,10 @@ import (
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/pow"
+ "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
)
-const (
- epochLength = 30000
- ethashRevision = 23
-
- autoDAGcheckInterval = 10 * time.Hour
- autoDAGepochHeight = epochLength / 2
-)
-
-var (
- datadirInUseErrnos = map[uint]bool{11: true, 32: true, 35: true}
- portInUseErrRE = regexp.MustCompile("address already in use")
-)
-
-type Config struct {
- ChainConfig *params.ChainConfig // chain configuration
-
- NetworkId int // Network ID to use for selecting peers to connect to
- Genesis string // Genesis JSON to seed the chain database with
- FastSync bool // Enables the state download based fast synchronisation algorithm
- LightMode bool // Running in light client mode
- LightServ int // Maximum percentage of time allowed for serving LES requests
- LightPeers int // Maximum number of LES client peers
- MaxPeers int // Maximum number of global peers
-
- SkipBcVersionCheck bool // e.g. blockchain export
- DatabaseCache int
- DatabaseHandles int
-
- DocRoot string
- PowFake bool
- PowTest bool
- PowShared bool
- ExtraData []byte
-
- EthashCacheDir string
- EthashCachesInMem int
- EthashCachesOnDisk int
- EthashDatasetDir string
- EthashDatasetsInMem int
- EthashDatasetsOnDisk int
-
- Etherbase common.Address
- GasPrice *big.Int
- MinerThreads int
- SolcPath string
-
- GpoMinGasPrice *big.Int
- GpoMaxGasPrice *big.Int
- GpoFullBlockRatio int
- GpobaseStepDown int
- GpobaseStepUp int
- GpobaseCorrectionFactor int
-
- EnablePreimageRecording bool
-
- TestGenesisBlock *types.Block // Genesis block to seed the chain database with (testing only!)
- TestGenesisState ethdb.Database // Genesis state to seed the database with (testing only!)
-}
-
type LesServer interface {
Start(srvr *p2p.Server)
Stop()
@@ -127,7 +70,7 @@ type Ethereum struct {
chainDb ethdb.Database // Block chain database
eventMux *event.TypeMux
- pow pow.PoW
+ engine consensus.Engine
accountManager *accounts.Manager
ApiBackend *EthApiBackend
@@ -136,7 +79,6 @@ type Ethereum struct {
Mining bool
MinerThreads int
etherbase common.Address
- solcPath string
netVersionId int
netRPCService *ethapi.PublicNetAPI
@@ -150,25 +92,35 @@ func (s *Ethereum) AddLesServer(ls LesServer) {
// New creates a new Ethereum object (including the
// initialisation of the common Ethereum object)
func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
+ if config.SyncMode == downloader.LightSync {
+ return nil, errors.New("can't run eth.Ethereum in light sync mode, use les.LightEthereum")
+ }
+ if !config.SyncMode.IsValid() {
+ return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode)
+ }
+
chainDb, err := CreateDB(ctx, config, "chaindata")
if err != nil {
return nil, err
}
stopDbUpgrade := upgradeSequentialKeys(chainDb)
- if err := SetupGenesisBlock(&chainDb, config); err != nil {
- return nil, err
+ chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis)
+ if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok {
+ return nil, genesisErr
}
+ log.Info("Initialised chain configuration", "config", chainConfig)
+
eth := &Ethereum{
chainDb: chainDb,
+ chainConfig: chainConfig,
eventMux: ctx.EventMux,
accountManager: ctx.AccountManager,
- pow: CreatePoW(ctx, config),
+ engine: CreateConsensusEngine(ctx, config, chainConfig, chainDb),
shutdownChan: make(chan bool),
stopDbUpgrade: stopDbUpgrade,
netVersionId: config.NetworkId,
etherbase: config.Etherbase,
MinerThreads: config.MinerThreads,
- solcPath: config.SolcPath,
}
if err := addMipmapBloomBins(chainDb); err != nil {
@@ -184,33 +136,18 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
core.WriteBlockChainVersion(chainDb, core.BlockChainVersion)
}
- // load the genesis block or write a new one if no genesis
- // block is prenent in the database.
- genesis := core.GetBlock(chainDb, core.GetCanonicalHash(chainDb, 0), 0)
- if genesis == nil {
- genesis, err = core.WriteDefaultGenesisBlock(chainDb)
- if err != nil {
- return nil, err
- }
- log.Warn("Wrote default Ethereum genesis block")
- }
-
- if config.ChainConfig == nil {
- return nil, errors.New("missing chain config")
- }
- core.WriteChainConfig(chainDb, genesis.Hash(), config.ChainConfig)
-
- eth.chainConfig = config.ChainConfig
-
- log.Info("Initialised chain configuration", "config", eth.chainConfig)
-
- eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.pow, eth.EventMux(), vm.Config{EnablePreimageRecording: config.EnablePreimageRecording})
+ vmConfig := vm.Config{EnablePreimageRecording: config.EnablePreimageRecording}
+ eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.engine, eth.eventMux, vmConfig)
if err != nil {
- if err == core.ErrNoGenesis {
- return nil, fmt.Errorf(`No chain found. Please initialise a new chain using the "init" subcommand.`)
- }
return nil, err
}
+ // Rewind the chain in case of an incompatible config upgrade.
+ if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
+ log.Warn("Rewinding chain to upgrade configuration", "err", compat)
+ eth.blockchain.SetHead(compat.RewindTo)
+ core.WriteChainConfig(chainDb, genesisHash, chainConfig)
+ }
+
newPool := core.NewTxPool(eth.chainConfig, eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit)
eth.txPool = newPool
@@ -225,27 +162,41 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
}
}
- if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.FastSync, config.NetworkId, maxPeers, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb); err != nil {
+ if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.SyncMode, config.NetworkId, maxPeers, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb); err != nil {
return nil, err
}
- eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.pow)
+
+ eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine)
eth.miner.SetGasPrice(config.GasPrice)
- eth.miner.SetExtra(config.ExtraData)
-
- gpoParams := &gasprice.GpoParams{
- GpoMinGasPrice: config.GpoMinGasPrice,
- GpoMaxGasPrice: config.GpoMaxGasPrice,
- GpoFullBlockRatio: config.GpoFullBlockRatio,
- GpobaseStepDown: config.GpobaseStepDown,
- GpobaseStepUp: config.GpobaseStepUp,
- GpobaseCorrectionFactor: config.GpobaseCorrectionFactor,
+ eth.miner.SetExtra(makeExtraData(config.ExtraData))
+
+ eth.ApiBackend = &EthApiBackend{eth, nil}
+ gpoParams := config.GPO
+ if gpoParams.Default == nil {
+ gpoParams.Default = config.GasPrice
}
- gpo := gasprice.NewGasPriceOracle(eth.blockchain, chainDb, eth.eventMux, gpoParams)
- eth.ApiBackend = &EthApiBackend{eth, gpo}
+ eth.ApiBackend.gpo = gasprice.NewOracle(eth.ApiBackend, gpoParams)
return eth, nil
}
+func makeExtraData(extra []byte) []byte {
+ if len(extra) == 0 {
+ // create default extradata
+ extra, _ = rlp.EncodeToBytes([]interface{}{
+ uint(params.VersionMajor<<16 | params.VersionMinor<<8 | params.VersionPatch),
+ "geth",
+ runtime.Version(),
+ runtime.GOOS,
+ })
+ }
+ if uint64(len(extra)) > params.MaximumExtraDataSize {
+ log.Warn("Miner extra data exceed limit", "extra", hexutil.Bytes(extra), "limit", params.MaximumExtraDataSize)
+ extra = nil
+ }
+ return extra
+}
+
// CreateDB creates the chain database.
func CreateDB(ctx *node.ServiceContext, config *Config, name string) (ethdb.Database, error) {
db, err := ctx.OpenDatabase(name, config.DatabaseCache, config.DatabaseHandles)
@@ -255,51 +206,41 @@ func CreateDB(ctx *node.ServiceContext, config *Config, name string) (ethdb.Data
return db, err
}
-// SetupGenesisBlock initializes the genesis block for an Ethereum service
-func SetupGenesisBlock(chainDb *ethdb.Database, config *Config) error {
- // Load up any custom genesis block if requested
- if len(config.Genesis) > 0 {
- block, err := core.WriteGenesisBlock(*chainDb, strings.NewReader(config.Genesis))
- if err != nil {
- return err
- }
- log.Info("Successfully wrote custom genesis block", "hash", block.Hash())
- }
- // Load up a test setup if directly injected
- if config.TestGenesisState != nil {
- *chainDb = config.TestGenesisState
+// CreateConsensusEngine creates the required type of consensus engine instance for an Ethereum service
+func CreateConsensusEngine(ctx *node.ServiceContext, config *Config, chainConfig *params.ChainConfig, db ethdb.Database) consensus.Engine {
+ // If proof-of-authority is requested, set it up
+ if chainConfig.Clique != nil {
+ return clique.New(chainConfig.Clique, db)
}
- if config.TestGenesisBlock != nil {
- core.WriteTd(*chainDb, config.TestGenesisBlock.Hash(), config.TestGenesisBlock.NumberU64(), config.TestGenesisBlock.Difficulty())
- core.WriteBlock(*chainDb, config.TestGenesisBlock)
- core.WriteCanonicalHash(*chainDb, config.TestGenesisBlock.Hash(), config.TestGenesisBlock.NumberU64())
- core.WriteHeadBlockHash(*chainDb, config.TestGenesisBlock.Hash())
- }
- return nil
-}
-
-// CreatePoW creates the required type of PoW instance for an Ethereum service
-func CreatePoW(ctx *node.ServiceContext, config *Config) pow.PoW {
+ // Otherwise assume proof-of-work
switch {
case config.PowFake:
log.Warn("Ethash used in fake mode")
- return pow.FakePow{}
+ return ethash.NewFaker()
case config.PowTest:
log.Warn("Ethash used in test mode")
- return pow.NewTestEthash()
+ return ethash.NewTester()
case config.PowShared:
log.Warn("Ethash used in shared mode")
- return pow.NewSharedEthash()
+ return ethash.NewShared()
default:
- return pow.NewFullEthash(ctx.ResolvePath(config.EthashCacheDir), config.EthashCachesInMem, config.EthashCachesOnDisk,
+ engine := ethash.New(ctx.ResolvePath(config.EthashCacheDir), config.EthashCachesInMem, config.EthashCachesOnDisk,
config.EthashDatasetDir, config.EthashDatasetsInMem, config.EthashDatasetsOnDisk)
+ engine.SetThreads(-1) // Disable CPU mining
+ return engine
}
}
// APIs returns the collection of RPC services the ethereum package offers.
// NOTE, some of these services probably need to be moved to somewhere else.
func (s *Ethereum) APIs() []rpc.API {
- return append(ethapi.GetAPIs(s.ApiBackend, s.solcPath), []rpc.API{
+ apis := ethapi.GetAPIs(s.ApiBackend)
+
+ // Append any APIs exposed explicitly by the consensus engine
+ apis = append(apis, s.engine.APIs(s.BlockChain())...)
+
+ // Append all the local APIs and return
+ return append(apis, []rpc.API{
{
Namespace: "eth",
Version: "1.0",
@@ -369,13 +310,28 @@ func (self *Ethereum) SetEtherbase(etherbase common.Address) {
self.miner.SetEtherbase(etherbase)
}
-func (s *Ethereum) StartMining(threads int) error {
+func (s *Ethereum) StartMining(local bool) error {
eb, err := s.Etherbase()
if err != nil {
log.Error("Cannot start mining without etherbase", "err", err)
return fmt.Errorf("etherbase missing: %v", err)
}
- go s.miner.Start(eb, threads)
+ if clique, ok := s.engine.(*clique.Clique); ok {
+ wallet, err := s.accountManager.Find(accounts.Account{Address: eb})
+ if wallet == nil || err != nil {
+ log.Error("Etherbase account unavailable locally", "err", err)
+ return fmt.Errorf("singer missing: %v", err)
+ }
+ clique.Authorize(eb, wallet.SignHash)
+ }
+ if local {
+ // If local (CPU) mining is started, we can disable the transaction rejection
+ // mechanism introduced to speed sync times. CPU mining on mainnet is ludicrous
+ // so noone will ever hit this path, whereas marking sync done on CPU mining
+ // will ensure that private networks work in single miner mode too.
+ atomic.StoreUint32(&s.protocolManager.acceptTxs, 1)
+ }
+ go s.miner.Start(eb)
return nil
}
@@ -387,7 +343,7 @@ func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager
func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
-func (s *Ethereum) Pow() pow.PoW { return s.pow }
+func (s *Ethereum) Engine() consensus.Engine { return s.engine }
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
func (s *Ethereum) IsListening() bool { return true } // Always listening
func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
@@ -436,8 +392,3 @@ func (s *Ethereum) Stop() error {
return nil
}
-
-// This function will wait for a shutdown and resumes main thread execution
-func (s *Ethereum) WaitForShutdown() {
- <-s.shutdownChan
-}