mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-24 12:37:16 +00:00
1ca95e8352
This PR reimplements the light client server pool. It is also a first step to move certain logic into a new lespay package. This package will contain the implementation of the lespay token sale functions, the token buying and selling logic and other components related to peer selection/prioritization and service quality evaluation. Over the long term this package will be reusable for incentivizing future protocols. Since the LES peer logic is now based on enode.Iterator, it can now use DNS-based fallback discovery to find servers. This document describes the function of the new components: https://gist.github.com/zsfelfoldi/3c7ace895234b7b345ab4f71dab102d4 # Conflicts: # cmd/utils/flags.go # core/forkid/forkid.go # les/client.go # les/client_handler.go # les/commons.go # les/distributor.go # les/enr_entry.go # les/fetcher.go # les/lespay/client/valuetracker.go # les/metrics.go # les/peer.go # les/protocol.go # les/retrieve.go # les/server.go # les/serverpool.go # les/test_helper.go # les/utils/expiredvalue.go # les/utils/weighted_select.go # les/utils/weighted_select_test.go # params/bootnodes.go
664 lines
22 KiB
Go
664 lines
22 KiB
Go
// Copyright 2014 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 <http://www.gnu.org/licenses/>.
|
|
|
|
// Package eth implements the Ethereum protocol.
|
|
package eth
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math/big"
|
|
"reflect"
|
|
"runtime"
|
|
"sync"
|
|
"sync/atomic"
|
|
|
|
"github.com/ledgerwatch/turbo-geth/accounts"
|
|
"github.com/ledgerwatch/turbo-geth/accounts/abi/bind"
|
|
"github.com/ledgerwatch/turbo-geth/common"
|
|
"github.com/ledgerwatch/turbo-geth/common/hexutil"
|
|
"github.com/ledgerwatch/turbo-geth/consensus"
|
|
"github.com/ledgerwatch/turbo-geth/consensus/clique"
|
|
"github.com/ledgerwatch/turbo-geth/consensus/ethash"
|
|
"github.com/ledgerwatch/turbo-geth/core"
|
|
"github.com/ledgerwatch/turbo-geth/core/bloombits"
|
|
"github.com/ledgerwatch/turbo-geth/core/rawdb"
|
|
"github.com/ledgerwatch/turbo-geth/core/types"
|
|
"github.com/ledgerwatch/turbo-geth/core/vm"
|
|
"github.com/ledgerwatch/turbo-geth/eth/downloader"
|
|
"github.com/ledgerwatch/turbo-geth/eth/filters"
|
|
"github.com/ledgerwatch/turbo-geth/eth/gasprice"
|
|
"github.com/ledgerwatch/turbo-geth/ethdb"
|
|
"github.com/ledgerwatch/turbo-geth/ethdb/remote/remotedbserver"
|
|
"github.com/ledgerwatch/turbo-geth/event"
|
|
"github.com/ledgerwatch/turbo-geth/internal/ethapi"
|
|
"github.com/ledgerwatch/turbo-geth/log"
|
|
"github.com/ledgerwatch/turbo-geth/migrations"
|
|
"github.com/ledgerwatch/turbo-geth/miner"
|
|
"github.com/ledgerwatch/turbo-geth/node"
|
|
"github.com/ledgerwatch/turbo-geth/p2p"
|
|
"github.com/ledgerwatch/turbo-geth/p2p/enode"
|
|
"github.com/ledgerwatch/turbo-geth/p2p/enr"
|
|
"github.com/ledgerwatch/turbo-geth/params"
|
|
"github.com/ledgerwatch/turbo-geth/rlp"
|
|
"github.com/ledgerwatch/turbo-geth/rpc"
|
|
)
|
|
|
|
type LesServer interface {
|
|
Start(srvr *p2p.Server)
|
|
Stop()
|
|
APIs() []rpc.API
|
|
Protocols() []p2p.Protocol
|
|
SetBloomBitsIndexer(bbIndexer *core.ChainIndexer)
|
|
SetContractBackend(bind.ContractBackend)
|
|
}
|
|
|
|
// Ethereum implements the Ethereum full node service.
|
|
type Ethereum struct {
|
|
config *Config
|
|
|
|
// Handlers
|
|
txPool *core.TxPool
|
|
blockchain *core.BlockChain
|
|
protocolManager *ProtocolManager
|
|
lesServer LesServer
|
|
dialCandidates enode.Iterator
|
|
|
|
// DB interfaces
|
|
chainDb ethdb.Database // Block chain database
|
|
chainKV ethdb.KV // Same as chainDb, but different interface
|
|
|
|
eventMux *event.TypeMux
|
|
engine consensus.Engine
|
|
accountManager *accounts.Manager
|
|
|
|
bloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests
|
|
bloomIndexer *core.ChainIndexer // Bloom indexer operating during block imports
|
|
closeBloomHandler chan struct{}
|
|
|
|
APIBackend *EthAPIBackend
|
|
|
|
miner *miner.Miner
|
|
gasPrice *big.Int
|
|
etherbase common.Address
|
|
|
|
networkID uint64
|
|
netRPCService *ethapi.PublicNetAPI
|
|
|
|
lock sync.RWMutex // Protects the variadic fields (e.g. gas price and etherbase)
|
|
}
|
|
|
|
func (s *Ethereum) AddLesServer(ls LesServer) {
|
|
s.lesServer = ls
|
|
ls.SetBloomBitsIndexer(s.bloomIndexer)
|
|
}
|
|
|
|
// SetClient sets a rpc client which connecting to our local node.
|
|
func (s *Ethereum) SetContractBackend(backend bind.ContractBackend) {
|
|
// Pass the rpc client to les server if it is enabled.
|
|
if s.lesServer != nil {
|
|
s.lesServer.SetContractBackend(backend)
|
|
}
|
|
}
|
|
|
|
// New creates a new Ethereum object (including the
|
|
// initialisation of the common Ethereum object)
|
|
func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
|
// Ensure configuration values are compatible and sane
|
|
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)
|
|
}
|
|
if config.Miner.GasPrice == nil || config.Miner.GasPrice.Cmp(common.Big0) <= 0 {
|
|
log.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", DefaultConfig.Miner.GasPrice)
|
|
config.Miner.GasPrice = new(big.Int).Set(DefaultConfig.Miner.GasPrice)
|
|
}
|
|
if !config.Pruning && config.TrieDirtyCache > 0 {
|
|
if config.SnapshotCache > 0 {
|
|
config.TrieCleanCache += config.TrieDirtyCache * 3 / 5
|
|
config.SnapshotCache += config.TrieDirtyCache * 2 / 5
|
|
} else {
|
|
config.TrieCleanCache += config.TrieDirtyCache
|
|
}
|
|
config.TrieDirtyCache = 0
|
|
}
|
|
log.Info("Allocated trie memory caches", "clean", common.StorageSize(config.TrieCleanCache)*1024*1024, "dirty", common.StorageSize(config.TrieDirtyCache)*1024*1024)
|
|
|
|
// Assemble the Ethereum object
|
|
chainDb, err := ctx.OpenDatabaseWithFreezer("chaindata", config.DatabaseFreezer)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var chainKV ethdb.KV
|
|
if hasKV, ok := chainDb.(ethdb.HasKV); ok {
|
|
chainKV = hasKV.KV()
|
|
} else {
|
|
return nil, fmt.Errorf("database %T does not implement KV", chainDb)
|
|
}
|
|
if ctx.Config.RemoteDbListenAddress != "" {
|
|
remotedbserver.StartDeprecated(chainKV, ctx.Config.RemoteDbListenAddress)
|
|
}
|
|
|
|
chainConfig, genesisHash, _, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis, config.StorageMode.History)
|
|
|
|
if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok {
|
|
return nil, genesisErr
|
|
}
|
|
log.Info("Initialised chain configuration", "config", chainConfig)
|
|
|
|
eth := &Ethereum{
|
|
config: config,
|
|
chainDb: chainDb,
|
|
chainKV: chainKV,
|
|
eventMux: ctx.EventMux,
|
|
accountManager: ctx.AccountManager,
|
|
engine: CreateConsensusEngine(ctx, chainConfig, &config.Ethash, config.Miner.Notify, config.Miner.Noverify, chainDb),
|
|
closeBloomHandler: make(chan struct{}),
|
|
networkID: config.NetworkID,
|
|
gasPrice: config.Miner.GasPrice,
|
|
etherbase: config.Miner.Etherbase,
|
|
bloomRequests: make(chan chan *bloombits.Retrieval),
|
|
bloomIndexer: NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms),
|
|
}
|
|
|
|
log.Info("Initialising Ethereum protocol", "versions", ProtocolVersions, "network", config.NetworkID)
|
|
|
|
bcVersion := rawdb.ReadDatabaseVersion(chainDb)
|
|
var dbVer = "<nil>"
|
|
if bcVersion != nil {
|
|
dbVer = fmt.Sprintf("%d", *bcVersion)
|
|
}
|
|
|
|
if !config.SkipBcVersionCheck {
|
|
if bcVersion != nil && *bcVersion > core.BlockChainVersion {
|
|
return nil, fmt.Errorf("database version is v%d, Geth %s only supports v%d", *bcVersion, params.VersionWithMeta, core.BlockChainVersion)
|
|
} else if bcVersion == nil || *bcVersion < core.BlockChainVersion {
|
|
log.Warn("Upgrade blockchain database version", "from", dbVer, "to", core.BlockChainVersion)
|
|
rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion)
|
|
}
|
|
}
|
|
|
|
err = ethdb.SetStorageModeIfNotExist(chainDb, config.StorageMode)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
sm, err := ethdb.GetStorageModeFromDB(chainDb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !reflect.DeepEqual(sm, config.StorageMode) {
|
|
return nil, errors.New("mode is " + config.StorageMode.ToString() + " original mode is " + sm.ToString())
|
|
}
|
|
|
|
err = migrations.NewMigrator().Apply(
|
|
chainDb,
|
|
config.StorageMode.History,
|
|
config.StorageMode.Receipts,
|
|
config.StorageMode.TxIndex,
|
|
config.StorageMode.Preimages,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var (
|
|
vmConfig = vm.Config{
|
|
EnablePreimageRecording: config.EnablePreimageRecording,
|
|
EWASMInterpreter: config.EWASMInterpreter,
|
|
EVMInterpreter: config.EVMInterpreter,
|
|
}
|
|
cacheConfig = &core.CacheConfig{
|
|
Pruning: config.Pruning,
|
|
BlocksBeforePruning: config.BlocksBeforePruning,
|
|
BlocksToPrune: config.BlocksToPrune,
|
|
PruneTimeout: config.PruningTimeout,
|
|
TrieCleanLimit: config.TrieCleanCache,
|
|
TrieCleanNoPrefetch: config.NoPrefetch,
|
|
TrieDirtyLimit: config.TrieDirtyCache,
|
|
TrieTimeLimit: config.TrieTimeout,
|
|
DownloadOnly: config.DownloadOnly,
|
|
NoHistory: !config.StorageMode.History,
|
|
ArchiveSyncInterval: uint64(config.ArchiveSyncInterval),
|
|
}
|
|
)
|
|
|
|
dests := vm.NewDestsCache(50000)
|
|
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit, dests)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if config.SyncMode != downloader.StagedSync {
|
|
_, err = eth.blockchain.GetTrieDbState()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
eth.blockchain.EnableReceipts(config.StorageMode.Receipts)
|
|
eth.blockchain.EnableTxLookupIndex(config.StorageMode.TxIndex)
|
|
eth.blockchain.EnablePreimages(config.StorageMode.Preimages)
|
|
|
|
// 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)
|
|
rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig)
|
|
}
|
|
if config.SyncMode != downloader.StagedSync {
|
|
eth.bloomIndexer.Start(eth.blockchain)
|
|
}
|
|
|
|
if config.TxPool.Journal != "" {
|
|
config.TxPool.Journal = ctx.ResolvePath(config.TxPool.Journal)
|
|
}
|
|
if config.SyncMode != downloader.StagedSync {
|
|
eth.txPool = core.NewTxPool(config.TxPool, chainConfig, eth.blockchain)
|
|
}
|
|
|
|
checkpoint := config.Checkpoint
|
|
if checkpoint == nil {
|
|
//checkpoint = params.TrustedCheckpoints[genesisHash]
|
|
}
|
|
|
|
if eth.protocolManager, err = NewProtocolManager(chainConfig, checkpoint, config.SyncMode, config.NetworkID, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb, config.Whitelist); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
eth.protocolManager.SetDataDir(ctx.Config.DataDir)
|
|
|
|
if config.SyncMode != downloader.StagedSync {
|
|
eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock, dests)
|
|
_ = eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData))
|
|
}
|
|
|
|
if config.SyncMode != downloader.StagedSync {
|
|
eth.APIBackend = &EthAPIBackend{ctx.ExtRPCEnabled(), eth, nil}
|
|
gpoParams := config.GPO
|
|
if gpoParams.Default == nil {
|
|
gpoParams.Default = config.Miner.GasPrice
|
|
}
|
|
eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, gpoParams)
|
|
}
|
|
|
|
eth.dialCandidates, err = eth.setupDiscovery(&ctx.Config.P2P)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
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),
|
|
"turbo-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
|
|
}
|
|
|
|
// CreateConsensusEngine creates the required type of consensus engine instance for an Ethereum service
|
|
func CreateConsensusEngine(ctx *node.ServiceContext, chainConfig *params.ChainConfig, config *ethash.Config, notify []string, noverify bool, db ethdb.Database) consensus.Engine {
|
|
// If proof-of-authority is requested, set it up
|
|
if chainConfig.Clique != nil {
|
|
return clique.New(chainConfig.Clique, db)
|
|
}
|
|
// Otherwise assume proof-of-work
|
|
switch config.PowMode {
|
|
case ethash.ModeFake:
|
|
log.Warn("Ethash used in fake mode")
|
|
return ethash.NewFaker()
|
|
case ethash.ModeTest:
|
|
log.Warn("Ethash used in test mode")
|
|
return ethash.NewTester(nil, noverify)
|
|
case ethash.ModeShared:
|
|
log.Warn("Ethash used in shared mode")
|
|
return ethash.NewShared()
|
|
default:
|
|
engine := ethash.New(ethash.Config{
|
|
CacheDir: ctx.ResolvePath(config.CacheDir),
|
|
CachesInMem: config.CachesInMem,
|
|
CachesOnDisk: config.CachesOnDisk,
|
|
CachesLockMmap: config.CachesLockMmap,
|
|
DatasetDir: config.DatasetDir,
|
|
DatasetsInMem: config.DatasetsInMem,
|
|
DatasetsOnDisk: config.DatasetsOnDisk,
|
|
DatasetsLockMmap: config.DatasetsLockMmap,
|
|
}, notify, noverify)
|
|
engine.SetThreads(-1) // Disable CPU mining
|
|
return engine
|
|
}
|
|
}
|
|
|
|
// APIs return 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 {
|
|
if s.APIBackend == nil {
|
|
return []rpc.API{}
|
|
}
|
|
apis := ethapi.GetAPIs(s.APIBackend)
|
|
|
|
// Append any APIs exposed explicitly by the les server
|
|
if s.lesServer != nil {
|
|
apis = append(apis, s.lesServer.APIs()...)
|
|
}
|
|
// Append any APIs exposed explicitly by the consensus engine
|
|
apis = append(apis, s.engine.APIs(s.BlockChain())...)
|
|
|
|
// Append any APIs exposed explicitly by the les server
|
|
if s.lesServer != nil {
|
|
apis = append(apis, s.lesServer.APIs()...)
|
|
}
|
|
|
|
// Append all the local APIs and return
|
|
return append(apis, []rpc.API{
|
|
{
|
|
Namespace: "eth",
|
|
Version: "1.0",
|
|
Service: NewPublicEthereumAPI(s),
|
|
Public: true,
|
|
}, {
|
|
Namespace: "eth",
|
|
Version: "1.0",
|
|
Service: NewPublicMinerAPI(s),
|
|
Public: true,
|
|
}, {
|
|
Namespace: "eth",
|
|
Version: "1.0",
|
|
Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux),
|
|
Public: true,
|
|
}, {
|
|
Namespace: "miner",
|
|
Version: "1.0",
|
|
Service: NewPrivateMinerAPI(s),
|
|
Public: false,
|
|
}, {
|
|
Namespace: "eth",
|
|
Version: "1.0",
|
|
Service: filters.NewPublicFilterAPI(s.APIBackend, false),
|
|
Public: true,
|
|
}, {
|
|
Namespace: "admin",
|
|
Version: "1.0",
|
|
Service: NewPrivateAdminAPI(s),
|
|
}, {
|
|
Namespace: "debug",
|
|
Version: "1.0",
|
|
Service: NewPublicDebugAPI(s),
|
|
Public: true,
|
|
}, {
|
|
Namespace: "debug",
|
|
Version: "1.0",
|
|
Service: NewPrivateDebugAPI(s),
|
|
}, {
|
|
Namespace: "net",
|
|
Version: "1.0",
|
|
Service: s.netRPCService,
|
|
Public: true,
|
|
},
|
|
}...)
|
|
}
|
|
|
|
func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) {
|
|
s.blockchain.ResetWithGenesisBlock(gb)
|
|
}
|
|
|
|
func (s *Ethereum) Etherbase() (eb common.Address, err error) {
|
|
s.lock.RLock()
|
|
etherbase := s.etherbase
|
|
s.lock.RUnlock()
|
|
|
|
if etherbase != (common.Address{}) {
|
|
return etherbase, nil
|
|
}
|
|
if wallets := s.AccountManager().Wallets(); len(wallets) > 0 {
|
|
if accounts := wallets[0].Accounts(); len(accounts) > 0 {
|
|
etherbase := accounts[0].Address
|
|
|
|
s.lock.Lock()
|
|
s.etherbase = etherbase
|
|
s.lock.Unlock()
|
|
|
|
log.Info("Etherbase automatically configured", "address", etherbase)
|
|
return etherbase, nil
|
|
}
|
|
}
|
|
return common.Address{}, fmt.Errorf("etherbase must be explicitly specified")
|
|
}
|
|
|
|
// isLocalBlock checks whether the specified block is mined
|
|
// by local miner accounts.
|
|
//
|
|
// We regard two types of accounts as local miner account: etherbase
|
|
// and accounts specified via `txpool.locals` flag.
|
|
func (s *Ethereum) isLocalBlock(block *types.Block) bool {
|
|
author, err := s.engine.Author(block.Header())
|
|
if err != nil {
|
|
log.Warn("Failed to retrieve block author", "number", block.NumberU64(), "hash", block.Hash(), "err", err)
|
|
return false
|
|
}
|
|
// Check whether the given address is etherbase.
|
|
s.lock.RLock()
|
|
etherbase := s.etherbase
|
|
s.lock.RUnlock()
|
|
if author == etherbase {
|
|
return true
|
|
}
|
|
// Check whether the given address is specified by `txpool.local`
|
|
// CLI flag.
|
|
for _, account := range s.config.TxPool.Locals {
|
|
if account == author {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// shouldPreserve checks whether we should preserve the given block
|
|
// during the chain reorg depending on whether the author of block
|
|
// is a local account.
|
|
func (s *Ethereum) shouldPreserve(block *types.Block) bool {
|
|
// The reason we need to disable the self-reorg preserving for clique
|
|
// is it can be probable to introduce a deadlock.
|
|
//
|
|
// e.g. If there are 7 available signers
|
|
//
|
|
// r1 A
|
|
// r2 B
|
|
// r3 C
|
|
// r4 D
|
|
// r5 A [X] F G
|
|
// r6 [X]
|
|
//
|
|
// In the round5, the inturn signer E is offline, so the worst case
|
|
// is A, F and G sign the block of round5 and reject the block of opponents
|
|
// and in the round6, the last available signer B is offline, the whole
|
|
// network is stuck.
|
|
if _, ok := s.engine.(*clique.Clique); ok {
|
|
return false
|
|
}
|
|
return s.isLocalBlock(block)
|
|
}
|
|
|
|
// SetEtherbase sets the mining reward address.
|
|
func (s *Ethereum) SetEtherbase(etherbase common.Address) {
|
|
s.lock.Lock()
|
|
s.etherbase = etherbase
|
|
s.lock.Unlock()
|
|
|
|
s.miner.SetEtherbase(etherbase)
|
|
}
|
|
|
|
// StartMining starts the miner with the given number of CPU threads. If mining
|
|
// is already running, this method adjust the number of threads allowed to use
|
|
// and updates the minimum price required by the transaction pool.
|
|
func (s *Ethereum) StartMining(threads int, dests vm.Cache) error {
|
|
// Update the thread count within the consensus engine
|
|
type threaded interface {
|
|
SetThreads(threads int)
|
|
}
|
|
if th, ok := s.engine.(threaded); ok {
|
|
log.Info("Updated mining threads", "threads", threads)
|
|
if threads == 0 {
|
|
threads = -1 // Disable the miner from within
|
|
}
|
|
th.SetThreads(threads)
|
|
}
|
|
// If the miner was not running, initialize it
|
|
if !s.IsMining() {
|
|
// Propagate the initial price point to the transaction pool
|
|
s.lock.RLock()
|
|
price := s.gasPrice
|
|
s.lock.RUnlock()
|
|
s.txPool.SetGasPrice(price)
|
|
|
|
// Configure the local mining address
|
|
eb, err := s.Etherbase()
|
|
if err != nil {
|
|
log.Error("Cannot start mining without etherbase", "err", err)
|
|
return fmt.Errorf("etherbase missing: %v", err)
|
|
}
|
|
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("signer missing: %v", err)
|
|
}
|
|
clique.Authorize(eb, wallet.SignData)
|
|
}
|
|
// If mining is started, we can disable the transaction rejection mechanism
|
|
// introduced to speed sync times.
|
|
atomic.StoreUint32(&s.protocolManager.acceptTxs, 1)
|
|
|
|
go s.miner.Start(eb, dests)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// StopMining terminates the miner, both at the consensus engine level as well as
|
|
// at the block creation level.
|
|
func (s *Ethereum) StopMining() {
|
|
// Update the thread count within the consensus engine
|
|
type threaded interface {
|
|
SetThreads(threads int)
|
|
}
|
|
if th, ok := s.engine.(threaded); ok {
|
|
th.SetThreads(-1)
|
|
}
|
|
// Stop the block creating itself
|
|
s.miner.Stop()
|
|
}
|
|
|
|
func (s *Ethereum) IsMining() bool { return s.miner.Mining() }
|
|
func (s *Ethereum) Miner() *miner.Miner { return s.miner }
|
|
|
|
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) Engine() consensus.Engine { return s.engine }
|
|
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
|
|
func (s *Ethereum) ChainKV() ethdb.KV { return s.chainKV }
|
|
func (s *Ethereum) IsListening() bool { return true } // Always listening
|
|
func (s *Ethereum) EthVersion() int { return int(ProtocolVersions[0]) }
|
|
func (s *Ethereum) NetVersion() uint64 { return s.networkID }
|
|
func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
|
|
func (s *Ethereum) Synced() bool { return atomic.LoadUint32(&s.protocolManager.acceptTxs) == 1 }
|
|
func (s *Ethereum) ArchiveMode() bool { return !s.config.Pruning }
|
|
|
|
// Protocols implements node.Service, returning all the currently configured
|
|
// network protocols to start.
|
|
func (s *Ethereum) Protocols() []p2p.Protocol {
|
|
protos := make([]p2p.Protocol, len(ProtocolVersions))
|
|
for i, vsn := range ProtocolVersions {
|
|
protos[i] = s.protocolManager.makeProtocol(vsn)
|
|
protos[i].Attributes = []enr.Entry{s.currentEthEntry()}
|
|
protos[i].DialCandidates = s.dialCandidates
|
|
}
|
|
|
|
if s.config.EnableDebugProtocol {
|
|
// Debug
|
|
protos = append(protos, s.protocolManager.makeDebugProtocol())
|
|
}
|
|
|
|
// MGR
|
|
protos = append(protos, s.protocolManager.makeMgrProtocol())
|
|
|
|
if s.lesServer != nil {
|
|
protos = append(protos, s.lesServer.Protocols()...)
|
|
}
|
|
return protos
|
|
}
|
|
|
|
// Start implements node.Service, starting all internal goroutines needed by the
|
|
// Ethereum protocol implementation.
|
|
func (s *Ethereum) Start(srvr *p2p.Server) error {
|
|
s.startEthEntryUpdate(srvr.LocalNode())
|
|
|
|
// Start the bloom bits servicing goroutines
|
|
s.startBloomHandlers(params.BloomBitsBlocks)
|
|
|
|
// Start the RPC service
|
|
s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.NetVersion())
|
|
|
|
// Figure out a max peers count based on the server limits
|
|
maxPeers := srvr.MaxPeers
|
|
if s.config.LightServ > 0 {
|
|
if s.config.LightPeers >= srvr.MaxPeers {
|
|
return fmt.Errorf("invalid peer config: light peer count (%d) >= total peer count (%d)", s.config.LightPeers, srvr.MaxPeers)
|
|
}
|
|
maxPeers -= s.config.LightPeers
|
|
}
|
|
// Start the networking layer and the light server if requested
|
|
s.protocolManager.Start(maxPeers)
|
|
if s.lesServer != nil {
|
|
s.lesServer.Start(srvr)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Stop implements node.Service, terminating all internal goroutines used by the
|
|
// Ethereum protocol.
|
|
func (s *Ethereum) Stop() error {
|
|
// Stop all the peer-related stuff first.
|
|
s.protocolManager.Stop()
|
|
if s.lesServer != nil {
|
|
s.lesServer.Stop()
|
|
}
|
|
|
|
// Then stop everything else.
|
|
s.bloomIndexer.Close()
|
|
close(s.closeBloomHandler)
|
|
s.txPool.Stop()
|
|
s.miner.Stop()
|
|
s.blockchain.Stop()
|
|
s.engine.Close()
|
|
s.chainDb.Close()
|
|
s.eventMux.Stop()
|
|
return nil
|
|
}
|