node: refactor package node (#21105)
This PR significantly changes the APIs for instantiating Ethereum nodes in a Go program. The new APIs are not backwards-compatible, but we feel that this is made up for by the much simpler way of registering services on node.Node. You can find more information and rationale in the design document: https://gist.github.com/renaynay/5bec2de19fde66f4d04c535fd24f0775. There is also a new feature in Node's Go API: it is now possible to register arbitrary handlers on the user-facing HTTP server. In geth, this facility is used to enable GraphQL. There is a single minor change relevant for geth users in this PR: The GraphQL API is no longer available separately from the JSON-RPC HTTP server. If you want GraphQL, you need to enable it using the ./geth --http --graphql flag combination. The --graphql.port and --graphql.addr flags are no longer available.
This commit is contained in:
@ -26,7 +26,6 @@ import (
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
@ -54,15 +53,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/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
|
||||
@ -71,7 +61,6 @@ type Ethereum struct {
|
||||
txPool *core.TxPool
|
||||
blockchain *core.BlockChain
|
||||
protocolManager *ProtocolManager
|
||||
lesServer LesServer
|
||||
dialCandidates enode.Iterator
|
||||
|
||||
// DB interfaces
|
||||
@ -94,25 +83,14 @@ type Ethereum struct {
|
||||
networkID uint64
|
||||
netRPCService *ethapi.PublicNetAPI
|
||||
|
||||
p2pServer *p2p.Server
|
||||
|
||||
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) {
|
||||
func New(stack *node.Node, 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")
|
||||
@ -136,7 +114,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||
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.DatabaseCache, config.DatabaseHandles, config.DatabaseFreezer, "eth/db/chaindata/")
|
||||
chainDb, err := stack.OpenDatabaseWithFreezer("chaindata", config.DatabaseCache, config.DatabaseHandles, config.DatabaseFreezer, "eth/db/chaindata/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -149,15 +127,16 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||
eth := &Ethereum{
|
||||
config: config,
|
||||
chainDb: chainDb,
|
||||
eventMux: ctx.EventMux,
|
||||
accountManager: ctx.AccountManager,
|
||||
engine: CreateConsensusEngine(ctx, chainConfig, &config.Ethash, config.Miner.Notify, config.Miner.Noverify, chainDb),
|
||||
eventMux: stack.EventMux(),
|
||||
accountManager: stack.AccountManager(),
|
||||
engine: CreateConsensusEngine(stack, 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),
|
||||
p2pServer: stack.Server(),
|
||||
}
|
||||
|
||||
bcVersion := rawdb.ReadDatabaseVersion(chainDb)
|
||||
@ -183,7 +162,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||
}
|
||||
cacheConfig = &core.CacheConfig{
|
||||
TrieCleanLimit: config.TrieCleanCache,
|
||||
TrieCleanJournal: ctx.ResolvePath(config.TrieCleanCacheJournal),
|
||||
TrieCleanJournal: stack.ResolvePath(config.TrieCleanCacheJournal),
|
||||
TrieCleanRejournal: config.TrieCleanCacheRejournal,
|
||||
TrieCleanNoPrefetch: config.NoPrefetch,
|
||||
TrieDirtyLimit: config.TrieDirtyCache,
|
||||
@ -205,7 +184,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||
eth.bloomIndexer.Start(eth.blockchain)
|
||||
|
||||
if config.TxPool.Journal != "" {
|
||||
config.TxPool.Journal = ctx.ResolvePath(config.TxPool.Journal)
|
||||
config.TxPool.Journal = stack.ResolvePath(config.TxPool.Journal)
|
||||
}
|
||||
eth.txPool = core.NewTxPool(config.TxPool, chainConfig, eth.blockchain)
|
||||
|
||||
@ -221,18 +200,25 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||
eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock)
|
||||
eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData))
|
||||
|
||||
eth.APIBackend = &EthAPIBackend{ctx.ExtRPCEnabled(), eth, nil}
|
||||
eth.APIBackend = &EthAPIBackend{stack.Config().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)
|
||||
eth.dialCandidates, err = eth.setupDiscovery(&stack.Config().P2P)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Start the RPC service
|
||||
eth.netRPCService = ethapi.NewPublicNetAPI(eth.p2pServer, eth.NetVersion())
|
||||
|
||||
// Register the backend on the node
|
||||
stack.RegisterAPIs(eth.APIs())
|
||||
stack.RegisterProtocols(eth.Protocols())
|
||||
stack.RegisterLifecycle(eth)
|
||||
return eth, nil
|
||||
}
|
||||
|
||||
@ -254,7 +240,7 @@ func makeExtraData(extra []byte) []byte {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
func CreateConsensusEngine(stack *node.Node, 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)
|
||||
@ -272,7 +258,7 @@ func CreateConsensusEngine(ctx *node.ServiceContext, chainConfig *params.ChainCo
|
||||
return ethash.NewShared()
|
||||
default:
|
||||
engine := ethash.New(ethash.Config{
|
||||
CacheDir: ctx.ResolvePath(config.CacheDir),
|
||||
CacheDir: stack.ResolvePath(config.CacheDir),
|
||||
CachesInMem: config.CachesInMem,
|
||||
CachesOnDisk: config.CachesOnDisk,
|
||||
CachesLockMmap: config.CachesLockMmap,
|
||||
@ -291,18 +277,9 @@ func CreateConsensusEngine(ctx *node.ServiceContext, chainConfig *params.ChainCo
|
||||
func (s *Ethereum) APIs() []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{
|
||||
{
|
||||
@ -517,8 +494,9 @@ 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.NoPruning }
|
||||
func (s *Ethereum) BloomIndexer() *core.ChainIndexer { return s.bloomIndexer }
|
||||
|
||||
// Protocols implements node.Service, returning all the currently configured
|
||||
// Protocols returns all the currently configured
|
||||
// network protocols to start.
|
||||
func (s *Ethereum) Protocols() []p2p.Protocol {
|
||||
protos := make([]p2p.Protocol, len(ProtocolVersions))
|
||||
@ -527,47 +505,35 @@ func (s *Ethereum) Protocols() []p2p.Protocol {
|
||||
protos[i].Attributes = []enr.Entry{s.currentEthEntry()}
|
||||
protos[i].DialCandidates = s.dialCandidates
|
||||
}
|
||||
if s.lesServer != nil {
|
||||
protos = append(protos, s.lesServer.Protocols()...)
|
||||
}
|
||||
return protos
|
||||
}
|
||||
|
||||
// Start implements node.Service, starting all internal goroutines needed by the
|
||||
// Start implements node.Lifecycle, starting all internal goroutines needed by the
|
||||
// Ethereum protocol implementation.
|
||||
func (s *Ethereum) Start(srvr *p2p.Server) error {
|
||||
s.startEthEntryUpdate(srvr.LocalNode())
|
||||
func (s *Ethereum) Start() error {
|
||||
s.startEthEntryUpdate(s.p2pServer.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
|
||||
maxPeers := s.p2pServer.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)
|
||||
if s.config.LightPeers >= s.p2pServer.MaxPeers {
|
||||
return fmt.Errorf("invalid peer config: light peer count (%d) >= total peer count (%d)", s.config.LightPeers, s.p2pServer.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
|
||||
// Stop implements node.Lifecycle, 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()
|
||||
|
Reference in New Issue
Block a user