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