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