From c0e922f0af9a59ed00bae72332cfd164121484c0 Mon Sep 17 00:00:00 2001 From: Ivan Belyakov Date: Mon, 13 May 2024 15:02:22 +0200 Subject: [PATCH] fix(wallet)_: fix crash on nil db for statusd if wallet is enabled Fixed some other issues with prevented startup of statusd: - ensure paths for DBs are created - ensure DBs are opened before calling `StartNode` - ignore error if multiacc database is not available Updates #14693 --- cmd/statusd/main.go | 100 +++++++++++++++++------- multiaccounts/accounts/database.go | 4 + rpc/client.go | 4 + services/wallet/history/service_test.go | 2 +- services/wallet/token/token_test.go | 2 +- services/wallet/transfer/controller.go | 6 +- transactions/transactor_test.go | 5 +- 7 files changed, 89 insertions(+), 34 deletions(-) diff --git a/cmd/statusd/main.go b/cmd/statusd/main.go index 9fa310550..f8cbcdb66 100644 --- a/cmd/statusd/main.go +++ b/cmd/statusd/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "database/sql" "errors" "flag" "fmt" @@ -157,6 +158,40 @@ func main() { logger.Error("Failed to init keystore", "error", err) return } + + if config.NodeKey == "" { + logger.Error("node key needs to be set if running a push notification server") + return + } + + identity, err := crypto.HexToECDSA(config.NodeKey) + if err != nil { + logger.Error("node key is invalid", "error", err) + return + } + + // Generate installationID from public key, so it's always the same + installationID, err := uuid.FromBytes(crypto.CompressPubkey(&identity.PublicKey)[:16]) + if err != nil { + logger.Error("cannot create installation id", "error", err) + return + } + + err = createDirsFromConfig(config) + if err != nil { + logger.Error("failed to create directories", "error", err) + return + } + + appDB, walletDB, err := openDatabases(config.DataDir + "/" + installationID.String()) + if err != nil { + log.Error("failed to open databases") + return + } + + backend.StatusNode().SetAppDB(appDB) + backend.StatusNode().SetWalletDB(walletDB) + err = backend.StartNode(config) if err != nil { logger.Error("Node start failed", "error", err) @@ -191,35 +226,6 @@ func main() { } if config.PushNotificationServerConfig.Enabled { - if config.NodeKey == "" { - logger.Error("node key needs to be set if running a push notification server") - return - } - - identity, err := crypto.HexToECDSA(config.NodeKey) - if err != nil { - logger.Error("node key is invalid", "error", err) - return - } - - // Generate installationID from public key, so it's always the same - installationID, err := uuid.FromBytes(crypto.CompressPubkey(&identity.PublicKey)[:16]) - if err != nil { - logger.Error("cannot create installation id", "error", err) - return - } - - walletDB, err := walletdatabase.InitializeDB(config.DataDir+"/"+installationID.String()+"-wallet.db", "", dbsetup.ReducedKDFIterationsNumber) - if err != nil { - logger.Error("failed to initialize app db", "error", err) - return - } - - appDB, err := appdatabase.InitializeDB(config.DataDir+"/"+installationID.String()+".db", "", dbsetup.ReducedKDFIterationsNumber) - if err != nil { - logger.Error("failed to initialize app db", "error", err) - return - } options := []protocol.Option{ protocol.WithPushNotifications(), protocol.WithPushNotificationServerConfig(&pushnotificationserver.Config{ @@ -432,3 +438,39 @@ func retrieveMessagesLoop(messenger *protocol.Messenger, tick time.Duration, can } } } + +func openDatabases(path string) (*sql.DB, *sql.DB, error) { + walletDB, err := walletdatabase.InitializeDB(path+"-wallet.db", "", dbsetup.ReducedKDFIterationsNumber) + if err != nil { + logger.Error("failed to initialize wallet db", "error", err) + return nil, nil, err + } + + appDB, err := appdatabase.InitializeDB(path+".db", "", dbsetup.ReducedKDFIterationsNumber) + if err != nil { + logger.Error("failed to initialize app db", "error", err) + return nil, nil, err + } + + return appDB, walletDB, nil +} + +func createDirsFromConfig(config *params.NodeConfig) error { + // If DataDir is empty, it means we want to create an ephemeral node + // keeping data only in memory. + if config.DataDir != "" { + // make sure data directory exists + if err := os.MkdirAll(filepath.Clean(config.DataDir), os.ModePerm); err != nil { + return fmt.Errorf("make node: make data directory: %v", err) + } + } + + if config.KeyStoreDir != "" { + // make sure keys directory exists + if err := os.MkdirAll(filepath.Clean(config.KeyStoreDir), os.ModePerm); err != nil { + return fmt.Errorf("make node: make keys directory: %v", err) + } + } + + return nil +} diff --git a/multiaccounts/accounts/database.go b/multiaccounts/accounts/database.go index 29afda5cd..1381456fb 100644 --- a/multiaccounts/accounts/database.go +++ b/multiaccounts/accounts/database.go @@ -296,6 +296,10 @@ type Database struct { // NewDB returns a new instance of *Database func NewDB(db *sql.DB) (*Database, error) { + if db == nil { + return nil, errDbPassedParameterIsNil + } + sDB, err := settings.MakeNewDB(db) if err != nil { return nil, err diff --git a/rpc/client.go b/rpc/client.go index c404cf69b..59ca47e4d 100644 --- a/rpc/client.go +++ b/rpc/client.go @@ -79,6 +79,10 @@ func NewClient(client *gethrpc.Client, upstreamChainID uint64, upstream params.U log := log.New("package", "status-go/rpc.Client") networkManager := network.NewManager(db) + if networkManager == nil { + return nil, errors.New("failed to create network manager") + } + err = networkManager.Init(networks) if err != nil { log.Error("Network manager failed to initialize", "error", err) diff --git a/services/wallet/history/service_test.go b/services/wallet/history/service_test.go index b9637b752..36ea2b1b8 100644 --- a/services/wallet/history/service_test.go +++ b/services/wallet/history/service_test.go @@ -371,7 +371,7 @@ func Test_removeBalanceHistoryOnEventAccountRemoved(t *testing.T) { txServiceMockCtrl := gomock.NewController(t) server, _ := fake.NewTestServer(txServiceMockCtrl) client := gethrpc.DialInProc(server) - rpcClient, _ := rpc.NewClient(client, chainID, params.UpstreamRPCConfig{}, nil, nil) + rpcClient, _ := rpc.NewClient(client, chainID, params.UpstreamRPCConfig{}, nil, appDB) rpcClient.UpstreamChainID = chainID service := NewService(walletDB, accountsDB, &accountFeed, &walletFeed, rpcClient, nil, nil, nil) diff --git a/services/wallet/token/token_test.go b/services/wallet/token/token_test.go index db64f91cb..2d5d407fb 100644 --- a/services/wallet/token/token_test.go +++ b/services/wallet/token/token_test.go @@ -328,7 +328,7 @@ func Test_removeTokenBalanceOnEventAccountRemoved(t *testing.T) { txServiceMockCtrl := gomock.NewController(t) server, _ := fake.NewTestServer(txServiceMockCtrl) client := gethrpc.DialInProc(server) - rpcClient, _ := rpc.NewClient(client, chainID, params.UpstreamRPCConfig{}, nil, nil) + rpcClient, _ := rpc.NewClient(client, chainID, params.UpstreamRPCConfig{}, nil, appDB) rpcClient.UpstreamChainID = chainID nm := network.NewManager(appDB) mediaServer, err := mediaserver.NewMediaServer(appDB, nil, nil, walletDB) diff --git a/services/wallet/transfer/controller.go b/services/wallet/transfer/controller.go index e89f60386..5c68a45d7 100644 --- a/services/wallet/transfer/controller.go +++ b/services/wallet/transfer/controller.go @@ -119,12 +119,14 @@ func (c *Controller) CheckRecentHistory(chainIDs []uint64, accounts []common.Add return nil } + omitHistory := false multiaccSettings, err := c.accountsDB.GetSettings() if err != nil { - return err + log.Error("Failed to get multiacc settings") // not critical + } else { + omitHistory = multiaccSettings.OmitTransfersHistoryScan } - omitHistory := multiaccSettings.OmitTransfersHistoryScan if omitHistory { err := c.accountsDB.SaveSettingField(settings.OmitTransfersHistoryScan, false) if err != nil { diff --git a/transactions/transactor_test.go b/transactions/transactor_test.go index 1ba14a2f3..bcd6ecee2 100644 --- a/transactions/transactor_test.go +++ b/transactions/transactor_test.go @@ -23,6 +23,7 @@ import ( "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/params" "github.com/status-im/status-go/rpc" + "github.com/status-im/status-go/sqlite" "github.com/status-im/status-go/t/utils" "github.com/status-im/status-go/transactions/fake" ) @@ -51,7 +52,9 @@ func (s *TransactorSuite) SetupTest() { // expected by simulated backend chainID := gethparams.AllEthashProtocolChanges.ChainID.Uint64() - rpcClient, _ := rpc.NewClient(s.client, chainID, params.UpstreamRPCConfig{}, nil, nil) + db, err := sqlite.OpenUnecryptedDB(sqlite.InMemoryPath) // dummy to make rpc.Client happy + s.Require().NoError(err) + rpcClient, _ := rpc.NewClient(s.client, chainID, params.UpstreamRPCConfig{}, nil, db) rpcClient.UpstreamChainID = chainID nodeConfig, err := utils.MakeTestNodeConfigWithDataDir("", "/tmp", chainID) s.Require().NoError(err)