node.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  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"
  21. "os"
  22. "path/filepath"
  23. "reflect"
  24. "strings"
  25. "sync"
  26. "github.com/ethereum/go-ethereum/accounts"
  27. "github.com/ethereum/go-ethereum/core/rawdb"
  28. "github.com/ethereum/go-ethereum/ethdb"
  29. "github.com/ethereum/go-ethereum/event"
  30. "github.com/ethereum/go-ethereum/internal/debug"
  31. "github.com/ethereum/go-ethereum/log"
  32. "github.com/ethereum/go-ethereum/p2p"
  33. "github.com/ethereum/go-ethereum/rpc"
  34. "github.com/prometheus/tsdb/fileutil"
  35. )
  36. // Node is a container on which services can be registered.
  37. type Node struct {
  38. eventmux *event.TypeMux // Event multiplexer used between the services of a stack
  39. config *Config
  40. accman *accounts.Manager
  41. ephemeralKeystore string // if non-empty, the key directory that will be removed by Stop
  42. instanceDirLock fileutil.Releaser // prevents concurrent use of instance directory
  43. serverConfig p2p.Config
  44. server *p2p.Server // Currently running P2P networking layer
  45. serviceFuncs []ServiceConstructor // Service constructors (in dependency order)
  46. services map[reflect.Type]Service // Currently running services
  47. rpcAPIs []rpc.API // List of APIs currently provided by the node
  48. inprocHandler *rpc.Server // In-process RPC request handler to process the API requests
  49. ipcEndpoint string // IPC endpoint to listen at (empty = IPC disabled)
  50. ipcListener net.Listener // IPC RPC listener socket to serve API requests
  51. ipcHandler *rpc.Server // IPC RPC request handler to process the API requests
  52. httpEndpoint string // HTTP endpoint (interface + port) to listen at (empty = HTTP disabled)
  53. httpWhitelist []string // HTTP RPC modules to allow through this endpoint
  54. httpListener net.Listener // HTTP RPC listener socket to server API requests
  55. httpHandler *rpc.Server // HTTP RPC request handler to process the API requests
  56. wsEndpoint string // Websocket endpoint (interface + port) to listen at (empty = websocket disabled)
  57. wsListener net.Listener // Websocket RPC listener socket to server API requests
  58. wsHandler *rpc.Server // Websocket RPC request handler to process the API requests
  59. stop chan struct{} // Channel to wait for termination notifications
  60. lock sync.RWMutex
  61. log log.Logger
  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. // Ensure that the instance name doesn't cause weird conflicts with
  77. // other files in the data directory.
  78. if strings.ContainsAny(conf.Name, `/\`) {
  79. return nil, errors.New(`Config.Name must not contain '/' or '\'`)
  80. }
  81. if conf.Name == datadirDefaultKeyStore {
  82. return nil, errors.New(`Config.Name cannot be "` + datadirDefaultKeyStore + `"`)
  83. }
  84. if strings.HasSuffix(conf.Name, ".ipc") {
  85. return nil, errors.New(`Config.Name cannot end in ".ipc"`)
  86. }
  87. // Ensure that the AccountManager method works before the node has started.
  88. // We rely on this in cmd/geth.
  89. am, ephemeralKeystore, err := makeAccountManager(conf)
  90. if err != nil {
  91. return nil, err
  92. }
  93. if conf.Logger == nil {
  94. conf.Logger = log.New()
  95. }
  96. // Note: any interaction with Config that would create/touch files
  97. // in the data directory or instance directory is delayed until Start.
  98. return &Node{
  99. accman: am,
  100. ephemeralKeystore: ephemeralKeystore,
  101. config: conf,
  102. serviceFuncs: []ServiceConstructor{},
  103. ipcEndpoint: conf.IPCEndpoint(),
  104. httpEndpoint: conf.HTTPEndpoint(),
  105. wsEndpoint: conf.WSEndpoint(),
  106. eventmux: new(event.TypeMux),
  107. log: conf.Logger,
  108. }, nil
  109. }
  110. // Close stops the Node and releases resources acquired in
  111. // Node constructor New.
  112. func (n *Node) Close() error {
  113. var errs []error
  114. // Terminate all subsystems and collect any errors
  115. if err := n.Stop(); err != nil && err != ErrNodeStopped {
  116. errs = append(errs, err)
  117. }
  118. if err := n.accman.Close(); err != nil {
  119. errs = append(errs, err)
  120. }
  121. // Report any errors that might have occurred
  122. switch len(errs) {
  123. case 0:
  124. return nil
  125. case 1:
  126. return errs[0]
  127. default:
  128. return fmt.Errorf("%v", errs)
  129. }
  130. }
  131. // Register injects a new service into the node's stack. The service created by
  132. // the passed constructor must be unique in its type with regard to sibling ones.
  133. func (n *Node) Register(constructor ServiceConstructor) error {
  134. n.lock.Lock()
  135. defer n.lock.Unlock()
  136. if n.server != nil {
  137. return ErrNodeRunning
  138. }
  139. n.serviceFuncs = append(n.serviceFuncs, constructor)
  140. return nil
  141. }
  142. // Start create a live P2P node and starts running it.
  143. func (n *Node) Start() error {
  144. n.lock.Lock()
  145. defer n.lock.Unlock()
  146. // Short circuit if the node's already running
  147. if n.server != nil {
  148. return ErrNodeRunning
  149. }
  150. if err := n.openDataDir(); err != nil {
  151. return err
  152. }
  153. // Initialize the p2p server. This creates the node key and
  154. // discovery databases.
  155. n.serverConfig = n.config.P2P
  156. n.serverConfig.PrivateKey = n.config.NodeKey()
  157. n.serverConfig.Name = n.config.NodeName()
  158. n.serverConfig.Logger = n.log
  159. if n.serverConfig.StaticNodes == nil {
  160. n.serverConfig.StaticNodes = n.config.StaticNodes()
  161. }
  162. if n.serverConfig.TrustedNodes == nil {
  163. n.serverConfig.TrustedNodes = n.config.TrustedNodes()
  164. }
  165. if n.serverConfig.NodeDatabase == "" {
  166. n.serverConfig.NodeDatabase = n.config.NodeDB()
  167. }
  168. running := &p2p.Server{Config: n.serverConfig}
  169. n.log.Info("Starting peer-to-peer node", "instance", n.serverConfig.Name)
  170. // Otherwise copy and specialize the P2P configuration
  171. services := make(map[reflect.Type]Service)
  172. for _, constructor := range n.serviceFuncs {
  173. // Create a new context for the particular service
  174. ctx := &ServiceContext{
  175. config: n.config,
  176. services: make(map[reflect.Type]Service),
  177. EventMux: n.eventmux,
  178. AccountManager: n.accman,
  179. }
  180. for kind, s := range services { // copy needed for threaded access
  181. ctx.services[kind] = s
  182. }
  183. // Construct and save the service
  184. service, err := constructor(ctx)
  185. if err != nil {
  186. return err
  187. }
  188. kind := reflect.TypeOf(service)
  189. if _, exists := services[kind]; exists {
  190. return &DuplicateServiceError{Kind: kind}
  191. }
  192. services[kind] = service
  193. }
  194. // Gather the protocols and start the freshly assembled P2P server
  195. for _, service := range services {
  196. running.Protocols = append(running.Protocols, service.Protocols()...)
  197. }
  198. if err := running.Start(); err != nil {
  199. return convertFileLockError(err)
  200. }
  201. // Start each of the services
  202. var started []reflect.Type
  203. for kind, service := range services {
  204. // Start the next service, stopping all previous upon failure
  205. if err := service.Start(running); err != nil {
  206. for _, kind := range started {
  207. services[kind].Stop()
  208. }
  209. running.Stop()
  210. return err
  211. }
  212. // Mark the service started for potential cleanup
  213. started = append(started, kind)
  214. }
  215. // Lastly start the configured RPC interfaces
  216. if err := n.startRPC(services); err != nil {
  217. for _, service := range services {
  218. service.Stop()
  219. }
  220. running.Stop()
  221. return err
  222. }
  223. // Finish initializing the startup
  224. n.services = services
  225. n.server = running
  226. n.stop = make(chan struct{})
  227. return nil
  228. }
  229. // Config returns the configuration of node.
  230. func (n *Node) Config() *Config {
  231. return n.config
  232. }
  233. func (n *Node) openDataDir() error {
  234. if n.config.DataDir == "" {
  235. return nil // ephemeral
  236. }
  237. instdir := filepath.Join(n.config.DataDir, n.config.name())
  238. if err := os.MkdirAll(instdir, 0700); err != nil {
  239. return err
  240. }
  241. // Lock the instance directory to prevent concurrent use by another instance as well as
  242. // accidental use of the instance directory as a database.
  243. release, _, err := fileutil.Flock(filepath.Join(instdir, "LOCK"))
  244. if err != nil {
  245. return convertFileLockError(err)
  246. }
  247. n.instanceDirLock = release
  248. return nil
  249. }
  250. // startRPC is a helper method to start all the various RPC endpoint during node
  251. // startup. It's not meant to be called at any time afterwards as it makes certain
  252. // assumptions about the state of the node.
  253. func (n *Node) startRPC(services map[reflect.Type]Service) error {
  254. // Gather all the possible APIs to surface
  255. apis := n.apis()
  256. for _, service := range services {
  257. apis = append(apis, service.APIs()...)
  258. }
  259. // Start the various API endpoints, terminating all in case of errors
  260. if err := n.startInProc(apis); err != nil {
  261. return err
  262. }
  263. if err := n.startIPC(apis); err != nil {
  264. n.stopInProc()
  265. return err
  266. }
  267. if err := n.startHTTP(n.httpEndpoint, apis, n.config.HTTPModules, n.config.HTTPCors, n.config.HTTPVirtualHosts, n.config.HTTPTimeouts); err != nil {
  268. n.stopIPC()
  269. n.stopInProc()
  270. return err
  271. }
  272. if err := n.startWS(n.wsEndpoint, apis, n.config.WSModules, n.config.WSOrigins, n.config.WSExposeAll); err != nil {
  273. n.stopHTTP()
  274. n.stopIPC()
  275. n.stopInProc()
  276. return err
  277. }
  278. // All API endpoints started successfully
  279. n.rpcAPIs = apis
  280. return nil
  281. }
  282. // startInProc initializes an in-process RPC endpoint.
  283. func (n *Node) startInProc(apis []rpc.API) error {
  284. // Register all the APIs exposed by the services
  285. handler := rpc.NewServer()
  286. for _, api := range apis {
  287. if err := handler.RegisterName(api.Namespace, api.Service); err != nil {
  288. return err
  289. }
  290. n.log.Debug("InProc registered", "namespace", api.Namespace)
  291. }
  292. n.inprocHandler = handler
  293. return nil
  294. }
  295. // stopInProc terminates the in-process RPC endpoint.
  296. func (n *Node) stopInProc() {
  297. if n.inprocHandler != nil {
  298. n.inprocHandler.Stop()
  299. n.inprocHandler = nil
  300. }
  301. }
  302. // startIPC initializes and starts the IPC RPC endpoint.
  303. func (n *Node) startIPC(apis []rpc.API) error {
  304. if n.ipcEndpoint == "" {
  305. return nil // IPC disabled.
  306. }
  307. listener, handler, err := rpc.StartIPCEndpoint(n.ipcEndpoint, apis)
  308. if err != nil {
  309. return err
  310. }
  311. n.ipcListener = listener
  312. n.ipcHandler = handler
  313. n.log.Info("IPC endpoint opened", "url", n.ipcEndpoint)
  314. return nil
  315. }
  316. // stopIPC terminates the IPC RPC endpoint.
  317. func (n *Node) stopIPC() {
  318. if n.ipcListener != nil {
  319. n.ipcListener.Close()
  320. n.ipcListener = nil
  321. n.log.Info("IPC endpoint closed", "url", n.ipcEndpoint)
  322. }
  323. if n.ipcHandler != nil {
  324. n.ipcHandler.Stop()
  325. n.ipcHandler = nil
  326. }
  327. }
  328. // startHTTP initializes and starts the HTTP RPC endpoint.
  329. func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors []string, vhosts []string, timeouts rpc.HTTPTimeouts) error {
  330. // Short circuit if the HTTP endpoint isn't being exposed
  331. if endpoint == "" {
  332. return nil
  333. }
  334. listener, handler, err := rpc.StartHTTPEndpoint(endpoint, apis, modules, cors, vhosts, timeouts)
  335. if err != nil {
  336. return err
  337. }
  338. n.log.Info("HTTP endpoint opened", "url", fmt.Sprintf("http://%s", endpoint), "cors", strings.Join(cors, ","), "vhosts", strings.Join(vhosts, ","))
  339. // All listeners booted successfully
  340. n.httpEndpoint = endpoint
  341. n.httpListener = listener
  342. n.httpHandler = handler
  343. return nil
  344. }
  345. // stopHTTP terminates the HTTP RPC endpoint.
  346. func (n *Node) stopHTTP() {
  347. if n.httpListener != nil {
  348. n.httpListener.Close()
  349. n.httpListener = nil
  350. n.log.Info("HTTP endpoint closed", "url", fmt.Sprintf("http://%s", n.httpEndpoint))
  351. }
  352. if n.httpHandler != nil {
  353. n.httpHandler.Stop()
  354. n.httpHandler = nil
  355. }
  356. }
  357. // startWS initializes and starts the websocket RPC endpoint.
  358. func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrigins []string, exposeAll bool) error {
  359. // Short circuit if the WS endpoint isn't being exposed
  360. if endpoint == "" {
  361. return nil
  362. }
  363. listener, handler, err := rpc.StartWSEndpoint(endpoint, apis, modules, wsOrigins, exposeAll)
  364. if err != nil {
  365. return err
  366. }
  367. n.log.Info("WebSocket endpoint opened", "url", fmt.Sprintf("ws://%s", listener.Addr()))
  368. // All listeners booted successfully
  369. n.wsEndpoint = endpoint
  370. n.wsListener = listener
  371. n.wsHandler = handler
  372. return nil
  373. }
  374. // stopWS terminates the websocket RPC endpoint.
  375. func (n *Node) stopWS() {
  376. if n.wsListener != nil {
  377. n.wsListener.Close()
  378. n.wsListener = nil
  379. n.log.Info("WebSocket endpoint closed", "url", fmt.Sprintf("ws://%s", n.wsEndpoint))
  380. }
  381. if n.wsHandler != nil {
  382. n.wsHandler.Stop()
  383. n.wsHandler = nil
  384. }
  385. }
  386. // Stop terminates a running node along with all it's services. In the node was
  387. // not started, an error is returned.
  388. func (n *Node) Stop() error {
  389. n.lock.Lock()
  390. defer n.lock.Unlock()
  391. // Short circuit if the node's not running
  392. if n.server == nil {
  393. return ErrNodeStopped
  394. }
  395. // Terminate the API, services and the p2p server.
  396. n.stopWS()
  397. n.stopHTTP()
  398. n.stopIPC()
  399. n.rpcAPIs = nil
  400. failure := &StopError{
  401. Services: make(map[reflect.Type]error),
  402. }
  403. for kind, service := range n.services {
  404. if err := service.Stop(); err != nil {
  405. failure.Services[kind] = err
  406. }
  407. }
  408. n.server.Stop()
  409. n.services = nil
  410. n.server = nil
  411. // Release instance directory lock.
  412. if n.instanceDirLock != nil {
  413. if err := n.instanceDirLock.Release(); err != nil {
  414. n.log.Error("Can't release datadir lock", "err", err)
  415. }
  416. n.instanceDirLock = nil
  417. }
  418. // unblock n.Wait
  419. close(n.stop)
  420. // Remove the keystore if it was created ephemerally.
  421. var keystoreErr error
  422. if n.ephemeralKeystore != "" {
  423. keystoreErr = os.RemoveAll(n.ephemeralKeystore)
  424. }
  425. if len(failure.Services) > 0 {
  426. return failure
  427. }
  428. if keystoreErr != nil {
  429. return keystoreErr
  430. }
  431. return nil
  432. }
  433. // Wait blocks the thread until the node is stopped. If the node is not running
  434. // at the time of invocation, the method immediately returns.
  435. func (n *Node) Wait() {
  436. n.lock.RLock()
  437. if n.server == nil {
  438. n.lock.RUnlock()
  439. return
  440. }
  441. stop := n.stop
  442. n.lock.RUnlock()
  443. <-stop
  444. }
  445. // Restart terminates a running node and boots up a new one in its place. If the
  446. // node isn't running, an error is returned.
  447. func (n *Node) Restart() error {
  448. if err := n.Stop(); err != nil {
  449. return err
  450. }
  451. if err := n.Start(); err != nil {
  452. return err
  453. }
  454. return nil
  455. }
  456. // Attach creates an RPC client attached to an in-process API handler.
  457. func (n *Node) Attach() (*rpc.Client, error) {
  458. n.lock.RLock()
  459. defer n.lock.RUnlock()
  460. if n.server == nil {
  461. return nil, ErrNodeStopped
  462. }
  463. return rpc.DialInProc(n.inprocHandler), nil
  464. }
  465. // RPCHandler returns the in-process RPC request handler.
  466. func (n *Node) RPCHandler() (*rpc.Server, error) {
  467. n.lock.RLock()
  468. defer n.lock.RUnlock()
  469. if n.inprocHandler == nil {
  470. return nil, ErrNodeStopped
  471. }
  472. return n.inprocHandler, nil
  473. }
  474. // Server retrieves the currently running P2P network layer. This method is meant
  475. // only to inspect fields of the currently running server, life cycle management
  476. // should be left to this Node entity.
  477. func (n *Node) Server() *p2p.Server {
  478. n.lock.RLock()
  479. defer n.lock.RUnlock()
  480. return n.server
  481. }
  482. // Service retrieves a currently running service registered of a specific type.
  483. func (n *Node) Service(service interface{}) error {
  484. n.lock.RLock()
  485. defer n.lock.RUnlock()
  486. // Short circuit if the node's not running
  487. if n.server == nil {
  488. return ErrNodeStopped
  489. }
  490. // Otherwise try to find the service to return
  491. element := reflect.ValueOf(service).Elem()
  492. if running, ok := n.services[element.Type()]; ok {
  493. element.Set(reflect.ValueOf(running))
  494. return nil
  495. }
  496. return ErrServiceUnknown
  497. }
  498. // DataDir retrieves the current datadir used by the protocol stack.
  499. // Deprecated: No files should be stored in this directory, use InstanceDir instead.
  500. func (n *Node) DataDir() string {
  501. return n.config.DataDir
  502. }
  503. // InstanceDir retrieves the instance directory used by the protocol stack.
  504. func (n *Node) InstanceDir() string {
  505. return n.config.instanceDir()
  506. }
  507. // AccountManager retrieves the account manager used by the protocol stack.
  508. func (n *Node) AccountManager() *accounts.Manager {
  509. return n.accman
  510. }
  511. // IPCEndpoint retrieves the current IPC endpoint used by the protocol stack.
  512. func (n *Node) IPCEndpoint() string {
  513. return n.ipcEndpoint
  514. }
  515. // HTTPEndpoint retrieves the current HTTP endpoint used by the protocol stack.
  516. func (n *Node) HTTPEndpoint() string {
  517. n.lock.Lock()
  518. defer n.lock.Unlock()
  519. if n.httpListener != nil {
  520. return n.httpListener.Addr().String()
  521. }
  522. return n.httpEndpoint
  523. }
  524. // WSEndpoint retrieves the current WS endpoint used by the protocol stack.
  525. func (n *Node) WSEndpoint() string {
  526. n.lock.Lock()
  527. defer n.lock.Unlock()
  528. if n.wsListener != nil {
  529. return n.wsListener.Addr().String()
  530. }
  531. return n.wsEndpoint
  532. }
  533. // EventMux retrieves the event multiplexer used by all the network services in
  534. // the current protocol stack.
  535. func (n *Node) EventMux() *event.TypeMux {
  536. return n.eventmux
  537. }
  538. // OpenDatabase opens an existing database with the given name (or creates one if no
  539. // previous can be found) from within the node's instance directory. If the node is
  540. // ephemeral, a memory database is returned.
  541. func (n *Node) OpenDatabase(name string, cache, handles int, namespace string) (ethdb.Database, error) {
  542. if n.config.DataDir == "" {
  543. return rawdb.NewMemoryDatabase(), nil
  544. }
  545. return rawdb.NewLevelDBDatabase(n.config.ResolvePath(name), cache, handles, namespace)
  546. }
  547. // OpenDatabaseWithFreezer opens an existing database with the given name (or
  548. // creates one if no previous can be found) from within the node's data directory,
  549. // also attaching a chain freezer to it that moves ancient chain data from the
  550. // database to immutable append-only files. If the node is an ephemeral one, a
  551. // memory database is returned.
  552. func (n *Node) OpenDatabaseWithFreezer(name string, cache, handles int, freezer, namespace string) (ethdb.Database, error) {
  553. if n.config.DataDir == "" {
  554. return rawdb.NewMemoryDatabase(), nil
  555. }
  556. root := n.config.ResolvePath(name)
  557. switch {
  558. case freezer == "":
  559. freezer = filepath.Join(root, "ancient")
  560. case !filepath.IsAbs(freezer):
  561. freezer = n.config.ResolvePath(freezer)
  562. }
  563. return rawdb.NewLevelDBDatabaseWithFreezer(root, cache, handles, freezer, namespace)
  564. }
  565. // ResolvePath returns the absolute path of a resource in the instance directory.
  566. func (n *Node) ResolvePath(x string) string {
  567. return n.config.ResolvePath(x)
  568. }
  569. // apis returns the collection of RPC descriptors this node offers.
  570. func (n *Node) apis() []rpc.API {
  571. return []rpc.API{
  572. {
  573. Namespace: "admin",
  574. Version: "1.0",
  575. Service: NewPrivateAdminAPI(n),
  576. }, {
  577. Namespace: "admin",
  578. Version: "1.0",
  579. Service: NewPublicAdminAPI(n),
  580. Public: true,
  581. }, {
  582. Namespace: "debug",
  583. Version: "1.0",
  584. Service: debug.Handler,
  585. }, {
  586. Namespace: "web3",
  587. Version: "1.0",
  588. Service: NewPublicWeb3API(n),
  589. Public: true,
  590. },
  591. }
  592. }