github.com/phillinzzz/newBsc@v1.1.6/node/node.go (about)

     1  // Copyright 2015 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 node
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"net/http"
    23  	"os"
    24  	"path"
    25  	"path/filepath"
    26  	"reflect"
    27  	"strings"
    28  	"sync"
    29  
    30  	"github.com/phillinzzz/newBsc/accounts"
    31  	"github.com/phillinzzz/newBsc/core/rawdb"
    32  	"github.com/phillinzzz/newBsc/ethdb"
    33  	"github.com/phillinzzz/newBsc/ethdb/leveldb"
    34  	"github.com/phillinzzz/newBsc/event"
    35  	"github.com/phillinzzz/newBsc/log"
    36  	"github.com/phillinzzz/newBsc/p2p"
    37  	"github.com/phillinzzz/newBsc/rpc"
    38  	"github.com/prometheus/tsdb/fileutil"
    39  )
    40  
    41  // Node is a container on which services can be registered.
    42  type Node struct {
    43  	eventmux      *event.TypeMux
    44  	config        *Config
    45  	accman        *accounts.Manager
    46  	log           log.Logger
    47  	ephemKeystore string            // if non-empty, the key directory that will be removed by Stop
    48  	dirLock       fileutil.Releaser // prevents concurrent use of instance directory
    49  	stop          chan struct{}     // Channel to wait for termination notifications
    50  	server        *p2p.Server       // Currently running P2P networking layer
    51  	startStopLock sync.Mutex        // Start/Stop are protected by an additional lock
    52  	state         int               // Tracks state of node lifecycle
    53  
    54  	lock          sync.Mutex
    55  	lifecycles    []Lifecycle // All registered backends, services, and auxiliary services that have a lifecycle
    56  	rpcAPIs       []rpc.API   // List of APIs currently provided by the node
    57  	http          *httpServer //
    58  	ws            *httpServer //
    59  	ipc           *ipcServer  // Stores information about the ipc http server
    60  	inprocHandler *rpc.Server // In-process RPC request handler to process the API requests
    61  
    62  	databases map[*closeTrackingDB]struct{} // All open databases
    63  }
    64  
    65  const (
    66  	initializingState = iota
    67  	runningState
    68  	closedState
    69  )
    70  
    71  // New creates a new P2P node, ready for protocol registration.
    72  func New(conf *Config) (*Node, error) {
    73  	// Copy config and resolve the datadir so future changes to the current
    74  	// working directory don't affect the node.
    75  	confCopy := *conf
    76  	conf = &confCopy
    77  	if conf.DataDir != "" {
    78  		absdatadir, err := filepath.Abs(conf.DataDir)
    79  		if err != nil {
    80  			return nil, err
    81  		}
    82  		conf.DataDir = absdatadir
    83  	}
    84  	if conf.LogConfig != nil {
    85  		logFilePath := ""
    86  		if conf.LogConfig.FileRoot == "" {
    87  			logFilePath = path.Join(conf.DataDir, conf.LogConfig.FilePath)
    88  		} else {
    89  			logFilePath = path.Join(conf.LogConfig.FileRoot, conf.LogConfig.FilePath)
    90  		}
    91  		log.Root().SetHandler(log.NewFileLvlHandler(logFilePath, conf.LogConfig.MaxBytesSize, conf.LogConfig.Level))
    92  	}
    93  	if conf.Logger == nil {
    94  		conf.Logger = log.New()
    95  	}
    96  
    97  	// Ensure that the instance name doesn't cause weird conflicts with
    98  	// other files in the data directory.
    99  	if strings.ContainsAny(conf.Name, `/\`) {
   100  		return nil, errors.New(`Config.Name must not contain '/' or '\'`)
   101  	}
   102  	if conf.Name == datadirDefaultKeyStore {
   103  		return nil, errors.New(`Config.Name cannot be "` + datadirDefaultKeyStore + `"`)
   104  	}
   105  	if strings.HasSuffix(conf.Name, ".ipc") {
   106  		return nil, errors.New(`Config.Name cannot end in ".ipc"`)
   107  	}
   108  
   109  	node := &Node{
   110  		config:        conf,
   111  		inprocHandler: rpc.NewServer(),
   112  		eventmux:      new(event.TypeMux),
   113  		log:           conf.Logger,
   114  		stop:          make(chan struct{}),
   115  		server:        &p2p.Server{Config: conf.P2P},
   116  		databases:     make(map[*closeTrackingDB]struct{}),
   117  	}
   118  
   119  	// Register built-in APIs.
   120  	node.rpcAPIs = append(node.rpcAPIs, node.apis()...)
   121  
   122  	// Acquire the instance directory lock.
   123  	if err := node.openDataDir(); err != nil {
   124  		return nil, err
   125  	}
   126  	// Ensure that the AccountManager method works before the node has started. We rely on
   127  	// this in cmd/geth.
   128  	am, ephemeralKeystore, err := makeAccountManager(conf)
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  	node.accman = am
   133  	node.ephemKeystore = ephemeralKeystore
   134  
   135  	// Initialize the p2p server. This creates the node key and discovery databases.
   136  	node.server.Config.PrivateKey = node.config.NodeKey()
   137  	node.server.Config.Name = node.config.NodeName()
   138  	node.server.Config.Logger = node.log
   139  	if node.server.Config.StaticNodes == nil {
   140  		node.server.Config.StaticNodes = node.config.StaticNodes()
   141  	}
   142  	if node.server.Config.TrustedNodes == nil {
   143  		node.server.Config.TrustedNodes = node.config.TrustedNodes()
   144  	}
   145  	if node.server.Config.NodeDatabase == "" {
   146  		node.server.Config.NodeDatabase = node.config.NodeDB()
   147  	}
   148  
   149  	// Check HTTP/WS prefixes are valid.
   150  	if err := validatePrefix("HTTP", conf.HTTPPathPrefix); err != nil {
   151  		return nil, err
   152  	}
   153  	if err := validatePrefix("WebSocket", conf.WSPathPrefix); err != nil {
   154  		return nil, err
   155  	}
   156  
   157  	// Configure RPC servers.
   158  	node.http = newHTTPServer(node.log, conf.HTTPTimeouts)
   159  	node.ws = newHTTPServer(node.log, rpc.DefaultHTTPTimeouts)
   160  	node.ipc = newIPCServer(node.log, conf.IPCEndpoint())
   161  
   162  	return node, nil
   163  }
   164  
   165  // Start starts all registered lifecycles, RPC services and p2p networking.
   166  // Node can only be started once.
   167  func (n *Node) Start() error {
   168  	n.startStopLock.Lock()
   169  	defer n.startStopLock.Unlock()
   170  
   171  	n.lock.Lock()
   172  	switch n.state {
   173  	case runningState:
   174  		n.lock.Unlock()
   175  		return ErrNodeRunning
   176  	case closedState:
   177  		n.lock.Unlock()
   178  		return ErrNodeStopped
   179  	}
   180  	n.state = runningState
   181  	// open networking and RPC endpoints
   182  	err := n.openEndpoints()
   183  	lifecycles := make([]Lifecycle, len(n.lifecycles))
   184  	copy(lifecycles, n.lifecycles)
   185  	n.lock.Unlock()
   186  
   187  	// Check if endpoint startup failed.
   188  	if err != nil {
   189  		n.doClose(nil)
   190  		return err
   191  	}
   192  	// Start all registered lifecycles.
   193  	var started []Lifecycle
   194  	for _, lifecycle := range lifecycles {
   195  		if err = lifecycle.Start(); err != nil {
   196  			break
   197  		}
   198  		started = append(started, lifecycle)
   199  	}
   200  	// Check if any lifecycle failed to start.
   201  	if err != nil {
   202  		n.stopServices(started)
   203  		n.doClose(nil)
   204  	}
   205  	return err
   206  }
   207  
   208  // Close stops the Node and releases resources acquired in
   209  // Node constructor New.
   210  func (n *Node) Close() error {
   211  	n.startStopLock.Lock()
   212  	defer n.startStopLock.Unlock()
   213  
   214  	n.lock.Lock()
   215  	state := n.state
   216  	n.lock.Unlock()
   217  	switch state {
   218  	case initializingState:
   219  		// The node was never started.
   220  		return n.doClose(nil)
   221  	case runningState:
   222  		// The node was started, release resources acquired by Start().
   223  		var errs []error
   224  		if err := n.stopServices(n.lifecycles); err != nil {
   225  			errs = append(errs, err)
   226  		}
   227  		return n.doClose(errs)
   228  	case closedState:
   229  		return ErrNodeStopped
   230  	default:
   231  		panic(fmt.Sprintf("node is in unknown state %d", state))
   232  	}
   233  }
   234  
   235  // doClose releases resources acquired by New(), collecting errors.
   236  func (n *Node) doClose(errs []error) error {
   237  	// Close databases. This needs the lock because it needs to
   238  	// synchronize with OpenDatabase*.
   239  	n.lock.Lock()
   240  	n.state = closedState
   241  	errs = append(errs, n.closeDatabases()...)
   242  	n.lock.Unlock()
   243  
   244  	if err := n.accman.Close(); err != nil {
   245  		errs = append(errs, err)
   246  	}
   247  	if n.ephemKeystore != "" {
   248  		if err := os.RemoveAll(n.ephemKeystore); err != nil {
   249  			errs = append(errs, err)
   250  		}
   251  	}
   252  
   253  	// Release instance directory lock.
   254  	n.closeDataDir()
   255  
   256  	// Unblock n.Wait.
   257  	close(n.stop)
   258  
   259  	// Report any errors that might have occurred.
   260  	switch len(errs) {
   261  	case 0:
   262  		return nil
   263  	case 1:
   264  		return errs[0]
   265  	default:
   266  		return fmt.Errorf("%v", errs)
   267  	}
   268  }
   269  
   270  // openEndpoints starts all network and RPC endpoints.
   271  func (n *Node) openEndpoints() error {
   272  	// start networking endpoints
   273  	n.log.Info("Starting peer-to-peer node", "instance", n.server.Name)
   274  	if err := n.server.Start(); err != nil {
   275  		return convertFileLockError(err)
   276  	}
   277  	// start RPC endpoints
   278  	err := n.startRPC()
   279  	if err != nil {
   280  		n.stopRPC()
   281  		n.server.Stop()
   282  	}
   283  	return err
   284  }
   285  
   286  // containsLifecycle checks if 'lfs' contains 'l'.
   287  func containsLifecycle(lfs []Lifecycle, l Lifecycle) bool {
   288  	for _, obj := range lfs {
   289  		if obj == l {
   290  			return true
   291  		}
   292  	}
   293  	return false
   294  }
   295  
   296  // stopServices terminates running services, RPC and p2p networking.
   297  // It is the inverse of Start.
   298  func (n *Node) stopServices(running []Lifecycle) error {
   299  	n.stopRPC()
   300  
   301  	// Stop running lifecycles in reverse order.
   302  	failure := &StopError{Services: make(map[reflect.Type]error)}
   303  	for i := len(running) - 1; i >= 0; i-- {
   304  		if err := running[i].Stop(); err != nil {
   305  			failure.Services[reflect.TypeOf(running[i])] = err
   306  		}
   307  	}
   308  
   309  	// Stop p2p networking.
   310  	n.server.Stop()
   311  
   312  	if len(failure.Services) > 0 {
   313  		return failure
   314  	}
   315  	return nil
   316  }
   317  
   318  func (n *Node) openDataDir() error {
   319  	if n.config.DataDir == "" {
   320  		return nil // ephemeral
   321  	}
   322  
   323  	instdir := filepath.Join(n.config.DataDir, n.config.name())
   324  	if err := os.MkdirAll(instdir, 0700); err != nil {
   325  		return err
   326  	}
   327  	// Lock the instance directory to prevent concurrent use by another instance as well as
   328  	// accidental use of the instance directory as a database.
   329  	release, _, err := fileutil.Flock(filepath.Join(instdir, "LOCK"))
   330  	if err != nil {
   331  		return convertFileLockError(err)
   332  	}
   333  	n.dirLock = release
   334  	return nil
   335  }
   336  
   337  func (n *Node) closeDataDir() {
   338  	// Release instance directory lock.
   339  	if n.dirLock != nil {
   340  		if err := n.dirLock.Release(); err != nil {
   341  			n.log.Error("Can't release datadir lock", "err", err)
   342  		}
   343  		n.dirLock = nil
   344  	}
   345  }
   346  
   347  // configureRPC is a helper method to configure all the various RPC endpoints during node
   348  // startup. It's not meant to be called at any time afterwards as it makes certain
   349  // assumptions about the state of the node.
   350  func (n *Node) startRPC() error {
   351  	if err := n.startInProc(); err != nil {
   352  		return err
   353  	}
   354  
   355  	// Configure IPC.
   356  	if n.ipc.endpoint != "" {
   357  		if err := n.ipc.start(n.rpcAPIs); err != nil {
   358  			return err
   359  		}
   360  	}
   361  
   362  	// Configure HTTP.
   363  	if n.config.HTTPHost != "" {
   364  		config := httpConfig{
   365  			CorsAllowedOrigins: n.config.HTTPCors,
   366  			Vhosts:             n.config.HTTPVirtualHosts,
   367  			Modules:            n.config.HTTPModules,
   368  			prefix:             n.config.HTTPPathPrefix,
   369  		}
   370  		if err := n.http.setListenAddr(n.config.HTTPHost, n.config.HTTPPort); err != nil {
   371  			return err
   372  		}
   373  		if err := n.http.enableRPC(n.rpcAPIs, config); err != nil {
   374  			return err
   375  		}
   376  	}
   377  
   378  	// Configure WebSocket.
   379  	if n.config.WSHost != "" {
   380  		server := n.wsServerForPort(n.config.WSPort)
   381  		config := wsConfig{
   382  			Modules: n.config.WSModules,
   383  			Origins: n.config.WSOrigins,
   384  			prefix:  n.config.WSPathPrefix,
   385  		}
   386  		if err := server.setListenAddr(n.config.WSHost, n.config.WSPort); err != nil {
   387  			return err
   388  		}
   389  		if err := server.enableWS(n.rpcAPIs, config); err != nil {
   390  			return err
   391  		}
   392  	}
   393  
   394  	if err := n.http.start(); err != nil {
   395  		return err
   396  	}
   397  	return n.ws.start()
   398  }
   399  
   400  func (n *Node) wsServerForPort(port int) *httpServer {
   401  	if n.config.HTTPHost == "" || n.http.port == port {
   402  		return n.http
   403  	}
   404  	return n.ws
   405  }
   406  
   407  func (n *Node) stopRPC() {
   408  	n.http.stop()
   409  	n.ws.stop()
   410  	n.ipc.stop()
   411  	n.stopInProc()
   412  }
   413  
   414  // startInProc registers all RPC APIs on the inproc server.
   415  func (n *Node) startInProc() error {
   416  	for _, api := range n.rpcAPIs {
   417  		if err := n.inprocHandler.RegisterName(api.Namespace, api.Service); err != nil {
   418  			return err
   419  		}
   420  	}
   421  	return nil
   422  }
   423  
   424  // stopInProc terminates the in-process RPC endpoint.
   425  func (n *Node) stopInProc() {
   426  	n.inprocHandler.Stop()
   427  }
   428  
   429  // Wait blocks until the node is closed.
   430  func (n *Node) Wait() {
   431  	<-n.stop
   432  }
   433  
   434  // RegisterLifecycle registers the given Lifecycle on the node.
   435  func (n *Node) RegisterLifecycle(lifecycle Lifecycle) {
   436  	n.lock.Lock()
   437  	defer n.lock.Unlock()
   438  
   439  	if n.state != initializingState {
   440  		panic("can't register lifecycle on running/stopped node")
   441  	}
   442  	if containsLifecycle(n.lifecycles, lifecycle) {
   443  		panic(fmt.Sprintf("attempt to register lifecycle %T more than once", lifecycle))
   444  	}
   445  	n.lifecycles = append(n.lifecycles, lifecycle)
   446  }
   447  
   448  // RegisterProtocols adds backend's protocols to the node's p2p server.
   449  func (n *Node) RegisterProtocols(protocols []p2p.Protocol) {
   450  	n.lock.Lock()
   451  	defer n.lock.Unlock()
   452  
   453  	if n.state != initializingState {
   454  		panic("can't register protocols on running/stopped node")
   455  	}
   456  	n.server.Protocols = append(n.server.Protocols, protocols...)
   457  }
   458  
   459  // RegisterAPIs registers the APIs a service provides on the node.
   460  func (n *Node) RegisterAPIs(apis []rpc.API) {
   461  	n.lock.Lock()
   462  	defer n.lock.Unlock()
   463  
   464  	if n.state != initializingState {
   465  		panic("can't register APIs on running/stopped node")
   466  	}
   467  	n.rpcAPIs = append(n.rpcAPIs, apis...)
   468  }
   469  
   470  // RegisterHandler mounts a handler on the given path on the canonical HTTP server.
   471  //
   472  // The name of the handler is shown in a log message when the HTTP server starts
   473  // and should be a descriptive term for the service provided by the handler.
   474  func (n *Node) RegisterHandler(name, path string, handler http.Handler) {
   475  	n.lock.Lock()
   476  	defer n.lock.Unlock()
   477  
   478  	if n.state != initializingState {
   479  		panic("can't register HTTP handler on running/stopped node")
   480  	}
   481  
   482  	n.http.mux.Handle(path, handler)
   483  	n.http.handlerNames[path] = name
   484  }
   485  
   486  // Attach creates an RPC client attached to an in-process API handler.
   487  func (n *Node) Attach() (*rpc.Client, error) {
   488  	return rpc.DialInProc(n.inprocHandler), nil
   489  }
   490  
   491  // RPCHandler returns the in-process RPC request handler.
   492  func (n *Node) RPCHandler() (*rpc.Server, error) {
   493  	n.lock.Lock()
   494  	defer n.lock.Unlock()
   495  
   496  	if n.state == closedState {
   497  		return nil, ErrNodeStopped
   498  	}
   499  	return n.inprocHandler, nil
   500  }
   501  
   502  // Config returns the configuration of node.
   503  func (n *Node) Config() *Config {
   504  	return n.config
   505  }
   506  
   507  // Server retrieves the currently running P2P network layer. This method is meant
   508  // only to inspect fields of the currently running server. Callers should not
   509  // start or stop the returned server.
   510  func (n *Node) Server() *p2p.Server {
   511  	n.lock.Lock()
   512  	defer n.lock.Unlock()
   513  
   514  	return n.server
   515  }
   516  
   517  // DataDir retrieves the current datadir used by the protocol stack.
   518  // Deprecated: No files should be stored in this directory, use InstanceDir instead.
   519  func (n *Node) DataDir() string {
   520  	return n.config.DataDir
   521  }
   522  
   523  // InstanceDir retrieves the instance directory used by the protocol stack.
   524  func (n *Node) InstanceDir() string {
   525  	return n.config.instanceDir()
   526  }
   527  
   528  // AccountManager retrieves the account manager used by the protocol stack.
   529  func (n *Node) AccountManager() *accounts.Manager {
   530  	return n.accman
   531  }
   532  
   533  // IPCEndpoint retrieves the current IPC endpoint used by the protocol stack.
   534  func (n *Node) IPCEndpoint() string {
   535  	return n.ipc.endpoint
   536  }
   537  
   538  // HTTPEndpoint returns the URL of the HTTP server. Note that this URL does not
   539  // contain the JSON-RPC path prefix set by HTTPPathPrefix.
   540  func (n *Node) HTTPEndpoint() string {
   541  	return "http://" + n.http.listenAddr()
   542  }
   543  
   544  // WSEndpoint returns the current JSON-RPC over WebSocket endpoint.
   545  func (n *Node) WSEndpoint() string {
   546  	if n.http.wsAllowed() {
   547  		return "ws://" + n.http.listenAddr() + n.http.wsConfig.prefix
   548  	}
   549  	return "ws://" + n.ws.listenAddr() + n.ws.wsConfig.prefix
   550  }
   551  
   552  // EventMux retrieves the event multiplexer used by all the network services in
   553  // the current protocol stack.
   554  func (n *Node) EventMux() *event.TypeMux {
   555  	return n.eventmux
   556  }
   557  
   558  // OpenDatabase opens an existing database with the given name (or creates one if no
   559  // previous can be found) from within the node's instance directory. If the node is
   560  // ephemeral, a memory database is returned.
   561  func (n *Node) OpenDatabase(name string, cache, handles int, namespace string, readonly bool) (ethdb.Database, error) {
   562  	n.lock.Lock()
   563  	defer n.lock.Unlock()
   564  	if n.state == closedState {
   565  		return nil, ErrNodeStopped
   566  	}
   567  
   568  	var db ethdb.Database
   569  	var err error
   570  	if n.config.DataDir == "" {
   571  		db = rawdb.NewMemoryDatabase()
   572  	} else {
   573  		db, err = rawdb.NewLevelDBDatabase(n.ResolvePath(name), cache, handles, namespace, readonly)
   574  	}
   575  
   576  	if err == nil {
   577  		db = n.wrapDatabase(db)
   578  	}
   579  	return db, err
   580  }
   581  
   582  func (n *Node) OpenAndMergeDatabase(name string, cache, handles int, freezer, diff, namespace string, readonly, persistDiff bool) (ethdb.Database, error) {
   583  	chainDB, err := n.OpenDatabaseWithFreezer(name, cache, handles, freezer, namespace, readonly)
   584  	if err != nil {
   585  		return nil, err
   586  	}
   587  	if persistDiff {
   588  		diffStore, err := n.OpenDiffDatabase(name, handles, diff, namespace, readonly)
   589  		if err != nil {
   590  			chainDB.Close()
   591  			return nil, err
   592  		}
   593  		chainDB.SetDiffStore(diffStore)
   594  	}
   595  	return chainDB, nil
   596  }
   597  
   598  // OpenDatabaseWithFreezer opens an existing database with the given name (or
   599  // creates one if no previous can be found) from within the node's data directory,
   600  // also attaching a chain freezer to it that moves ancient chain data from the
   601  // database to immutable append-only files. If the node is an ephemeral one, a
   602  // memory database is returned.
   603  func (n *Node) OpenDatabaseWithFreezer(name string, cache, handles int, freezer, namespace string, readonly bool) (ethdb.Database, error) {
   604  	n.lock.Lock()
   605  	defer n.lock.Unlock()
   606  	if n.state == closedState {
   607  		return nil, ErrNodeStopped
   608  	}
   609  
   610  	var db ethdb.Database
   611  	var err error
   612  	if n.config.DataDir == "" {
   613  		db = rawdb.NewMemoryDatabase()
   614  	} else {
   615  		root := n.ResolvePath(name)
   616  		switch {
   617  		case freezer == "":
   618  			freezer = filepath.Join(root, "ancient")
   619  		case !filepath.IsAbs(freezer):
   620  			freezer = n.ResolvePath(freezer)
   621  		}
   622  		db, err = rawdb.NewLevelDBDatabaseWithFreezer(root, cache, handles, freezer, namespace, readonly)
   623  	}
   624  
   625  	if err == nil {
   626  		db = n.wrapDatabase(db)
   627  	}
   628  	return db, err
   629  }
   630  
   631  func (n *Node) OpenDiffDatabase(name string, handles int, diff, namespace string, readonly bool) (*leveldb.Database, error) {
   632  	n.lock.Lock()
   633  	defer n.lock.Unlock()
   634  	if n.state == closedState {
   635  		return nil, ErrNodeStopped
   636  	}
   637  
   638  	var db *leveldb.Database
   639  	var err error
   640  	if n.config.DataDir == "" {
   641  		panic("datadir is missing")
   642  	}
   643  	root := n.ResolvePath(name)
   644  	switch {
   645  	case diff == "":
   646  		diff = filepath.Join(root, "diff")
   647  	case !filepath.IsAbs(diff):
   648  		diff = n.ResolvePath(diff)
   649  	}
   650  	db, err = leveldb.New(diff, 0, handles, namespace, readonly)
   651  
   652  	return db, err
   653  }
   654  
   655  // ResolvePath returns the absolute path of a resource in the instance directory.
   656  func (n *Node) ResolvePath(x string) string {
   657  	return n.config.ResolvePath(x)
   658  }
   659  
   660  // closeTrackingDB wraps the Close method of a database. When the database is closed by the
   661  // service, the wrapper removes it from the node's database map. This ensures that Node
   662  // won't auto-close the database if it is closed by the service that opened it.
   663  type closeTrackingDB struct {
   664  	ethdb.Database
   665  	n *Node
   666  }
   667  
   668  func (db *closeTrackingDB) Close() error {
   669  	db.n.lock.Lock()
   670  	delete(db.n.databases, db)
   671  	db.n.lock.Unlock()
   672  	return db.Database.Close()
   673  }
   674  
   675  // wrapDatabase ensures the database will be auto-closed when Node is closed.
   676  func (n *Node) wrapDatabase(db ethdb.Database) ethdb.Database {
   677  	wrapper := &closeTrackingDB{db, n}
   678  	n.databases[wrapper] = struct{}{}
   679  	return wrapper
   680  }
   681  
   682  // closeDatabases closes all open databases.
   683  func (n *Node) closeDatabases() (errors []error) {
   684  	for db := range n.databases {
   685  		delete(n.databases, db)
   686  		if err := db.Database.Close(); err != nil {
   687  			errors = append(errors, err)
   688  		}
   689  	}
   690  	return errors
   691  }