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