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