From 99ead2d90cc30df2c22195a97d550805a537b7cd Mon Sep 17 00:00:00 2001 From: Ivan Belyakov Date: Sun, 2 Jun 2024 20:28:55 +0200 Subject: [PATCH] feat(wallet)_: add support to statusd to create account from seed phrase fixed minor issues Closes #5175 --- api/backend_test.go | 28 ++++ api/defaults.go | 6 +- cmd/statusd/main.go | 228 +++++++++++++++++----------- protocol/requests/create_account.go | 6 +- services/wallet/history/service.go | 8 +- 5 files changed, 186 insertions(+), 90 deletions(-) diff --git a/api/backend_test.go b/api/backend_test.go index 30789d5b0..56cdc8753 100644 --- a/api/backend_test.go +++ b/api/backend_test.go @@ -1622,3 +1622,31 @@ func TestTestnetEnabledSettingOnCreateAccount(t *testing.T) { require.NoError(t, b.Logout()) } + +func TestRestoreAccountAndLogin(t *testing.T) { + utils.Init() + tmpdir := t.TempDir() + + backend := NewGethStatusBackend() + + // Test case 1: Valid restore account request + restoreRequest := &requests.RestoreAccount{ + Mnemonic: "test test test test test test test test test test test test", + FetchBackup: false, + CreateAccount: requests.CreateAccount{ + DisplayName: "Account1", + DeviceName: "StatusIM", + Password: "password", + CustomizationColor: "0x000000", + BackupDisabledDataDir: tmpdir, + }, + } + account, err := backend.RestoreAccountAndLogin(restoreRequest) + require.NoError(t, err) + require.NotNil(t, account) + + // Test case 2: Invalid restore account request + invalidRequest := &requests.RestoreAccount{} + _, err = backend.RestoreAccountAndLogin(invalidRequest) + require.Error(t, err) +} diff --git a/api/defaults.go b/api/defaults.go index 8b007c009..78596a4b1 100644 --- a/api/defaults.go +++ b/api/defaults.go @@ -237,7 +237,11 @@ func defaultNodeConfig(installationID string, request *requests.CreateAccount, o nodeConfig.LogEnabled = false } - nodeConfig.Networks = BuildDefaultNetworks(&request.WalletSecretsConfig) + if request.TestOverrideNetworks != nil { + nodeConfig.Networks = request.TestOverrideNetworks + } else { + nodeConfig.Networks = BuildDefaultNetworks(&request.WalletSecretsConfig) + } if request.NetworkID != nil { nodeConfig.NetworkID = *request.NetworkID diff --git a/cmd/statusd/main.go b/cmd/statusd/main.go index b6e9d8174..b084c89a9 100644 --- a/cmd/statusd/main.go +++ b/cmd/statusd/main.go @@ -34,6 +34,7 @@ import ( "github.com/status-im/status-go/profiling" "github.com/status-im/status-go/protocol" "github.com/status-im/status-go/protocol/pushnotificationserver" + "github.com/status-im/status-go/protocol/requests" "github.com/status-im/status-go/walletdatabase" ) @@ -86,6 +87,8 @@ var ( // don't change the name of this flag, https://github.com/ethereum/go-ethereum/blob/master/metrics/metrics.go#L41 metricsEnabled = flag.Bool("metrics", false, "Expose ethereum metrics with debug_metrics jsonrpc call") metricsPort = flag.Int("metrics-port", 9305, "Port for the Prometheus /metrics endpoint") + seedPhrase = flag.String("seed-phrase", "", "Seed phrase for account creation") + password = flag.String("password", "", "Password for account") ) // All general log messages in this package should be routed through this logger. @@ -110,6 +113,12 @@ func main() { os.Exit(1) } + if *seedPhrase != "" && *password == "" { + printUsage() + logger.Error("password is required when seed phrase is provided") + os.Exit(1) + } + opts := []params.Option{params.WithFleet(*fleet)} if *mailserver { opts = append(opts, params.WithMailserver()) @@ -162,12 +171,6 @@ func main() { } backend := api.NewGethStatusBackend() - err = backend.AccountManager().InitKeystore(config.KeyStoreDir) - if err != nil { - 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 @@ -186,96 +189,119 @@ func main() { return } - err = createDirsFromConfig(config) - if err != nil { - logger.Error("failed to create directories", "error", err) - return - } + if *seedPhrase != "" { + // Remove data inside dir to avoid conflicts with existing data or account restoration fails + if err := os.RemoveAll(config.DataDir); err != nil { + logger.Error("failed to remove data dir", "error", err) + return + } - appDB, walletDB, err := openDatabases(config.DataDir + "/" + installationID.String()) - if err != nil { - log.Error("failed to open databases") - return - } + if err := createDirsFromConfig(config); err != nil { + logger.Error("failed to create directories", "error", err) + return + } - backend.StatusNode().SetAppDB(appDB) - backend.StatusNode().SetWalletDB(walletDB) + request := requests.RestoreAccount{ + Mnemonic: *seedPhrase, + FetchBackup: false, + CreateAccount: requests.CreateAccount{ + DisplayName: "Account1", + DeviceName: "StatusIM", + Password: *password, + CustomizationColor: "0x000000", + BackupDisabledDataDir: config.DataDir, + APIConfig: &requests.APIConfig{ + HTTPHost: config.HTTPHost, + HTTPPort: config.HTTPPort, + APIModules: config.APIModules, + }, + NetworkID: &config.NetworkID, + TestOverrideNetworks: config.Networks, + }, + } - err = backend.StartNode(config) - if err != nil { - logger.Error("Node start failed", "error", err) - return - } - - err = sdnotify.Ready() - if err == sdnotify.ErrSdNotifyNoSocket { - logger.Debug("sd_notify socket not available") - } else if err != nil { - logger.Warn("sd_notify READY call failed", "error", err) + _, err := backend.RestoreAccountAndLogin(&request) + if err != nil { + logger.Error("failed to import account", "error", err) + return + } } else { - // systemd aliveness notifications, affects only Linux - go startSystemDWatchdog() - } - - // handle interrupt signals - interruptCh := haltOnInterruptSignal(backend.StatusNode()) - - // Start collecting metrics. Metrics can be enabled by providing `-metrics` flag - // or setting `gethmetrics.Enabled` to true during compilation time: - // https://github.com/status-im/go-ethereum/pull/76. - if *metricsEnabled || gethmetrics.Enabled { - go startCollectingNodeMetrics(interruptCh, backend.StatusNode()) - go gethmetrics.CollectProcessMetrics(3 * time.Second) - go metrics.NewMetricsServer(*metricsPort, gethmetrics.DefaultRegistry).Listen() - } - - // Check if profiling shall be enabled. - if *pprofEnabled { - profiling.NewProfiler(*pprofPort).Go() - } - - if config.PushNotificationServerConfig.Enabled { - options := []protocol.Option{ - protocol.WithPushNotifications(), - protocol.WithPushNotificationServerConfig(&pushnotificationserver.Config{ - Enabled: config.PushNotificationServerConfig.Enabled, - Identity: config.PushNotificationServerConfig.Identity, - GorushURL: config.PushNotificationServerConfig.GorushURL, - }), - protocol.WithDatabase(appDB), - protocol.WithWalletDatabase(walletDB), - protocol.WithTorrentConfig(&config.TorrentConfig), - protocol.WithWalletConfig(&config.WalletConfig), - protocol.WithAccountManager(backend.AccountManager()), - } - - messenger, err := protocol.NewMessenger( - config.Name, - identity, - gethbridge.NewNodeBridge(backend.StatusNode().GethNode(), backend.StatusNode().WakuService(), backend.StatusNode().WakuV2Service()), - installationID.String(), - nil, - options..., - ) + appDB, walletDB, err := startNode(config, backend, installationID) if err != nil { - logger.Error("failed to create messenger", "error", err) + logger.Error("failed to start node", "error", err) return } - err = messenger.Init() - if err != nil { - logger.Error("failed to init messenger", "error", err) - return + err = sdnotify.Ready() + if err == sdnotify.ErrSdNotifyNoSocket { + logger.Debug("sd_notify socket not available") + } else if err != nil { + logger.Warn("sd_notify READY call failed", "error", err) + } else { + // systemd aliveness notifications, affects only Linux + go startSystemDWatchdog() } - // This will start the push notification server as well as - // the config is set to Enabled - _, err = messenger.Start() - if err != nil { - logger.Error("failed to start messenger", "error", err) - return + // handle interrupt signals + interruptCh := haltOnInterruptSignal(backend.StatusNode()) + + // Start collecting metrics. Metrics can be enabled by providing `-metrics` flag + // or setting `gethmetrics.Enabled` to true during compilation time: + // https://github.com/status-im/go-ethereum/pull/76. + if *metricsEnabled || gethmetrics.Enabled { + go startCollectingNodeMetrics(interruptCh, backend.StatusNode()) + go gethmetrics.CollectProcessMetrics(3 * time.Second) + go metrics.NewMetricsServer(*metricsPort, gethmetrics.DefaultRegistry).Listen() + } + + // Check if profiling shall be enabled. + if *pprofEnabled { + profiling.NewProfiler(*pprofPort).Go() + } + + if config.PushNotificationServerConfig.Enabled { + options := []protocol.Option{ + protocol.WithPushNotifications(), + protocol.WithPushNotificationServerConfig(&pushnotificationserver.Config{ + Enabled: config.PushNotificationServerConfig.Enabled, + Identity: config.PushNotificationServerConfig.Identity, + GorushURL: config.PushNotificationServerConfig.GorushURL, + }), + protocol.WithDatabase(appDB), + protocol.WithWalletDatabase(walletDB), + protocol.WithTorrentConfig(&config.TorrentConfig), + protocol.WithWalletConfig(&config.WalletConfig), + protocol.WithAccountManager(backend.AccountManager()), + } + + messenger, err := protocol.NewMessenger( + config.Name, + identity, + gethbridge.NewNodeBridge(backend.StatusNode().GethNode(), backend.StatusNode().WakuService(), backend.StatusNode().WakuV2Service()), + installationID.String(), + nil, + options..., + ) + if err != nil { + logger.Error("failed to create messenger", "error", err) + return + } + + err = messenger.Init() + if err != nil { + logger.Error("failed to init messenger", "error", err) + return + } + + // This will start the push notification server as well as + // the config is set to Enabled + _, err = messenger.Start() + if err != nil { + logger.Error("failed to start messenger", "error", err) + return + } + go retrieveMessagesLoop(messenger, 300*time.Millisecond, interruptCh) } - go retrieveMessagesLoop(messenger, 300*time.Millisecond, interruptCh) } gethNode := backend.StatusNode().GethNode() @@ -402,6 +428,7 @@ Examples: statusd -c ./default.json # run node with configuration specified in ./default.json file statusd -c ./default.json -c ./standalone.json # run node with configuration specified in ./default.json file, after merging ./standalone.json file statusd -c ./default.json -metrics # run node with configuration specified in ./default.json file, and expose ethereum metrics with debug_metrics jsonrpc call + statusd -c ./default.json -log DEBUG --seed-phrase="test test test test test test test test test test test junk" --password=password # run node with configuration specified in ./default.json file, and import account with seed phrase and password Options: ` @@ -483,3 +510,34 @@ func createDirsFromConfig(config *params.NodeConfig) error { return nil } + +func startNode(config *params.NodeConfig, backend *api.GethStatusBackend, installationID uuid.UUID) (*sql.DB, *sql.DB, error) { + err := backend.AccountManager().InitKeystore(config.KeyStoreDir) + if err != nil { + logger.Error("Failed to init keystore", "error", err) + return nil, nil, err + } + + err = createDirsFromConfig(config) + if err != nil { + logger.Error("failed to create directories", "error", err) + return nil, nil, err + } + + appDB, walletDB, err := openDatabases(config.DataDir + "/" + installationID.String()) + if err != nil { + log.Error("failed to open databases") + return nil, nil, err + } + + backend.StatusNode().SetAppDB(appDB) + backend.StatusNode().SetWalletDB(walletDB) + + err = backend.StartNode(config) + if err != nil { + logger.Error("Node start failed", "error", err) + return nil, nil, err + } + + return appDB, walletDB, nil +} diff --git a/protocol/requests/create_account.go b/protocol/requests/create_account.go index b24b8a3b5..e8c68ae07 100644 --- a/protocol/requests/create_account.go +++ b/protocol/requests/create_account.go @@ -4,6 +4,7 @@ import ( "github.com/pkg/errors" utils "github.com/status-im/status-go/common" + "github.com/status-im/status-go/params" ) var ErrCreateAccountInvalidDisplayName = errors.New("create-account: invalid display name") @@ -56,8 +57,9 @@ type CreateAccount struct { // Deprecated: CurrentNetwork is deprecated. It was passed and not used, so nothing should be passed instead. // If you want to use non-default network, use NetworkID. - CurrentNetwork string `json:"currentNetwork"` - NetworkID *uint64 `json:"networkId"` + CurrentNetwork string `json:"currentNetwork"` + NetworkID *uint64 `json:"networkId"` + TestOverrideNetworks []params.Network `json:"-"` // This is used for testing purposes only TestNetworksEnabled bool `json:"testNetworksEnabled"` diff --git a/services/wallet/history/service.go b/services/wallet/history/service.go index a09778836..859363506 100644 --- a/services/wallet/history/service.go +++ b/services/wallet/history/service.go @@ -486,11 +486,15 @@ func (s *Service) startTransfersWatcher() { return } + network := s.networkManager.Find(chainID) + if network == nil { + log.Error("Network not found", "chainID", chainID) + return + } + transferDB := transfer.NewDB(s.db) for _, address := range addresses { - network := s.networkManager.Find(chainID) - transfers, err := transferDB.GetTransfersByAddressAndBlock(chainID, address, block, 1500) // 1500 is quite arbitrary and far from real, but should be enough to cover all transfers in a block if err != nil { log.Error("Error getting transfers", "chainID", chainID, "address", address.String(), "err", err)