github.com/status-im/status-go@v1.1.0/node/get_status_node.go (about)

     1  package node
     2  
     3  import (
     4  	"database/sql"
     5  	"errors"
     6  	"fmt"
     7  	"net"
     8  	"os"
     9  	"path/filepath"
    10  	"reflect"
    11  	"sync"
    12  
    13  	"github.com/syndtr/goleveldb/leveldb"
    14  
    15  	"github.com/ethereum/go-ethereum/accounts"
    16  	"github.com/ethereum/go-ethereum/event"
    17  	"github.com/ethereum/go-ethereum/log"
    18  	"github.com/ethereum/go-ethereum/node"
    19  	"github.com/ethereum/go-ethereum/p2p"
    20  	"github.com/ethereum/go-ethereum/p2p/enode"
    21  	"github.com/ethereum/go-ethereum/p2p/enr"
    22  
    23  	"github.com/status-im/status-go/account"
    24  	"github.com/status-im/status-go/common"
    25  	"github.com/status-im/status-go/connection"
    26  	"github.com/status-im/status-go/db"
    27  	"github.com/status-im/status-go/discovery"
    28  	"github.com/status-im/status-go/ipfs"
    29  	"github.com/status-im/status-go/multiaccounts"
    30  	"github.com/status-im/status-go/params"
    31  	"github.com/status-im/status-go/peers"
    32  	"github.com/status-im/status-go/rpc"
    33  	"github.com/status-im/status-go/server"
    34  	accountssvc "github.com/status-im/status-go/services/accounts"
    35  	appgeneral "github.com/status-im/status-go/services/app-general"
    36  	appmetricsservice "github.com/status-im/status-go/services/appmetrics"
    37  	"github.com/status-im/status-go/services/browsers"
    38  	"github.com/status-im/status-go/services/chat"
    39  	"github.com/status-im/status-go/services/communitytokens"
    40  	"github.com/status-im/status-go/services/connector"
    41  	"github.com/status-im/status-go/services/ens"
    42  	"github.com/status-im/status-go/services/gif"
    43  	localnotifications "github.com/status-im/status-go/services/local-notifications"
    44  	"github.com/status-im/status-go/services/mailservers"
    45  	"github.com/status-im/status-go/services/peer"
    46  	"github.com/status-im/status-go/services/permissions"
    47  	"github.com/status-im/status-go/services/personal"
    48  	"github.com/status-im/status-go/services/rpcfilters"
    49  	"github.com/status-im/status-go/services/rpcstats"
    50  	"github.com/status-im/status-go/services/status"
    51  	"github.com/status-im/status-go/services/stickers"
    52  	"github.com/status-im/status-go/services/subscriptions"
    53  	"github.com/status-im/status-go/services/updates"
    54  	"github.com/status-im/status-go/services/wakuext"
    55  	"github.com/status-im/status-go/services/wakuv2ext"
    56  	"github.com/status-im/status-go/services/wallet"
    57  	"github.com/status-im/status-go/services/web3provider"
    58  	"github.com/status-im/status-go/timesource"
    59  	"github.com/status-im/status-go/transactions"
    60  	"github.com/status-im/status-go/waku"
    61  	"github.com/status-im/status-go/wakuv2"
    62  )
    63  
    64  // errors
    65  var (
    66  	ErrNodeRunning            = errors.New("node is already running")
    67  	ErrNoGethNode             = errors.New("geth node is not available")
    68  	ErrNoRunningNode          = errors.New("there is no running node")
    69  	ErrAccountKeyStoreMissing = errors.New("account key store is not set")
    70  	ErrServiceUnknown         = errors.New("service unknown")
    71  	ErrDiscoveryRunning       = errors.New("discovery is already running")
    72  	ErrRPCMethodUnavailable   = `{"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"the method called does not exist/is not available"}}`
    73  )
    74  
    75  // StatusNode abstracts contained geth node and provides helper methods to
    76  // interact with it.
    77  type StatusNode struct {
    78  	mu sync.RWMutex
    79  
    80  	appDB           *sql.DB
    81  	multiaccountsDB *multiaccounts.Database
    82  	walletDB        *sql.DB
    83  
    84  	config    *params.NodeConfig // Status node configuration
    85  	gethNode  *node.Node         // reference to Geth P2P stack/node
    86  	rpcClient *rpc.Client        // reference to an RPC client
    87  
    88  	downloader *ipfs.Downloader
    89  	httpServer *server.MediaServer
    90  
    91  	discovery discovery.Discovery
    92  	register  *peers.Register
    93  	peerPool  *peers.PeerPool
    94  	db        *leveldb.DB // used as a cache for PeerPool
    95  
    96  	log log.Logger
    97  
    98  	gethAccountManager *account.GethManager
    99  	accountsManager    *accounts.Manager
   100  	transactor         *transactions.Transactor
   101  
   102  	// services
   103  	services      []common.StatusService
   104  	publicMethods map[string]bool
   105  	// we explicitly list every service, we could use interfaces
   106  	// and store them in a nicer way and user reflection, but for now stupid is good
   107  	rpcFiltersSrvc         *rpcfilters.Service
   108  	subscriptionsSrvc      *subscriptions.Service
   109  	rpcStatsSrvc           *rpcstats.Service
   110  	statusPublicSrvc       *status.Service
   111  	accountsSrvc           *accountssvc.Service
   112  	browsersSrvc           *browsers.Service
   113  	permissionsSrvc        *permissions.Service
   114  	mailserversSrvc        *mailservers.Service
   115  	providerSrvc           *web3provider.Service
   116  	appMetricsSrvc         *appmetricsservice.Service
   117  	walletSrvc             *wallet.Service
   118  	peerSrvc               *peer.Service
   119  	localNotificationsSrvc *localnotifications.Service
   120  	personalSrvc           *personal.Service
   121  	timeSourceSrvc         *timesource.NTPTimeSource
   122  	wakuSrvc               *waku.Waku
   123  	wakuExtSrvc            *wakuext.Service
   124  	wakuV2Srvc             *wakuv2.Waku
   125  	wakuV2ExtSrvc          *wakuv2ext.Service
   126  	ensSrvc                *ens.Service
   127  	communityTokensSrvc    *communitytokens.Service
   128  	gifSrvc                *gif.Service
   129  	stickersSrvc           *stickers.Service
   130  	chatSrvc               *chat.Service
   131  	updatesSrvc            *updates.Service
   132  	pendingTracker         *transactions.PendingTxTracker
   133  	connectorSrvc          *connector.Service
   134  	appGeneralSrvc         *appgeneral.Service
   135  
   136  	accountsFeed event.Feed
   137  	walletFeed   event.Feed
   138  }
   139  
   140  // New makes new instance of StatusNode.
   141  func New(transactor *transactions.Transactor) *StatusNode {
   142  	return &StatusNode{
   143  		gethAccountManager: account.NewGethManager(),
   144  		transactor:         transactor,
   145  		log:                log.New("package", "status-go/node.StatusNode"),
   146  		publicMethods:      make(map[string]bool),
   147  	}
   148  }
   149  
   150  // Config exposes reference to running node's configuration
   151  func (n *StatusNode) Config() *params.NodeConfig {
   152  	n.mu.RLock()
   153  	defer n.mu.RUnlock()
   154  
   155  	return n.config
   156  }
   157  
   158  // GethNode returns underlying geth node.
   159  func (n *StatusNode) GethNode() *node.Node {
   160  	n.mu.RLock()
   161  	defer n.mu.RUnlock()
   162  
   163  	return n.gethNode
   164  }
   165  
   166  func (n *StatusNode) HTTPServer() *server.MediaServer {
   167  	n.mu.RLock()
   168  	defer n.mu.RUnlock()
   169  
   170  	return n.httpServer
   171  }
   172  
   173  // Server retrieves the currently running P2P network layer.
   174  func (n *StatusNode) Server() *p2p.Server {
   175  	n.mu.RLock()
   176  	defer n.mu.RUnlock()
   177  
   178  	if n.gethNode == nil {
   179  		return nil
   180  	}
   181  
   182  	return n.gethNode.Server()
   183  }
   184  
   185  // Start starts current StatusNode, failing if it's already started.
   186  // It accepts a list of services that should be added to the node.
   187  func (n *StatusNode) Start(config *params.NodeConfig, accs *accounts.Manager) error {
   188  	return n.StartWithOptions(config, StartOptions{
   189  		StartDiscovery:  true,
   190  		AccountsManager: accs,
   191  	})
   192  }
   193  
   194  // StartOptions allows to control some parameters of Start() method.
   195  type StartOptions struct {
   196  	StartDiscovery  bool
   197  	AccountsManager *accounts.Manager
   198  }
   199  
   200  // StartMediaServerWithoutDB starts media server without starting the node
   201  // The server can only handle requests that don't require appdb or IPFS downloader
   202  func (n *StatusNode) StartMediaServerWithoutDB() error {
   203  	if n.isRunning() {
   204  		n.log.Debug("node is already running, no need to StartMediaServerWithoutDB")
   205  		return nil
   206  	}
   207  
   208  	if n.httpServer != nil {
   209  		if err := n.httpServer.Stop(); err != nil {
   210  			return err
   211  		}
   212  	}
   213  
   214  	httpServer, err := server.NewMediaServer(nil, nil, n.multiaccountsDB, nil)
   215  	if err != nil {
   216  		return err
   217  	}
   218  
   219  	n.httpServer = httpServer
   220  
   221  	if err := n.httpServer.Start(); err != nil {
   222  		return err
   223  	}
   224  
   225  	return nil
   226  }
   227  
   228  // StartWithOptions starts current StatusNode, failing if it's already started.
   229  // It takes some options that allows to further configure starting process.
   230  func (n *StatusNode) StartWithOptions(config *params.NodeConfig, options StartOptions) error {
   231  	n.mu.Lock()
   232  	defer n.mu.Unlock()
   233  
   234  	if n.isRunning() {
   235  		n.log.Debug("node is already running")
   236  		return ErrNodeRunning
   237  	}
   238  
   239  	n.accountsManager = options.AccountsManager
   240  
   241  	n.log.Debug("starting with options", "ClusterConfig", config.ClusterConfig)
   242  
   243  	db, err := db.Create(config.DataDir, params.StatusDatabase)
   244  	if err != nil {
   245  		return fmt.Errorf("failed to create database at %s: %v", config.DataDir, err)
   246  	}
   247  
   248  	n.db = db
   249  
   250  	err = n.startWithDB(config, options.AccountsManager, db)
   251  
   252  	// continue only if there was no error when starting node with a db
   253  	if err == nil && options.StartDiscovery && n.discoveryEnabled() {
   254  		err = n.startDiscovery()
   255  	}
   256  
   257  	if err != nil {
   258  		if dberr := db.Close(); dberr != nil {
   259  			n.log.Error("error while closing leveldb after node crash", "error", dberr)
   260  		}
   261  		n.db = nil
   262  		return err
   263  	}
   264  
   265  	return nil
   266  }
   267  
   268  func (n *StatusNode) startWithDB(config *params.NodeConfig, accs *accounts.Manager, db *leveldb.DB) error {
   269  	if err := n.createNode(config, accs, db); err != nil {
   270  		return err
   271  	}
   272  	n.config = config
   273  
   274  	if err := n.setupRPCClient(); err != nil {
   275  		return err
   276  	}
   277  
   278  	n.downloader = ipfs.NewDownloader(config.RootDataDir)
   279  
   280  	if n.httpServer != nil {
   281  		if err := n.httpServer.Stop(); err != nil {
   282  			return err
   283  		}
   284  	}
   285  
   286  	httpServer, err := server.NewMediaServer(n.appDB, n.downloader, n.multiaccountsDB, n.walletDB)
   287  	if err != nil {
   288  		return err
   289  	}
   290  
   291  	n.httpServer = httpServer
   292  
   293  	if err := n.httpServer.Start(); err != nil {
   294  		return err
   295  	}
   296  
   297  	if err := n.initServices(config, n.httpServer); err != nil {
   298  		return err
   299  	}
   300  	return n.startGethNode()
   301  }
   302  
   303  func (n *StatusNode) createNode(config *params.NodeConfig, accs *accounts.Manager, db *leveldb.DB) (err error) {
   304  	n.gethNode, err = MakeNode(config, accs, db)
   305  	return err
   306  }
   307  
   308  // startGethNode starts current StatusNode, will fail if it's already started.
   309  func (n *StatusNode) startGethNode() error {
   310  	return n.gethNode.Start()
   311  }
   312  
   313  func (n *StatusNode) setupRPCClient() (err error) {
   314  	// setup RPC client
   315  	gethNodeClient, err := n.gethNode.Attach()
   316  	if err != nil {
   317  		return
   318  	}
   319  
   320  	// ProviderConfigs should be passed not in wallet secrets config on login
   321  	// but some other way, as it's not wallet specific and should not be passed with login request
   322  	// but currently there is no other way to pass it
   323  	providerConfigs := []params.ProviderConfig{
   324  		{
   325  			Enabled:  n.config.WalletConfig.StatusProxyEnabled,
   326  			Name:     rpc.ProviderStatusProxy,
   327  			User:     n.config.WalletConfig.StatusProxyBlockchainUser,
   328  			Password: n.config.WalletConfig.StatusProxyBlockchainPassword,
   329  		},
   330  	}
   331  
   332  	n.rpcClient, err = rpc.NewClient(gethNodeClient, n.config.NetworkID, n.config.UpstreamConfig, n.config.Networks, n.appDB, providerConfigs)
   333  	if err != nil {
   334  		return
   335  	}
   336  
   337  	return
   338  }
   339  
   340  func (n *StatusNode) discoveryEnabled() bool {
   341  	return n.config != nil && (!n.config.NoDiscovery) && n.config.ClusterConfig.Enabled
   342  }
   343  
   344  func (n *StatusNode) discoverNode() (*enode.Node, error) {
   345  	if !n.isRunning() {
   346  		return nil, nil
   347  	}
   348  
   349  	server := n.gethNode.Server()
   350  	discNode := server.Self()
   351  
   352  	if n.config.AdvertiseAddr == "" {
   353  		return discNode, nil
   354  	}
   355  
   356  	n.log.Info("Using AdvertiseAddr for rendezvous", "addr", n.config.AdvertiseAddr)
   357  
   358  	r := discNode.Record()
   359  	r.Set(enr.IP(net.ParseIP(n.config.AdvertiseAddr)))
   360  	if err := enode.SignV4(r, server.PrivateKey); err != nil {
   361  		return nil, err
   362  	}
   363  	return enode.New(enode.ValidSchemes[r.IdentityScheme()], r)
   364  }
   365  
   366  // StartDiscovery starts the peers discovery protocols depending on the node config.
   367  func (n *StatusNode) StartDiscovery() error {
   368  	n.mu.Lock()
   369  	defer n.mu.Unlock()
   370  
   371  	if n.discoveryEnabled() {
   372  		return n.startDiscovery()
   373  	}
   374  
   375  	return nil
   376  }
   377  
   378  func (n *StatusNode) startDiscovery() error {
   379  	if n.isDiscoveryRunning() {
   380  		return ErrDiscoveryRunning
   381  	}
   382  
   383  	discoveries := []discovery.Discovery{}
   384  	if !n.config.NoDiscovery {
   385  		discoveries = append(discoveries, discovery.NewDiscV5(
   386  			n.gethNode.Server().PrivateKey,
   387  			n.config.ListenAddr,
   388  			parseNodesV5(n.config.ClusterConfig.BootNodes)))
   389  	}
   390  
   391  	if len(discoveries) == 0 {
   392  		return errors.New("wasn't able to register any discovery")
   393  	} else if len(discoveries) > 1 {
   394  		n.discovery = discovery.NewMultiplexer(discoveries)
   395  	} else {
   396  		n.discovery = discoveries[0]
   397  	}
   398  	log.Debug(
   399  		"using discovery",
   400  		"instance", reflect.TypeOf(n.discovery),
   401  		"registerTopics", n.config.RegisterTopics,
   402  		"requireTopics", n.config.RequireTopics,
   403  	)
   404  	n.register = peers.NewRegister(n.discovery, n.config.RegisterTopics...)
   405  	options := peers.NewDefaultOptions()
   406  	// TODO(dshulyak) consider adding a flag to define this behaviour
   407  	options.AllowStop = len(n.config.RegisterTopics) == 0
   408  	options.TrustedMailServers = parseNodesToNodeID(n.config.ClusterConfig.TrustedMailServers)
   409  
   410  	n.peerPool = peers.NewPeerPool(
   411  		n.discovery,
   412  		n.config.RequireTopics,
   413  		peers.NewCache(n.db),
   414  		options,
   415  	)
   416  	if err := n.discovery.Start(); err != nil {
   417  		return err
   418  	}
   419  	if err := n.register.Start(); err != nil {
   420  		return err
   421  	}
   422  	return n.peerPool.Start(n.gethNode.Server())
   423  }
   424  
   425  // Stop will stop current StatusNode. A stopped node cannot be resumed.
   426  func (n *StatusNode) Stop() error {
   427  	n.mu.Lock()
   428  	defer n.mu.Unlock()
   429  
   430  	if !n.isRunning() {
   431  		return ErrNoRunningNode
   432  	}
   433  
   434  	return n.stop()
   435  }
   436  
   437  // stop will stop current StatusNode. A stopped node cannot be resumed.
   438  func (n *StatusNode) stop() error {
   439  	if n.isDiscoveryRunning() {
   440  		if err := n.stopDiscovery(); err != nil {
   441  			n.log.Error("Error stopping the discovery components", "error", err)
   442  		}
   443  		n.register = nil
   444  		n.peerPool = nil
   445  		n.discovery = nil
   446  	}
   447  
   448  	if err := n.gethNode.Close(); err != nil {
   449  		return err
   450  	}
   451  
   452  	n.rpcClient = nil
   453  	// We need to clear `gethNode` because config is passed to `Start()`
   454  	// and may be completely different. Similarly with `config`.
   455  	n.gethNode = nil
   456  	n.config = nil
   457  
   458  	err := n.httpServer.Stop()
   459  	if err != nil {
   460  		return err
   461  	}
   462  	n.httpServer = nil
   463  
   464  	n.downloader.Stop()
   465  	n.downloader = nil
   466  
   467  	if n.db != nil {
   468  		if err = n.db.Close(); err != nil {
   469  			n.log.Error("Error closing the leveldb of status node", "error", err)
   470  			return err
   471  		}
   472  		n.db = nil
   473  	}
   474  
   475  	n.rpcFiltersSrvc = nil
   476  	n.subscriptionsSrvc = nil
   477  	n.rpcStatsSrvc = nil
   478  	n.accountsSrvc = nil
   479  	n.browsersSrvc = nil
   480  	n.permissionsSrvc = nil
   481  	n.mailserversSrvc = nil
   482  	n.providerSrvc = nil
   483  	n.appMetricsSrvc = nil
   484  	n.walletSrvc = nil
   485  	n.peerSrvc = nil
   486  	n.localNotificationsSrvc = nil
   487  	n.personalSrvc = nil
   488  	n.timeSourceSrvc = nil
   489  	n.wakuSrvc = nil
   490  	n.wakuExtSrvc = nil
   491  	n.wakuV2Srvc = nil
   492  	n.wakuV2ExtSrvc = nil
   493  	n.ensSrvc = nil
   494  	n.communityTokensSrvc = nil
   495  	n.stickersSrvc = nil
   496  	n.connectorSrvc = nil
   497  	n.publicMethods = make(map[string]bool)
   498  	n.pendingTracker = nil
   499  	n.appGeneralSrvc = nil
   500  	n.log.Debug("status node stopped")
   501  	return nil
   502  }
   503  
   504  func (n *StatusNode) isDiscoveryRunning() bool {
   505  	return n.register != nil || n.peerPool != nil || n.discovery != nil
   506  }
   507  
   508  func (n *StatusNode) stopDiscovery() error {
   509  	n.register.Stop()
   510  	n.peerPool.Stop()
   511  	return n.discovery.Stop()
   512  }
   513  
   514  // ResetChainData removes chain data if node is not running.
   515  func (n *StatusNode) ResetChainData(config *params.NodeConfig) error {
   516  	n.mu.Lock()
   517  	defer n.mu.Unlock()
   518  
   519  	if n.isRunning() {
   520  		return ErrNodeRunning
   521  	}
   522  
   523  	chainDataDir := filepath.Join(config.DataDir, config.Name, "lightchaindata")
   524  	if _, err := os.Stat(chainDataDir); os.IsNotExist(err) {
   525  		return err
   526  	}
   527  	err := os.RemoveAll(chainDataDir)
   528  	if err == nil {
   529  		n.log.Info("Chain data has been removed", "dir", chainDataDir)
   530  	}
   531  	return err
   532  }
   533  
   534  // IsRunning confirm that node is running.
   535  func (n *StatusNode) IsRunning() bool {
   536  	n.mu.RLock()
   537  	defer n.mu.RUnlock()
   538  
   539  	return n.isRunning()
   540  }
   541  
   542  func (n *StatusNode) isRunning() bool {
   543  	return n.gethNode != nil && n.gethNode.Server() != nil
   544  }
   545  
   546  // populateStaticPeers connects current node with our publicly available LES/SHH/Swarm cluster
   547  func (n *StatusNode) populateStaticPeers() error {
   548  	if !n.config.ClusterConfig.Enabled {
   549  		n.log.Info("Static peers are disabled")
   550  		return nil
   551  	}
   552  
   553  	for _, enode := range n.config.ClusterConfig.StaticNodes {
   554  		if err := n.addPeer(enode); err != nil {
   555  			n.log.Error("Static peer addition failed", "error", err)
   556  			return err
   557  		}
   558  		n.log.Info("Static peer added", "enode", enode)
   559  	}
   560  
   561  	return nil
   562  }
   563  
   564  func (n *StatusNode) removeStaticPeers() error {
   565  	if !n.config.ClusterConfig.Enabled {
   566  		n.log.Info("Static peers are disabled")
   567  		return nil
   568  	}
   569  
   570  	for _, enode := range n.config.ClusterConfig.StaticNodes {
   571  		if err := n.removePeer(enode); err != nil {
   572  			n.log.Error("Static peer deletion failed", "error", err)
   573  			return err
   574  		}
   575  		n.log.Info("Static peer deleted", "enode", enode)
   576  	}
   577  	return nil
   578  }
   579  
   580  // ReconnectStaticPeers removes and adds static peers to a server.
   581  func (n *StatusNode) ReconnectStaticPeers() error {
   582  	n.mu.Lock()
   583  	defer n.mu.Unlock()
   584  
   585  	if !n.isRunning() {
   586  		return ErrNoRunningNode
   587  	}
   588  
   589  	if err := n.removeStaticPeers(); err != nil {
   590  		return err
   591  	}
   592  
   593  	return n.populateStaticPeers()
   594  }
   595  
   596  // AddPeer adds new static peer node
   597  func (n *StatusNode) AddPeer(url string) error {
   598  	n.mu.RLock()
   599  	defer n.mu.RUnlock()
   600  
   601  	return n.addPeer(url)
   602  }
   603  
   604  // addPeer adds new static peer node
   605  func (n *StatusNode) addPeer(url string) error {
   606  	parsedNode, err := enode.ParseV4(url)
   607  	if err != nil {
   608  		return err
   609  	}
   610  
   611  	if !n.isRunning() {
   612  		return ErrNoRunningNode
   613  	}
   614  
   615  	n.gethNode.Server().AddPeer(parsedNode)
   616  
   617  	return nil
   618  }
   619  
   620  func (n *StatusNode) removePeer(url string) error {
   621  	parsedNode, err := enode.ParseV4(url)
   622  	if err != nil {
   623  		return err
   624  	}
   625  
   626  	if !n.isRunning() {
   627  		return ErrNoRunningNode
   628  	}
   629  
   630  	n.gethNode.Server().RemovePeer(parsedNode)
   631  
   632  	return nil
   633  }
   634  
   635  // PeerCount returns the number of connected peers.
   636  func (n *StatusNode) PeerCount() int {
   637  	n.mu.RLock()
   638  	defer n.mu.RUnlock()
   639  
   640  	if !n.isRunning() {
   641  		return 0
   642  	}
   643  
   644  	return n.gethNode.Server().PeerCount()
   645  }
   646  
   647  func (n *StatusNode) ConnectionChanged(state connection.State) {
   648  	if n.wakuExtSrvc != nil {
   649  		n.wakuExtSrvc.ConnectionChanged(state)
   650  	}
   651  
   652  	if n.wakuV2ExtSrvc != nil {
   653  		n.wakuV2ExtSrvc.ConnectionChanged(state)
   654  	}
   655  }
   656  
   657  // AccountManager exposes reference to node's accounts manager
   658  func (n *StatusNode) AccountManager() (*accounts.Manager, error) {
   659  	n.mu.RLock()
   660  	defer n.mu.RUnlock()
   661  
   662  	if n.gethNode == nil {
   663  		return nil, ErrNoGethNode
   664  	}
   665  
   666  	return n.gethNode.AccountManager(), nil
   667  }
   668  
   669  // RPCClient exposes reference to RPC client connected to the running node.
   670  func (n *StatusNode) RPCClient() *rpc.Client {
   671  	n.mu.RLock()
   672  	defer n.mu.RUnlock()
   673  	return n.rpcClient
   674  }
   675  
   676  // Discover sets up the discovery for a specific topic.
   677  func (n *StatusNode) Discover(topic string, max, min int) (err error) {
   678  	if n.peerPool == nil {
   679  		return errors.New("peerPool not running")
   680  	}
   681  	return n.peerPool.UpdateTopic(topic, params.Limits{
   682  		Max: max,
   683  		Min: min,
   684  	})
   685  }
   686  
   687  func (n *StatusNode) SetAppDB(db *sql.DB) {
   688  	n.appDB = db
   689  }
   690  
   691  func (n *StatusNode) SetMultiaccountsDB(db *multiaccounts.Database) {
   692  	n.multiaccountsDB = db
   693  }
   694  
   695  func (n *StatusNode) SetWalletDB(db *sql.DB) {
   696  	n.walletDB = db
   697  }