github.com/calmw/ethereum@v0.1.1/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  	crand "crypto/rand"
    21  	"errors"
    22  	"fmt"
    23  	"hash/crc32"
    24  	"net/http"
    25  	"os"
    26  	"path/filepath"
    27  	"reflect"
    28  	"strings"
    29  	"sync"
    30  
    31  	"github.com/calmw/ethereum/accounts"
    32  	"github.com/calmw/ethereum/common"
    33  	"github.com/calmw/ethereum/common/hexutil"
    34  	"github.com/calmw/ethereum/core/rawdb"
    35  	"github.com/calmw/ethereum/ethdb"
    36  	"github.com/calmw/ethereum/event"
    37  	"github.com/calmw/ethereum/log"
    38  	"github.com/calmw/ethereum/p2p"
    39  	"github.com/calmw/ethereum/rpc"
    40  	"github.com/gofrs/flock"
    41  )
    42  
    43  // Node is a container on which services can be registered.
    44  type Node struct {
    45  	eventmux      *event.TypeMux
    46  	config        *Config
    47  	accman        *accounts.Manager
    48  	log           log.Logger
    49  	keyDir        string        // key store directory
    50  	keyDirTemp    bool          // If true, key directory will be removed by Stop
    51  	dirLock       *flock.Flock  // prevents concurrent use of instance directory
    52  	stop          chan struct{} // Channel to wait for termination notifications
    53  	server        *p2p.Server   // Currently running P2P networking layer
    54  	startStopLock sync.Mutex    // Start/Stop are protected by an additional lock
    55  	state         int           // Tracks state of node lifecycle
    56  
    57  	lock          sync.Mutex
    58  	lifecycles    []Lifecycle // All registered backends, services, and auxiliary services that have a lifecycle
    59  	rpcAPIs       []rpc.API   // List of APIs currently provided by the node
    60  	http          *httpServer //
    61  	ws            *httpServer //
    62  	httpAuth      *httpServer //
    63  	wsAuth        *httpServer //
    64  	ipc           *ipcServer  // Stores information about the ipc http server
    65  	inprocHandler *rpc.Server // In-process RPC request handler to process the API requests
    66  
    67  	databases map[*closeTrackingDB]struct{} // All open databases
    68  }
    69  
    70  const (
    71  	initializingState = iota
    72  	runningState
    73  	closedState
    74  )
    75  
    76  // New creates a new P2P node, ready for protocol registration.
    77  func New(conf *Config) (*Node, error) {
    78  	// Copy config and resolve the datadir so future changes to the current
    79  	// working directory don't affect the node.
    80  	confCopy := *conf
    81  	conf = &confCopy
    82  	if conf.DataDir != "" {
    83  		absdatadir, err := filepath.Abs(conf.DataDir)
    84  		if err != nil {
    85  			return nil, err
    86  		}
    87  		conf.DataDir = absdatadir
    88  	}
    89  	if conf.Logger == nil {
    90  		conf.Logger = log.New()
    91  	}
    92  
    93  	// Ensure that the instance name doesn't cause weird conflicts with
    94  	// other files in the data directory.
    95  	if strings.ContainsAny(conf.Name, `/\`) {
    96  		return nil, errors.New(`Config.Name must not contain '/' or '\'`)
    97  	}
    98  	if conf.Name == datadirDefaultKeyStore {
    99  		return nil, errors.New(`Config.Name cannot be "` + datadirDefaultKeyStore + `"`)
   100  	}
   101  	if strings.HasSuffix(conf.Name, ".ipc") {
   102  		return nil, errors.New(`Config.Name cannot end in ".ipc"`)
   103  	}
   104  
   105  	node := &Node{
   106  		config:        conf,
   107  		inprocHandler: rpc.NewServer(),
   108  		eventmux:      new(event.TypeMux),
   109  		log:           conf.Logger,
   110  		stop:          make(chan struct{}),
   111  		server:        &p2p.Server{Config: conf.P2P},
   112  		databases:     make(map[*closeTrackingDB]struct{}),
   113  	}
   114  
   115  	// Register built-in APIs.
   116  	node.rpcAPIs = append(node.rpcAPIs, node.apis()...)
   117  
   118  	// Acquire the instance directory lock.
   119  	if err := node.openDataDir(); err != nil {
   120  		return nil, err
   121  	}
   122  	keyDir, isEphem, err := conf.GetKeyStoreDir()
   123  	if err != nil {
   124  		return nil, err
   125  	}
   126  	node.keyDir = keyDir
   127  	node.keyDirTemp = isEphem
   128  	// Creates an empty AccountManager with no backends. Callers (e.g. cmd/geth)
   129  	// are required to add the backends later on.
   130  	node.accman = accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: conf.InsecureUnlockAllowed})
   131  
   132  	// Initialize the p2p server. This creates the node key and discovery databases.
   133  	node.server.Config.PrivateKey = node.config.NodeKey()
   134  	node.server.Config.Name = node.config.NodeName()
   135  	node.server.Config.Logger = node.log
   136  	node.config.checkLegacyFiles()
   137  	if node.server.Config.NodeDatabase == "" {
   138  		node.server.Config.NodeDatabase = node.config.NodeDB()
   139  	}
   140  
   141  	// Check HTTP/WS prefixes are valid.
   142  	if err := validatePrefix("HTTP", conf.HTTPPathPrefix); err != nil {
   143  		return nil, err
   144  	}
   145  	if err := validatePrefix("WebSocket", conf.WSPathPrefix); err != nil {
   146  		return nil, err
   147  	}
   148  
   149  	// Configure RPC servers.
   150  	node.http = newHTTPServer(node.log, conf.HTTPTimeouts)
   151  	node.httpAuth = newHTTPServer(node.log, conf.HTTPTimeouts)
   152  	node.ws = newHTTPServer(node.log, rpc.DefaultHTTPTimeouts)
   153  	node.wsAuth = newHTTPServer(node.log, rpc.DefaultHTTPTimeouts)
   154  	node.ipc = newIPCServer(node.log, conf.IPCEndpoint())
   155  
   156  	return node, nil
   157  }
   158  
   159  // Start starts all registered lifecycles, RPC services and p2p networking.
   160  // Node can only be started once.
   161  func (n *Node) Start() error {
   162  	n.startStopLock.Lock()
   163  	defer n.startStopLock.Unlock()
   164  
   165  	n.lock.Lock()
   166  	switch n.state {
   167  	case runningState:
   168  		n.lock.Unlock()
   169  		return ErrNodeRunning
   170  	case closedState:
   171  		n.lock.Unlock()
   172  		return ErrNodeStopped
   173  	}
   174  	n.state = runningState
   175  	// open networking and RPC endpoints
   176  	err := n.openEndpoints()
   177  	lifecycles := make([]Lifecycle, len(n.lifecycles))
   178  	copy(lifecycles, n.lifecycles)
   179  	n.lock.Unlock()
   180  
   181  	// Check if endpoint startup failed.
   182  	if err != nil {
   183  		n.doClose(nil)
   184  		return err
   185  	}
   186  	// Start all registered lifecycles.
   187  	var started []Lifecycle
   188  	for _, lifecycle := range lifecycles {
   189  		if err = lifecycle.Start(); err != nil {
   190  			break
   191  		}
   192  		started = append(started, lifecycle)
   193  	}
   194  	// Check if any lifecycle failed to start.
   195  	if err != nil {
   196  		n.stopServices(started)
   197  		n.doClose(nil)
   198  	}
   199  	return err
   200  }
   201  
   202  // Close stops the Node and releases resources acquired in
   203  // Node constructor New.
   204  func (n *Node) Close() error {
   205  	n.startStopLock.Lock()
   206  	defer n.startStopLock.Unlock()
   207  
   208  	n.lock.Lock()
   209  	state := n.state
   210  	n.lock.Unlock()
   211  	switch state {
   212  	case initializingState:
   213  		// The node was never started.
   214  		return n.doClose(nil)
   215  	case runningState:
   216  		// The node was started, release resources acquired by Start().
   217  		var errs []error
   218  		if err := n.stopServices(n.lifecycles); err != nil {
   219  			errs = append(errs, err)
   220  		}
   221  		return n.doClose(errs)
   222  	case closedState:
   223  		return ErrNodeStopped
   224  	default:
   225  		panic(fmt.Sprintf("node is in unknown state %d", state))
   226  	}
   227  }
   228  
   229  // doClose releases resources acquired by New(), collecting errors.
   230  func (n *Node) doClose(errs []error) error {
   231  	// Close databases. This needs the lock because it needs to
   232  	// synchronize with OpenDatabase*.
   233  	n.lock.Lock()
   234  	n.state = closedState
   235  	errs = append(errs, n.closeDatabases()...)
   236  	n.lock.Unlock()
   237  
   238  	if err := n.accman.Close(); err != nil {
   239  		errs = append(errs, err)
   240  	}
   241  	if n.keyDirTemp {
   242  		if err := os.RemoveAll(n.keyDir); err != nil {
   243  			errs = append(errs, err)
   244  		}
   245  	}
   246  
   247  	// Release instance directory lock.
   248  	n.closeDataDir()
   249  
   250  	// Unblock n.Wait.
   251  	close(n.stop)
   252  
   253  	// Report any errors that might have occurred.
   254  	switch len(errs) {
   255  	case 0:
   256  		return nil
   257  	case 1:
   258  		return errs[0]
   259  	default:
   260  		return fmt.Errorf("%v", errs)
   261  	}
   262  }
   263  
   264  // openEndpoints starts all network and RPC endpoints.
   265  func (n *Node) openEndpoints() error {
   266  	// start networking endpoints
   267  	n.log.Info("Starting peer-to-peer node", "instance", n.server.Name)
   268  	if err := n.server.Start(); err != nil {
   269  		return convertFileLockError(err)
   270  	}
   271  	// start RPC endpoints
   272  	err := n.startRPC()
   273  	if err != nil {
   274  		n.stopRPC()
   275  		n.server.Stop()
   276  	}
   277  	return err
   278  }
   279  
   280  // containsLifecycle checks if 'lfs' contains 'l'.
   281  func containsLifecycle(lfs []Lifecycle, l Lifecycle) bool {
   282  	for _, obj := range lfs {
   283  		if obj == l {
   284  			return true
   285  		}
   286  	}
   287  	return false
   288  }
   289  
   290  // stopServices terminates running services, RPC and p2p networking.
   291  // It is the inverse of Start.
   292  func (n *Node) stopServices(running []Lifecycle) error {
   293  	n.stopRPC()
   294  
   295  	// Stop running lifecycles in reverse order.
   296  	failure := &StopError{Services: make(map[reflect.Type]error)}
   297  	for i := len(running) - 1; i >= 0; i-- {
   298  		if err := running[i].Stop(); err != nil {
   299  			failure.Services[reflect.TypeOf(running[i])] = err
   300  		}
   301  	}
   302  
   303  	// Stop p2p networking.
   304  	n.server.Stop()
   305  
   306  	if len(failure.Services) > 0 {
   307  		return failure
   308  	}
   309  	return nil
   310  }
   311  
   312  func (n *Node) openDataDir() error {
   313  	if n.config.DataDir == "" {
   314  		return nil // ephemeral
   315  	}
   316  
   317  	instdir := filepath.Join(n.config.DataDir, n.config.name())
   318  	if err := os.MkdirAll(instdir, 0700); err != nil {
   319  		return err
   320  	}
   321  	// Lock the instance directory to prevent concurrent use by another instance as well as
   322  	// accidental use of the instance directory as a database.
   323  	n.dirLock = flock.New(filepath.Join(instdir, "LOCK"))
   324  
   325  	if locked, err := n.dirLock.TryLock(); err != nil {
   326  		return err
   327  	} else if !locked {
   328  		return ErrDatadirUsed
   329  	}
   330  	return nil
   331  }
   332  
   333  func (n *Node) closeDataDir() {
   334  	// Release instance directory lock.
   335  	if n.dirLock != nil && n.dirLock.Locked() {
   336  		n.dirLock.Unlock()
   337  		n.dirLock = nil
   338  	}
   339  }
   340  
   341  // obtainJWTSecret loads the jwt-secret, either from the provided config,
   342  // or from the default location. If neither of those are present, it generates
   343  // a new secret and stores to the default location.
   344  func (n *Node) obtainJWTSecret(cliParam string) ([]byte, error) {
   345  	fileName := cliParam
   346  	if len(fileName) == 0 {
   347  		// no path provided, use default
   348  		fileName = n.ResolvePath(datadirJWTKey)
   349  	}
   350  	// try reading from file
   351  	if data, err := os.ReadFile(fileName); err == nil {
   352  		jwtSecret := common.FromHex(strings.TrimSpace(string(data)))
   353  		if len(jwtSecret) == 32 {
   354  			log.Info("Loaded JWT secret file", "path", fileName, "crc32", fmt.Sprintf("%#x", crc32.ChecksumIEEE(jwtSecret)))
   355  			return jwtSecret, nil
   356  		}
   357  		log.Error("Invalid JWT secret", "path", fileName, "length", len(jwtSecret))
   358  		return nil, errors.New("invalid JWT secret")
   359  	}
   360  	// Need to generate one
   361  	jwtSecret := make([]byte, 32)
   362  	crand.Read(jwtSecret)
   363  	// if we're in --dev mode, don't bother saving, just show it
   364  	if fileName == "" {
   365  		log.Info("Generated ephemeral JWT secret", "secret", hexutil.Encode(jwtSecret))
   366  		return jwtSecret, nil
   367  	}
   368  	if err := os.WriteFile(fileName, []byte(hexutil.Encode(jwtSecret)), 0600); err != nil {
   369  		return nil, err
   370  	}
   371  	log.Info("Generated JWT secret", "path", fileName)
   372  	return jwtSecret, nil
   373  }
   374  
   375  // startRPC is a helper method to configure all the various RPC endpoints during node
   376  // startup. It's not meant to be called at any time afterwards as it makes certain
   377  // assumptions about the state of the node.
   378  func (n *Node) startRPC() error {
   379  	// Filter out personal api
   380  	var apis []rpc.API
   381  	for _, api := range n.rpcAPIs {
   382  		if api.Namespace == "personal" {
   383  			if n.config.EnablePersonal {
   384  				log.Warn("Deprecated personal namespace activated")
   385  			} else {
   386  				continue
   387  			}
   388  		}
   389  		apis = append(apis, api)
   390  	}
   391  	if err := n.startInProc(apis); err != nil {
   392  		return err
   393  	}
   394  
   395  	// Configure IPC.
   396  	if n.ipc.endpoint != "" {
   397  		if err := n.ipc.start(apis); err != nil {
   398  			return err
   399  		}
   400  	}
   401  	var (
   402  		servers           []*httpServer
   403  		openAPIs, allAPIs = n.getAPIs()
   404  	)
   405  
   406  	initHttp := func(server *httpServer, port int) error {
   407  		if err := server.setListenAddr(n.config.HTTPHost, port); err != nil {
   408  			return err
   409  		}
   410  		if err := server.enableRPC(openAPIs, httpConfig{
   411  			CorsAllowedOrigins: n.config.HTTPCors,
   412  			Vhosts:             n.config.HTTPVirtualHosts,
   413  			Modules:            n.config.HTTPModules,
   414  			prefix:             n.config.HTTPPathPrefix,
   415  		}); err != nil {
   416  			return err
   417  		}
   418  		servers = append(servers, server)
   419  		return nil
   420  	}
   421  
   422  	initWS := func(port int) error {
   423  		server := n.wsServerForPort(port, false)
   424  		if err := server.setListenAddr(n.config.WSHost, port); err != nil {
   425  			return err
   426  		}
   427  		if err := server.enableWS(openAPIs, wsConfig{
   428  			Modules: n.config.WSModules,
   429  			Origins: n.config.WSOrigins,
   430  			prefix:  n.config.WSPathPrefix,
   431  		}); err != nil {
   432  			return err
   433  		}
   434  		servers = append(servers, server)
   435  		return nil
   436  	}
   437  
   438  	initAuth := func(port int, secret []byte) error {
   439  		// Enable auth via HTTP
   440  		server := n.httpAuth
   441  		if err := server.setListenAddr(n.config.AuthAddr, port); err != nil {
   442  			return err
   443  		}
   444  		if err := server.enableRPC(allAPIs, httpConfig{
   445  			CorsAllowedOrigins: DefaultAuthCors,
   446  			Vhosts:             n.config.AuthVirtualHosts,
   447  			Modules:            DefaultAuthModules,
   448  			prefix:             DefaultAuthPrefix,
   449  			jwtSecret:          secret,
   450  		}); err != nil {
   451  			return err
   452  		}
   453  		servers = append(servers, server)
   454  		// Enable auth via WS
   455  		server = n.wsServerForPort(port, true)
   456  		if err := server.setListenAddr(n.config.AuthAddr, port); err != nil {
   457  			return err
   458  		}
   459  		if err := server.enableWS(allAPIs, wsConfig{
   460  			Modules:   DefaultAuthModules,
   461  			Origins:   DefaultAuthOrigins,
   462  			prefix:    DefaultAuthPrefix,
   463  			jwtSecret: secret,
   464  		}); err != nil {
   465  			return err
   466  		}
   467  		servers = append(servers, server)
   468  		return nil
   469  	}
   470  
   471  	// Set up HTTP.
   472  	if n.config.HTTPHost != "" {
   473  		// Configure legacy unauthenticated HTTP.
   474  		if err := initHttp(n.http, n.config.HTTPPort); err != nil {
   475  			return err
   476  		}
   477  	}
   478  	// Configure WebSocket.
   479  	if n.config.WSHost != "" {
   480  		// legacy unauthenticated
   481  		if err := initWS(n.config.WSPort); err != nil {
   482  			return err
   483  		}
   484  	}
   485  	// Configure authenticated API
   486  	if len(openAPIs) != len(allAPIs) {
   487  		jwtSecret, err := n.obtainJWTSecret(n.config.JWTSecret)
   488  		if err != nil {
   489  			return err
   490  		}
   491  		if err := initAuth(n.config.AuthPort, jwtSecret); err != nil {
   492  			return err
   493  		}
   494  	}
   495  	// Start the servers
   496  	for _, server := range servers {
   497  		if err := server.start(); err != nil {
   498  			return err
   499  		}
   500  	}
   501  	return nil
   502  }
   503  
   504  func (n *Node) wsServerForPort(port int, authenticated bool) *httpServer {
   505  	httpServer, wsServer := n.http, n.ws
   506  	if authenticated {
   507  		httpServer, wsServer = n.httpAuth, n.wsAuth
   508  	}
   509  	if n.config.HTTPHost == "" || httpServer.port == port {
   510  		return httpServer
   511  	}
   512  	return wsServer
   513  }
   514  
   515  func (n *Node) stopRPC() {
   516  	n.http.stop()
   517  	n.ws.stop()
   518  	n.httpAuth.stop()
   519  	n.wsAuth.stop()
   520  	n.ipc.stop()
   521  	n.stopInProc()
   522  }
   523  
   524  // startInProc registers all RPC APIs on the inproc server.
   525  func (n *Node) startInProc(apis []rpc.API) error {
   526  	for _, api := range apis {
   527  		if err := n.inprocHandler.RegisterName(api.Namespace, api.Service); err != nil {
   528  			return err
   529  		}
   530  	}
   531  	return nil
   532  }
   533  
   534  // stopInProc terminates the in-process RPC endpoint.
   535  func (n *Node) stopInProc() {
   536  	n.inprocHandler.Stop()
   537  }
   538  
   539  // Wait blocks until the node is closed.
   540  func (n *Node) Wait() {
   541  	<-n.stop
   542  }
   543  
   544  // RegisterLifecycle registers the given Lifecycle on the node.
   545  func (n *Node) RegisterLifecycle(lifecycle Lifecycle) {
   546  	n.lock.Lock()
   547  	defer n.lock.Unlock()
   548  
   549  	if n.state != initializingState {
   550  		panic("can't register lifecycle on running/stopped node")
   551  	}
   552  	if containsLifecycle(n.lifecycles, lifecycle) {
   553  		panic(fmt.Sprintf("attempt to register lifecycle %T more than once", lifecycle))
   554  	}
   555  	n.lifecycles = append(n.lifecycles, lifecycle)
   556  }
   557  
   558  // RegisterProtocols adds backend's protocols to the node's p2p server.
   559  func (n *Node) RegisterProtocols(protocols []p2p.Protocol) {
   560  	n.lock.Lock()
   561  	defer n.lock.Unlock()
   562  
   563  	if n.state != initializingState {
   564  		panic("can't register protocols on running/stopped node")
   565  	}
   566  	n.server.Protocols = append(n.server.Protocols, protocols...)
   567  }
   568  
   569  // RegisterAPIs registers the APIs a service provides on the node.
   570  func (n *Node) RegisterAPIs(apis []rpc.API) {
   571  	n.lock.Lock()
   572  	defer n.lock.Unlock()
   573  
   574  	if n.state != initializingState {
   575  		panic("can't register APIs on running/stopped node")
   576  	}
   577  	n.rpcAPIs = append(n.rpcAPIs, apis...)
   578  }
   579  
   580  // getAPIs return two sets of APIs, both the ones that do not require
   581  // authentication, and the complete set
   582  func (n *Node) getAPIs() (unauthenticated, all []rpc.API) {
   583  	for _, api := range n.rpcAPIs {
   584  		if !api.Authenticated {
   585  			unauthenticated = append(unauthenticated, api)
   586  		}
   587  	}
   588  	return unauthenticated, n.rpcAPIs
   589  }
   590  
   591  // RegisterHandler mounts a handler on the given path on the canonical HTTP server.
   592  //
   593  // The name of the handler is shown in a log message when the HTTP server starts
   594  // and should be a descriptive term for the service provided by the handler.
   595  func (n *Node) RegisterHandler(name, path string, handler http.Handler) {
   596  	n.lock.Lock()
   597  	defer n.lock.Unlock()
   598  
   599  	if n.state != initializingState {
   600  		panic("can't register HTTP handler on running/stopped node")
   601  	}
   602  
   603  	n.http.mux.Handle(path, handler)
   604  	n.http.handlerNames[path] = name
   605  }
   606  
   607  // Attach creates an RPC client attached to an in-process API handler.
   608  func (n *Node) Attach() (*rpc.Client, error) {
   609  	return rpc.DialInProc(n.inprocHandler), nil
   610  }
   611  
   612  // RPCHandler returns the in-process RPC request handler.
   613  func (n *Node) RPCHandler() (*rpc.Server, error) {
   614  	n.lock.Lock()
   615  	defer n.lock.Unlock()
   616  
   617  	if n.state == closedState {
   618  		return nil, ErrNodeStopped
   619  	}
   620  	return n.inprocHandler, nil
   621  }
   622  
   623  // Config returns the configuration of node.
   624  func (n *Node) Config() *Config {
   625  	return n.config
   626  }
   627  
   628  // Server retrieves the currently running P2P network layer. This method is meant
   629  // only to inspect fields of the currently running server. Callers should not
   630  // start or stop the returned server.
   631  func (n *Node) Server() *p2p.Server {
   632  	n.lock.Lock()
   633  	defer n.lock.Unlock()
   634  
   635  	return n.server
   636  }
   637  
   638  // DataDir retrieves the current datadir used by the protocol stack.
   639  // Deprecated: No files should be stored in this directory, use InstanceDir instead.
   640  func (n *Node) DataDir() string {
   641  	return n.config.DataDir
   642  }
   643  
   644  // InstanceDir retrieves the instance directory used by the protocol stack.
   645  func (n *Node) InstanceDir() string {
   646  	return n.config.instanceDir()
   647  }
   648  
   649  // KeyStoreDir retrieves the key directory
   650  func (n *Node) KeyStoreDir() string {
   651  	return n.keyDir
   652  }
   653  
   654  // AccountManager retrieves the account manager used by the protocol stack.
   655  func (n *Node) AccountManager() *accounts.Manager {
   656  	return n.accman
   657  }
   658  
   659  // IPCEndpoint retrieves the current IPC endpoint used by the protocol stack.
   660  func (n *Node) IPCEndpoint() string {
   661  	return n.ipc.endpoint
   662  }
   663  
   664  // HTTPEndpoint returns the URL of the HTTP server. Note that this URL does not
   665  // contain the JSON-RPC path prefix set by HTTPPathPrefix.
   666  func (n *Node) HTTPEndpoint() string {
   667  	return "http://" + n.http.listenAddr()
   668  }
   669  
   670  // WSEndpoint returns the current JSON-RPC over WebSocket endpoint.
   671  func (n *Node) WSEndpoint() string {
   672  	if n.http.wsAllowed() {
   673  		return "ws://" + n.http.listenAddr() + n.http.wsConfig.prefix
   674  	}
   675  	return "ws://" + n.ws.listenAddr() + n.ws.wsConfig.prefix
   676  }
   677  
   678  // HTTPAuthEndpoint returns the URL of the authenticated HTTP server.
   679  func (n *Node) HTTPAuthEndpoint() string {
   680  	return "http://" + n.httpAuth.listenAddr()
   681  }
   682  
   683  // WSAuthEndpoint returns the current authenticated JSON-RPC over WebSocket endpoint.
   684  func (n *Node) WSAuthEndpoint() string {
   685  	if n.httpAuth.wsAllowed() {
   686  		return "ws://" + n.httpAuth.listenAddr() + n.httpAuth.wsConfig.prefix
   687  	}
   688  	return "ws://" + n.wsAuth.listenAddr() + n.wsAuth.wsConfig.prefix
   689  }
   690  
   691  // EventMux retrieves the event multiplexer used by all the network services in
   692  // the current protocol stack.
   693  func (n *Node) EventMux() *event.TypeMux {
   694  	return n.eventmux
   695  }
   696  
   697  // OpenDatabase opens an existing database with the given name (or creates one if no
   698  // previous can be found) from within the node's instance directory. If the node is
   699  // ephemeral, a memory database is returned.
   700  func (n *Node) OpenDatabase(name string, cache, handles int, namespace string, readonly bool) (ethdb.Database, error) {
   701  	n.lock.Lock()
   702  	defer n.lock.Unlock()
   703  	if n.state == closedState {
   704  		return nil, ErrNodeStopped
   705  	}
   706  
   707  	var db ethdb.Database
   708  	var err error
   709  	if n.config.DataDir == "" {
   710  		db = rawdb.NewMemoryDatabase()
   711  	} else {
   712  		db, err = rawdb.Open(rawdb.OpenOptions{
   713  			Type:      n.config.DBEngine,
   714  			Directory: n.ResolvePath(name),
   715  			Namespace: namespace,
   716  			Cache:     cache,
   717  			Handles:   handles,
   718  			ReadOnly:  readonly,
   719  		})
   720  	}
   721  
   722  	if err == nil {
   723  		db = n.wrapDatabase(db)
   724  	}
   725  	return db, err
   726  }
   727  
   728  // OpenDatabaseWithFreezer opens an existing database with the given name (or
   729  // creates one if no previous can be found) from within the node's data directory,
   730  // also attaching a chain freezer to it that moves ancient chain data from the
   731  // database to immutable append-only files. If the node is an ephemeral one, a
   732  // memory database is returned.
   733  func (n *Node) OpenDatabaseWithFreezer(name string, cache, handles int, ancient string, namespace string, readonly bool) (ethdb.Database, error) {
   734  	n.lock.Lock()
   735  	defer n.lock.Unlock()
   736  	if n.state == closedState {
   737  		return nil, ErrNodeStopped
   738  	}
   739  	var db ethdb.Database
   740  	var err error
   741  	if n.config.DataDir == "" {
   742  		db = rawdb.NewMemoryDatabase()
   743  	} else {
   744  		db, err = rawdb.Open(rawdb.OpenOptions{
   745  			Type:              n.config.DBEngine,
   746  			Directory:         n.ResolvePath(name),
   747  			AncientsDirectory: n.ResolveAncient(name, ancient),
   748  			Namespace:         namespace,
   749  			Cache:             cache,
   750  			Handles:           handles,
   751  			ReadOnly:          readonly,
   752  		})
   753  	}
   754  
   755  	if err == nil {
   756  		db = n.wrapDatabase(db)
   757  	}
   758  	return db, err
   759  }
   760  
   761  // ResolvePath returns the absolute path of a resource in the instance directory.
   762  func (n *Node) ResolvePath(x string) string {
   763  	return n.config.ResolvePath(x)
   764  }
   765  
   766  // ResolveAncient returns the absolute path of the root ancient directory.
   767  func (n *Node) ResolveAncient(name string, ancient string) string {
   768  	switch {
   769  	case ancient == "":
   770  		ancient = filepath.Join(n.ResolvePath(name), "ancient")
   771  	case !filepath.IsAbs(ancient):
   772  		ancient = n.ResolvePath(ancient)
   773  	}
   774  	return ancient
   775  }
   776  
   777  // closeTrackingDB wraps the Close method of a database. When the database is closed by the
   778  // service, the wrapper removes it from the node's database map. This ensures that Node
   779  // won't auto-close the database if it is closed by the service that opened it.
   780  type closeTrackingDB struct {
   781  	ethdb.Database
   782  	n *Node
   783  }
   784  
   785  func (db *closeTrackingDB) Close() error {
   786  	db.n.lock.Lock()
   787  	delete(db.n.databases, db)
   788  	db.n.lock.Unlock()
   789  	return db.Database.Close()
   790  }
   791  
   792  // wrapDatabase ensures the database will be auto-closed when Node is closed.
   793  func (n *Node) wrapDatabase(db ethdb.Database) ethdb.Database {
   794  	wrapper := &closeTrackingDB{db, n}
   795  	n.databases[wrapper] = struct{}{}
   796  	return wrapper
   797  }
   798  
   799  // closeDatabases closes all open databases.
   800  func (n *Node) closeDatabases() (errors []error) {
   801  	for db := range n.databases {
   802  		delete(n.databases, db)
   803  		if err := db.Database.Close(); err != nil {
   804  			errors = append(errors, err)
   805  		}
   806  	}
   807  	return errors
   808  }