github.com/core-coin/go-core/v2@v2.1.9/xcb/backend.go (about)

     1  // Copyright 2023 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core 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-core 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-core library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // Package xcb implements the Core protocol.
    18  package xcb
    19  
    20  import (
    21  	"errors"
    22  	"fmt"
    23  	"math/big"
    24  	"runtime"
    25  	"sync"
    26  	"sync/atomic"
    27  
    28  	"github.com/core-coin/go-core/v2/xcb/energyprice"
    29  
    30  	"github.com/core-coin/go-core/v2/xcbdb"
    31  
    32  	"github.com/core-coin/go-core/v2/consensus/cryptore"
    33  
    34  	"github.com/core-coin/go-core/v2/internal/xcbapi"
    35  
    36  	"github.com/core-coin/go-core/v2/accounts"
    37  	"github.com/core-coin/go-core/v2/common"
    38  	"github.com/core-coin/go-core/v2/common/hexutil"
    39  	"github.com/core-coin/go-core/v2/consensus"
    40  	"github.com/core-coin/go-core/v2/consensus/clique"
    41  	"github.com/core-coin/go-core/v2/core"
    42  	"github.com/core-coin/go-core/v2/core/bloombits"
    43  	"github.com/core-coin/go-core/v2/core/rawdb"
    44  	"github.com/core-coin/go-core/v2/core/types"
    45  	"github.com/core-coin/go-core/v2/core/vm"
    46  	"github.com/core-coin/go-core/v2/event"
    47  	"github.com/core-coin/go-core/v2/log"
    48  	"github.com/core-coin/go-core/v2/miner"
    49  	"github.com/core-coin/go-core/v2/node"
    50  	"github.com/core-coin/go-core/v2/p2p"
    51  	"github.com/core-coin/go-core/v2/p2p/enode"
    52  	"github.com/core-coin/go-core/v2/p2p/enr"
    53  	"github.com/core-coin/go-core/v2/params"
    54  	"github.com/core-coin/go-core/v2/rlp"
    55  	"github.com/core-coin/go-core/v2/rpc"
    56  	"github.com/core-coin/go-core/v2/xcb/downloader"
    57  	"github.com/core-coin/go-core/v2/xcb/filters"
    58  )
    59  
    60  // Core implements the Core full node service.
    61  type Core struct {
    62  	config *Config
    63  
    64  	// Handlers
    65  	txPool          *core.TxPool
    66  	blockchain      *core.BlockChain
    67  	protocolManager *ProtocolManager
    68  	dialCandidates  enode.Iterator
    69  
    70  	// DB interfaces
    71  	chainDb xcbdb.Database // Block chain database
    72  
    73  	eventMux       *event.TypeMux
    74  	engine         consensus.Engine
    75  	accountManager *accounts.Manager
    76  
    77  	bloomRequests     chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests
    78  	bloomIndexer      *core.ChainIndexer             // Bloom indexer operating during block imports
    79  	closeBloomHandler chan struct{}
    80  
    81  	APIBackend *XcbAPIBackend
    82  
    83  	miner       *miner.Miner
    84  	energyPrice *big.Int
    85  	corebase    common.Address
    86  
    87  	networkID     uint64
    88  	netRPCService *xcbapi.PublicNetAPI
    89  
    90  	p2pServer *p2p.Server
    91  
    92  	lock sync.RWMutex // Protects the variadic fields (e.g. energy price and corebase)
    93  }
    94  
    95  // New creates a new Core object (including the
    96  // initialisation of the common Core object)
    97  func New(stack *node.Node, config *Config) (*Core, error) {
    98  	// Ensure configuration values are compatible and sane
    99  	if config.SyncMode == downloader.LightSync {
   100  		return nil, errors.New("can't run xcb.Core in light sync mode, use les.LightCore")
   101  	}
   102  	if !config.SyncMode.IsValid() {
   103  		return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode)
   104  	}
   105  	if config.Miner.EnergyPrice == nil || config.Miner.EnergyPrice.Cmp(common.Big0) <= 0 {
   106  		log.Warn("Sanitizing invalid miner energy price", "provided", config.Miner.EnergyPrice, "updated", DefaultConfig.Miner.EnergyPrice)
   107  		config.Miner.EnergyPrice = new(big.Int).Set(DefaultConfig.Miner.EnergyPrice)
   108  	}
   109  	if config.NoPruning && config.TrieDirtyCache > 0 {
   110  		if config.SnapshotCache > 0 {
   111  			config.TrieCleanCache += config.TrieDirtyCache * 3 / 5
   112  			config.SnapshotCache += config.TrieDirtyCache * 2 / 5
   113  		} else {
   114  			config.TrieCleanCache += config.TrieDirtyCache
   115  		}
   116  		config.TrieDirtyCache = 0
   117  	}
   118  	log.Info("Allocated trie memory caches", "clean", common.StorageSize(config.TrieCleanCache)*1024*1024, "dirty", common.StorageSize(config.TrieDirtyCache)*1024*1024)
   119  
   120  	// Assemble the Core object
   121  	chainDb, err := stack.OpenDatabaseWithFreezer("chaindata", config.DatabaseCache, config.DatabaseHandles, config.DatabaseFreezer, "xcb/db/chaindata/")
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  	chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis)
   126  	if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok {
   127  		return nil, genesisErr
   128  	}
   129  	log.Info("Initialised chain configuration", "config", chainConfig)
   130  
   131  	xcb := &Core{
   132  		config:            config,
   133  		chainDb:           chainDb,
   134  		eventMux:          stack.EventMux(),
   135  		accountManager:    stack.AccountManager(),
   136  		engine:            CreateConsensusEngine(stack, chainConfig, &config.Cryptore, config.Miner.Notify, config.Miner.Noverify, chainDb),
   137  		closeBloomHandler: make(chan struct{}),
   138  		networkID:         config.NetworkId,
   139  		energyPrice:       config.Miner.EnergyPrice,
   140  		corebase:          config.Miner.Corebase,
   141  		bloomRequests:     make(chan chan *bloombits.Retrieval),
   142  		bloomIndexer:      NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms),
   143  		p2pServer:         stack.Server(),
   144  	}
   145  
   146  	bcVersion := rawdb.ReadDatabaseVersion(chainDb)
   147  	var dbVer = "<nil>"
   148  	if bcVersion != nil {
   149  		dbVer = fmt.Sprintf("%d", *bcVersion)
   150  	}
   151  	log.Info("Initialising Core protocol", "versions", ProtocolVersions, "network", config.NetworkId, "dbversion", dbVer)
   152  
   153  	if !config.SkipBcVersionCheck {
   154  		if bcVersion != nil && *bcVersion > core.BlockChainVersion {
   155  			return nil, fmt.Errorf("database version is v%d, Gocore only supports v%d", *bcVersion, core.BlockChainVersion)
   156  		} else if bcVersion == nil || *bcVersion < core.BlockChainVersion {
   157  			log.Warn("Upgrade blockchain database version", "from", dbVer, "to", core.BlockChainVersion)
   158  			rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion)
   159  		}
   160  	}
   161  	var (
   162  		vmConfig = vm.Config{
   163  			EnablePreimageRecording: config.EnablePreimageRecording,
   164  			EWASMInterpreter:        config.EWASMInterpreter,
   165  			CVMInterpreter:          config.CVMInterpreter,
   166  		}
   167  		cacheConfig = &core.CacheConfig{
   168  			TrieCleanLimit:      config.TrieCleanCache,
   169  			TrieCleanJournal:    stack.ResolvePath(config.TrieCleanCacheJournal),
   170  			TrieCleanRejournal:  config.TrieCleanCacheRejournal,
   171  			TrieCleanNoPrefetch: config.NoPrefetch,
   172  			TrieDirtyLimit:      config.TrieDirtyCache,
   173  			TrieDirtyDisabled:   config.NoPruning,
   174  			TrieTimeLimit:       config.TrieTimeout,
   175  			SnapshotLimit:       config.SnapshotCache,
   176  			Preimages:           config.Preimages,
   177  		}
   178  	)
   179  	xcb.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, xcb.engine, vmConfig, xcb.shouldPreserve, &config.TxLookupLimit)
   180  	if err != nil {
   181  		return nil, err
   182  	}
   183  	// Rewind the chain in case of an incompatible config upgrade.
   184  	if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
   185  		log.Warn("Rewinding chain to upgrade configuration", "err", compat)
   186  		xcb.blockchain.SetHead(compat.RewindTo)
   187  		rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig)
   188  	}
   189  	xcb.bloomIndexer.Start(xcb.blockchain)
   190  
   191  	if config.TxPool.Journal != "" {
   192  		config.TxPool.Journal = stack.ResolvePath(config.TxPool.Journal)
   193  	}
   194  	xcb.txPool = core.NewTxPool(config.TxPool, chainConfig, xcb.blockchain)
   195  
   196  	// Permit the downloader to use the trie cache allowance during fast sync
   197  	cacheLimit := cacheConfig.TrieCleanLimit + cacheConfig.TrieDirtyLimit + cacheConfig.SnapshotLimit
   198  	checkpoint := config.Checkpoint
   199  	if checkpoint == nil {
   200  		checkpoint = params.TrustedCheckpoints[genesisHash]
   201  	}
   202  	if xcb.protocolManager, err = NewProtocolManager(chainConfig, checkpoint, config.SyncMode, config.NetworkId, xcb.eventMux, xcb.txPool, xcb.engine, xcb.blockchain, chainDb, cacheLimit, config.Whitelist, stack.Config().BTTP); err != nil {
   203  		return nil, err
   204  	}
   205  	xcb.miner = miner.New(xcb, &config.Miner, chainConfig, xcb.EventMux(), xcb.engine, xcb.isLocalBlock)
   206  	xcb.miner.SetExtra(makeExtraData(config.Miner.ExtraData))
   207  
   208  	xcb.APIBackend = &XcbAPIBackend{stack.Config().ExtRPCEnabled(), xcb, nil}
   209  	gpoParams := config.GPO
   210  	if gpoParams.Default == nil {
   211  		gpoParams.Default = config.Miner.EnergyPrice
   212  	}
   213  	xcb.APIBackend.gpo = energyprice.NewOracle(xcb.APIBackend, gpoParams)
   214  
   215  	xcb.dialCandidates, err = xcb.setupDiscovery()
   216  	if err != nil {
   217  		return nil, err
   218  	}
   219  
   220  	// Start the RPC service
   221  	xcb.netRPCService = xcbapi.NewPublicNetAPI(xcb.p2pServer, xcb.NetVersion())
   222  
   223  	// Register the backend on the node
   224  	stack.RegisterAPIs(xcb.APIs())
   225  	stack.RegisterProtocols(xcb.Protocols())
   226  	stack.RegisterLifecycle(xcb)
   227  	return xcb, nil
   228  }
   229  
   230  func makeExtraData(extra []byte) []byte {
   231  	if len(extra) == 0 {
   232  		// create default extradata
   233  		extra, _ = rlp.EncodeToBytes([]interface{}{
   234  			"gocore",
   235  			runtime.Version(),
   236  			runtime.GOOS,
   237  		})
   238  	}
   239  	if uint64(len(extra)) > params.MaximumExtraDataSize {
   240  		log.Warn("Miner extra data exceed limit", "extra", hexutil.Bytes(extra), "limit", params.MaximumExtraDataSize)
   241  		extra = nil
   242  	}
   243  	return extra
   244  }
   245  
   246  // CreateConsensusEngine creates the required type of consensus engine instance for an Core service
   247  func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, config *cryptore.Config, notify []string, noverify bool, db xcbdb.Database) consensus.Engine {
   248  	// If proof-of-authority is requested, set it up
   249  	if chainConfig.Clique != nil {
   250  		return clique.New(chainConfig.Clique, db)
   251  	}
   252  	// Otherwise assume proof-of-work
   253  	switch config.PowMode {
   254  	case cryptore.ModeFake:
   255  		log.Warn("Cryptore used in fake mode")
   256  		return cryptore.NewFaker()
   257  	case cryptore.ModeTest:
   258  		log.Warn("Cryptore used in test mode")
   259  		return cryptore.NewTester(nil, noverify)
   260  	case cryptore.ModeShared:
   261  		log.Warn("Cryptore used in shared mode")
   262  		return cryptore.NewShared()
   263  	default:
   264  		engine := cryptore.New(cryptore.Config{}, notify, noverify)
   265  		engine.SetThreads(-1) // Disable CPU mining
   266  		return engine
   267  	}
   268  }
   269  
   270  // APIs return the collection of RPC services the core package offers.
   271  // NOTE, some of these services probably need to be moved to somewhere else.
   272  func (s *Core) APIs() []rpc.API {
   273  	apis := xcbapi.GetAPIs(s.APIBackend)
   274  
   275  	// Append any APIs exposed explicitly by the consensus engine
   276  	apis = append(apis, s.engine.APIs(s.BlockChain())...)
   277  
   278  	// Append all the local APIs and return
   279  	return append(apis, []rpc.API{
   280  		{
   281  			Namespace: "xcb",
   282  			Version:   "1.0",
   283  			Service:   NewPublicCoreAPI(s),
   284  			Public:    true,
   285  		}, {
   286  			Namespace: "xcb",
   287  			Version:   "1.0",
   288  			Service:   NewPublicMinerAPI(s),
   289  			Public:    true,
   290  		}, {
   291  			Namespace: "xcb",
   292  			Version:   "1.0",
   293  			Service:   downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux),
   294  			Public:    true,
   295  		}, {
   296  			Namespace: "miner",
   297  			Version:   "1.0",
   298  			Service:   NewPrivateMinerAPI(s),
   299  			Public:    false,
   300  		}, {
   301  			Namespace: "xcb",
   302  			Version:   "1.0",
   303  			Service:   filters.NewPublicFilterAPI(s.APIBackend, false),
   304  			Public:    true,
   305  		}, {
   306  			Namespace: "admin",
   307  			Version:   "1.0",
   308  			Service:   NewPrivateAdminAPI(s),
   309  		}, {
   310  			Namespace: "debug",
   311  			Version:   "1.0",
   312  			Service:   NewPublicDebugAPI(s),
   313  			Public:    true,
   314  		}, {
   315  			Namespace: "debug",
   316  			Version:   "1.0",
   317  			Service:   NewPrivateDebugAPI(s),
   318  		}, {
   319  			Namespace: "net",
   320  			Version:   "1.0",
   321  			Service:   s.netRPCService,
   322  			Public:    true,
   323  		},
   324  	}...)
   325  }
   326  
   327  func (s *Core) ResetWithGenesisBlock(gb *types.Block) {
   328  	s.blockchain.ResetWithGenesisBlock(gb)
   329  }
   330  
   331  func (s *Core) Corebase() (eb common.Address, err error) {
   332  	s.lock.RLock()
   333  	corebase := s.corebase
   334  	s.lock.RUnlock()
   335  
   336  	if corebase != (common.Address{}) {
   337  		return corebase, nil
   338  	}
   339  	if wallets := s.AccountManager().Wallets(); len(wallets) > 0 {
   340  		if accounts := wallets[0].Accounts(); len(accounts) > 0 {
   341  			corebase := accounts[0].Address
   342  
   343  			s.lock.Lock()
   344  			s.corebase = corebase
   345  			s.lock.Unlock()
   346  
   347  			log.Info("Corebase automatically configured", "address", corebase)
   348  			return corebase, nil
   349  		}
   350  	}
   351  	return common.Address{}, fmt.Errorf("corebase must be explicitly specified")
   352  }
   353  
   354  // isLocalBlock checks whether the specified block is mined
   355  // by local miner accounts.
   356  //
   357  // We regard two types of accounts as local miner account: corebase
   358  // and accounts specified via `txpool.locals` flag.
   359  func (s *Core) isLocalBlock(block *types.Block) bool {
   360  	author, err := s.engine.Author(block.Header())
   361  	if err != nil {
   362  		log.Warn("Failed to retrieve block author", "number", block.NumberU64(), "hash", block.Hash(), "err", err)
   363  		return false
   364  	}
   365  	// Check whether the given address is corebase.
   366  	s.lock.RLock()
   367  	corebase := s.corebase
   368  	s.lock.RUnlock()
   369  	if author == corebase {
   370  		return true
   371  	}
   372  	// Check whether the given address is specified by `txpool.local`
   373  	// CLI flag.
   374  	for _, account := range s.config.TxPool.Locals {
   375  		if account == author {
   376  			return true
   377  		}
   378  	}
   379  	return false
   380  }
   381  
   382  // shouldPreserve checks whether we should preserve the given block
   383  // during the chain reorg depending on whether the author of block
   384  // is a local account.
   385  func (s *Core) shouldPreserve(block *types.Block) bool {
   386  	// The reason we need to disable the self-reorg preserving for clique
   387  	// is it can be probable to introduce a deadlock.
   388  	//
   389  	// e.g. If there are 7 available signers
   390  	//
   391  	// r1   A
   392  	// r2     B
   393  	// r3       C
   394  	// r4         D
   395  	// r5   A      [X] F G
   396  	// r6    [X]
   397  	//
   398  	// In the round5, the inturn signer E is offline, so the worst case
   399  	// is A, F and G sign the block of round5 and reject the block of opponents
   400  	// and in the round6, the last available signer B is offline, the whole
   401  	// network is stuck.
   402  	if _, ok := s.engine.(*clique.Clique); ok {
   403  		return false
   404  	}
   405  	return s.isLocalBlock(block)
   406  }
   407  
   408  // SetCorebase sets the mining reward address.
   409  func (s *Core) SetCorebase(corebase common.Address) {
   410  	s.lock.Lock()
   411  	s.corebase = corebase
   412  	s.lock.Unlock()
   413  
   414  	s.miner.SetCorebase(corebase)
   415  }
   416  
   417  // StartMining starts the miner with the given number of CPU threads. If mining
   418  // is already running, this method adjust the number of threads allowed to use
   419  // and updates the minimum price required by the transaction pool.
   420  func (s *Core) StartMining(threads int) error {
   421  	// Update the thread count within the consensus engine
   422  	type threaded interface {
   423  		SetThreads(threads int)
   424  	}
   425  	if th, ok := s.engine.(threaded); ok {
   426  		log.Info("Updated mining threads", "threads", threads)
   427  		if threads == 0 {
   428  			threads = -1 // Disable the miner from within
   429  		}
   430  		th.SetThreads(threads)
   431  	}
   432  	// If the miner was not running, initialize it
   433  	if !s.IsMining() {
   434  		// Propagate the initial price point to the transaction pool
   435  		s.lock.RLock()
   436  		price := s.energyPrice
   437  		s.lock.RUnlock()
   438  		s.txPool.SetEnergyPrice(price)
   439  
   440  		// Configure the local mining address
   441  		eb, err := s.Corebase()
   442  		if err != nil {
   443  			log.Error("Cannot start mining without corebase", "err", err)
   444  			return fmt.Errorf("corebase missing: %v", err)
   445  		}
   446  		if clique, ok := s.engine.(*clique.Clique); ok {
   447  			wallet, err := s.accountManager.Find(accounts.Account{Address: eb})
   448  			if wallet == nil || err != nil {
   449  				log.Error("Corebase account unavailable locally", "err", err)
   450  				return fmt.Errorf("signer missing: %v", err)
   451  			}
   452  			clique.Authorize(eb, wallet.SignData)
   453  		}
   454  		// If mining is started, we can disable the transaction rejection mechanism
   455  		// introduced to speed sync times.
   456  		atomic.StoreUint32(&s.protocolManager.acceptTxs, 1)
   457  
   458  		go s.miner.Start(eb)
   459  	}
   460  	return nil
   461  }
   462  
   463  // StopMining terminates the miner, both at the consensus engine level as well as
   464  // at the block creation level.
   465  func (s *Core) StopMining() {
   466  	// Update the thread count within the consensus engine
   467  	type threaded interface {
   468  		SetThreads(threads int)
   469  	}
   470  	if th, ok := s.engine.(threaded); ok {
   471  		th.SetThreads(-1)
   472  	}
   473  	// Stop the block creating itself
   474  	s.miner.Stop()
   475  }
   476  
   477  func (s *Core) IsMining() bool      { return s.miner.Mining() }
   478  func (s *Core) Miner() *miner.Miner { return s.miner }
   479  
   480  func (s *Core) AccountManager() *accounts.Manager  { return s.accountManager }
   481  func (s *Core) BlockChain() *core.BlockChain       { return s.blockchain }
   482  func (s *Core) TxPool() *core.TxPool               { return s.txPool }
   483  func (s *Core) EventMux() *event.TypeMux           { return s.eventMux }
   484  func (s *Core) Engine() consensus.Engine           { return s.engine }
   485  func (s *Core) ChainDb() xcbdb.Database            { return s.chainDb }
   486  func (s *Core) IsListening() bool                  { return true } // Always listening
   487  func (s *Core) XcbVersion() int                    { return int(ProtocolVersions[0]) }
   488  func (s *Core) NetVersion() uint64                 { return s.networkID }
   489  func (s *Core) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
   490  func (s *Core) Synced() bool                       { return atomic.LoadUint32(&s.protocolManager.acceptTxs) == 1 }
   491  func (s *Core) ArchiveMode() bool                  { return s.config.NoPruning }
   492  func (s *Core) BloomIndexer() *core.ChainIndexer   { return s.bloomIndexer }
   493  
   494  // Protocols returns all the currently configured
   495  // network protocols to start.
   496  func (s *Core) Protocols() []p2p.Protocol {
   497  	protos := make([]p2p.Protocol, len(ProtocolVersions))
   498  	for i, vsn := range ProtocolVersions {
   499  		protos[i] = s.protocolManager.makeProtocol(vsn)
   500  		protos[i].Attributes = []enr.Entry{s.currentXcbEntry()}
   501  		protos[i].DialCandidates = s.dialCandidates
   502  	}
   503  	return protos
   504  }
   505  
   506  // Start implements node.Lifecycle, starting all internal goroutines needed by the
   507  // Core protocol implementation.
   508  func (s *Core) Start() error {
   509  	s.startXcbEntryUpdate(s.p2pServer.LocalNode())
   510  
   511  	// Start the bloom bits servicing goroutines
   512  	s.startBloomHandlers(params.BloomBitsBlocks)
   513  
   514  	// Figure out a max peers count based on the server limits
   515  	maxPeers := s.p2pServer.MaxPeers
   516  	if s.config.LightServ > 0 {
   517  		if s.config.LightPeers >= s.p2pServer.MaxPeers {
   518  			return fmt.Errorf("invalid peer config: light peer count (%d) >= total peer count (%d)", s.config.LightPeers, s.p2pServer.MaxPeers)
   519  		}
   520  		maxPeers -= s.config.LightPeers
   521  	}
   522  	// Start the networking layer and the light server if requested
   523  	s.protocolManager.Start(maxPeers)
   524  	return nil
   525  }
   526  
   527  // Stop implements node.Lifecycle, terminating all internal goroutines used by the
   528  // Core protocol.
   529  func (s *Core) Stop() error {
   530  	// Stop all the peer-related stuff first.
   531  	s.protocolManager.Stop()
   532  
   533  	// Then stop everything else.
   534  	s.bloomIndexer.Close()
   535  	close(s.closeBloomHandler)
   536  	s.txPool.Stop()
   537  	s.miner.Stop()
   538  	s.blockchain.Stop()
   539  	s.engine.Close()
   540  	s.chainDb.Close()
   541  	s.eventMux.Stop()
   542  	return nil
   543  }