2017-05-16 12:09:52 +00:00
|
|
|
package node
|
|
|
|
|
|
|
|
import (
|
2024-10-15 15:59:17 +00:00
|
|
|
"context"
|
2021-06-30 11:40:54 +00:00
|
|
|
"database/sql"
|
2017-05-16 12:09:52 +00:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
2018-08-17 06:25:55 +00:00
|
|
|
"net"
|
2017-05-16 12:09:52 +00:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2018-08-17 06:25:55 +00:00
|
|
|
"reflect"
|
2017-05-16 12:09:52 +00:00
|
|
|
"sync"
|
|
|
|
|
2020-01-02 09:10:19 +00:00
|
|
|
"github.com/syndtr/goleveldb/leveldb"
|
2024-10-28 20:54:17 +00:00
|
|
|
"go.uber.org/zap"
|
2020-01-02 09:10:19 +00:00
|
|
|
|
2017-05-16 12:09:52 +00:00
|
|
|
"github.com/ethereum/go-ethereum/accounts"
|
2023-08-01 18:50:30 +00:00
|
|
|
"github.com/ethereum/go-ethereum/event"
|
2017-05-16 12:09:52 +00:00
|
|
|
"github.com/ethereum/go-ethereum/node"
|
2018-06-27 07:55:25 +00:00
|
|
|
"github.com/ethereum/go-ethereum/p2p"
|
2018-11-14 07:03:58 +00:00
|
|
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
|
|
|
"github.com/ethereum/go-ethereum/p2p/enr"
|
2020-01-02 09:10:19 +00:00
|
|
|
|
2021-06-30 11:40:54 +00:00
|
|
|
"github.com/status-im/status-go/account"
|
2021-07-09 13:19:33 +00:00
|
|
|
"github.com/status-im/status-go/common"
|
2021-05-14 10:55:42 +00:00
|
|
|
"github.com/status-im/status-go/connection"
|
2018-06-08 11:29:50 +00:00
|
|
|
"github.com/status-im/status-go/db"
|
2018-07-04 10:51:47 +00:00
|
|
|
"github.com/status-im/status-go/discovery"
|
2022-05-09 13:07:57 +00:00
|
|
|
"github.com/status-im/status-go/ipfs"
|
2021-06-30 11:40:54 +00:00
|
|
|
"github.com/status-im/status-go/multiaccounts"
|
2018-06-08 11:29:50 +00:00
|
|
|
"github.com/status-im/status-go/params"
|
|
|
|
"github.com/status-im/status-go/peers"
|
|
|
|
"github.com/status-im/status-go/rpc"
|
2022-05-09 13:07:57 +00:00
|
|
|
"github.com/status-im/status-go/server"
|
2021-06-30 11:40:54 +00:00
|
|
|
accountssvc "github.com/status-im/status-go/services/accounts"
|
2024-08-29 10:33:24 +00:00
|
|
|
appgeneral "github.com/status-im/status-go/services/app-general"
|
2021-06-30 11:40:54 +00:00
|
|
|
appmetricsservice "github.com/status-im/status-go/services/appmetrics"
|
2019-07-17 05:28:37 +00:00
|
|
|
"github.com/status-im/status-go/services/browsers"
|
2022-02-10 15:15:27 +00:00
|
|
|
"github.com/status-im/status-go/services/chat"
|
2023-08-25 15:36:39 +00:00
|
|
|
"github.com/status-im/status-go/services/communitytokens"
|
2024-06-24 14:29:40 +00:00
|
|
|
"github.com/status-im/status-go/services/connector"
|
2021-12-21 15:05:09 +00:00
|
|
|
"github.com/status-im/status-go/services/ens"
|
2024-10-04 12:55:28 +00:00
|
|
|
"github.com/status-im/status-go/services/eth"
|
2022-01-31 12:58:03 +00:00
|
|
|
"github.com/status-im/status-go/services/gif"
|
2020-10-28 07:56:14 +00:00
|
|
|
localnotifications "github.com/status-im/status-go/services/local-notifications"
|
2021-06-30 11:40:54 +00:00
|
|
|
"github.com/status-im/status-go/services/mailservers"
|
2018-07-16 07:40:40 +00:00
|
|
|
"github.com/status-im/status-go/services/peer"
|
2019-07-21 05:41:30 +00:00
|
|
|
"github.com/status-im/status-go/services/permissions"
|
2021-06-30 11:40:54 +00:00
|
|
|
"github.com/status-im/status-go/services/personal"
|
|
|
|
"github.com/status-im/status-go/services/rpcfilters"
|
|
|
|
"github.com/status-im/status-go/services/rpcstats"
|
2021-08-05 13:27:47 +00:00
|
|
|
"github.com/status-im/status-go/services/status"
|
2022-02-02 22:50:55 +00:00
|
|
|
"github.com/status-im/status-go/services/stickers"
|
2021-06-30 11:40:54 +00:00
|
|
|
"github.com/status-im/status-go/services/subscriptions"
|
2022-06-08 12:38:26 +00:00
|
|
|
"github.com/status-im/status-go/services/updates"
|
2020-01-20 20:56:06 +00:00
|
|
|
"github.com/status-im/status-go/services/wakuext"
|
2021-06-16 20:19:45 +00:00
|
|
|
"github.com/status-im/status-go/services/wakuv2ext"
|
2019-06-14 10:16:30 +00:00
|
|
|
"github.com/status-im/status-go/services/wallet"
|
2021-12-21 15:44:37 +00:00
|
|
|
"github.com/status-im/status-go/services/web3provider"
|
2021-06-30 11:40:54 +00:00
|
|
|
"github.com/status-im/status-go/timesource"
|
2021-12-21 15:44:37 +00:00
|
|
|
"github.com/status-im/status-go/transactions"
|
2020-01-20 20:56:06 +00:00
|
|
|
"github.com/status-im/status-go/waku"
|
2021-06-16 20:19:45 +00:00
|
|
|
"github.com/status-im/status-go/wakuv2"
|
2017-05-16 12:09:52 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// errors
|
|
|
|
var (
|
2018-04-16 12:36:09 +00:00
|
|
|
ErrNodeRunning = errors.New("node is already running")
|
|
|
|
ErrNoGethNode = errors.New("geth node is not available")
|
2018-04-09 07:16:43 +00:00
|
|
|
ErrNoRunningNode = errors.New("there is no running node")
|
|
|
|
ErrAccountKeyStoreMissing = errors.New("account key store is not set")
|
2018-04-20 15:39:53 +00:00
|
|
|
ErrServiceUnknown = errors.New("service unknown")
|
2019-01-02 18:57:36 +00:00
|
|
|
ErrDiscoveryRunning = errors.New("discovery is already running")
|
2021-07-09 13:19:33 +00:00
|
|
|
ErrRPCMethodUnavailable = `{"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"the method called does not exist/is not available"}}`
|
2017-05-16 12:09:52 +00:00
|
|
|
)
|
|
|
|
|
2018-04-05 09:45:26 +00:00
|
|
|
// StatusNode abstracts contained geth node and provides helper methods to
|
|
|
|
// interact with it.
|
|
|
|
type StatusNode struct {
|
2018-04-12 16:17:10 +00:00
|
|
|
mu sync.RWMutex
|
|
|
|
|
2021-06-30 11:40:54 +00:00
|
|
|
appDB *sql.DB
|
|
|
|
multiaccountsDB *multiaccounts.Database
|
2023-08-11 11:25:14 +00:00
|
|
|
walletDB *sql.DB
|
2021-06-30 11:40:54 +00:00
|
|
|
|
2021-07-09 13:19:33 +00:00
|
|
|
config *params.NodeConfig // Status node configuration
|
|
|
|
gethNode *node.Node // reference to Geth P2P stack/node
|
|
|
|
rpcClient *rpc.Client // reference to an RPC client
|
2018-04-09 07:16:43 +00:00
|
|
|
|
2022-05-09 13:07:57 +00:00
|
|
|
downloader *ipfs.Downloader
|
2024-11-22 02:23:17 +00:00
|
|
|
|
|
|
|
mediaServerEnableTLS *bool
|
|
|
|
httpServer *server.MediaServer
|
2022-05-09 13:07:57 +00:00
|
|
|
|
2018-07-04 10:51:47 +00:00
|
|
|
discovery discovery.Discovery
|
2018-07-03 11:27:04 +00:00
|
|
|
register *peers.Register
|
|
|
|
peerPool *peers.PeerPool
|
|
|
|
db *leveldb.DB // used as a cache for PeerPool
|
2018-04-10 06:44:09 +00:00
|
|
|
|
2024-10-28 20:54:17 +00:00
|
|
|
logger *zap.Logger
|
2021-06-30 11:40:54 +00:00
|
|
|
|
|
|
|
gethAccountManager *account.GethManager
|
|
|
|
accountsManager *accounts.Manager
|
2021-12-21 15:44:37 +00:00
|
|
|
transactor *transactions.Transactor
|
2021-06-30 11:40:54 +00:00
|
|
|
|
|
|
|
// services
|
2021-07-09 13:19:33 +00:00
|
|
|
services []common.StatusService
|
|
|
|
publicMethods map[string]bool
|
2021-07-07 06:11:09 +00:00
|
|
|
// we explicitly list every service, we could use interfaces
|
2021-07-09 13:19:33 +00:00
|
|
|
// and store them in a nicer way and user reflection, but for now stupid is good
|
2021-06-30 11:40:54 +00:00
|
|
|
rpcFiltersSrvc *rpcfilters.Service
|
|
|
|
subscriptionsSrvc *subscriptions.Service
|
|
|
|
rpcStatsSrvc *rpcstats.Service
|
2021-08-05 13:27:47 +00:00
|
|
|
statusPublicSrvc *status.Service
|
2021-06-30 11:40:54 +00:00
|
|
|
accountsSrvc *accountssvc.Service
|
|
|
|
browsersSrvc *browsers.Service
|
|
|
|
permissionsSrvc *permissions.Service
|
|
|
|
mailserversSrvc *mailservers.Service
|
2021-12-21 15:44:37 +00:00
|
|
|
providerSrvc *web3provider.Service
|
2021-06-30 11:40:54 +00:00
|
|
|
appMetricsSrvc *appmetricsservice.Service
|
|
|
|
walletSrvc *wallet.Service
|
|
|
|
peerSrvc *peer.Service
|
|
|
|
localNotificationsSrvc *localnotifications.Service
|
|
|
|
personalSrvc *personal.Service
|
|
|
|
timeSourceSrvc *timesource.NTPTimeSource
|
|
|
|
wakuSrvc *waku.Waku
|
|
|
|
wakuExtSrvc *wakuext.Service
|
|
|
|
wakuV2Srvc *wakuv2.Waku
|
|
|
|
wakuV2ExtSrvc *wakuv2ext.Service
|
2021-12-21 15:05:09 +00:00
|
|
|
ensSrvc *ens.Service
|
2023-08-25 15:36:39 +00:00
|
|
|
communityTokensSrvc *communitytokens.Service
|
2022-01-31 12:58:03 +00:00
|
|
|
gifSrvc *gif.Service
|
2022-02-02 22:50:55 +00:00
|
|
|
stickersSrvc *stickers.Service
|
2022-02-10 15:15:27 +00:00
|
|
|
chatSrvc *chat.Service
|
2022-06-08 12:38:26 +00:00
|
|
|
updatesSrvc *updates.Service
|
2023-08-01 18:50:30 +00:00
|
|
|
pendingTracker *transactions.PendingTxTracker
|
2024-06-24 14:29:40 +00:00
|
|
|
connectorSrvc *connector.Service
|
2024-08-29 10:33:24 +00:00
|
|
|
appGeneralSrvc *appgeneral.Service
|
2024-10-04 12:55:28 +00:00
|
|
|
ethSrvc *eth.Service
|
2023-08-01 18:50:30 +00:00
|
|
|
|
2024-08-30 19:28:03 +00:00
|
|
|
accountsFeed event.Feed
|
|
|
|
walletFeed event.Feed
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
|
2018-04-05 09:45:26 +00:00
|
|
|
// New makes new instance of StatusNode.
|
2024-10-28 20:54:17 +00:00
|
|
|
func New(transactor *transactions.Transactor, logger *zap.Logger) *StatusNode {
|
|
|
|
logger = logger.Named("StatusNode")
|
2018-04-05 09:45:26 +00:00
|
|
|
return &StatusNode{
|
2024-10-28 20:54:17 +00:00
|
|
|
gethAccountManager: account.NewGethManager(logger),
|
2021-12-21 15:44:37 +00:00
|
|
|
transactor: transactor,
|
2024-10-28 20:54:17 +00:00
|
|
|
logger: logger,
|
2021-07-09 13:19:33 +00:00
|
|
|
publicMethods: make(map[string]bool),
|
2018-03-20 18:35:28 +00:00
|
|
|
}
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
|
2018-04-16 12:36:09 +00:00
|
|
|
// Config exposes reference to running node's configuration
|
|
|
|
func (n *StatusNode) Config() *params.NodeConfig {
|
|
|
|
n.mu.RLock()
|
|
|
|
defer n.mu.RUnlock()
|
|
|
|
|
|
|
|
return n.config
|
|
|
|
}
|
|
|
|
|
|
|
|
// GethNode returns underlying geth node.
|
|
|
|
func (n *StatusNode) GethNode() *node.Node {
|
|
|
|
n.mu.RLock()
|
|
|
|
defer n.mu.RUnlock()
|
|
|
|
|
|
|
|
return n.gethNode
|
|
|
|
}
|
|
|
|
|
2022-06-15 14:49:31 +00:00
|
|
|
func (n *StatusNode) HTTPServer() *server.MediaServer {
|
2022-05-09 13:07:57 +00:00
|
|
|
n.mu.RLock()
|
|
|
|
defer n.mu.RUnlock()
|
|
|
|
|
|
|
|
return n.httpServer
|
|
|
|
}
|
|
|
|
|
2018-06-27 07:55:25 +00:00
|
|
|
// Server retrieves the currently running P2P network layer.
|
|
|
|
func (n *StatusNode) Server() *p2p.Server {
|
|
|
|
n.mu.RLock()
|
|
|
|
defer n.mu.RUnlock()
|
|
|
|
|
|
|
|
if n.gethNode == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return n.gethNode.Server()
|
|
|
|
}
|
|
|
|
|
2019-01-02 18:57:36 +00:00
|
|
|
// Start starts current StatusNode, failing if it's already started.
|
|
|
|
// It accepts a list of services that should be added to the node.
|
2021-06-30 11:40:54 +00:00
|
|
|
func (n *StatusNode) Start(config *params.NodeConfig, accs *accounts.Manager) error {
|
2019-01-02 18:57:36 +00:00
|
|
|
return n.StartWithOptions(config, StartOptions{
|
2019-08-20 15:38:40 +00:00
|
|
|
StartDiscovery: true,
|
|
|
|
AccountsManager: accs,
|
2019-01-02 18:57:36 +00:00
|
|
|
})
|
|
|
|
}
|
2018-04-16 08:01:37 +00:00
|
|
|
|
2019-01-02 18:57:36 +00:00
|
|
|
// StartOptions allows to control some parameters of Start() method.
|
|
|
|
type StartOptions struct {
|
2019-08-20 15:38:40 +00:00
|
|
|
StartDiscovery bool
|
|
|
|
AccountsManager *accounts.Manager
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
|
2023-05-18 06:27:16 +00:00
|
|
|
// StartMediaServerWithoutDB starts media server without starting the node
|
|
|
|
// The server can only handle requests that don't require appdb or IPFS downloader
|
|
|
|
func (n *StatusNode) StartMediaServerWithoutDB() error {
|
|
|
|
if n.isRunning() {
|
2024-10-28 20:54:17 +00:00
|
|
|
n.logger.Debug("node is already running, no need to StartMediaServerWithoutDB")
|
2023-05-18 06:27:16 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if n.httpServer != nil {
|
|
|
|
if err := n.httpServer.Stop(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-22 02:23:17 +00:00
|
|
|
var opts []server.MediaServerOption
|
|
|
|
if n.mediaServerEnableTLS != nil {
|
|
|
|
opts = append(opts, server.WithMediaServerDisableTLS(!*n.mediaServerEnableTLS))
|
|
|
|
}
|
|
|
|
httpServer, err := server.NewMediaServer(nil, nil, n.multiaccountsDB, nil, opts...)
|
2023-05-18 06:27:16 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
n.httpServer = httpServer
|
|
|
|
|
|
|
|
if err := n.httpServer.Start(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-01-02 18:57:36 +00:00
|
|
|
// StartWithOptions starts current StatusNode, failing if it's already started.
|
|
|
|
// It takes some options that allows to further configure starting process.
|
|
|
|
func (n *StatusNode) StartWithOptions(config *params.NodeConfig, options StartOptions) error {
|
2018-05-02 12:14:08 +00:00
|
|
|
n.mu.Lock()
|
|
|
|
defer n.mu.Unlock()
|
|
|
|
|
|
|
|
if n.isRunning() {
|
2024-10-28 20:54:17 +00:00
|
|
|
n.logger.Debug("node is already running")
|
2018-05-02 12:14:08 +00:00
|
|
|
return ErrNodeRunning
|
|
|
|
}
|
2018-04-20 11:26:54 +00:00
|
|
|
|
2021-06-30 11:40:54 +00:00
|
|
|
n.accountsManager = options.AccountsManager
|
|
|
|
|
2024-10-28 20:54:17 +00:00
|
|
|
n.logger.Debug("starting with options", zap.Stringer("ClusterConfig", &config.ClusterConfig))
|
2018-09-13 16:31:29 +00:00
|
|
|
|
2018-05-02 12:14:08 +00:00
|
|
|
db, err := db.Create(config.DataDir, params.StatusDatabase)
|
|
|
|
if err != nil {
|
2019-08-20 15:38:40 +00:00
|
|
|
return fmt.Errorf("failed to create database at %s: %v", config.DataDir, err)
|
2018-04-20 11:26:54 +00:00
|
|
|
}
|
2018-05-02 12:14:08 +00:00
|
|
|
|
2018-05-11 07:12:50 +00:00
|
|
|
n.db = db
|
|
|
|
|
2021-06-30 11:40:54 +00:00
|
|
|
err = n.startWithDB(config, options.AccountsManager, db)
|
2019-01-02 18:57:36 +00:00
|
|
|
|
|
|
|
// continue only if there was no error when starting node with a db
|
|
|
|
if err == nil && options.StartDiscovery && n.discoveryEnabled() {
|
|
|
|
err = n.startDiscovery()
|
|
|
|
}
|
2018-05-02 12:14:08 +00:00
|
|
|
|
2018-04-20 11:26:54 +00:00
|
|
|
if err != nil {
|
2018-05-02 12:14:08 +00:00
|
|
|
if dberr := db.Close(); dberr != nil {
|
2024-10-28 20:54:17 +00:00
|
|
|
n.logger.Error("error while closing leveldb after node crash", zap.Error(dberr))
|
2018-05-02 12:14:08 +00:00
|
|
|
}
|
|
|
|
n.db = nil
|
2018-04-20 11:26:54 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-05-02 12:14:08 +00:00
|
|
|
return nil
|
2018-04-20 11:26:54 +00:00
|
|
|
}
|
|
|
|
|
2024-11-22 02:23:17 +00:00
|
|
|
func (n *StatusNode) SetMediaServerEnableTLS(enableTLS *bool) {
|
|
|
|
n.mediaServerEnableTLS = enableTLS
|
|
|
|
}
|
|
|
|
|
2021-06-30 11:40:54 +00:00
|
|
|
func (n *StatusNode) startWithDB(config *params.NodeConfig, accs *accounts.Manager, db *leveldb.DB) error {
|
2019-08-20 15:38:40 +00:00
|
|
|
if err := n.createNode(config, accs, db); err != nil {
|
2019-01-02 18:57:36 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
n.config = config
|
|
|
|
|
2021-06-30 11:40:54 +00:00
|
|
|
if err := n.setupRPCClient(); err != nil {
|
2019-01-02 18:57:36 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-05-09 13:07:57 +00:00
|
|
|
n.downloader = ipfs.NewDownloader(config.RootDataDir)
|
|
|
|
|
2023-05-18 06:27:16 +00:00
|
|
|
if n.httpServer != nil {
|
|
|
|
if err := n.httpServer.Stop(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-22 02:23:17 +00:00
|
|
|
var opts []server.MediaServerOption
|
|
|
|
if n.mediaServerEnableTLS != nil {
|
|
|
|
opts = append(opts, server.WithMediaServerDisableTLS(!*n.mediaServerEnableTLS))
|
|
|
|
}
|
|
|
|
|
|
|
|
httpServer, err := server.NewMediaServer(n.appDB, n.downloader, n.multiaccountsDB, n.walletDB, opts...)
|
2022-05-09 13:07:57 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-05-18 06:27:16 +00:00
|
|
|
n.httpServer = httpServer
|
|
|
|
|
|
|
|
if err := n.httpServer.Start(); err != nil {
|
2022-05-09 13:07:57 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-05-18 06:27:16 +00:00
|
|
|
if err := n.initServices(config, n.httpServer); err != nil {
|
2019-01-02 18:57:36 +00:00
|
|
|
return err
|
|
|
|
}
|
2021-06-30 11:40:54 +00:00
|
|
|
return n.startGethNode()
|
2019-01-02 18:57:36 +00:00
|
|
|
}
|
|
|
|
|
2019-08-20 15:38:40 +00:00
|
|
|
func (n *StatusNode) createNode(config *params.NodeConfig, accs *accounts.Manager, db *leveldb.DB) (err error) {
|
|
|
|
n.gethNode, err = MakeNode(config, accs, db)
|
2018-05-02 12:14:08 +00:00
|
|
|
return err
|
2018-04-16 12:36:09 +00:00
|
|
|
}
|
|
|
|
|
2020-02-26 19:35:47 +00:00
|
|
|
// startGethNode starts current StatusNode, will fail if it's already started.
|
2021-06-30 11:40:54 +00:00
|
|
|
func (n *StatusNode) startGethNode() error {
|
2018-04-16 12:36:09 +00:00
|
|
|
return n.gethNode.Start()
|
|
|
|
}
|
|
|
|
|
2018-04-16 08:01:37 +00:00
|
|
|
func (n *StatusNode) setupRPCClient() (err error) {
|
2021-07-09 13:19:33 +00:00
|
|
|
// setup RPC client
|
2021-06-30 11:40:54 +00:00
|
|
|
gethNodeClient, err := n.gethNode.Attach()
|
2018-04-16 08:01:37 +00:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2024-07-31 06:21:11 +00:00
|
|
|
|
|
|
|
// ProviderConfigs should be passed not in wallet secrets config on login
|
|
|
|
// but some other way, as it's not wallet specific and should not be passed with login request
|
|
|
|
// but currently there is no other way to pass it
|
|
|
|
providerConfigs := []params.ProviderConfig{
|
|
|
|
{
|
|
|
|
Enabled: n.config.WalletConfig.StatusProxyEnabled,
|
|
|
|
Name: rpc.ProviderStatusProxy,
|
|
|
|
User: n.config.WalletConfig.StatusProxyBlockchainUser,
|
|
|
|
Password: n.config.WalletConfig.StatusProxyBlockchainPassword,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2024-10-15 15:59:17 +00:00
|
|
|
config := rpc.ClientConfig{
|
|
|
|
Client: gethNodeClient,
|
|
|
|
UpstreamChainID: n.config.NetworkID,
|
|
|
|
Networks: n.config.Networks,
|
|
|
|
DB: n.appDB,
|
|
|
|
WalletFeed: &n.walletFeed,
|
|
|
|
ProviderConfigs: providerConfigs,
|
|
|
|
}
|
|
|
|
n.rpcClient, err = rpc.NewClient(config)
|
|
|
|
n.rpcClient.Start(context.Background())
|
2018-04-16 08:01:37 +00:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-07-03 11:27:04 +00:00
|
|
|
func (n *StatusNode) discoveryEnabled() bool {
|
2024-08-22 17:12:37 +00:00
|
|
|
return n.config != nil && (!n.config.NoDiscovery) && n.config.ClusterConfig.Enabled
|
2018-07-04 10:51:47 +00:00
|
|
|
}
|
|
|
|
|
2018-11-14 07:03:58 +00:00
|
|
|
func (n *StatusNode) discoverNode() (*enode.Node, error) {
|
2018-08-17 06:25:55 +00:00
|
|
|
if !n.isRunning() {
|
2018-11-14 07:03:58 +00:00
|
|
|
return nil, nil
|
2018-08-17 06:25:55 +00:00
|
|
|
}
|
|
|
|
|
2019-01-25 18:17:08 +00:00
|
|
|
server := n.gethNode.Server()
|
|
|
|
discNode := server.Self()
|
2018-11-14 07:03:58 +00:00
|
|
|
|
|
|
|
if n.config.AdvertiseAddr == "" {
|
|
|
|
return discNode, nil
|
|
|
|
}
|
|
|
|
|
2024-10-28 20:54:17 +00:00
|
|
|
n.logger.Info("Using AdvertiseAddr for rendezvous", zap.String("addr", n.config.AdvertiseAddr))
|
2018-11-14 07:03:58 +00:00
|
|
|
|
|
|
|
r := discNode.Record()
|
|
|
|
r.Set(enr.IP(net.ParseIP(n.config.AdvertiseAddr)))
|
2019-01-25 18:17:08 +00:00
|
|
|
if err := enode.SignV4(r, server.PrivateKey); err != nil {
|
2018-11-14 07:03:58 +00:00
|
|
|
return nil, err
|
2018-08-17 06:25:55 +00:00
|
|
|
}
|
2018-11-14 07:03:58 +00:00
|
|
|
return enode.New(enode.ValidSchemes[r.IdentityScheme()], r)
|
2018-08-17 06:25:55 +00:00
|
|
|
}
|
|
|
|
|
2019-01-02 18:57:36 +00:00
|
|
|
// StartDiscovery starts the peers discovery protocols depending on the node config.
|
|
|
|
func (n *StatusNode) StartDiscovery() error {
|
|
|
|
n.mu.Lock()
|
|
|
|
defer n.mu.Unlock()
|
|
|
|
|
|
|
|
if n.discoveryEnabled() {
|
|
|
|
return n.startDiscovery()
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-07-03 11:27:04 +00:00
|
|
|
func (n *StatusNode) startDiscovery() error {
|
2019-01-02 18:57:36 +00:00
|
|
|
if n.isDiscoveryRunning() {
|
|
|
|
return ErrDiscoveryRunning
|
|
|
|
}
|
|
|
|
|
2018-07-20 06:46:29 +00:00
|
|
|
discoveries := []discovery.Discovery{}
|
2018-07-04 10:51:47 +00:00
|
|
|
if !n.config.NoDiscovery {
|
2018-07-20 06:46:29 +00:00
|
|
|
discoveries = append(discoveries, discovery.NewDiscV5(
|
2018-07-04 10:51:47 +00:00
|
|
|
n.gethNode.Server().PrivateKey,
|
|
|
|
n.config.ListenAddr,
|
2018-07-20 06:46:29 +00:00
|
|
|
parseNodesV5(n.config.ClusterConfig.BootNodes)))
|
|
|
|
}
|
2024-08-22 17:12:37 +00:00
|
|
|
|
2018-07-20 06:46:29 +00:00
|
|
|
if len(discoveries) == 0 {
|
|
|
|
return errors.New("wasn't able to register any discovery")
|
|
|
|
} else if len(discoveries) > 1 {
|
|
|
|
n.discovery = discovery.NewMultiplexer(discoveries)
|
|
|
|
} else {
|
|
|
|
n.discovery = discoveries[0]
|
2018-07-04 10:51:47 +00:00
|
|
|
}
|
2024-10-28 20:54:17 +00:00
|
|
|
n.logger.Debug("using discovery",
|
|
|
|
zap.Any("instance", reflect.TypeOf(n.discovery)),
|
|
|
|
zap.Any("registerTopics", n.config.RegisterTopics),
|
|
|
|
zap.Any("requireTopics", n.config.RequireTopics),
|
2018-08-17 06:25:55 +00:00
|
|
|
)
|
2018-07-03 11:27:04 +00:00
|
|
|
n.register = peers.NewRegister(n.discovery, n.config.RegisterTopics...)
|
2018-05-15 09:16:25 +00:00
|
|
|
options := peers.NewDefaultOptions()
|
2018-04-10 06:44:09 +00:00
|
|
|
// TODO(dshulyak) consider adding a flag to define this behaviour
|
2018-05-15 09:16:25 +00:00
|
|
|
options.AllowStop = len(n.config.RegisterTopics) == 0
|
2018-07-25 14:48:02 +00:00
|
|
|
options.TrustedMailServers = parseNodesToNodeID(n.config.ClusterConfig.TrustedMailServers)
|
2018-08-20 13:55:43 +00:00
|
|
|
|
2018-05-15 09:16:25 +00:00
|
|
|
n.peerPool = peers.NewPeerPool(
|
2018-07-03 11:27:04 +00:00
|
|
|
n.discovery,
|
2018-05-15 09:16:25 +00:00
|
|
|
n.config.RequireTopics,
|
2018-04-10 06:44:09 +00:00
|
|
|
peers.NewCache(n.db),
|
2018-05-15 09:16:25 +00:00
|
|
|
options,
|
2018-04-10 06:44:09 +00:00
|
|
|
)
|
2018-07-03 11:27:04 +00:00
|
|
|
if err := n.discovery.Start(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := n.register.Start(); err != nil {
|
2018-04-10 06:44:09 +00:00
|
|
|
return err
|
|
|
|
}
|
2021-09-22 17:49:20 +00:00
|
|
|
return n.peerPool.Start(n.gethNode.Server())
|
2018-04-10 06:44:09 +00:00
|
|
|
}
|
|
|
|
|
2018-04-05 09:45:26 +00:00
|
|
|
// Stop will stop current StatusNode. A stopped node cannot be resumed.
|
|
|
|
func (n *StatusNode) Stop() error {
|
|
|
|
n.mu.Lock()
|
|
|
|
defer n.mu.Unlock()
|
2018-04-16 12:36:09 +00:00
|
|
|
|
|
|
|
if !n.isRunning() {
|
|
|
|
return ErrNoRunningNode
|
|
|
|
}
|
|
|
|
|
2018-04-05 09:45:26 +00:00
|
|
|
return n.stop()
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
|
2018-04-05 09:45:26 +00:00
|
|
|
// stop will stop current StatusNode. A stopped node cannot be resumed.
|
|
|
|
func (n *StatusNode) stop() error {
|
2019-01-17 11:02:45 +00:00
|
|
|
if n.isDiscoveryRunning() {
|
2018-07-03 11:27:04 +00:00
|
|
|
if err := n.stopDiscovery(); err != nil {
|
2024-10-28 20:54:17 +00:00
|
|
|
n.logger.Error("Error stopping the discovery components", zap.Error(err))
|
2018-07-03 11:27:04 +00:00
|
|
|
}
|
|
|
|
n.register = nil
|
|
|
|
n.peerPool = nil
|
2019-01-17 11:02:45 +00:00
|
|
|
n.discovery = nil
|
2018-04-10 06:44:09 +00:00
|
|
|
}
|
2018-04-16 12:36:09 +00:00
|
|
|
|
2021-06-30 11:40:54 +00:00
|
|
|
if err := n.gethNode.Close(); err != nil {
|
2018-02-09 13:37:56 +00:00
|
|
|
return err
|
2017-05-24 14:13:30 +00:00
|
|
|
}
|
2018-04-16 12:36:09 +00:00
|
|
|
|
2024-10-15 15:59:17 +00:00
|
|
|
n.rpcClient.Stop()
|
2018-04-05 09:45:26 +00:00
|
|
|
n.rpcClient = nil
|
2018-04-16 12:36:09 +00:00
|
|
|
// We need to clear `gethNode` because config is passed to `Start()`
|
|
|
|
// and may be completely different. Similarly with `config`.
|
|
|
|
n.gethNode = nil
|
|
|
|
n.config = nil
|
|
|
|
|
2022-05-09 13:07:57 +00:00
|
|
|
err := n.httpServer.Stop()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
n.httpServer = nil
|
|
|
|
|
|
|
|
n.downloader.Stop()
|
|
|
|
n.downloader = nil
|
|
|
|
|
2018-05-02 12:14:08 +00:00
|
|
|
if n.db != nil {
|
2024-02-26 23:03:57 +00:00
|
|
|
if err = n.db.Close(); err != nil {
|
2024-10-28 20:54:17 +00:00
|
|
|
n.logger.Error("Error closing the leveldb of status node", zap.Error(err))
|
2024-02-26 23:03:57 +00:00
|
|
|
return err
|
|
|
|
}
|
2018-05-02 12:14:08 +00:00
|
|
|
n.db = nil
|
2018-04-20 11:26:54 +00:00
|
|
|
}
|
|
|
|
|
2021-07-07 06:11:09 +00:00
|
|
|
n.rpcFiltersSrvc = nil
|
|
|
|
n.subscriptionsSrvc = nil
|
|
|
|
n.rpcStatsSrvc = nil
|
|
|
|
n.accountsSrvc = nil
|
|
|
|
n.browsersSrvc = nil
|
|
|
|
n.permissionsSrvc = nil
|
|
|
|
n.mailserversSrvc = nil
|
2021-12-21 15:44:37 +00:00
|
|
|
n.providerSrvc = nil
|
2021-07-07 06:11:09 +00:00
|
|
|
n.appMetricsSrvc = nil
|
|
|
|
n.walletSrvc = nil
|
|
|
|
n.peerSrvc = nil
|
|
|
|
n.localNotificationsSrvc = nil
|
|
|
|
n.personalSrvc = nil
|
|
|
|
n.timeSourceSrvc = nil
|
|
|
|
n.wakuSrvc = nil
|
|
|
|
n.wakuExtSrvc = nil
|
|
|
|
n.wakuV2Srvc = nil
|
|
|
|
n.wakuV2ExtSrvc = nil
|
2021-12-21 15:05:09 +00:00
|
|
|
n.ensSrvc = nil
|
2023-08-25 15:36:39 +00:00
|
|
|
n.communityTokensSrvc = nil
|
2022-02-02 22:50:55 +00:00
|
|
|
n.stickersSrvc = nil
|
2024-06-24 14:29:40 +00:00
|
|
|
n.connectorSrvc = nil
|
2021-07-09 13:19:33 +00:00
|
|
|
n.publicMethods = make(map[string]bool)
|
2023-08-01 18:50:30 +00:00
|
|
|
n.pendingTracker = nil
|
2024-08-29 10:33:24 +00:00
|
|
|
n.appGeneralSrvc = nil
|
2024-10-28 20:54:17 +00:00
|
|
|
n.logger.Debug("status node stopped")
|
2018-02-09 13:37:56 +00:00
|
|
|
return nil
|
2019-01-02 18:57:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (n *StatusNode) isDiscoveryRunning() bool {
|
|
|
|
return n.register != nil || n.peerPool != nil || n.discovery != nil
|
2018-02-09 13:37:56 +00:00
|
|
|
}
|
2017-05-24 14:13:30 +00:00
|
|
|
|
2018-07-03 11:27:04 +00:00
|
|
|
func (n *StatusNode) stopDiscovery() error {
|
2018-04-10 06:44:09 +00:00
|
|
|
n.register.Stop()
|
|
|
|
n.peerPool.Stop()
|
2018-07-03 11:27:04 +00:00
|
|
|
return n.discovery.Stop()
|
2018-04-10 06:44:09 +00:00
|
|
|
}
|
|
|
|
|
2018-02-09 13:37:56 +00:00
|
|
|
// ResetChainData removes chain data if node is not running.
|
2018-04-05 09:45:26 +00:00
|
|
|
func (n *StatusNode) ResetChainData(config *params.NodeConfig) error {
|
|
|
|
n.mu.Lock()
|
|
|
|
defer n.mu.Unlock()
|
2018-04-16 12:36:09 +00:00
|
|
|
|
|
|
|
if n.isRunning() {
|
|
|
|
return ErrNodeRunning
|
|
|
|
}
|
|
|
|
|
2018-02-14 16:32:36 +00:00
|
|
|
chainDataDir := filepath.Join(config.DataDir, config.Name, "lightchaindata")
|
2018-02-09 13:37:56 +00:00
|
|
|
if _, err := os.Stat(chainDataDir); os.IsNotExist(err) {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err := os.RemoveAll(chainDataDir)
|
|
|
|
if err == nil {
|
2024-10-28 20:54:17 +00:00
|
|
|
n.logger.Info("Chain data has been removed", zap.String("dir", chainDataDir))
|
2018-02-09 13:37:56 +00:00
|
|
|
}
|
|
|
|
return err
|
2017-05-25 13:14:52 +00:00
|
|
|
}
|
|
|
|
|
2018-04-05 09:45:26 +00:00
|
|
|
// IsRunning confirm that node is running.
|
|
|
|
func (n *StatusNode) IsRunning() bool {
|
|
|
|
n.mu.RLock()
|
|
|
|
defer n.mu.RUnlock()
|
2017-05-25 13:14:52 +00:00
|
|
|
|
2018-04-16 12:36:09 +00:00
|
|
|
return n.isRunning()
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
|
2018-04-16 12:36:09 +00:00
|
|
|
func (n *StatusNode) isRunning() bool {
|
|
|
|
return n.gethNode != nil && n.gethNode.Server() != nil
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// populateStaticPeers connects current node with our publicly available LES/SHH/Swarm cluster
|
2018-04-05 09:45:26 +00:00
|
|
|
func (n *StatusNode) populateStaticPeers() error {
|
2018-09-13 16:31:29 +00:00
|
|
|
if !n.config.ClusterConfig.Enabled {
|
2024-10-28 20:54:17 +00:00
|
|
|
n.logger.Info("Static peers are disabled")
|
2017-05-16 12:09:52 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-04-05 09:45:26 +00:00
|
|
|
for _, enode := range n.config.ClusterConfig.StaticNodes {
|
2018-04-16 12:36:09 +00:00
|
|
|
if err := n.addPeer(enode); err != nil {
|
2024-10-28 20:54:17 +00:00
|
|
|
n.logger.Error("Static peer addition failed", zap.Error(err))
|
2018-04-16 12:36:09 +00:00
|
|
|
return err
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
2024-10-28 20:54:17 +00:00
|
|
|
n.logger.Info("Static peer added", zap.String("enode", enode))
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-04-05 09:45:26 +00:00
|
|
|
func (n *StatusNode) removeStaticPeers() error {
|
2018-09-13 16:31:29 +00:00
|
|
|
if !n.config.ClusterConfig.Enabled {
|
2024-10-28 20:54:17 +00:00
|
|
|
n.logger.Info("Static peers are disabled")
|
2018-02-19 15:32:58 +00:00
|
|
|
return nil
|
|
|
|
}
|
2018-04-16 12:36:09 +00:00
|
|
|
|
2018-04-05 09:45:26 +00:00
|
|
|
for _, enode := range n.config.ClusterConfig.StaticNodes {
|
2018-04-16 12:36:09 +00:00
|
|
|
if err := n.removePeer(enode); err != nil {
|
2024-10-28 20:54:17 +00:00
|
|
|
n.logger.Error("Static peer deletion failed", zap.Error(err))
|
2018-02-19 15:32:58 +00:00
|
|
|
return err
|
|
|
|
}
|
2024-10-28 20:54:17 +00:00
|
|
|
n.logger.Info("Static peer deleted", zap.String("enode", enode))
|
2018-02-19 15:32:58 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReconnectStaticPeers removes and adds static peers to a server.
|
2018-04-05 09:45:26 +00:00
|
|
|
func (n *StatusNode) ReconnectStaticPeers() error {
|
|
|
|
n.mu.Lock()
|
|
|
|
defer n.mu.Unlock()
|
2018-04-16 12:36:09 +00:00
|
|
|
|
|
|
|
if !n.isRunning() {
|
|
|
|
return ErrNoRunningNode
|
|
|
|
}
|
|
|
|
|
2018-04-05 09:45:26 +00:00
|
|
|
if err := n.removeStaticPeers(); err != nil {
|
2018-02-19 15:32:58 +00:00
|
|
|
return err
|
|
|
|
}
|
2018-04-16 12:36:09 +00:00
|
|
|
|
2018-04-05 09:45:26 +00:00
|
|
|
return n.populateStaticPeers()
|
2018-02-19 15:32:58 +00:00
|
|
|
}
|
|
|
|
|
2017-05-16 12:09:52 +00:00
|
|
|
// AddPeer adds new static peer node
|
2018-04-05 09:45:26 +00:00
|
|
|
func (n *StatusNode) AddPeer(url string) error {
|
|
|
|
n.mu.RLock()
|
|
|
|
defer n.mu.RUnlock()
|
2018-04-16 12:36:09 +00:00
|
|
|
|
2018-04-05 09:45:26 +00:00
|
|
|
return n.addPeer(url)
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// addPeer adds new static peer node
|
2018-04-05 09:45:26 +00:00
|
|
|
func (n *StatusNode) addPeer(url string) error {
|
2018-11-14 07:03:58 +00:00
|
|
|
parsedNode, err := enode.ParseV4(url)
|
2017-05-16 12:09:52 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-04-16 12:36:09 +00:00
|
|
|
|
|
|
|
if !n.isRunning() {
|
|
|
|
return ErrNoRunningNode
|
|
|
|
}
|
|
|
|
|
2018-04-09 07:16:43 +00:00
|
|
|
n.gethNode.Server().AddPeer(parsedNode)
|
2018-04-16 12:36:09 +00:00
|
|
|
|
2017-05-16 12:09:52 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-04-05 09:45:26 +00:00
|
|
|
func (n *StatusNode) removePeer(url string) error {
|
2018-11-14 07:03:58 +00:00
|
|
|
parsedNode, err := enode.ParseV4(url)
|
2018-02-19 15:32:58 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-04-16 12:36:09 +00:00
|
|
|
|
|
|
|
if !n.isRunning() {
|
|
|
|
return ErrNoRunningNode
|
|
|
|
}
|
|
|
|
|
2018-04-09 07:16:43 +00:00
|
|
|
n.gethNode.Server().RemovePeer(parsedNode)
|
2018-04-16 12:36:09 +00:00
|
|
|
|
2018-02-19 15:32:58 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-01-25 10:16:59 +00:00
|
|
|
// PeerCount returns the number of connected peers.
|
2018-04-05 09:45:26 +00:00
|
|
|
func (n *StatusNode) PeerCount() int {
|
|
|
|
n.mu.RLock()
|
|
|
|
defer n.mu.RUnlock()
|
2017-05-16 12:09:52 +00:00
|
|
|
|
2018-04-16 12:36:09 +00:00
|
|
|
if !n.isRunning() {
|
|
|
|
return 0
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
2018-04-16 12:36:09 +00:00
|
|
|
|
|
|
|
return n.gethNode.Server().PeerCount()
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
|
2021-06-30 11:40:54 +00:00
|
|
|
func (n *StatusNode) ConnectionChanged(state connection.State) {
|
2023-03-20 11:52:24 +00:00
|
|
|
if n.wakuExtSrvc != nil {
|
|
|
|
n.wakuExtSrvc.ConnectionChanged(state)
|
2019-07-17 05:28:37 +00:00
|
|
|
}
|
|
|
|
|
2023-03-20 11:52:24 +00:00
|
|
|
if n.wakuV2ExtSrvc != nil {
|
|
|
|
n.wakuV2ExtSrvc.ConnectionChanged(state)
|
|
|
|
}
|
2019-07-21 05:41:30 +00:00
|
|
|
}
|
|
|
|
|
2017-05-16 12:09:52 +00:00
|
|
|
// AccountManager exposes reference to node's accounts manager
|
2018-04-05 09:45:26 +00:00
|
|
|
func (n *StatusNode) AccountManager() (*accounts.Manager, error) {
|
|
|
|
n.mu.RLock()
|
|
|
|
defer n.mu.RUnlock()
|
2017-05-16 12:09:52 +00:00
|
|
|
|
2018-04-16 12:36:09 +00:00
|
|
|
if n.gethNode == nil {
|
|
|
|
return nil, ErrNoGethNode
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
2018-04-16 12:36:09 +00:00
|
|
|
|
|
|
|
return n.gethNode.AccountManager(), nil
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
|
2017-09-14 20:14:31 +00:00
|
|
|
// RPCClient exposes reference to RPC client connected to the running node.
|
2018-04-05 09:45:26 +00:00
|
|
|
func (n *StatusNode) RPCClient() *rpc.Client {
|
2018-08-20 13:55:43 +00:00
|
|
|
n.mu.RLock()
|
|
|
|
defer n.mu.RUnlock()
|
2018-04-05 09:45:26 +00:00
|
|
|
return n.rpcClient
|
2017-05-28 13:57:30 +00:00
|
|
|
}
|
2017-09-01 18:44:50 +00:00
|
|
|
|
2018-07-16 07:40:40 +00:00
|
|
|
// Discover sets up the discovery for a specific topic.
|
|
|
|
func (n *StatusNode) Discover(topic string, max, min int) (err error) {
|
|
|
|
if n.peerPool == nil {
|
|
|
|
return errors.New("peerPool not running")
|
|
|
|
}
|
|
|
|
return n.peerPool.UpdateTopic(topic, params.Limits{
|
|
|
|
Max: max,
|
|
|
|
Min: min,
|
|
|
|
})
|
|
|
|
}
|
2021-06-30 11:40:54 +00:00
|
|
|
|
|
|
|
func (n *StatusNode) SetAppDB(db *sql.DB) {
|
|
|
|
n.appDB = db
|
|
|
|
}
|
|
|
|
|
2024-10-06 13:40:22 +00:00
|
|
|
func (n *StatusNode) GetAppDB() *sql.DB {
|
|
|
|
return n.appDB
|
|
|
|
}
|
|
|
|
|
2021-06-30 11:40:54 +00:00
|
|
|
func (n *StatusNode) SetMultiaccountsDB(db *multiaccounts.Database) {
|
|
|
|
n.multiaccountsDB = db
|
|
|
|
}
|
2023-08-11 11:25:14 +00:00
|
|
|
|
|
|
|
func (n *StatusNode) SetWalletDB(db *sql.DB) {
|
|
|
|
n.walletDB = db
|
|
|
|
}
|
2024-10-06 13:40:22 +00:00
|
|
|
|
|
|
|
func (n *StatusNode) GetWalletDB() *sql.DB {
|
|
|
|
return n.walletDB
|
|
|
|
}
|