node.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  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. package node
  17. import (
  18. "errors"
  19. "fmt"
  20. "net/http"
  21. "os"
  22. "path"
  23. "path/filepath"
  24. "reflect"
  25. "strings"
  26. "sync"
  27. "github.com/ethereum/go-ethereum/accounts"
  28. "github.com/ethereum/go-ethereum/core/rawdb"
  29. "github.com/ethereum/go-ethereum/ethdb"
  30. "github.com/ethereum/go-ethereum/ethdb/leveldb"
  31. "github.com/ethereum/go-ethereum/event"
  32. "github.com/ethereum/go-ethereum/log"
  33. "github.com/ethereum/go-ethereum/p2p"
  34. "github.com/ethereum/go-ethereum/rpc"
  35. "github.com/prometheus/tsdb/fileutil"
  36. )
  37. // Node is a container on which services can be registered.
  38. type Node struct {
  39. eventmux *event.TypeMux
  40. config *Config
  41. accman *accounts.Manager
  42. log log.Logger
  43. ephemKeystore string // if non-empty, the key directory that will be removed by Stop
  44. dirLock fileutil.Releaser // prevents concurrent use of instance directory
  45. stop chan struct{} // Channel to wait for termination notifications
  46. server *p2p.Server // Currently running P2P networking layer
  47. startStopLock sync.Mutex // Start/Stop are protected by an additional lock
  48. state int // Tracks state of node lifecycle
  49. lock sync.Mutex
  50. lifecycles []Lifecycle // All registered backends, services, and auxiliary services that have a lifecycle
  51. rpcAPIs []rpc.API // List of APIs currently provided by the node
  52. http *httpServer //
  53. ws *httpServer //
  54. ipc *ipcServer // Stores information about the ipc http server
  55. inprocHandler *rpc.Server // In-process RPC request handler to process the API requests
  56. databases map[*closeTrackingDB]struct{} // All open databases
  57. }
  58. const (
  59. initializingState = iota
  60. runningState
  61. closedState
  62. )
  63. // New creates a new P2P node, ready for protocol registration.
  64. func New(conf *Config) (*Node, error) {
  65. // Copy config and resolve the datadir so future changes to the current
  66. // working directory don't affect the node.
  67. confCopy := *conf
  68. conf = &confCopy
  69. if conf.DataDir != "" {
  70. absdatadir, err := filepath.Abs(conf.DataDir)
  71. if err != nil {
  72. return nil, err
  73. }
  74. conf.DataDir = absdatadir
  75. }
  76. if conf.LogConfig != nil {
  77. logFilePath := ""
  78. if conf.LogConfig.FileRoot == "" {
  79. logFilePath = path.Join(conf.DataDir, conf.LogConfig.FilePath)
  80. } else {
  81. logFilePath = path.Join(conf.LogConfig.FileRoot, conf.LogConfig.FilePath)
  82. }
  83. log.Root().SetHandler(log.NewFileLvlHandler(logFilePath, conf.LogConfig.MaxBytesSize, conf.LogConfig.Level))
  84. }
  85. if conf.Logger == nil {
  86. conf.Logger = log.New()
  87. }
  88. // Ensure that the instance name doesn't cause weird conflicts with
  89. // other files in the data directory.
  90. if strings.ContainsAny(conf.Name, `/\`) {
  91. return nil, errors.New(`Config.Name must not contain '/' or '\'`)
  92. }
  93. if conf.Name == datadirDefaultKeyStore {
  94. return nil, errors.New(`Config.Name cannot be "` + datadirDefaultKeyStore + `"`)
  95. }
  96. if strings.HasSuffix(conf.Name, ".ipc") {
  97. return nil, errors.New(`Config.Name cannot end in ".ipc"`)
  98. }
  99. node := &Node{
  100. config: conf,
  101. inprocHandler: rpc.NewServer(),
  102. eventmux: new(event.TypeMux),
  103. log: conf.Logger,
  104. stop: make(chan struct{}),
  105. server: &p2p.Server{Config: conf.P2P},
  106. databases: make(map[*closeTrackingDB]struct{}),
  107. }
  108. // Register built-in APIs.
  109. node.rpcAPIs = append(node.rpcAPIs, node.apis()...)
  110. // Acquire the instance directory lock.
  111. if err := node.openDataDir(); err != nil {
  112. return nil, err
  113. }
  114. // Ensure that the AccountManager method works before the node has started. We rely on
  115. // this in cmd/geth.
  116. am, ephemeralKeystore, err := makeAccountManager(conf)
  117. if err != nil {
  118. return nil, err
  119. }
  120. node.accman = am
  121. node.ephemKeystore = ephemeralKeystore
  122. // Initialize the p2p server. This creates the node key and discovery databases.
  123. node.server.Config.PrivateKey = node.config.NodeKey()
  124. node.server.Config.Name = node.config.NodeName()
  125. node.server.Config.Logger = node.log
  126. if node.server.Config.StaticNodes == nil {
  127. node.server.Config.StaticNodes = node.config.StaticNodes()
  128. }
  129. if node.server.Config.TrustedNodes == nil {
  130. node.server.Config.TrustedNodes = node.config.TrustedNodes()
  131. }
  132. if node.server.Config.NodeDatabase == "" {
  133. node.server.Config.NodeDatabase = node.config.NodeDB()
  134. }
  135. // Check HTTP/WS prefixes are valid.
  136. if err := validatePrefix("HTTP", conf.HTTPPathPrefix); err != nil {
  137. return nil, err
  138. }
  139. if err := validatePrefix("WebSocket", conf.WSPathPrefix); err != nil {
  140. return nil, err
  141. }
  142. // Configure RPC servers.
  143. node.http = newHTTPServer(node.log, conf.HTTPTimeouts)
  144. node.ws = newHTTPServer(node.log, rpc.DefaultHTTPTimeouts)
  145. node.ipc = newIPCServer(node.log, conf.IPCEndpoint())
  146. return node, nil
  147. }
  148. // Start starts all registered lifecycles, RPC services and p2p networking.
  149. // Node can only be started once.
  150. func (n *Node) Start() error {
  151. n.startStopLock.Lock()
  152. defer n.startStopLock.Unlock()
  153. n.lock.Lock()
  154. switch n.state {
  155. case runningState:
  156. n.lock.Unlock()
  157. return ErrNodeRunning
  158. case closedState:
  159. n.lock.Unlock()
  160. return ErrNodeStopped
  161. }
  162. n.state = runningState
  163. // open networking and RPC endpoints
  164. err := n.openEndpoints()
  165. lifecycles := make([]Lifecycle, len(n.lifecycles))
  166. copy(lifecycles, n.lifecycles)
  167. n.lock.Unlock()
  168. // Check if endpoint startup failed.
  169. if err != nil {
  170. n.doClose(nil)
  171. return err
  172. }
  173. // Start all registered lifecycles.
  174. var started []Lifecycle
  175. for _, lifecycle := range lifecycles {
  176. if err = lifecycle.Start(); err != nil {
  177. break
  178. }
  179. started = append(started, lifecycle)
  180. }
  181. // Check if any lifecycle failed to start.
  182. if err != nil {
  183. n.stopServices(started)
  184. n.doClose(nil)
  185. }
  186. return err
  187. }
  188. // Close stops the Node and releases resources acquired in
  189. // Node constructor New.
  190. func (n *Node) Close() error {
  191. n.startStopLock.Lock()
  192. defer n.startStopLock.Unlock()
  193. n.lock.Lock()
  194. state := n.state
  195. n.lock.Unlock()
  196. switch state {
  197. case initializingState:
  198. // The node was never started.
  199. return n.doClose(nil)
  200. case runningState:
  201. // The node was started, release resources acquired by Start().
  202. var errs []error
  203. if err := n.stopServices(n.lifecycles); err != nil {
  204. errs = append(errs, err)
  205. }
  206. return n.doClose(errs)
  207. case closedState:
  208. return ErrNodeStopped
  209. default:
  210. panic(fmt.Sprintf("node is in unknown state %d", state))
  211. }
  212. }
  213. // doClose releases resources acquired by New(), collecting errors.
  214. func (n *Node) doClose(errs []error) error {
  215. // Close databases. This needs the lock because it needs to
  216. // synchronize with OpenDatabase*.
  217. n.lock.Lock()
  218. n.state = closedState
  219. errs = append(errs, n.closeDatabases()...)
  220. n.lock.Unlock()
  221. if err := n.accman.Close(); err != nil {
  222. errs = append(errs, err)
  223. }
  224. if n.ephemKeystore != "" {
  225. if err := os.RemoveAll(n.ephemKeystore); err != nil {
  226. errs = append(errs, err)
  227. }
  228. }
  229. // Release instance directory lock.
  230. n.closeDataDir()
  231. // Unblock n.Wait.
  232. close(n.stop)
  233. // Report any errors that might have occurred.
  234. switch len(errs) {
  235. case 0:
  236. return nil
  237. case 1:
  238. return errs[0]
  239. default:
  240. return fmt.Errorf("%v", errs)
  241. }
  242. }
  243. // openEndpoints starts all network and RPC endpoints.
  244. func (n *Node) openEndpoints() error {
  245. // start networking endpoints
  246. n.log.Info("Starting peer-to-peer node", "instance", n.server.Name)
  247. if err := n.server.Start(); err != nil {
  248. return convertFileLockError(err)
  249. }
  250. // start RPC endpoints
  251. err := n.startRPC()
  252. if err != nil {
  253. n.stopRPC()
  254. n.server.Stop()
  255. }
  256. return err
  257. }
  258. // containsLifecycle checks if 'lfs' contains 'l'.
  259. func containsLifecycle(lfs []Lifecycle, l Lifecycle) bool {
  260. for _, obj := range lfs {
  261. if obj == l {
  262. return true
  263. }
  264. }
  265. return false
  266. }
  267. // stopServices terminates running services, RPC and p2p networking.
  268. // It is the inverse of Start.
  269. func (n *Node) stopServices(running []Lifecycle) error {
  270. n.stopRPC()
  271. // Stop running lifecycles in reverse order.
  272. failure := &StopError{Services: make(map[reflect.Type]error)}
  273. for i := len(running) - 1; i >= 0; i-- {
  274. if err := running[i].Stop(); err != nil {
  275. failure.Services[reflect.TypeOf(running[i])] = err
  276. }
  277. }
  278. // Stop p2p networking.
  279. n.server.Stop()
  280. if len(failure.Services) > 0 {
  281. return failure
  282. }
  283. return nil
  284. }
  285. func (n *Node) openDataDir() error {
  286. if n.config.DataDir == "" {
  287. return nil // ephemeral
  288. }
  289. instdir := filepath.Join(n.config.DataDir, n.config.name())
  290. if err := os.MkdirAll(instdir, 0700); err != nil {
  291. return err
  292. }
  293. // Lock the instance directory to prevent concurrent use by another instance as well as
  294. // accidental use of the instance directory as a database.
  295. release, _, err := fileutil.Flock(filepath.Join(instdir, "LOCK"))
  296. if err != nil {
  297. return convertFileLockError(err)
  298. }
  299. n.dirLock = release
  300. return nil
  301. }
  302. func (n *Node) closeDataDir() {
  303. // Release instance directory lock.
  304. if n.dirLock != nil {
  305. if err := n.dirLock.Release(); err != nil {
  306. n.log.Error("Can't release datadir lock", "err", err)
  307. }
  308. n.dirLock = nil
  309. }
  310. }
  311. // configureRPC is a helper method to configure all the various RPC endpoints during node
  312. // startup. It's not meant to be called at any time afterwards as it makes certain
  313. // assumptions about the state of the node.
  314. func (n *Node) startRPC() error {
  315. if err := n.startInProc(); err != nil {
  316. return err
  317. }
  318. // Configure IPC.
  319. if n.ipc.endpoint != "" {
  320. if err := n.ipc.start(n.rpcAPIs); err != nil {
  321. return err
  322. }
  323. }
  324. // Configure HTTP.
  325. if n.config.HTTPHost != "" {
  326. config := httpConfig{
  327. CorsAllowedOrigins: n.config.HTTPCors,
  328. Vhosts: n.config.HTTPVirtualHosts,
  329. Modules: n.config.HTTPModules,
  330. prefix: n.config.HTTPPathPrefix,
  331. }
  332. if err := n.http.setListenAddr(n.config.HTTPHost, n.config.HTTPPort); err != nil {
  333. return err
  334. }
  335. if err := n.http.enableRPC(n.rpcAPIs, config); err != nil {
  336. return err
  337. }
  338. }
  339. // Configure WebSocket.
  340. if n.config.WSHost != "" {
  341. server := n.wsServerForPort(n.config.WSPort)
  342. config := wsConfig{
  343. Modules: n.config.WSModules,
  344. Origins: n.config.WSOrigins,
  345. prefix: n.config.WSPathPrefix,
  346. }
  347. if err := server.setListenAddr(n.config.WSHost, n.config.WSPort); err != nil {
  348. return err
  349. }
  350. if err := server.enableWS(n.rpcAPIs, config); err != nil {
  351. return err
  352. }
  353. }
  354. if err := n.http.start(); err != nil {
  355. return err
  356. }
  357. return n.ws.start()
  358. }
  359. func (n *Node) wsServerForPort(port int) *httpServer {
  360. if n.config.HTTPHost == "" || n.http.port == port {
  361. return n.http
  362. }
  363. return n.ws
  364. }
  365. func (n *Node) stopRPC() {
  366. n.http.stop()
  367. n.ws.stop()
  368. n.ipc.stop()
  369. n.stopInProc()
  370. }
  371. // startInProc registers all RPC APIs on the inproc server.
  372. func (n *Node) startInProc() error {
  373. for _, api := range n.rpcAPIs {
  374. if err := n.inprocHandler.RegisterName(api.Namespace, api.Service); err != nil {
  375. return err
  376. }
  377. }
  378. return nil
  379. }
  380. // stopInProc terminates the in-process RPC endpoint.
  381. func (n *Node) stopInProc() {
  382. n.inprocHandler.Stop()
  383. }
  384. // Wait blocks until the node is closed.
  385. func (n *Node) Wait() {
  386. <-n.stop
  387. }
  388. // RegisterLifecycle registers the given Lifecycle on the node.
  389. func (n *Node) RegisterLifecycle(lifecycle Lifecycle) {
  390. n.lock.Lock()
  391. defer n.lock.Unlock()
  392. if n.state != initializingState {
  393. panic("can't register lifecycle on running/stopped node")
  394. }
  395. if containsLifecycle(n.lifecycles, lifecycle) {
  396. panic(fmt.Sprintf("attempt to register lifecycle %T more than once", lifecycle))
  397. }
  398. n.lifecycles = append(n.lifecycles, lifecycle)
  399. }
  400. // RegisterProtocols adds backend's protocols to the node's p2p server.
  401. func (n *Node) RegisterProtocols(protocols []p2p.Protocol) {
  402. n.lock.Lock()
  403. defer n.lock.Unlock()
  404. if n.state != initializingState {
  405. panic("can't register protocols on running/stopped node")
  406. }
  407. n.server.Protocols = append(n.server.Protocols, protocols...)
  408. }
  409. // RegisterAPIs registers the APIs a service provides on the node.
  410. func (n *Node) RegisterAPIs(apis []rpc.API) {
  411. n.lock.Lock()
  412. defer n.lock.Unlock()
  413. if n.state != initializingState {
  414. panic("can't register APIs on running/stopped node")
  415. }
  416. n.rpcAPIs = append(n.rpcAPIs, apis...)
  417. }
  418. // RegisterHandler mounts a handler on the given path on the canonical HTTP server.
  419. //
  420. // The name of the handler is shown in a log message when the HTTP server starts
  421. // and should be a descriptive term for the service provided by the handler.
  422. func (n *Node) RegisterHandler(name, path string, handler http.Handler) {
  423. n.lock.Lock()
  424. defer n.lock.Unlock()
  425. if n.state != initializingState {
  426. panic("can't register HTTP handler on running/stopped node")
  427. }
  428. n.http.mux.Handle(path, handler)
  429. n.http.handlerNames[path] = name
  430. }
  431. // Attach creates an RPC client attached to an in-process API handler.
  432. func (n *Node) Attach() (*rpc.Client, error) {
  433. return rpc.DialInProc(n.inprocHandler), nil
  434. }
  435. // RPCHandler returns the in-process RPC request handler.
  436. func (n *Node) RPCHandler() (*rpc.Server, error) {
  437. n.lock.Lock()
  438. defer n.lock.Unlock()
  439. if n.state == closedState {
  440. return nil, ErrNodeStopped
  441. }
  442. return n.inprocHandler, nil
  443. }
  444. // Config returns the configuration of node.
  445. func (n *Node) Config() *Config {
  446. return n.config
  447. }
  448. // Server retrieves the currently running P2P network layer. This method is meant
  449. // only to inspect fields of the currently running server. Callers should not
  450. // start or stop the returned server.
  451. func (n *Node) Server() *p2p.Server {
  452. n.lock.Lock()
  453. defer n.lock.Unlock()
  454. return n.server
  455. }
  456. // DataDir retrieves the current datadir used by the protocol stack.
  457. // Deprecated: No files should be stored in this directory, use InstanceDir instead.
  458. func (n *Node) DataDir() string {
  459. return n.config.DataDir
  460. }
  461. // InstanceDir retrieves the instance directory used by the protocol stack.
  462. func (n *Node) InstanceDir() string {
  463. return n.config.instanceDir()
  464. }
  465. // AccountManager retrieves the account manager used by the protocol stack.
  466. func (n *Node) AccountManager() *accounts.Manager {
  467. return n.accman
  468. }
  469. // IPCEndpoint retrieves the current IPC endpoint used by the protocol stack.
  470. func (n *Node) IPCEndpoint() string {
  471. return n.ipc.endpoint
  472. }
  473. // HTTPEndpoint returns the URL of the HTTP server. Note that this URL does not
  474. // contain the JSON-RPC path prefix set by HTTPPathPrefix.
  475. func (n *Node) HTTPEndpoint() string {
  476. return "http://" + n.http.listenAddr()
  477. }
  478. // WSEndpoint returns the current JSON-RPC over WebSocket endpoint.
  479. func (n *Node) WSEndpoint() string {
  480. if n.http.wsAllowed() {
  481. return "ws://" + n.http.listenAddr() + n.http.wsConfig.prefix
  482. }
  483. return "ws://" + n.ws.listenAddr() + n.ws.wsConfig.prefix
  484. }
  485. // EventMux retrieves the event multiplexer used by all the network services in
  486. // the current protocol stack.
  487. func (n *Node) EventMux() *event.TypeMux {
  488. return n.eventmux
  489. }
  490. // OpenDatabase opens an existing database with the given name (or creates one if no
  491. // previous can be found) from within the node's instance directory. If the node is
  492. // ephemeral, a memory database is returned.
  493. func (n *Node) OpenDatabase(name string, cache, handles int, namespace string, readonly bool) (ethdb.Database, error) {
  494. n.lock.Lock()
  495. defer n.lock.Unlock()
  496. if n.state == closedState {
  497. return nil, ErrNodeStopped
  498. }
  499. var db ethdb.Database
  500. var err error
  501. if n.config.DataDir == "" {
  502. db = rawdb.NewMemoryDatabase()
  503. } else {
  504. db, err = rawdb.NewLevelDBDatabase(n.ResolvePath(name), cache, handles, namespace, readonly)
  505. }
  506. if err == nil {
  507. db = n.wrapDatabase(db)
  508. }
  509. return db, err
  510. }
  511. func (n *Node) OpenAndMergeDatabase(name string, cache, handles int, freezer, diff, namespace string, readonly, persistDiff bool) (ethdb.Database, error) {
  512. chainDB, err := n.OpenDatabaseWithFreezer(name, cache, handles, freezer, namespace, readonly)
  513. if err != nil {
  514. return nil, err
  515. }
  516. if persistDiff {
  517. diffStore, err := n.OpenDiffDatabase(name, handles, diff, namespace, readonly)
  518. if err != nil {
  519. chainDB.Close()
  520. return nil, err
  521. }
  522. chainDB.SetDiffStore(diffStore)
  523. }
  524. return chainDB, nil
  525. }
  526. // OpenDatabaseWithFreezer opens an existing database with the given name (or
  527. // creates one if no previous can be found) from within the node's data directory,
  528. // also attaching a chain freezer to it that moves ancient chain data from the
  529. // database to immutable append-only files. If the node is an ephemeral one, a
  530. // memory database is returned.
  531. func (n *Node) OpenDatabaseWithFreezer(name string, cache, handles int, freezer, namespace string, readonly bool) (ethdb.Database, error) {
  532. n.lock.Lock()
  533. defer n.lock.Unlock()
  534. if n.state == closedState {
  535. return nil, ErrNodeStopped
  536. }
  537. var db ethdb.Database
  538. var err error
  539. if n.config.DataDir == "" {
  540. db = rawdb.NewMemoryDatabase()
  541. } else {
  542. root := n.ResolvePath(name)
  543. switch {
  544. case freezer == "":
  545. freezer = filepath.Join(root, "ancient")
  546. case !filepath.IsAbs(freezer):
  547. freezer = n.ResolvePath(freezer)
  548. }
  549. db, err = rawdb.NewLevelDBDatabaseWithFreezer(root, cache, handles, freezer, namespace, readonly)
  550. }
  551. if err == nil {
  552. db = n.wrapDatabase(db)
  553. }
  554. return db, err
  555. }
  556. func (n *Node) OpenDiffDatabase(name string, handles int, diff, namespace string, readonly bool) (*leveldb.Database, error) {
  557. n.lock.Lock()
  558. defer n.lock.Unlock()
  559. if n.state == closedState {
  560. return nil, ErrNodeStopped
  561. }
  562. var db *leveldb.Database
  563. var err error
  564. if n.config.DataDir == "" {
  565. panic("datadir is missing")
  566. }
  567. root := n.ResolvePath(name)
  568. switch {
  569. case diff == "":
  570. diff = filepath.Join(root, "diff")
  571. case !filepath.IsAbs(diff):
  572. diff = n.ResolvePath(diff)
  573. }
  574. db, err = leveldb.New(diff, 0, handles, namespace, readonly)
  575. return db, err
  576. }
  577. // ResolvePath returns the absolute path of a resource in the instance directory.
  578. func (n *Node) ResolvePath(x string) string {
  579. return n.config.ResolvePath(x)
  580. }
  581. // closeTrackingDB wraps the Close method of a database. When the database is closed by the
  582. // service, the wrapper removes it from the node's database map. This ensures that Node
  583. // won't auto-close the database if it is closed by the service that opened it.
  584. type closeTrackingDB struct {
  585. ethdb.Database
  586. n *Node
  587. }
  588. func (db *closeTrackingDB) Close() error {
  589. db.n.lock.Lock()
  590. delete(db.n.databases, db)
  591. db.n.lock.Unlock()
  592. return db.Database.Close()
  593. }
  594. // wrapDatabase ensures the database will be auto-closed when Node is closed.
  595. func (n *Node) wrapDatabase(db ethdb.Database) ethdb.Database {
  596. wrapper := &closeTrackingDB{db, n}
  597. n.databases[wrapper] = struct{}{}
  598. return wrapper
  599. }
  600. // closeDatabases closes all open databases.
  601. func (n *Node) closeDatabases() (errors []error) {
  602. for db := range n.databases {
  603. delete(n.databases, db)
  604. if err := db.Database.Close(); err != nil {
  605. errors = append(errors, err)
  606. }
  607. }
  608. return errors
  609. }