github.com/ethereum/go-ethereum@v1.16.1/eth/backend.go (about)

     1  // Copyright 2014 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // Package eth implements the Ethereum protocol.
    18  package eth
    19  
    20  import (
    21  	"context"
    22  	"encoding/json"
    23  	"fmt"
    24  	"math"
    25  	"math/big"
    26  	"runtime"
    27  	"sync"
    28  	"time"
    29  
    30  	"github.com/ethereum/go-ethereum/accounts"
    31  	"github.com/ethereum/go-ethereum/common"
    32  	"github.com/ethereum/go-ethereum/common/hexutil"
    33  	"github.com/ethereum/go-ethereum/consensus"
    34  	"github.com/ethereum/go-ethereum/core"
    35  	"github.com/ethereum/go-ethereum/core/filtermaps"
    36  	"github.com/ethereum/go-ethereum/core/rawdb"
    37  	"github.com/ethereum/go-ethereum/core/state/pruner"
    38  	"github.com/ethereum/go-ethereum/core/txpool"
    39  	"github.com/ethereum/go-ethereum/core/txpool/blobpool"
    40  	"github.com/ethereum/go-ethereum/core/txpool/legacypool"
    41  	"github.com/ethereum/go-ethereum/core/txpool/locals"
    42  	"github.com/ethereum/go-ethereum/core/types"
    43  	"github.com/ethereum/go-ethereum/core/vm"
    44  	"github.com/ethereum/go-ethereum/eth/downloader"
    45  	"github.com/ethereum/go-ethereum/eth/ethconfig"
    46  	"github.com/ethereum/go-ethereum/eth/gasprice"
    47  	"github.com/ethereum/go-ethereum/eth/protocols/eth"
    48  	"github.com/ethereum/go-ethereum/eth/protocols/snap"
    49  	"github.com/ethereum/go-ethereum/eth/tracers"
    50  	"github.com/ethereum/go-ethereum/ethdb"
    51  	"github.com/ethereum/go-ethereum/event"
    52  	"github.com/ethereum/go-ethereum/internal/ethapi"
    53  	"github.com/ethereum/go-ethereum/internal/shutdowncheck"
    54  	"github.com/ethereum/go-ethereum/internal/version"
    55  	"github.com/ethereum/go-ethereum/log"
    56  	"github.com/ethereum/go-ethereum/miner"
    57  	"github.com/ethereum/go-ethereum/node"
    58  	"github.com/ethereum/go-ethereum/p2p"
    59  	"github.com/ethereum/go-ethereum/p2p/dnsdisc"
    60  	"github.com/ethereum/go-ethereum/p2p/enode"
    61  	"github.com/ethereum/go-ethereum/params"
    62  	"github.com/ethereum/go-ethereum/rlp"
    63  	"github.com/ethereum/go-ethereum/rpc"
    64  	gethversion "github.com/ethereum/go-ethereum/version"
    65  )
    66  
    67  const (
    68  	// This is the fairness knob for the discovery mixer. When looking for peers, we'll
    69  	// wait this long for a single source of candidates before moving on and trying other
    70  	// sources. If this timeout expires, the source will be skipped in this round, but it
    71  	// will continue to fetch in the background and will have a chance with a new timeout
    72  	// in the next rounds, giving it overall more time but a proportionally smaller share.
    73  	// We expect a normal source to produce ~10 candidates per second.
    74  	discmixTimeout = 100 * time.Millisecond
    75  
    76  	// discoveryPrefetchBuffer is the number of peers to pre-fetch from a discovery
    77  	// source. It is useful to avoid the negative effects of potential longer timeouts
    78  	// in the discovery, keeping dial progress while waiting for the next batch of
    79  	// candidates.
    80  	discoveryPrefetchBuffer = 32
    81  
    82  	// maxParallelENRRequests is the maximum number of parallel ENR requests that can be
    83  	// performed by a disc/v4 source.
    84  	maxParallelENRRequests = 16
    85  )
    86  
    87  // Config contains the configuration options of the ETH protocol.
    88  // Deprecated: use ethconfig.Config instead.
    89  type Config = ethconfig.Config
    90  
    91  // Ethereum implements the Ethereum full node service.
    92  type Ethereum struct {
    93  	// core protocol objects
    94  	config         *ethconfig.Config
    95  	txPool         *txpool.TxPool
    96  	blobTxPool     *blobpool.BlobPool
    97  	localTxTracker *locals.TxTracker
    98  	blockchain     *core.BlockChain
    99  
   100  	handler *handler
   101  	discmix *enode.FairMix
   102  	dropper *dropper
   103  
   104  	// DB interfaces
   105  	chainDb ethdb.Database // Block chain database
   106  
   107  	eventMux       *event.TypeMux
   108  	engine         consensus.Engine
   109  	accountManager *accounts.Manager
   110  
   111  	filterMaps      *filtermaps.FilterMaps
   112  	closeFilterMaps chan chan struct{}
   113  
   114  	APIBackend *EthAPIBackend
   115  
   116  	miner    *miner.Miner
   117  	gasPrice *big.Int
   118  
   119  	networkID     uint64
   120  	netRPCService *ethapi.NetAPI
   121  
   122  	p2pServer *p2p.Server
   123  
   124  	lock sync.RWMutex // Protects the variadic fields (e.g. gas price and etherbase)
   125  
   126  	shutdownTracker *shutdowncheck.ShutdownTracker // Tracks if and when the node has shutdown ungracefully
   127  }
   128  
   129  // New creates a new Ethereum object (including the initialisation of the common Ethereum object),
   130  // whose lifecycle will be managed by the provided node.
   131  func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
   132  	// Ensure configuration values are compatible and sane
   133  	if !config.SyncMode.IsValid() {
   134  		return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode)
   135  	}
   136  	if !config.HistoryMode.IsValid() {
   137  		return nil, fmt.Errorf("invalid history mode %d", config.HistoryMode)
   138  	}
   139  	if config.Miner.GasPrice == nil || config.Miner.GasPrice.Sign() <= 0 {
   140  		log.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", ethconfig.Defaults.Miner.GasPrice)
   141  		config.Miner.GasPrice = new(big.Int).Set(ethconfig.Defaults.Miner.GasPrice)
   142  	}
   143  	if config.NoPruning && config.TrieDirtyCache > 0 && config.StateScheme == rawdb.HashScheme {
   144  		if config.SnapshotCache > 0 {
   145  			config.TrieCleanCache += config.TrieDirtyCache * 3 / 5
   146  			config.SnapshotCache += config.TrieDirtyCache * 2 / 5
   147  		} else {
   148  			config.TrieCleanCache += config.TrieDirtyCache
   149  		}
   150  		config.TrieDirtyCache = 0
   151  	}
   152  	log.Info("Allocated trie memory caches", "clean", common.StorageSize(config.TrieCleanCache)*1024*1024, "dirty", common.StorageSize(config.TrieDirtyCache)*1024*1024)
   153  
   154  	dbOptions := node.DatabaseOptions{
   155  		Cache:             config.DatabaseCache,
   156  		Handles:           config.DatabaseHandles,
   157  		AncientsDirectory: config.DatabaseFreezer,
   158  		EraDirectory:      config.DatabaseEra,
   159  		MetricsNamespace:  "eth/db/chaindata/",
   160  	}
   161  	chainDb, err := stack.OpenDatabaseWithOptions("chaindata", dbOptions)
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  	scheme, err := rawdb.ParseStateScheme(config.StateScheme, chainDb)
   166  	if err != nil {
   167  		return nil, err
   168  	}
   169  	// Try to recover offline state pruning only in hash-based.
   170  	if scheme == rawdb.HashScheme {
   171  		if err := pruner.RecoverPruning(stack.ResolvePath(""), chainDb); err != nil {
   172  			log.Error("Failed to recover state", "error", err)
   173  		}
   174  	}
   175  
   176  	// Here we determine genesis hash and active ChainConfig.
   177  	// We need these to figure out the consensus parameters and to set up history pruning.
   178  	chainConfig, _, err := core.LoadChainConfig(chainDb, config.Genesis)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  	engine, err := ethconfig.CreateConsensusEngine(chainConfig, chainDb)
   183  	if err != nil {
   184  		return nil, err
   185  	}
   186  	// Set networkID to chainID by default.
   187  	networkID := config.NetworkId
   188  	if networkID == 0 {
   189  		networkID = chainConfig.ChainID.Uint64()
   190  	}
   191  
   192  	// Assemble the Ethereum object.
   193  	eth := &Ethereum{
   194  		config:          config,
   195  		chainDb:         chainDb,
   196  		eventMux:        stack.EventMux(),
   197  		accountManager:  stack.AccountManager(),
   198  		engine:          engine,
   199  		networkID:       networkID,
   200  		gasPrice:        config.Miner.GasPrice,
   201  		p2pServer:       stack.Server(),
   202  		discmix:         enode.NewFairMix(discmixTimeout),
   203  		shutdownTracker: shutdowncheck.NewShutdownTracker(chainDb),
   204  	}
   205  	bcVersion := rawdb.ReadDatabaseVersion(chainDb)
   206  	var dbVer = "<nil>"
   207  	if bcVersion != nil {
   208  		dbVer = fmt.Sprintf("%d", *bcVersion)
   209  	}
   210  	log.Info("Initialising Ethereum protocol", "network", networkID, "dbversion", dbVer)
   211  
   212  	// Create BlockChain object.
   213  	if !config.SkipBcVersionCheck {
   214  		if bcVersion != nil && *bcVersion > core.BlockChainVersion {
   215  			return nil, fmt.Errorf("database version is v%d, Geth %s only supports v%d", *bcVersion, version.WithMeta, core.BlockChainVersion)
   216  		} else if bcVersion == nil || *bcVersion < core.BlockChainVersion {
   217  			if bcVersion != nil { // only print warning on upgrade, not on init
   218  				log.Warn("Upgrade blockchain database version", "from", dbVer, "to", core.BlockChainVersion)
   219  			}
   220  			rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion)
   221  		}
   222  	}
   223  	var (
   224  		options = &core.BlockChainConfig{
   225  			TrieCleanLimit:   config.TrieCleanCache,
   226  			NoPrefetch:       config.NoPrefetch,
   227  			TrieDirtyLimit:   config.TrieDirtyCache,
   228  			ArchiveMode:      config.NoPruning,
   229  			TrieTimeLimit:    config.TrieTimeout,
   230  			SnapshotLimit:    config.SnapshotCache,
   231  			Preimages:        config.Preimages,
   232  			StateHistory:     config.StateHistory,
   233  			StateScheme:      scheme,
   234  			ChainHistoryMode: config.HistoryMode,
   235  			TxLookupLimit:    int64(min(config.TransactionHistory, math.MaxInt64)),
   236  			VmConfig: vm.Config{
   237  				EnablePreimageRecording: config.EnablePreimageRecording,
   238  			},
   239  		}
   240  	)
   241  
   242  	if config.VMTrace != "" {
   243  		traceConfig := json.RawMessage("{}")
   244  		if config.VMTraceJsonConfig != "" {
   245  			traceConfig = json.RawMessage(config.VMTraceJsonConfig)
   246  		}
   247  		t, err := tracers.LiveDirectory.New(config.VMTrace, traceConfig)
   248  		if err != nil {
   249  			return nil, fmt.Errorf("failed to create tracer %s: %v", config.VMTrace, err)
   250  		}
   251  		options.VmConfig.Tracer = t
   252  	}
   253  	// Override the chain config with provided settings.
   254  	var overrides core.ChainOverrides
   255  	if config.OverrideOsaka != nil {
   256  		overrides.OverrideOsaka = config.OverrideOsaka
   257  	}
   258  	if config.OverrideVerkle != nil {
   259  		overrides.OverrideVerkle = config.OverrideVerkle
   260  	}
   261  	options.Overrides = &overrides
   262  
   263  	eth.blockchain, err = core.NewBlockChain(chainDb, config.Genesis, eth.engine, options)
   264  	if err != nil {
   265  		return nil, err
   266  	}
   267  
   268  	// Initialize filtermaps log index.
   269  	fmConfig := filtermaps.Config{
   270  		History:        config.LogHistory,
   271  		Disabled:       config.LogNoHistory,
   272  		ExportFileName: config.LogExportCheckpoints,
   273  		HashScheme:     scheme == rawdb.HashScheme,
   274  	}
   275  	chainView := eth.newChainView(eth.blockchain.CurrentBlock())
   276  	historyCutoff, _ := eth.blockchain.HistoryPruningCutoff()
   277  	var finalBlock uint64
   278  	if fb := eth.blockchain.CurrentFinalBlock(); fb != nil {
   279  		finalBlock = fb.Number.Uint64()
   280  	}
   281  	filterMaps, err := filtermaps.NewFilterMaps(chainDb, chainView, historyCutoff, finalBlock, filtermaps.DefaultParams, fmConfig)
   282  	if err != nil {
   283  		return nil, err
   284  	}
   285  	eth.filterMaps = filterMaps
   286  	eth.closeFilterMaps = make(chan chan struct{})
   287  
   288  	// TxPool
   289  	if config.TxPool.Journal != "" {
   290  		config.TxPool.Journal = stack.ResolvePath(config.TxPool.Journal)
   291  	}
   292  	legacyPool := legacypool.New(config.TxPool, eth.blockchain)
   293  
   294  	if config.BlobPool.Datadir != "" {
   295  		config.BlobPool.Datadir = stack.ResolvePath(config.BlobPool.Datadir)
   296  	}
   297  	eth.blobTxPool = blobpool.New(config.BlobPool, eth.blockchain, legacyPool.HasPendingAuth)
   298  
   299  	eth.txPool, err = txpool.New(config.TxPool.PriceLimit, eth.blockchain, []txpool.SubPool{legacyPool, eth.blobTxPool})
   300  	if err != nil {
   301  		return nil, err
   302  	}
   303  
   304  	if !config.TxPool.NoLocals {
   305  		rejournal := config.TxPool.Rejournal
   306  		if rejournal < time.Second {
   307  			log.Warn("Sanitizing invalid txpool journal time", "provided", rejournal, "updated", time.Second)
   308  			rejournal = time.Second
   309  		}
   310  		eth.localTxTracker = locals.New(config.TxPool.Journal, rejournal, eth.blockchain.Config(), eth.txPool)
   311  		stack.RegisterLifecycle(eth.localTxTracker)
   312  	}
   313  
   314  	// Permit the downloader to use the trie cache allowance during fast sync
   315  	cacheLimit := options.TrieCleanLimit + options.TrieDirtyLimit + options.SnapshotLimit
   316  	if eth.handler, err = newHandler(&handlerConfig{
   317  		NodeID:         eth.p2pServer.Self().ID(),
   318  		Database:       chainDb,
   319  		Chain:          eth.blockchain,
   320  		TxPool:         eth.txPool,
   321  		Network:        networkID,
   322  		Sync:           config.SyncMode,
   323  		BloomCache:     uint64(cacheLimit),
   324  		EventMux:       eth.eventMux,
   325  		RequiredBlocks: config.RequiredBlocks,
   326  	}); err != nil {
   327  		return nil, err
   328  	}
   329  
   330  	eth.dropper = newDropper(eth.p2pServer.MaxDialedConns(), eth.p2pServer.MaxInboundConns())
   331  
   332  	eth.miner = miner.New(eth, config.Miner, eth.engine)
   333  	eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData))
   334  	eth.miner.SetPrioAddresses(config.TxPool.Locals)
   335  
   336  	eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil}
   337  	if eth.APIBackend.allowUnprotectedTxs {
   338  		log.Info("Unprotected transactions allowed")
   339  	}
   340  	eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, config.GPO, config.Miner.GasPrice)
   341  
   342  	// Start the RPC service
   343  	eth.netRPCService = ethapi.NewNetAPI(eth.p2pServer, networkID)
   344  
   345  	// Register the backend on the node
   346  	stack.RegisterAPIs(eth.APIs())
   347  	stack.RegisterProtocols(eth.Protocols())
   348  	stack.RegisterLifecycle(eth)
   349  
   350  	// Successful startup; push a marker and check previous unclean shutdowns.
   351  	eth.shutdownTracker.MarkStartup()
   352  
   353  	return eth, nil
   354  }
   355  
   356  func makeExtraData(extra []byte) []byte {
   357  	if len(extra) == 0 {
   358  		// create default extradata
   359  		extra, _ = rlp.EncodeToBytes([]interface{}{
   360  			uint(gethversion.Major<<16 | gethversion.Minor<<8 | gethversion.Patch),
   361  			"geth",
   362  			runtime.Version(),
   363  			runtime.GOOS,
   364  		})
   365  	}
   366  	if uint64(len(extra)) > params.MaximumExtraDataSize {
   367  		log.Warn("Miner extra data exceed limit", "extra", hexutil.Bytes(extra), "limit", params.MaximumExtraDataSize)
   368  		extra = nil
   369  	}
   370  	return extra
   371  }
   372  
   373  // APIs return the collection of RPC services the ethereum package offers.
   374  // NOTE, some of these services probably need to be moved to somewhere else.
   375  func (s *Ethereum) APIs() []rpc.API {
   376  	apis := ethapi.GetAPIs(s.APIBackend)
   377  
   378  	// Append all the local APIs and return
   379  	return append(apis, []rpc.API{
   380  		{
   381  			Namespace: "miner",
   382  			Service:   NewMinerAPI(s),
   383  		}, {
   384  			Namespace: "eth",
   385  			Service:   downloader.NewDownloaderAPI(s.handler.downloader, s.blockchain, s.eventMux),
   386  		}, {
   387  			Namespace: "admin",
   388  			Service:   NewAdminAPI(s),
   389  		}, {
   390  			Namespace: "debug",
   391  			Service:   NewDebugAPI(s),
   392  		}, {
   393  			Namespace: "net",
   394  			Service:   s.netRPCService,
   395  		},
   396  	}...)
   397  }
   398  
   399  func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) {
   400  	s.blockchain.ResetWithGenesisBlock(gb)
   401  }
   402  
   403  func (s *Ethereum) Miner() *miner.Miner { return s.miner }
   404  
   405  func (s *Ethereum) AccountManager() *accounts.Manager  { return s.accountManager }
   406  func (s *Ethereum) BlockChain() *core.BlockChain       { return s.blockchain }
   407  func (s *Ethereum) TxPool() *txpool.TxPool             { return s.txPool }
   408  func (s *Ethereum) BlobTxPool() *blobpool.BlobPool     { return s.blobTxPool }
   409  func (s *Ethereum) Engine() consensus.Engine           { return s.engine }
   410  func (s *Ethereum) ChainDb() ethdb.Database            { return s.chainDb }
   411  func (s *Ethereum) IsListening() bool                  { return true } // Always listening
   412  func (s *Ethereum) Downloader() *downloader.Downloader { return s.handler.downloader }
   413  func (s *Ethereum) Synced() bool                       { return s.handler.synced.Load() }
   414  func (s *Ethereum) SetSynced()                         { s.handler.enableSyncedFeatures() }
   415  func (s *Ethereum) ArchiveMode() bool                  { return s.config.NoPruning }
   416  
   417  // Protocols returns all the currently configured
   418  // network protocols to start.
   419  func (s *Ethereum) Protocols() []p2p.Protocol {
   420  	protos := eth.MakeProtocols((*ethHandler)(s.handler), s.networkID, s.discmix)
   421  	if s.config.SnapshotCache > 0 {
   422  		protos = append(protos, snap.MakeProtocols((*snapHandler)(s.handler))...)
   423  	}
   424  	return protos
   425  }
   426  
   427  // Start implements node.Lifecycle, starting all internal goroutines needed by the
   428  // Ethereum protocol implementation.
   429  func (s *Ethereum) Start() error {
   430  	if err := s.setupDiscovery(); err != nil {
   431  		return err
   432  	}
   433  
   434  	// Regularly update shutdown marker
   435  	s.shutdownTracker.Start()
   436  
   437  	// Start the networking layer
   438  	s.handler.Start(s.p2pServer.MaxPeers)
   439  
   440  	// Start the connection manager
   441  	s.dropper.Start(s.p2pServer, func() bool { return !s.Synced() })
   442  
   443  	// start log indexer
   444  	s.filterMaps.Start()
   445  	go s.updateFilterMapsHeads()
   446  	return nil
   447  }
   448  
   449  func (s *Ethereum) newChainView(head *types.Header) *filtermaps.ChainView {
   450  	if head == nil {
   451  		return nil
   452  	}
   453  	return filtermaps.NewChainView(s.blockchain, head.Number.Uint64(), head.Hash())
   454  }
   455  
   456  func (s *Ethereum) updateFilterMapsHeads() {
   457  	headEventCh := make(chan core.ChainEvent, 10)
   458  	blockProcCh := make(chan bool, 10)
   459  	sub := s.blockchain.SubscribeChainEvent(headEventCh)
   460  	sub2 := s.blockchain.SubscribeBlockProcessingEvent(blockProcCh)
   461  	defer func() {
   462  		sub.Unsubscribe()
   463  		sub2.Unsubscribe()
   464  		for {
   465  			select {
   466  			case <-headEventCh:
   467  			case <-blockProcCh:
   468  			default:
   469  				return
   470  			}
   471  		}
   472  	}()
   473  
   474  	var head *types.Header
   475  	setHead := func(newHead *types.Header) {
   476  		if newHead == nil {
   477  			return
   478  		}
   479  		if head == nil || newHead.Hash() != head.Hash() {
   480  			head = newHead
   481  			chainView := s.newChainView(head)
   482  			historyCutoff, _ := s.blockchain.HistoryPruningCutoff()
   483  			var finalBlock uint64
   484  			if fb := s.blockchain.CurrentFinalBlock(); fb != nil {
   485  				finalBlock = fb.Number.Uint64()
   486  			}
   487  			s.filterMaps.SetTarget(chainView, historyCutoff, finalBlock)
   488  		}
   489  	}
   490  	setHead(s.blockchain.CurrentBlock())
   491  
   492  	for {
   493  		select {
   494  		case ev := <-headEventCh:
   495  			setHead(ev.Header)
   496  		case blockProc := <-blockProcCh:
   497  			s.filterMaps.SetBlockProcessing(blockProc)
   498  		case <-time.After(time.Second * 10):
   499  			setHead(s.blockchain.CurrentBlock())
   500  		case ch := <-s.closeFilterMaps:
   501  			close(ch)
   502  			return
   503  		}
   504  	}
   505  }
   506  
   507  func (s *Ethereum) setupDiscovery() error {
   508  	eth.StartENRUpdater(s.blockchain, s.p2pServer.LocalNode())
   509  
   510  	// Add eth nodes from DNS.
   511  	dnsclient := dnsdisc.NewClient(dnsdisc.Config{})
   512  	if len(s.config.EthDiscoveryURLs) > 0 {
   513  		iter, err := dnsclient.NewIterator(s.config.EthDiscoveryURLs...)
   514  		if err != nil {
   515  			return err
   516  		}
   517  		s.discmix.AddSource(iter)
   518  	}
   519  
   520  	// Add snap nodes from DNS.
   521  	if len(s.config.SnapDiscoveryURLs) > 0 {
   522  		iter, err := dnsclient.NewIterator(s.config.SnapDiscoveryURLs...)
   523  		if err != nil {
   524  			return err
   525  		}
   526  		s.discmix.AddSource(iter)
   527  	}
   528  
   529  	// Add DHT nodes from discv4.
   530  	if s.p2pServer.DiscoveryV4() != nil {
   531  		iter := s.p2pServer.DiscoveryV4().RandomNodes()
   532  		resolverFunc := func(ctx context.Context, enr *enode.Node) *enode.Node {
   533  			// RequestENR does not yet support context. It will simply time out.
   534  			// If the ENR can't be resolved, RequestENR will return nil. We don't
   535  			// care about the specific error here, so we ignore it.
   536  			nn, _ := s.p2pServer.DiscoveryV4().RequestENR(enr)
   537  			return nn
   538  		}
   539  		iter = enode.AsyncFilter(iter, resolverFunc, maxParallelENRRequests)
   540  		iter = enode.Filter(iter, eth.NewNodeFilter(s.blockchain))
   541  		iter = enode.NewBufferIter(iter, discoveryPrefetchBuffer)
   542  		s.discmix.AddSource(iter)
   543  	}
   544  
   545  	// Add DHT nodes from discv5.
   546  	if s.p2pServer.DiscoveryV5() != nil {
   547  		filter := eth.NewNodeFilter(s.blockchain)
   548  		iter := enode.Filter(s.p2pServer.DiscoveryV5().RandomNodes(), filter)
   549  		iter = enode.NewBufferIter(iter, discoveryPrefetchBuffer)
   550  		s.discmix.AddSource(iter)
   551  	}
   552  
   553  	return nil
   554  }
   555  
   556  // Stop implements node.Lifecycle, terminating all internal goroutines used by the
   557  // Ethereum protocol.
   558  func (s *Ethereum) Stop() error {
   559  	// Stop all the peer-related stuff first.
   560  	s.discmix.Close()
   561  	s.dropper.Stop()
   562  	s.handler.Stop()
   563  
   564  	// Then stop everything else.
   565  	ch := make(chan struct{})
   566  	s.closeFilterMaps <- ch
   567  	<-ch
   568  	s.filterMaps.Stop()
   569  	s.txPool.Close()
   570  	s.blockchain.Stop()
   571  	s.engine.Close()
   572  
   573  	// Clean shutdown marker as the last thing before closing db
   574  	s.shutdownTracker.Stop()
   575  
   576  	s.chainDb.Close()
   577  	s.eventMux.Stop()
   578  
   579  	return nil
   580  }
   581  
   582  // SyncMode retrieves the current sync mode, either explicitly set, or derived
   583  // from the chain status.
   584  func (s *Ethereum) SyncMode() ethconfig.SyncMode {
   585  	// If we're in snap sync mode, return that directly
   586  	if s.handler.snapSync.Load() {
   587  		return ethconfig.SnapSync
   588  	}
   589  	// We are probably in full sync, but we might have rewound to before the
   590  	// snap sync pivot, check if we should re-enable snap sync.
   591  	head := s.blockchain.CurrentBlock()
   592  	if pivot := rawdb.ReadLastPivotNumber(s.chainDb); pivot != nil {
   593  		if head.Number.Uint64() < *pivot {
   594  			return ethconfig.SnapSync
   595  		}
   596  	}
   597  	// We are in a full sync, but the associated head state is missing. To complete
   598  	// the head state, forcefully rerun the snap sync. Note it doesn't mean the
   599  	// persistent state is corrupted, just mismatch with the head block.
   600  	if !s.blockchain.HasState(head.Root) {
   601  		log.Info("Reenabled snap sync as chain is stateless")
   602  		return ethconfig.SnapSync
   603  	}
   604  	// Nope, we're really full syncing
   605  	return ethconfig.FullSync
   606  }