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