github.com/status-im/status-go@v1.1.0/api/geth_backend.go (about) 1 package api 2 3 import ( 4 "context" 5 "crypto/ecdsa" 6 "database/sql" 7 "encoding/json" 8 "fmt" 9 "math/big" 10 "os" 11 "path/filepath" 12 "strings" 13 "sync" 14 "time" 15 16 "go.uber.org/zap" 17 18 "github.com/afex/hystrix-go/hystrix" 19 "github.com/pkg/errors" 20 21 "github.com/imdario/mergo" 22 23 "github.com/ethereum/go-ethereum/common" 24 "github.com/ethereum/go-ethereum/common/hexutil" 25 ethcrypto "github.com/ethereum/go-ethereum/crypto" 26 "github.com/ethereum/go-ethereum/log" 27 signercore "github.com/ethereum/go-ethereum/signer/core/apitypes" 28 29 "github.com/status-im/status-go/account" 30 "github.com/status-im/status-go/account/generator" 31 "github.com/status-im/status-go/appdatabase" 32 "github.com/status-im/status-go/centralizedmetrics" 33 centralizedmetricscommon "github.com/status-im/status-go/centralizedmetrics/common" 34 "github.com/status-im/status-go/common/dbsetup" 35 "github.com/status-im/status-go/connection" 36 "github.com/status-im/status-go/eth-node/crypto" 37 "github.com/status-im/status-go/eth-node/types" 38 "github.com/status-im/status-go/images" 39 "github.com/status-im/status-go/logutils" 40 "github.com/status-im/status-go/multiaccounts" 41 "github.com/status-im/status-go/multiaccounts/accounts" 42 multiacccommon "github.com/status-im/status-go/multiaccounts/common" 43 "github.com/status-im/status-go/multiaccounts/settings" 44 "github.com/status-im/status-go/node" 45 "github.com/status-im/status-go/nodecfg" 46 "github.com/status-im/status-go/params" 47 "github.com/status-im/status-go/protocol" 48 identityutils "github.com/status-im/status-go/protocol/identity" 49 "github.com/status-im/status-go/protocol/identity/colorhash" 50 "github.com/status-im/status-go/protocol/requests" 51 "github.com/status-im/status-go/rpc" 52 "github.com/status-im/status-go/server/pairing/statecontrol" 53 "github.com/status-im/status-go/services/ens" 54 "github.com/status-im/status-go/services/ext" 55 "github.com/status-im/status-go/services/personal" 56 "github.com/status-im/status-go/services/typeddata" 57 "github.com/status-im/status-go/services/wallet" 58 "github.com/status-im/status-go/signal" 59 "github.com/status-im/status-go/sqlite" 60 "github.com/status-im/status-go/transactions" 61 "github.com/status-im/status-go/walletdatabase" 62 ) 63 64 var ( 65 // ErrWhisperClearIdentitiesFailure clearing whisper identities has failed. 66 ErrWhisperClearIdentitiesFailure = errors.New("failed to clear whisper identities") 67 // ErrWhisperIdentityInjectionFailure injecting whisper identities has failed. 68 ErrWhisperIdentityInjectionFailure = errors.New("failed to inject identity into Whisper") 69 // ErrWakuIdentityInjectionFailure injecting whisper identities has failed. 70 ErrWakuIdentityInjectionFailure = errors.New("failed to inject identity into waku") 71 // ErrUnsupportedRPCMethod is for methods not supported by the RPC interface 72 ErrUnsupportedRPCMethod = errors.New("method is unsupported by RPC interface") 73 // ErrRPCClientUnavailable is returned if an RPC client can't be retrieved. 74 // This is a normal situation when a node is stopped. 75 ErrRPCClientUnavailable = errors.New("JSON-RPC client is unavailable") 76 // ErrDBNotAvailable is returned if a method is called before the DB is available for usage 77 ErrDBNotAvailable = errors.New("DB is unavailable") 78 ) 79 80 var _ StatusBackend = (*GethStatusBackend)(nil) 81 82 // GethStatusBackend implements the Status.im service over go-ethereum 83 type GethStatusBackend struct { 84 mu sync.Mutex 85 // rootDataDir is the same for all networks. 86 rootDataDir string 87 appDB *sql.DB 88 walletDB *sql.DB 89 config *params.NodeConfig 90 91 statusNode *node.StatusNode 92 personalAPI *personal.PublicAPI 93 multiaccountsDB *multiaccounts.Database 94 account *multiaccounts.Account 95 accountManager *account.GethManager 96 transactor *transactions.Transactor 97 connectionState connection.State 98 appState appState 99 selectedAccountKeyID string 100 log log.Logger 101 allowAllRPC bool // used only for tests, disables api method restrictions 102 LocalPairingStateManager *statecontrol.ProcessStateManager 103 centralizedMetrics *centralizedmetrics.MetricService 104 } 105 106 // NewGethStatusBackend create a new GethStatusBackend instance 107 func NewGethStatusBackend() *GethStatusBackend { 108 defer log.Info("Status backend initialized", "backend", "geth", "version", params.Version, "commit", params.GitCommit, "IpfsGatewayURL", params.IpfsGatewayURL) 109 110 backend := &GethStatusBackend{} 111 backend.initialize() 112 return backend 113 } 114 115 func (b *GethStatusBackend) initialize() { 116 accountManager := account.NewGethManager() 117 transactor := transactions.NewTransactor() 118 personalAPI := personal.NewAPI() 119 statusNode := node.New(transactor) 120 121 b.statusNode = statusNode 122 b.accountManager = accountManager 123 b.transactor = transactor 124 b.personalAPI = personalAPI 125 b.statusNode.SetMultiaccountsDB(b.multiaccountsDB) 126 b.log = log.New("package", "status-go/api.GethStatusBackend") 127 b.LocalPairingStateManager = new(statecontrol.ProcessStateManager) 128 b.LocalPairingStateManager.SetPairing(false) 129 } 130 131 // StatusNode returns reference to node manager 132 func (b *GethStatusBackend) StatusNode() *node.StatusNode { 133 return b.statusNode 134 } 135 136 // AccountManager returns reference to account manager 137 func (b *GethStatusBackend) AccountManager() *account.GethManager { 138 return b.accountManager 139 } 140 141 // Transactor returns reference to a status transactor 142 func (b *GethStatusBackend) Transactor() *transactions.Transactor { 143 return b.transactor 144 } 145 146 // SelectedAccountKeyID returns a Whisper key ID of the selected chat key pair. 147 func (b *GethStatusBackend) SelectedAccountKeyID() string { 148 return b.selectedAccountKeyID 149 } 150 151 // IsNodeRunning confirm that node is running 152 func (b *GethStatusBackend) IsNodeRunning() bool { 153 return b.statusNode.IsRunning() 154 } 155 156 // StartNode start Status node, fails if node is already started 157 func (b *GethStatusBackend) StartNode(config *params.NodeConfig) error { 158 b.mu.Lock() 159 defer b.mu.Unlock() 160 if err := b.startNode(config); err != nil { 161 signal.SendNodeCrashed(err) 162 return err 163 } 164 return nil 165 } 166 167 func (b *GethStatusBackend) UpdateRootDataDir(datadir string) { 168 b.mu.Lock() 169 defer b.mu.Unlock() 170 b.rootDataDir = datadir 171 } 172 173 func (b *GethStatusBackend) GetMultiaccountDB() *multiaccounts.Database { 174 return b.multiaccountsDB 175 } 176 177 func (b *GethStatusBackend) OpenAccounts() error { 178 b.mu.Lock() 179 defer b.mu.Unlock() 180 if b.multiaccountsDB != nil { 181 return nil 182 } 183 db, err := multiaccounts.InitializeDB(filepath.Join(b.rootDataDir, "accounts.sql")) 184 if err != nil { 185 b.log.Error("failed to initialize accounts db", "err", err) 186 return err 187 } 188 b.multiaccountsDB = db 189 190 b.centralizedMetrics = centralizedmetrics.NewDefaultMetricService(b.multiaccountsDB.DB()) 191 err = b.centralizedMetrics.EnsureStarted() 192 if err != nil { 193 return err 194 } 195 196 // Probably we should iron out a bit better how to create/dispose of the status-service 197 b.statusNode.SetMultiaccountsDB(db) 198 199 err = b.statusNode.StartMediaServerWithoutDB() 200 if err != nil { 201 b.log.Error("failed to start media server without app db", "err", err) 202 return err 203 } 204 205 return nil 206 } 207 208 func (b *GethStatusBackend) CentralizedMetricsInfo() (*centralizedmetrics.MetricsInfo, error) { 209 if b.centralizedMetrics == nil { 210 return nil, errors.New("centralized metrics not initialized") 211 } 212 213 return b.centralizedMetrics.Info() 214 } 215 216 func (b *GethStatusBackend) ToggleCentralizedMetrics(isEnabled bool) error { 217 if b.centralizedMetrics == nil { 218 return errors.New("centralized metrics nil") 219 } 220 221 return b.centralizedMetrics.ToggleEnabled(isEnabled) 222 } 223 224 func (b *GethStatusBackend) AddCentralizedMetric(metric centralizedmetricscommon.Metric) error { 225 if b.centralizedMetrics == nil { 226 return errors.New("centralized metrics nil") 227 } 228 return b.centralizedMetrics.AddMetric(metric) 229 230 } 231 232 func (b *GethStatusBackend) GetAccounts() ([]multiaccounts.Account, error) { 233 b.mu.Lock() 234 defer b.mu.Unlock() 235 if b.multiaccountsDB == nil { 236 return nil, errors.New("accounts db wasn't initialized") 237 } 238 return b.multiaccountsDB.GetAccounts() 239 } 240 241 func (b *GethStatusBackend) getAccountByKeyUID(keyUID string) (*multiaccounts.Account, error) { 242 b.mu.Lock() 243 defer b.mu.Unlock() 244 if b.multiaccountsDB == nil { 245 return nil, errors.New("accounts db wasn't initialized") 246 } 247 as, err := b.multiaccountsDB.GetAccounts() 248 if err != nil { 249 return nil, err 250 } 251 for _, acc := range as { 252 if acc.KeyUID == keyUID { 253 return &acc, nil 254 } 255 } 256 return nil, fmt.Errorf("account with keyUID %s not found", keyUID) 257 } 258 259 func (b *GethStatusBackend) SaveAccount(account multiaccounts.Account) error { 260 b.mu.Lock() 261 defer b.mu.Unlock() 262 if b.multiaccountsDB == nil { 263 return errors.New("accounts db wasn't initialized") 264 } 265 return b.multiaccountsDB.SaveAccount(account) 266 } 267 268 func (b *GethStatusBackend) DeleteMultiaccount(keyUID string, keyStoreDir string) error { 269 b.mu.Lock() 270 defer b.mu.Unlock() 271 if b.multiaccountsDB == nil { 272 return errors.New("accounts db wasn't initialized") 273 } 274 275 err := b.multiaccountsDB.DeleteAccount(keyUID) 276 if err != nil { 277 return err 278 } 279 280 appDbPath, err := b.getAppDBPath(keyUID) 281 if err != nil { 282 return err 283 } 284 285 walletDbPath, err := b.getWalletDBPath(keyUID) 286 if err != nil { 287 return err 288 } 289 290 dbFiles := []string{ 291 filepath.Join(b.rootDataDir, fmt.Sprintf("app-%x.sql", keyUID)), 292 filepath.Join(b.rootDataDir, fmt.Sprintf("app-%x.sql-shm", keyUID)), 293 filepath.Join(b.rootDataDir, fmt.Sprintf("app-%x.sql-wal", keyUID)), 294 filepath.Join(b.rootDataDir, fmt.Sprintf("%s.db", keyUID)), 295 filepath.Join(b.rootDataDir, fmt.Sprintf("%s.db-shm", keyUID)), 296 filepath.Join(b.rootDataDir, fmt.Sprintf("%s.db-wal", keyUID)), 297 appDbPath, 298 appDbPath + "-shm", 299 appDbPath + "-wal", 300 walletDbPath, 301 walletDbPath + "-shm", 302 walletDbPath + "-wal", 303 } 304 for _, path := range dbFiles { 305 if _, err := os.Stat(path); err == nil { 306 err = os.Remove(path) 307 if err != nil { 308 return err 309 } 310 } 311 } 312 313 if b.account != nil && b.account.KeyUID == keyUID { 314 // reset active account 315 b.account = nil 316 } 317 318 return os.RemoveAll(keyStoreDir) 319 } 320 321 func (b *GethStatusBackend) DeleteImportedKey(address, password, keyStoreDir string) error { 322 b.mu.Lock() 323 defer b.mu.Unlock() 324 325 err := filepath.Walk(keyStoreDir, func(path string, fileInfo os.FileInfo, err error) error { 326 if err != nil { 327 return err 328 } 329 if strings.Contains(fileInfo.Name(), address) { 330 _, err := b.accountManager.VerifyAccountPassword(keyStoreDir, "0x"+address, password) 331 if err != nil { 332 b.log.Error("failed to verify account", "account", address, "error", err) 333 return err 334 } 335 336 return os.Remove(path) 337 } 338 return nil 339 }) 340 341 return err 342 } 343 344 func (b *GethStatusBackend) runDBFileMigrations(account multiaccounts.Account, password string) (string, error) { 345 // Migrate file path to fix issue https://github.com/status-im/status-go/issues/2027 346 unsupportedPath := filepath.Join(b.rootDataDir, fmt.Sprintf("app-%x.sql", account.KeyUID)) 347 v3Path := filepath.Join(b.rootDataDir, fmt.Sprintf("%s.db", account.KeyUID)) 348 v4Path, err := b.getAppDBPath(account.KeyUID) 349 if err != nil { 350 return "", err 351 } 352 353 _, err = os.Stat(unsupportedPath) 354 if err == nil { 355 err := os.Rename(unsupportedPath, v3Path) 356 if err != nil { 357 return "", err 358 } 359 360 // rename journals as well, but ignore errors 361 _ = os.Rename(unsupportedPath+"-shm", v3Path+"-shm") 362 _ = os.Rename(unsupportedPath+"-wal", v3Path+"-wal") 363 } 364 365 if _, err = os.Stat(v3Path); err == nil { 366 if err := appdatabase.MigrateV3ToV4(v3Path, v4Path, password, account.KDFIterations, signal.SendReEncryptionStarted, signal.SendReEncryptionFinished); err != nil { 367 _ = os.Remove(v4Path) 368 _ = os.Remove(v4Path + "-shm") 369 _ = os.Remove(v4Path + "-wal") 370 return "", errors.New("Failed to migrate v3 db to v4: " + err.Error()) 371 } 372 _ = os.Remove(v3Path) 373 _ = os.Remove(v3Path + "-shm") 374 _ = os.Remove(v3Path + "-wal") 375 } 376 377 return v4Path, nil 378 } 379 380 func (b *GethStatusBackend) ensureDBsOpened(account multiaccounts.Account, password string) (err error) { 381 // After wallet DB initial migration, the tables moved to wallet DB are removed from appDB 382 // so better migrate wallet DB first to avoid removal if wallet DB migration fails 383 if err = b.ensureWalletDBOpened(account, password); err != nil { 384 return err 385 } 386 387 if err = b.ensureAppDBOpened(account, password); err != nil { 388 return err 389 } 390 391 return nil 392 } 393 394 func (b *GethStatusBackend) ensureAppDBOpened(account multiaccounts.Account, password string) (err error) { 395 b.mu.Lock() 396 defer b.mu.Unlock() 397 if b.appDB != nil { 398 return nil 399 } 400 if len(b.rootDataDir) == 0 { 401 return errors.New("root datadir wasn't provided") 402 } 403 404 dbFilePath, err := b.runDBFileMigrations(account, password) 405 if err != nil { 406 return errors.New("Failed to migrate db file: " + err.Error()) 407 } 408 409 appdatabase.CurrentAppDBKeyUID = account.KeyUID 410 b.appDB, err = appdatabase.InitializeDB(dbFilePath, password, account.KDFIterations) 411 if err != nil { 412 b.log.Error("failed to initialize db", "err", err.Error()) 413 return err 414 } 415 b.statusNode.SetAppDB(b.appDB) 416 return nil 417 } 418 419 func fileExists(path string) bool { 420 if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) { 421 return false 422 } 423 424 return true 425 } 426 427 func (b *GethStatusBackend) walletDBExists(keyUID string) bool { 428 path, err := b.getWalletDBPath(keyUID) 429 if err != nil { 430 return false 431 } 432 433 return fileExists(path) 434 } 435 436 func (b *GethStatusBackend) appDBExists(keyUID string) bool { 437 path, err := b.getAppDBPath(keyUID) 438 if err != nil { 439 return false 440 } 441 442 return fileExists(path) 443 } 444 445 func (b *GethStatusBackend) ensureWalletDBOpened(account multiaccounts.Account, password string) (err error) { 446 b.mu.Lock() 447 defer b.mu.Unlock() 448 if b.walletDB != nil { 449 return nil 450 } 451 452 dbWalletPath, err := b.getWalletDBPath(account.KeyUID) 453 if err != nil { 454 return err 455 } 456 457 b.walletDB, err = walletdatabase.InitializeDB(dbWalletPath, password, account.KDFIterations) 458 if err != nil { 459 b.log.Error("failed to initialize wallet db", "err", err.Error()) 460 return err 461 } 462 b.statusNode.SetWalletDB(b.walletDB) 463 return nil 464 } 465 466 func (b *GethStatusBackend) setupLogSettings() error { 467 logSettings := logutils.LogSettings{ 468 Enabled: b.config.LogEnabled, 469 MobileSystem: b.config.LogMobileSystem, 470 Level: b.config.LogLevel, 471 File: b.config.LogFile, 472 MaxSize: b.config.LogMaxSize, 473 MaxBackups: b.config.LogMaxBackups, 474 CompressRotated: b.config.LogCompressRotated, 475 } 476 if err := logutils.OverrideRootLogWithConfig(logSettings, false); err != nil { 477 return err 478 } 479 return nil 480 } 481 482 // Deprecated: Use StartNodeWithAccount instead. 483 func (b *GethStatusBackend) StartNodeWithKey(acc multiaccounts.Account, password string, keyHex string, nodecfg *params.NodeConfig) error { 484 if acc.KDFIterations == 0 { 485 kdfIterations, err := b.multiaccountsDB.GetAccountKDFIterationsNumber(acc.KeyUID) 486 if err != nil { 487 return err 488 } 489 490 acc.KDFIterations = kdfIterations 491 } 492 493 chatKey, err := ethcrypto.HexToECDSA(keyHex) 494 if err != nil { 495 return err 496 } 497 498 err = b.startNodeWithAccount(acc, password, nodecfg, chatKey) 499 if err != nil { 500 // Stop node for clean up 501 _ = b.StopNode() 502 } 503 // get logged in 504 if b.LocalPairingStateManager.IsPairing() { 505 return nil 506 } 507 return b.LoggedIn(acc.KeyUID, err) 508 } 509 510 func (b *GethStatusBackend) OverwriteNodeConfigValues(conf *params.NodeConfig, n *params.NodeConfig) (*params.NodeConfig, error) { 511 if err := mergo.Merge(conf, n, mergo.WithOverride); err != nil { 512 return nil, err 513 } 514 515 conf.Networks = n.Networks 516 517 if err := b.saveNodeConfig(conf); err != nil { 518 return nil, err 519 } 520 521 return conf, nil 522 } 523 524 func (b *GethStatusBackend) updateAccountColorHashAndColorID(keyUID string, accountsDB *accounts.Database) (*multiaccounts.Account, error) { 525 multiAccount, err := b.getAccountByKeyUID(keyUID) 526 if err != nil { 527 return nil, err 528 } 529 if multiAccount.ColorHash == nil { 530 keypair, err := accountsDB.GetKeypairByKeyUID(keyUID) 531 if err != nil { 532 return nil, err 533 } 534 publicKey := keypair.GetChatPublicKey() 535 if publicKey == nil { 536 return nil, errors.New("chat public key not found") 537 } 538 if err = enrichMultiAccountByPublicKey(multiAccount, publicKey); err != nil { 539 return nil, err 540 } 541 if err = b.multiaccountsDB.UpdateAccount(*multiAccount); err != nil { 542 return nil, err 543 } 544 } 545 return multiAccount, nil 546 } 547 548 func (b *GethStatusBackend) overrideNetworks(conf *params.NodeConfig, request *requests.Login) { 549 conf.Networks = setRPCs(defaultNetworks(request.WalletSecretsConfig.StatusProxyStageName), &request.WalletSecretsConfig) 550 } 551 552 func (b *GethStatusBackend) LoginAccount(request *requests.Login) error { 553 err := b.loginAccount(request) 554 if err != nil { 555 // Stop node for clean up 556 _ = b.StopNode() 557 } 558 if b.LocalPairingStateManager.IsPairing() { 559 return nil 560 } 561 return b.LoggedIn(request.KeyUID, err) 562 } 563 564 func (b *GethStatusBackend) loginAccount(request *requests.Login) error { 565 if err := request.Validate(); err != nil { 566 return err 567 } 568 569 if request.Mnemonic != "" { 570 info, err := b.generateAccountInfo(request.Mnemonic) 571 if err != nil { 572 return errors.Wrap(err, "failed to generate account info") 573 } 574 575 derivedAddresses, err := b.getDerivedAddresses(info.ID) 576 if err != nil { 577 return errors.Wrap(err, "failed to get derived addresses") 578 } 579 580 request.Password = derivedAddresses[pathEncryption].PublicKey 581 request.KeycardWhisperPrivateKey = derivedAddresses[pathDefaultChat].PrivateKey 582 } 583 584 acc := multiaccounts.Account{ 585 KeyUID: request.KeyUID, 586 KDFIterations: request.KdfIterations, 587 } 588 589 if acc.KDFIterations == 0 { 590 acc.KDFIterations = dbsetup.ReducedKDFIterationsNumber 591 } 592 593 err := b.ensureDBsOpened(acc, request.Password) 594 if err != nil { 595 return errors.Wrap(err, "failed to open database") 596 } 597 598 defaultCfg := ¶ms.NodeConfig{ 599 // why we need this? relate PR: https://github.com/status-im/status-go/pull/4014 600 KeycardPairingDataFile: DefaultKeycardPairingDataFile, 601 } 602 603 defaultCfg.WalletConfig = buildWalletConfig(&request.WalletSecretsConfig, request.StatusProxyEnabled) 604 605 err = b.UpdateNodeConfigFleet(acc, request.Password, defaultCfg) 606 if err != nil { 607 return errors.Wrap(err, "failed to update node config fleet") 608 } 609 610 err = b.loadNodeConfig(defaultCfg) 611 if err != nil { 612 return errors.Wrap(err, "failed to load node config") 613 } 614 615 if request.RuntimeLogLevel != "" { 616 b.config.LogLevel = request.RuntimeLogLevel 617 } 618 619 if b.config.WakuV2Config.Enabled && request.WakuV2Nameserver != "" { 620 b.config.WakuV2Config.Nameserver = request.WakuV2Nameserver 621 } 622 623 b.config.ShhextConfig.BandwidthStatsEnabled = request.BandwidthStatsEnabled 624 625 b.overrideNetworks(b.config, request) 626 627 if request.APIConfig != nil { 628 overrideApiConfig(b.config, request.APIConfig) 629 } 630 631 err = b.setupLogSettings() 632 if err != nil { 633 return errors.Wrap(err, "failed to setup log settings") 634 } 635 636 accountsDB, err := accounts.NewDB(b.appDB) 637 if err != nil { 638 return errors.Wrap(err, "failed to create accounts db") 639 } 640 641 multiAccount, err := b.updateAccountColorHashAndColorID(acc.KeyUID, accountsDB) 642 if err != nil { 643 return errors.Wrap(err, "failed to update account color hash and color id") 644 } 645 b.account = multiAccount 646 647 chatAddr, err := accountsDB.GetChatAddress() 648 if err != nil { 649 return errors.Wrap(err, "failed to get chat address") 650 } 651 walletAddr, err := accountsDB.GetWalletAddress() 652 if err != nil { 653 return errors.Wrap(err, "failed to get wallet address") 654 } 655 watchAddrs, err := accountsDB.GetWalletAddresses() 656 if err != nil { 657 return errors.Wrap(err, "failed to get wallet addresses") 658 } 659 login := account.LoginParams{ 660 Password: request.Password, 661 ChatAddress: chatAddr, 662 WatchAddresses: watchAddrs, 663 MainAccount: walletAddr, 664 } 665 666 err = b.StartNode(b.config) 667 if err != nil { 668 b.log.Info("failed to start node") 669 return errors.Wrap(err, "failed to start node") 670 } 671 672 if chatKey := request.ChatPrivateKey(); chatKey == nil { 673 err = b.SelectAccount(login) 674 if err != nil { 675 return errors.Wrap(err, "failed to select account") 676 } 677 } else { 678 // In case of keycard, we don't have a keystore, instead we have private key loaded from the keycard 679 if err := b.accountManager.SetChatAccount(chatKey); err != nil { 680 return errors.Wrap(err, "failed to set chat account") 681 } 682 _, err = b.accountManager.SelectedChatAccount() 683 if err != nil { 684 return errors.Wrap(err, "failed to get selected chat account") 685 } 686 687 b.accountManager.SetAccountAddresses(walletAddr, watchAddrs...) 688 err = b.injectAccountsIntoServices() 689 if err != nil { 690 return errors.Wrap(err, "failed to inject accounts into services") 691 } 692 } 693 694 err = b.multiaccountsDB.UpdateAccountTimestamp(acc.KeyUID, time.Now().Unix()) 695 if err != nil { 696 b.log.Error("failed to update account") 697 return errors.Wrap(err, "failed to update account") 698 } 699 700 return nil 701 } 702 703 // UpdateNodeConfigFleet loads the fleet from the settings and updates the node configuration 704 // If the fleet in settings is empty, or not supported anymore, it will be overridden with the default fleet. 705 // In that case settings fleet value remain the same, only runtime node configuration is updated. 706 func (b *GethStatusBackend) UpdateNodeConfigFleet(acc multiaccounts.Account, password string, config *params.NodeConfig) error { 707 if config == nil { 708 return nil 709 } 710 711 err := b.ensureDBsOpened(acc, password) 712 if err != nil { 713 return err 714 } 715 716 accountSettings, err := b.GetSettings() 717 if err != nil { 718 return err 719 } 720 721 fleet := accountSettings.GetFleet() 722 723 if !params.IsFleetSupported(fleet) { 724 b.log.Warn("fleet is not supported, overriding with default value", 725 "fleet", fleet, 726 "defaultFleet", DefaultFleet) 727 fleet = DefaultFleet 728 } 729 730 err = SetFleet(fleet, config) 731 if err != nil { 732 return err 733 } 734 735 return nil 736 } 737 738 // Deprecated: Use loginAccount instead 739 func (b *GethStatusBackend) startNodeWithAccount(acc multiaccounts.Account, password string, inputNodeCfg *params.NodeConfig, chatKey *ecdsa.PrivateKey) error { 740 err := b.ensureDBsOpened(acc, password) 741 if err != nil { 742 return err 743 } 744 745 err = b.loadNodeConfig(inputNodeCfg) 746 if err != nil { 747 return err 748 } 749 750 err = b.setupLogSettings() 751 if err != nil { 752 return err 753 } 754 755 accountsDB, err := accounts.NewDB(b.appDB) 756 if err != nil { 757 return err 758 } 759 760 if acc.ColorHash == nil { 761 multiAccount, err := b.updateAccountColorHashAndColorID(acc.KeyUID, accountsDB) 762 if err != nil { 763 return err 764 } 765 acc = *multiAccount 766 } 767 768 b.account = &acc 769 770 chatAddr, err := accountsDB.GetChatAddress() 771 if err != nil { 772 return err 773 } 774 walletAddr, err := accountsDB.GetWalletAddress() 775 if err != nil { 776 return err 777 } 778 watchAddrs, err := accountsDB.GetWalletAddresses() 779 if err != nil { 780 return err 781 } 782 login := account.LoginParams{ 783 Password: password, 784 ChatAddress: chatAddr, 785 WatchAddresses: watchAddrs, 786 MainAccount: walletAddr, 787 } 788 789 err = b.StartNode(b.config) 790 if err != nil { 791 b.log.Info("failed to start node") 792 return err 793 } 794 795 if chatKey == nil { 796 // Load account from keystore 797 err = b.SelectAccount(login) 798 if err != nil { 799 return err 800 } 801 } else { 802 // In case of keycard, we don't have keystore, but we directly have the private key 803 if err := b.accountManager.SetChatAccount(chatKey); err != nil { 804 return err 805 } 806 _, err = b.accountManager.SelectedChatAccount() 807 if err != nil { 808 return err 809 } 810 811 b.accountManager.SetAccountAddresses(walletAddr, watchAddrs...) 812 err = b.injectAccountsIntoServices() 813 if err != nil { 814 return err 815 } 816 } 817 818 err = b.multiaccountsDB.UpdateAccountTimestamp(acc.KeyUID, time.Now().Unix()) 819 if err != nil { 820 b.log.Info("failed to update account") 821 return err 822 } 823 824 return nil 825 } 826 827 func (b *GethStatusBackend) accountsDB() (*accounts.Database, error) { 828 return accounts.NewDB(b.appDB) 829 } 830 831 func (b *GethStatusBackend) GetSettings() (*settings.Settings, error) { 832 accountsDB, err := b.accountsDB() 833 if err != nil { 834 return nil, err 835 } 836 837 settings, err := accountsDB.GetSettings() 838 if err != nil { 839 return nil, err 840 } 841 842 return &settings, nil 843 } 844 845 func (b *GethStatusBackend) GetEnsUsernames() ([]*ens.UsernameDetail, error) { 846 db := ens.NewEnsDatabase(b.appDB) 847 removed := false 848 return db.GetEnsUsernames(&removed) 849 } 850 851 func (b *GethStatusBackend) MigrateKeyStoreDir(acc multiaccounts.Account, password, oldDir, newDir string) error { 852 err := b.ensureDBsOpened(acc, password) 853 if err != nil { 854 return err 855 } 856 857 accountDB, err := accounts.NewDB(b.appDB) 858 if err != nil { 859 return err 860 } 861 accounts, err := accountDB.GetActiveAccounts() 862 if err != nil { 863 return err 864 } 865 settings, err := accountDB.GetSettings() 866 if err != nil { 867 return err 868 } 869 addresses := []string{settings.EIP1581Address.Hex(), settings.WalletRootAddress.Hex()} 870 for _, account := range accounts { 871 addresses = append(addresses, account.Address.Hex()) 872 } 873 err = b.accountManager.MigrateKeyStoreDir(oldDir, newDir, addresses) 874 if err != nil { 875 return err 876 } 877 878 return nil 879 } 880 881 func (b *GethStatusBackend) Login(keyUID, password string) error { 882 return b.startNodeWithAccount(multiaccounts.Account{KeyUID: keyUID}, password, nil, nil) 883 } 884 885 func (b *GethStatusBackend) StartNodeWithAccount(acc multiaccounts.Account, password string, nodecfg *params.NodeConfig, chatKey *ecdsa.PrivateKey) error { 886 err := b.startNodeWithAccount(acc, password, nodecfg, chatKey) 887 if err != nil { 888 // Stop node for clean up 889 _ = b.StopNode() 890 } 891 // get logged in 892 if !b.LocalPairingStateManager.IsPairing() { 893 return b.LoggedIn(acc.KeyUID, err) 894 } 895 return err 896 } 897 898 func (b *GethStatusBackend) LoggedIn(keyUID string, err error) error { 899 if err != nil { 900 signal.SendLoggedIn(nil, nil, nil, err) 901 return err 902 } 903 settings, err := b.GetSettings() 904 if err != nil { 905 return err 906 } 907 account, err := b.getAccountByKeyUID(keyUID) 908 if err != nil { 909 return err 910 } 911 912 ensUsernames, err := b.GetEnsUsernames() 913 if err != nil { 914 return err 915 } 916 var ensUsernamesJSON json.RawMessage 917 if ensUsernames != nil { 918 ensUsernamesJSON, err = json.Marshal(ensUsernames) 919 if err != nil { 920 return err 921 } 922 } 923 signal.SendLoggedIn(account, settings, ensUsernamesJSON, nil) 924 return nil 925 } 926 927 func (b *GethStatusBackend) ExportUnencryptedDatabase(acc multiaccounts.Account, password, directory string) error { 928 b.mu.Lock() 929 defer b.mu.Unlock() 930 if b.appDB != nil { 931 return nil 932 } 933 if len(b.rootDataDir) == 0 { 934 return errors.New("root datadir wasn't provided") 935 } 936 937 dbPath, err := b.runDBFileMigrations(acc, password) 938 if err != nil { 939 return err 940 } 941 942 err = sqlite.DecryptDB(dbPath, directory, password, acc.KDFIterations) 943 if err != nil { 944 b.log.Error("failed to initialize db", "err", err) 945 return err 946 } 947 return nil 948 } 949 950 func (b *GethStatusBackend) ImportUnencryptedDatabase(acc multiaccounts.Account, password, databasePath string) error { 951 b.mu.Lock() 952 defer b.mu.Unlock() 953 if b.appDB != nil { 954 return nil 955 } 956 957 path, err := b.getAppDBPath(acc.KeyUID) 958 if err != nil { 959 return err 960 } 961 962 err = sqlite.EncryptDB(databasePath, path, password, acc.KDFIterations, signal.SendReEncryptionStarted, signal.SendReEncryptionFinished) 963 if err != nil { 964 b.log.Error("failed to initialize db", "err", err) 965 return err 966 } 967 return nil 968 } 969 970 func (b *GethStatusBackend) reEncryptKeyStoreDir(currentPassword string, newPassword string) error { 971 config := b.StatusNode().Config() 972 keyDir := "" 973 if config == nil { 974 keyDir = b.accountManager.Keydir 975 } else { 976 keyDir = config.KeyStoreDir 977 } 978 979 if keyDir != "" { 980 err := b.accountManager.ReEncryptKeyStoreDir(keyDir, currentPassword, newPassword) 981 if err != nil { 982 return fmt.Errorf("ReEncryptKeyStoreDir error: %v", err) 983 } 984 } 985 return nil 986 } 987 988 func (b *GethStatusBackend) ChangeDatabasePassword(keyUID string, password string, newPassword string) error { 989 account, err := b.multiaccountsDB.GetAccount(keyUID) 990 if err != nil { 991 return err 992 } 993 994 internalDbPath, err := dbsetup.GetDBFilename(b.appDB) 995 if err != nil { 996 return fmt.Errorf("failed to get database file name, %w", err) 997 } 998 999 appDBPath, err := b.getAppDBPath(keyUID) 1000 if err != nil { 1001 return err 1002 } 1003 1004 isCurrentAccount := appDBPath == internalDbPath 1005 1006 restartNode := func() { 1007 if isCurrentAccount { 1008 if err != nil { 1009 // TODO https://github.com/status-im/status-go/issues/3906 1010 // Fix restarting node, as it always fails but the error is ignored 1011 // because UI calls Logout and Quit afterwards. It should not be UI-dependent 1012 // and should be handled gracefully here if it makes sense to run dummy node after 1013 // logout 1014 _ = b.startNodeWithAccount(*account, password, nil, nil) 1015 } else { 1016 _ = b.startNodeWithAccount(*account, newPassword, nil, nil) 1017 } 1018 } 1019 } 1020 defer restartNode() 1021 1022 logout := func() { 1023 if isCurrentAccount { 1024 _ = b.Logout() 1025 } 1026 } 1027 noLogout := func() {} 1028 1029 // First change app DB password, because it also reencrypts the keystore, 1030 // otherwise if we call changeWalletDbPassword first and logout, we will fail 1031 // to reencrypt the keystore 1032 err = b.changeAppDBPassword(account, logout, password, newPassword) 1033 if err != nil { 1034 return err 1035 } 1036 1037 // Already logged out but pass a param to decouple the logic for testing 1038 err = b.changeWalletDBPassword(account, noLogout, password, newPassword) 1039 if err != nil { 1040 // Revert the password to original 1041 err2 := b.changeAppDBPassword(account, noLogout, newPassword, password) 1042 if err2 != nil { 1043 log.Error("failed to revert app db password", "err", err2) 1044 } 1045 1046 return err 1047 } 1048 1049 return nil 1050 } 1051 1052 func (b *GethStatusBackend) changeAppDBPassword(account *multiaccounts.Account, logout func(), password string, newPassword string) error { 1053 tmpDbPath, cleanup, err := b.createTempDBFile("v4.db") 1054 if err != nil { 1055 return err 1056 } 1057 defer cleanup() 1058 1059 dbPath, err := b.getAppDBPath(account.KeyUID) 1060 if err != nil { 1061 return err 1062 } 1063 1064 // Exporting database to a temporary file with a new password 1065 err = sqlite.ExportDB(dbPath, password, account.KDFIterations, tmpDbPath, newPassword, signal.SendReEncryptionStarted, signal.SendReEncryptionFinished) 1066 if err != nil { 1067 return err 1068 } 1069 1070 err = b.reEncryptKeyStoreDir(password, newPassword) 1071 if err != nil { 1072 return err 1073 } 1074 1075 // Replacing the old database with the new one requires closing all connections to the database 1076 // This is done by stopping the node and restarting it with the new DB 1077 logout() 1078 1079 // Replacing the old database files with the new ones, ignoring the wal and shm errors 1080 replaceCleanup, err := replaceDBFile(dbPath, tmpDbPath) 1081 if replaceCleanup != nil { 1082 defer replaceCleanup() 1083 } 1084 1085 if err != nil { 1086 // Restore the old account 1087 _ = b.reEncryptKeyStoreDir(newPassword, password) 1088 return err 1089 } 1090 1091 return nil 1092 } 1093 1094 func (b *GethStatusBackend) changeWalletDBPassword(account *multiaccounts.Account, logout func(), password string, newPassword string) error { 1095 tmpDbPath, cleanup, err := b.createTempDBFile("wallet.db") 1096 if err != nil { 1097 return err 1098 } 1099 defer cleanup() 1100 1101 dbPath, err := b.getWalletDBPath(account.KeyUID) 1102 if err != nil { 1103 return err 1104 } 1105 1106 // Exporting database to a temporary file with a new password 1107 err = sqlite.ExportDB(dbPath, password, account.KDFIterations, tmpDbPath, newPassword, signal.SendReEncryptionStarted, signal.SendReEncryptionFinished) 1108 if err != nil { 1109 return err 1110 } 1111 1112 // Replacing the old database with the new one requires closing all connections to the database 1113 // This is done by stopping the node and restarting it with the new DB 1114 logout() 1115 1116 // Replacing the old database files with the new ones, ignoring the wal and shm errors 1117 replaceCleanup, err := replaceDBFile(dbPath, tmpDbPath) 1118 if replaceCleanup != nil { 1119 defer replaceCleanup() 1120 } 1121 return err 1122 } 1123 1124 func (b *GethStatusBackend) createTempDBFile(pattern string) (tmpDbPath string, cleanup func(), err error) { 1125 if len(b.rootDataDir) == 0 { 1126 err = errors.New("root datadir wasn't provided") 1127 return 1128 } 1129 rootDataDir := b.rootDataDir 1130 //On iOS, the rootDataDir value does not contain a trailing slash. 1131 //This is causing an incorrectly formatted temporary file path to be generated, leading to an "operation not permitted" error. 1132 //e.g. value of rootDataDir is `/var/mobile/.../12906D5A-E831-49E9-BBE7-5FFE8E805D8A/Library`, 1133 //the file path generated is something like `/var/mobile/.../12906D5A-E831-49E9-BBE7-5FFE8E805D8A/123-v4.db` 1134 //which removed `Library` from the path. 1135 if !strings.HasSuffix(rootDataDir, "/") { 1136 rootDataDir += "/" 1137 } 1138 file, err := os.CreateTemp(filepath.Dir(rootDataDir), "*-"+pattern) 1139 if err != nil { 1140 return 1141 } 1142 err = file.Close() 1143 if err != nil { 1144 return 1145 } 1146 1147 tmpDbPath = file.Name() 1148 cleanup = func() { 1149 filePath := file.Name() 1150 _ = os.Remove(filePath) 1151 _ = os.Remove(filePath + "-wal") 1152 _ = os.Remove(filePath + "-shm") 1153 _ = os.Remove(filePath + "-journal") 1154 } 1155 return 1156 } 1157 1158 func replaceDBFile(dbPath string, newDBPath string) (cleanup func(), err error) { 1159 err = os.Rename(newDBPath, dbPath) 1160 if err != nil { 1161 return 1162 } 1163 1164 cleanup = func() { 1165 _ = os.Remove(dbPath + "-wal") 1166 _ = os.Remove(dbPath + "-shm") 1167 _ = os.Rename(newDBPath+"-wal", dbPath+"-wal") 1168 _ = os.Rename(newDBPath+"-shm", dbPath+"-shm") 1169 } 1170 1171 return 1172 } 1173 1174 func (b *GethStatusBackend) ConvertToKeycardAccount(account multiaccounts.Account, s settings.Settings, keycardUID string, password string, newPassword string) error { 1175 messenger := b.Messenger() 1176 if messenger == nil { 1177 return errors.New("cannot resolve messenger instance") 1178 } 1179 1180 err := b.multiaccountsDB.UpdateAccountKeycardPairing(account.KeyUID, account.KeycardPairing) 1181 if err != nil { 1182 return err 1183 } 1184 1185 err = b.ensureDBsOpened(account, password) 1186 if err != nil { 1187 return err 1188 } 1189 1190 accountDB, err := accounts.NewDB(b.appDB) 1191 if err != nil { 1192 return err 1193 } 1194 1195 keypair, err := accountDB.GetKeypairByKeyUID(account.KeyUID) 1196 if err != nil { 1197 if err == accounts.ErrDbKeypairNotFound { 1198 return errors.New("cannot convert an unknown keypair") 1199 } 1200 return err 1201 } 1202 1203 err = accountDB.SaveSettingField(settings.KeycardInstanceUID, s.KeycardInstanceUID) 1204 if err != nil { 1205 return err 1206 } 1207 1208 err = accountDB.SaveSettingField(settings.KeycardPairedOn, s.KeycardPairedOn) 1209 if err != nil { 1210 return err 1211 } 1212 1213 err = accountDB.SaveSettingField(settings.KeycardPairing, s.KeycardPairing) 1214 if err != nil { 1215 return err 1216 } 1217 1218 err = accountDB.SaveSettingField(settings.Mnemonic, nil) 1219 if err != nil { 1220 return err 1221 } 1222 1223 err = accountDB.SaveSettingField(settings.ProfileMigrationNeeded, false) 1224 if err != nil { 1225 return err 1226 } 1227 1228 // This check is added due to mobile app cause it doesn't support a Keycard features as desktop app. 1229 // We should remove the following line once mobile and desktop app align. 1230 if len(keycardUID) > 0 { 1231 displayName, err := accountDB.DisplayName() 1232 if err != nil { 1233 return err 1234 } 1235 1236 position, err := accountDB.GetPositionForNextNewKeycard() 1237 if err != nil { 1238 return err 1239 } 1240 1241 kc := accounts.Keycard{ 1242 KeycardUID: keycardUID, 1243 KeycardName: displayName, 1244 KeycardLocked: false, 1245 KeyUID: account.KeyUID, 1246 Position: position, 1247 } 1248 1249 for _, acc := range keypair.Accounts { 1250 kc.AccountsAddresses = append(kc.AccountsAddresses, acc.Address) 1251 } 1252 err = messenger.SaveOrUpdateKeycard(context.Background(), &kc) 1253 if err != nil { 1254 return err 1255 } 1256 } 1257 1258 masterAddress, err := accountDB.GetMasterAddress() 1259 if err != nil { 1260 return err 1261 } 1262 1263 eip1581Address, err := accountDB.GetEIP1581Address() 1264 if err != nil { 1265 return err 1266 } 1267 1268 walletRootAddress, err := accountDB.GetWalletRootAddress() 1269 if err != nil { 1270 return err 1271 } 1272 1273 err = b.closeDBs() 1274 if err != nil { 1275 return err 1276 } 1277 1278 err = b.ChangeDatabasePassword(account.KeyUID, password, newPassword) 1279 if err != nil { 1280 return err 1281 } 1282 1283 // We need to delete all accounts for the Keycard which is being added 1284 for _, acc := range keypair.Accounts { 1285 err = b.accountManager.DeleteAccount(acc.Address) 1286 if err != nil { 1287 return err 1288 } 1289 } 1290 1291 err = b.accountManager.DeleteAccount(masterAddress) 1292 if err != nil { 1293 return err 1294 } 1295 1296 err = b.accountManager.DeleteAccount(eip1581Address) 1297 if err != nil { 1298 return err 1299 } 1300 1301 err = b.accountManager.DeleteAccount(walletRootAddress) 1302 if err != nil { 1303 return err 1304 } 1305 1306 return nil 1307 } 1308 1309 func (b *GethStatusBackend) RestoreAccountAndLogin(request *requests.RestoreAccount) (*multiaccounts.Account, error) { 1310 1311 if err := request.Validate(); err != nil { 1312 return nil, err 1313 } 1314 1315 response, err := b.generateOrImportAccount(request.Mnemonic, 0, request.FetchBackup, &request.CreateAccount) 1316 if err != nil { 1317 return nil, err 1318 } 1319 1320 err = b.StartNodeWithAccountAndInitialConfig( 1321 *response.account, 1322 request.Password, 1323 *response.settings, 1324 response.nodeConfig, 1325 response.subAccounts, 1326 response.chatPrivateKey, 1327 ) 1328 1329 if err != nil { 1330 b.log.Error("start node", err) 1331 return nil, err 1332 } 1333 1334 return response.account, nil 1335 } 1336 1337 func (b *GethStatusBackend) RestoreKeycardAccountAndLogin(request *requests.RestoreAccount) (*multiaccounts.Account, error) { 1338 if err := request.Validate(); err != nil { 1339 return nil, err 1340 } 1341 1342 keyStoreDir, err := b.InitKeyStoreDirWithAccount(request.RootDataDir, request.Keycard.KeyUID) 1343 if err != nil { 1344 return nil, err 1345 } 1346 1347 derivedAddresses := map[string]generator.AccountInfo{ 1348 pathDefaultChat: { 1349 Address: request.Keycard.WhisperAddress, 1350 PublicKey: request.Keycard.WhisperPublicKey, 1351 PrivateKey: request.Keycard.WhisperPrivateKey, 1352 }, 1353 pathWalletRoot: { 1354 Address: request.Keycard.WalletRootAddress, 1355 }, 1356 pathDefaultWallet: { 1357 Address: request.Keycard.WalletAddress, 1358 PublicKey: request.Keycard.WalletPublicKey, 1359 }, 1360 pathEIP1581: { 1361 Address: request.Keycard.Eip1581Address, 1362 }, 1363 pathEncryption: { 1364 PublicKey: request.Keycard.EncryptionPublicKey, 1365 }, 1366 } 1367 1368 input := &prepareAccountInput{ 1369 customizationColorClock: 0, 1370 accountID: "", // empty for keycard 1371 keyUID: request.Keycard.KeyUID, 1372 address: request.Keycard.Address, 1373 mnemonic: "", 1374 restoringAccount: true, 1375 derivedAddresses: derivedAddresses, 1376 fetchBackup: request.FetchBackup, // WARNING: Ensure this value is correct 1377 keyStoreDir: keyStoreDir, 1378 } 1379 1380 response, err := b.prepareNodeAccount(&request.CreateAccount, input) 1381 if err != nil { 1382 return nil, err 1383 } 1384 1385 err = b.StartNodeWithAccountAndInitialConfig( 1386 *response.account, 1387 request.Password, 1388 *response.settings, 1389 response.nodeConfig, 1390 response.subAccounts, 1391 response.chatPrivateKey, //request.WhisperPrivateKey, 1392 ) 1393 1394 if err != nil { 1395 b.log.Error("start node", err) 1396 return nil, errors.Wrap(err, "failed to start node") 1397 } 1398 1399 return response.account, nil 1400 } 1401 1402 func (b *GethStatusBackend) GetKeyUIDByMnemonic(mnemonic string) (string, error) { 1403 accountGenerator := b.accountManager.AccountsGenerator() 1404 1405 info, err := accountGenerator.ImportMnemonic(mnemonic, "") 1406 if err != nil { 1407 return "", err 1408 } 1409 1410 return info.KeyUID, nil 1411 } 1412 1413 type prepareAccountInput struct { 1414 customizationColorClock uint64 1415 accountID string 1416 keyUID string 1417 address string 1418 mnemonic string 1419 restoringAccount bool 1420 derivedAddresses map[string]generator.AccountInfo 1421 fetchBackup bool 1422 keyStoreDir string 1423 opts []params.Option 1424 } 1425 1426 type accountBundle struct { 1427 account *multiaccounts.Account 1428 settings *settings.Settings 1429 nodeConfig *params.NodeConfig 1430 subAccounts []*accounts.Account 1431 chatPrivateKey *ecdsa.PrivateKey 1432 } 1433 1434 func (b *GethStatusBackend) generateOrImportAccount(mnemonic string, customizationColorClock uint64, fetchBackup bool, request *requests.CreateAccount, opts ...params.Option) (*accountBundle, error) { 1435 info, err := b.generateAccountInfo(mnemonic) 1436 if err != nil { 1437 return nil, err 1438 } 1439 1440 keyStoreDir, err := b.InitKeyStoreDirWithAccount(request.RootDataDir, info.KeyUID) 1441 if err != nil { 1442 return nil, err 1443 } 1444 1445 derivedAddresses, err := b.getDerivedAddresses(info.ID) 1446 if err != nil { 1447 return nil, err 1448 } 1449 1450 input := &prepareAccountInput{ 1451 customizationColorClock: customizationColorClock, 1452 accountID: info.ID, 1453 keyUID: info.KeyUID, 1454 address: info.Address, 1455 mnemonic: info.Mnemonic, 1456 restoringAccount: mnemonic != "", 1457 derivedAddresses: derivedAddresses, 1458 fetchBackup: fetchBackup, 1459 keyStoreDir: keyStoreDir, 1460 opts: opts, 1461 } 1462 1463 return b.prepareNodeAccount(request, input) 1464 } 1465 1466 func (b *GethStatusBackend) prepareNodeAccount(request *requests.CreateAccount, input *prepareAccountInput) (*accountBundle, error) { 1467 var err error 1468 response := &accountBundle{} 1469 1470 if request.KeycardInstanceUID != "" { 1471 request.Password = input.derivedAddresses[pathEncryption].PublicKey 1472 } 1473 1474 // NOTE: I intentionally left this condition separately and not an `else` branch. Technically it's an `else`, 1475 // but the statements inside are not the opposite statement of the first statement. It's just kinda like this: 1476 // - replace password when we're using keycard 1477 // - store account when we're not using keycard 1478 if request.KeycardInstanceUID == "" { 1479 err = b.storeAccount(input.accountID, request.Password, paths) 1480 if err != nil { 1481 return nil, err 1482 } 1483 } 1484 1485 response.account, err = b.buildAccount(request, input) 1486 if err != nil { 1487 return nil, errors.Wrap(err, "failed to build account") 1488 } 1489 1490 response.settings, err = b.prepareSettings(request, input) 1491 if err != nil { 1492 return nil, errors.Wrap(err, "failed to prepare settings") 1493 } 1494 1495 response.nodeConfig, err = b.prepareConfig(request, input, response.settings.InstallationID) 1496 if err != nil { 1497 return nil, errors.Wrap(err, "failed to prepare node config") 1498 } 1499 1500 response.subAccounts, err = b.prepareSubAccounts(request, input) 1501 if err != nil { 1502 return nil, errors.Wrap(err, "failed to prepare sub accounts") 1503 } 1504 1505 response, err = b.prepareForKeycard(request, input, response) 1506 if err != nil { 1507 return nil, errors.Wrap(err, "failed to prepare for keycard") 1508 } 1509 1510 return response, nil 1511 } 1512 1513 func (b *GethStatusBackend) InitKeyStoreDirWithAccount(rootDataDir, keyUID string) (string, error) { 1514 b.UpdateRootDataDir(rootDataDir) 1515 keyStoreRelativePath, keystoreAbsolutePath := DefaultKeystorePath(rootDataDir, keyUID) 1516 // Initialize keystore dir with account 1517 return keyStoreRelativePath, b.accountManager.InitKeystore(keystoreAbsolutePath) 1518 } 1519 1520 func (b *GethStatusBackend) generateAccountInfo(mnemonic string) (*generator.GeneratedAccountInfo, error) { 1521 accountGenerator := b.accountManager.AccountsGenerator() 1522 1523 var info generator.GeneratedAccountInfo 1524 var err error 1525 if mnemonic == "" { 1526 // generate 1(n) account with default mnemonic length and no passphrase 1527 generatedAccountInfos, err := accountGenerator.Generate(defaultMnemonicLength, 1, "") 1528 info = generatedAccountInfos[0] 1529 1530 if err != nil { 1531 return nil, err 1532 } 1533 } else { 1534 1535 info, err = accountGenerator.ImportMnemonic(mnemonic, "") 1536 if err != nil { 1537 return nil, err 1538 } 1539 } 1540 1541 return &info, nil 1542 } 1543 1544 func (b *GethStatusBackend) storeAccount(id string, password string, paths []string) error { 1545 accountGenerator := b.accountManager.AccountsGenerator() 1546 1547 _, err := accountGenerator.StoreAccount(id, password) 1548 if err != nil { 1549 return err 1550 } 1551 1552 _, err = accountGenerator.StoreDerivedAccounts(id, password, paths) 1553 if err != nil { 1554 return err 1555 } 1556 1557 return nil 1558 } 1559 1560 func (b *GethStatusBackend) buildAccount(request *requests.CreateAccount, input *prepareAccountInput) (*multiaccounts.Account, error) { 1561 err := b.OpenAccounts() 1562 if err != nil { 1563 return nil, err 1564 } 1565 1566 acc := &multiaccounts.Account{ 1567 KeyUID: input.keyUID, 1568 Name: request.DisplayName, 1569 CustomizationColor: multiacccommon.CustomizationColor(request.CustomizationColor), 1570 CustomizationColorClock: input.customizationColorClock, 1571 KDFIterations: request.KdfIterations, 1572 Timestamp: time.Now().Unix(), 1573 } 1574 1575 if acc.KDFIterations == 0 { 1576 acc.KDFIterations = dbsetup.ReducedKDFIterationsNumber 1577 } 1578 1579 if request.ImagePath != "" { 1580 imageCropRectangle := request.ImageCropRectangle 1581 if imageCropRectangle == nil { 1582 // Default crop rectangle used by mobile 1583 imageCropRectangle = &requests.ImageCropRectangle{ 1584 Ax: 0, 1585 Ay: 0, 1586 Bx: 1000, 1587 By: 1000, 1588 } 1589 } 1590 1591 iis, err := images.GenerateIdentityImages(request.ImagePath, 1592 imageCropRectangle.Ax, imageCropRectangle.Ay, imageCropRectangle.Bx, imageCropRectangle.By) 1593 1594 if err != nil { 1595 return nil, err 1596 } 1597 acc.Images = iis 1598 } 1599 1600 return acc, nil 1601 } 1602 1603 func (b *GethStatusBackend) prepareSettings(request *requests.CreateAccount, input *prepareAccountInput) (*settings.Settings, error) { 1604 settings, err := defaultSettings(input.keyUID, input.address, input.derivedAddresses) 1605 if err != nil { 1606 return nil, err 1607 } 1608 1609 settings.DeviceName = request.DeviceName 1610 settings.DisplayName = request.DisplayName 1611 settings.PreviewPrivacy = request.PreviewPrivacy 1612 settings.CurrentNetwork = request.CurrentNetwork 1613 settings.TestNetworksEnabled = request.TestNetworksEnabled 1614 if !input.restoringAccount { 1615 settings.Mnemonic = &input.mnemonic 1616 // TODO(rasom): uncomment it as soon as address will be properly 1617 // marked as shown on mobile client 1618 //settings.MnemonicWasNotShown = true 1619 } 1620 1621 if request.WakuV2Fleet != "" { 1622 settings.Fleet = &request.WakuV2Fleet 1623 } 1624 1625 return settings, nil 1626 } 1627 1628 func (b *GethStatusBackend) prepareConfig(request *requests.CreateAccount, input *prepareAccountInput, installationID string) (*params.NodeConfig, error) { 1629 nodeConfig, err := DefaultNodeConfig(installationID, request, input.opts...) 1630 if err != nil { 1631 return nil, err 1632 } 1633 nodeConfig.ProcessBackedupMessages = input.fetchBackup 1634 1635 // when we set nodeConfig.KeyStoreDir, value of nodeConfig.KeyStoreDir should not contain the rootDataDir 1636 // loadNodeConfig will add rootDataDir to nodeConfig.KeyStoreDir 1637 nodeConfig.KeyStoreDir = input.keyStoreDir 1638 1639 return nodeConfig, nil 1640 } 1641 1642 func (b *GethStatusBackend) prepareSubAccounts(request *requests.CreateAccount, input *prepareAccountInput) ([]*accounts.Account, error) { 1643 emoji, err := randomWalletEmoji() 1644 if err != nil { 1645 return nil, errors.Wrap(err, "failed to generate random emoji") 1646 } 1647 1648 walletDerivedAccount := input.derivedAddresses[pathDefaultWallet] 1649 walletAccount := &accounts.Account{ 1650 PublicKey: types.Hex2Bytes(walletDerivedAccount.PublicKey), 1651 KeyUID: input.keyUID, 1652 Address: types.HexToAddress(walletDerivedAccount.Address), 1653 ColorID: multiacccommon.CustomizationColor(request.CustomizationColor), 1654 Emoji: emoji, 1655 Wallet: true, 1656 Path: pathDefaultWallet, 1657 Name: walletAccountDefaultName, 1658 AddressWasNotShown: !input.restoringAccount, 1659 } 1660 1661 chatDerivedAccount := input.derivedAddresses[pathDefaultChat] 1662 chatAccount := &accounts.Account{ 1663 PublicKey: types.Hex2Bytes(chatDerivedAccount.PublicKey), 1664 KeyUID: input.keyUID, 1665 Address: types.HexToAddress(chatDerivedAccount.Address), 1666 Name: request.DisplayName, 1667 Chat: true, 1668 Path: pathDefaultChat, 1669 } 1670 1671 return []*accounts.Account{walletAccount, chatAccount}, nil 1672 } 1673 1674 func (b *GethStatusBackend) prepareForKeycard(request *requests.CreateAccount, input *prepareAccountInput, response *accountBundle) (*accountBundle, error) { 1675 if request.KeycardInstanceUID == "" { 1676 return response, nil 1677 } 1678 1679 kp := wallet.NewKeycardPairings() 1680 kp.SetKeycardPairingsFile(response.nodeConfig.KeycardPairingDataFile) 1681 pairings, err := kp.GetPairings() 1682 if err != nil { 1683 return nil, errors.Wrap(err, "failed to get keycard pairings") 1684 } 1685 1686 keycard, ok := pairings[request.KeycardInstanceUID] 1687 if !ok { 1688 return nil, errors.New("keycard not found in pairings file") 1689 } 1690 1691 response.settings.KeycardInstanceUID = request.KeycardInstanceUID 1692 response.settings.KeycardPairedOn = time.Now().Unix() 1693 response.settings.KeycardPairing = keycard.Key 1694 response.account.KeycardPairing = keycard.Key 1695 1696 privateKeyHex := strings.TrimPrefix(input.derivedAddresses[pathDefaultChat].PrivateKey, "0x") 1697 response.chatPrivateKey, err = crypto.HexToECDSA(privateKeyHex) 1698 if err != nil { 1699 return nil, errors.Wrap(err, "failed to parse chat private key hex") 1700 } 1701 1702 return response, nil 1703 } 1704 1705 func (b *GethStatusBackend) getDerivedAddresses(id string) (map[string]generator.AccountInfo, error) { 1706 accountGenerator := b.accountManager.AccountsGenerator() 1707 return accountGenerator.DeriveAddresses(id, paths) 1708 } 1709 1710 // CreateAccountAndLogin creates a new account and logs in with it. 1711 // NOTE: requests.CreateAccount is used for public, params.Option maybe used for internal usage. 1712 func (b *GethStatusBackend) CreateAccountAndLogin(request *requests.CreateAccount, opts ...params.Option) (*multiaccounts.Account, error) { 1713 validation := &requests.CreateAccountValidation{ 1714 AllowEmptyDisplayName: false, 1715 } 1716 if err := request.Validate(validation); err != nil { 1717 return nil, err 1718 } 1719 1720 response, err := b.generateOrImportAccount("", 1, false, request, opts...) 1721 if err != nil { 1722 return nil, err 1723 } 1724 1725 err = b.StartNodeWithAccountAndInitialConfig( 1726 *response.account, 1727 request.Password, 1728 *response.settings, 1729 response.nodeConfig, 1730 response.subAccounts, 1731 response.chatPrivateKey, 1732 ) 1733 1734 if err != nil { 1735 b.log.Error("start node", err) 1736 return nil, err 1737 } 1738 1739 return response.account, nil 1740 } 1741 1742 func (b *GethStatusBackend) ConvertToRegularAccount(mnemonic string, currPassword string, newPassword string) error { 1743 messenger := b.Messenger() 1744 if messenger == nil { 1745 return errors.New("cannot resolve messenger instance") 1746 } 1747 1748 mnemonicNoExtraSpaces := strings.Join(strings.Fields(mnemonic), " ") 1749 accountInfo, err := b.accountManager.AccountsGenerator().ImportMnemonic(mnemonicNoExtraSpaces, "") 1750 if err != nil { 1751 return err 1752 } 1753 1754 kdfIterations, err := b.multiaccountsDB.GetAccountKDFIterationsNumber(accountInfo.KeyUID) 1755 if err != nil { 1756 return err 1757 } 1758 1759 err = b.ensureDBsOpened(multiaccounts.Account{KeyUID: accountInfo.KeyUID, KDFIterations: kdfIterations}, currPassword) 1760 if err != nil { 1761 return err 1762 } 1763 1764 db, err := accounts.NewDB(b.appDB) 1765 if err != nil { 1766 return err 1767 } 1768 1769 knownAccounts, err := db.GetActiveAccounts() 1770 if err != nil { 1771 return err 1772 } 1773 1774 // We add these two paths, cause others will be added via `StoreAccount` function call 1775 const pathWalletRoot = "m/44'/60'/0'/0" 1776 const pathEIP1581 = "m/43'/60'/1581'" 1777 var paths []string 1778 paths = append(paths, pathWalletRoot, pathEIP1581) 1779 for _, acc := range knownAccounts { 1780 if accountInfo.KeyUID == acc.KeyUID { 1781 paths = append(paths, acc.Path) 1782 } 1783 } 1784 1785 _, err = b.accountManager.AccountsGenerator().StoreAccount(accountInfo.ID, currPassword) 1786 if err != nil { 1787 return err 1788 } 1789 1790 _, err = b.accountManager.AccountsGenerator().StoreDerivedAccounts(accountInfo.ID, currPassword, paths) 1791 if err != nil { 1792 return err 1793 } 1794 1795 err = b.multiaccountsDB.UpdateAccountKeycardPairing(accountInfo.KeyUID, "") 1796 if err != nil { 1797 return err 1798 } 1799 1800 err = messenger.DeleteAllKeycardsWithKeyUID(context.Background(), accountInfo.KeyUID) 1801 if err != nil { 1802 return err 1803 } 1804 1805 err = db.SaveSettingField(settings.KeycardInstanceUID, "") 1806 if err != nil { 1807 return err 1808 } 1809 1810 err = db.SaveSettingField(settings.KeycardPairedOn, 0) 1811 if err != nil { 1812 return err 1813 } 1814 1815 err = db.SaveSettingField(settings.KeycardPairing, "") 1816 if err != nil { 1817 return err 1818 } 1819 1820 err = db.SaveSettingField(settings.ProfileMigrationNeeded, false) 1821 if err != nil { 1822 return err 1823 } 1824 1825 err = b.closeDBs() 1826 if err != nil { 1827 return err 1828 } 1829 1830 return b.ChangeDatabasePassword(accountInfo.KeyUID, currPassword, newPassword) 1831 } 1832 1833 func (b *GethStatusBackend) VerifyDatabasePassword(keyUID string, password string) error { 1834 kdfIterations, err := b.multiaccountsDB.GetAccountKDFIterationsNumber(keyUID) 1835 if err != nil { 1836 return err 1837 } 1838 1839 if !b.appDBExists(keyUID) || !b.walletDBExists(keyUID) { 1840 return errors.New("One or more databases not created") 1841 } 1842 1843 err = b.ensureDBsOpened(multiaccounts.Account{KeyUID: keyUID, KDFIterations: kdfIterations}, password) 1844 if err != nil { 1845 return err 1846 } 1847 1848 err = b.closeDBs() 1849 if err != nil { 1850 return err 1851 } 1852 1853 return nil 1854 } 1855 1856 func enrichMultiAccountBySubAccounts(account *multiaccounts.Account, subaccs []*accounts.Account) error { 1857 if account.ColorHash != nil && account.ColorID != 0 { 1858 return nil 1859 } 1860 1861 for i, acc := range subaccs { 1862 subaccs[i].KeyUID = account.KeyUID 1863 if acc.Chat { 1864 pk := string(acc.PublicKey.Bytes()) 1865 colorHash, err := colorhash.GenerateFor(pk) 1866 if err != nil { 1867 return err 1868 } 1869 account.ColorHash = colorHash 1870 1871 colorID, err := identityutils.ToColorID(pk) 1872 if err != nil { 1873 return err 1874 } 1875 account.ColorID = colorID 1876 1877 break 1878 } 1879 } 1880 1881 return nil 1882 } 1883 1884 func enrichMultiAccountByPublicKey(account *multiaccounts.Account, publicKey types.HexBytes) error { 1885 pk := string(publicKey.Bytes()) 1886 colorHash, err := colorhash.GenerateFor(pk) 1887 if err != nil { 1888 return err 1889 } 1890 account.ColorHash = colorHash 1891 1892 colorID, err := identityutils.ToColorID(pk) 1893 if err != nil { 1894 return err 1895 } 1896 account.ColorID = colorID 1897 1898 return nil 1899 } 1900 1901 // Deprecated: Use CreateAccountAndLogin instead 1902 func (b *GethStatusBackend) SaveAccountAndStartNodeWithKey( 1903 account multiaccounts.Account, 1904 password string, 1905 settings settings.Settings, 1906 nodecfg *params.NodeConfig, 1907 subaccs []*accounts.Account, 1908 keyHex string, 1909 ) error { 1910 err := enrichMultiAccountBySubAccounts(&account, subaccs) 1911 if err != nil { 1912 return err 1913 } 1914 err = b.SaveAccount(account) 1915 if err != nil { 1916 return err 1917 } 1918 err = b.ensureDBsOpened(account, password) 1919 if err != nil { 1920 return err 1921 } 1922 err = b.saveAccountsAndSettings(settings, nodecfg, subaccs) 1923 if err != nil { 1924 return err 1925 } 1926 return b.StartNodeWithKey(account, password, keyHex, nodecfg) 1927 } 1928 1929 // StartNodeWithAccountAndInitialConfig is used after account and config was generated. 1930 // In current setup account name and config is generated on the client side. Once/if it will be generated on 1931 // status-go side this flow can be simplified. 1932 // TODO: Consider passing accountBundle here directly 1933 func (b *GethStatusBackend) StartNodeWithAccountAndInitialConfig( 1934 account multiaccounts.Account, 1935 password string, 1936 settings settings.Settings, 1937 nodecfg *params.NodeConfig, 1938 subaccs []*accounts.Account, 1939 chatKey *ecdsa.PrivateKey, 1940 ) error { 1941 err := enrichMultiAccountBySubAccounts(&account, subaccs) 1942 if err != nil { 1943 return err 1944 } 1945 err = b.SaveAccount(account) 1946 if err != nil { 1947 return err 1948 } 1949 err = b.ensureDBsOpened(account, password) 1950 if err != nil { 1951 return err 1952 } 1953 err = b.saveAccountsAndSettings(settings, nodecfg, subaccs) 1954 if err != nil { 1955 return err 1956 } 1957 return b.StartNodeWithAccount(account, password, nodecfg, chatKey) 1958 } 1959 1960 // TODO: change in `saveAccountsAndSettings` function param `subaccs []*accounts.Account` parameter to `profileKeypair *accounts.Keypair` parameter 1961 func (b *GethStatusBackend) saveAccountsAndSettings(settings settings.Settings, nodecfg *params.NodeConfig, subaccs []*accounts.Account) error { 1962 b.mu.Lock() 1963 defer b.mu.Unlock() 1964 accdb, err := accounts.NewDB(b.appDB) 1965 if err != nil { 1966 return err 1967 } 1968 err = accdb.CreateSettings(settings, *nodecfg) 1969 if err != nil { 1970 return err 1971 } 1972 1973 // In case of setting up new account either way (creating new, importing seed phrase, keycard account...) we should not 1974 // back up any data after login, as it was the case before, that's the reason why we're setting last backup time to the time 1975 // when an account was created. 1976 now := time.Now().Unix() 1977 err = accdb.SetLastBackup(uint64(now)) 1978 if err != nil { 1979 return err 1980 } 1981 1982 keypair := &accounts.Keypair{ 1983 KeyUID: settings.KeyUID, 1984 Name: settings.DisplayName, 1985 Type: accounts.KeypairTypeProfile, 1986 DerivedFrom: settings.Address.String(), 1987 LastUsedDerivationIndex: 0, 1988 } 1989 1990 // When creating a new account, the chat account should have position -1, cause it doesn't participate 1991 // in the wallet view and default wallet account should be at position 0. 1992 for _, acc := range subaccs { 1993 if acc.Chat { 1994 acc.Position = -1 1995 } 1996 if acc.Wallet { 1997 acc.Position = 0 1998 } 1999 acc.Operable = accounts.AccountFullyOperable 2000 keypair.Accounts = append(keypair.Accounts, acc) 2001 } 2002 2003 return accdb.SaveOrUpdateKeypair(keypair) 2004 } 2005 2006 func (b *GethStatusBackend) loadNodeConfig(inputNodeCfg *params.NodeConfig) error { 2007 b.mu.Lock() 2008 defer b.mu.Unlock() 2009 2010 conf, err := nodecfg.GetNodeConfigFromDB(b.appDB) 2011 if err != nil { 2012 return err 2013 } 2014 2015 if inputNodeCfg != nil { 2016 // If an installationID is provided, we override it 2017 if conf != nil && conf.ShhextConfig.InstallationID != "" { 2018 inputNodeCfg.ShhextConfig.InstallationID = conf.ShhextConfig.InstallationID 2019 } 2020 2021 conf, err = b.OverwriteNodeConfigValues(conf, inputNodeCfg) 2022 if err != nil { 2023 return err 2024 } 2025 } 2026 2027 // Start WakuV1 if WakuV2 is not enabled 2028 conf.WakuConfig.Enabled = !conf.WakuV2Config.Enabled 2029 // NodeConfig.Version should be taken from params.Version 2030 // which is set at the compile time. 2031 // What's cached is usually outdated so we overwrite it here. 2032 conf.Version = params.Version 2033 conf.RootDataDir = b.rootDataDir 2034 conf.DataDir = filepath.Join(b.rootDataDir, conf.DataDir) 2035 conf.KeyStoreDir = filepath.Join(b.rootDataDir, conf.KeyStoreDir) 2036 2037 if _, err = os.Stat(conf.RootDataDir); os.IsNotExist(err) { 2038 if err := os.MkdirAll(conf.RootDataDir, os.ModePerm); err != nil { 2039 b.log.Warn("failed to create data directory", zap.Error(err)) 2040 return err 2041 } 2042 } 2043 2044 if len(conf.LogDir) == 0 { 2045 conf.LogFile = filepath.Join(b.rootDataDir, conf.LogFile) 2046 } else { 2047 conf.LogFile = filepath.Join(conf.LogDir, conf.LogFile) 2048 } 2049 2050 b.config = conf 2051 2052 if inputNodeCfg != nil && inputNodeCfg.RuntimeLogLevel != "" { 2053 b.config.LogLevel = inputNodeCfg.RuntimeLogLevel 2054 } 2055 2056 return nil 2057 } 2058 2059 func (b *GethStatusBackend) saveNodeConfig(n *params.NodeConfig) error { 2060 err := nodecfg.SaveNodeConfig(b.appDB, n) 2061 if err != nil { 2062 return err 2063 } 2064 return nil 2065 } 2066 2067 func (b *GethStatusBackend) GetNodeConfig() (*params.NodeConfig, error) { 2068 return nodecfg.GetNodeConfigFromDB(b.appDB) 2069 } 2070 2071 func (b *GethStatusBackend) startNode(config *params.NodeConfig) (err error) { 2072 defer func() { 2073 if r := recover(); r != nil { 2074 err = fmt.Errorf("node crashed on start: %v", err) 2075 } 2076 }() 2077 2078 b.log.Info("status-go version details", "version", params.Version, "commit", params.GitCommit) 2079 b.log.Debug("starting node with config", "config", config) 2080 // Update config with some defaults. 2081 if err := config.UpdateWithDefaults(); err != nil { 2082 return err 2083 } 2084 2085 // Updating node config 2086 b.config = config 2087 2088 b.log.Debug("updated config with defaults", "config", config) 2089 2090 // Start by validating configuration 2091 if err := config.Validate(); err != nil { 2092 return err 2093 } 2094 2095 if b.accountManager.GetManager() == nil { 2096 err = b.accountManager.InitKeystore(config.KeyStoreDir) 2097 if err != nil { 2098 return err 2099 } 2100 } 2101 2102 manager := b.accountManager.GetManager() 2103 if manager == nil { 2104 return errors.New("ethereum accounts.Manager is nil") 2105 } 2106 2107 if err = b.statusNode.StartWithOptions(config, node.StartOptions{ 2108 // The peers discovery protocols are started manually after 2109 // `node.ready` signal is sent. 2110 // It was discussed in https://github.com/status-im/status-go/pull/1333. 2111 StartDiscovery: false, 2112 AccountsManager: manager, 2113 }); err != nil { 2114 return 2115 } 2116 b.accountManager.SetRPCClient(b.statusNode.RPCClient(), rpc.DefaultCallTimeout) 2117 signal.SendNodeStarted() 2118 2119 b.transactor.SetNetworkID(config.NetworkID) 2120 b.transactor.SetRPC(b.statusNode.RPCClient(), rpc.DefaultCallTimeout) 2121 b.personalAPI.SetRPC(b.statusNode.RPCClient(), rpc.DefaultCallTimeout) 2122 2123 if err = b.registerHandlers(); err != nil { 2124 b.log.Error("Handler registration failed", "err", err) 2125 return 2126 } 2127 b.log.Info("Handlers registered") 2128 2129 // Handle a case when a node is stopped and resumed. 2130 // If there is no account selected, an error is returned. 2131 if _, err := b.accountManager.SelectedChatAccount(); err == nil { 2132 if err := b.injectAccountsIntoServices(); err != nil { 2133 return err 2134 } 2135 } else if err != account.ErrNoAccountSelected { 2136 return err 2137 } 2138 2139 if b.statusNode.WalletService() != nil { 2140 b.statusNode.WalletService().KeycardPairings().SetKeycardPairingsFile(config.KeycardPairingDataFile) 2141 } 2142 2143 signal.SendNodeReady() 2144 2145 if err := b.statusNode.StartDiscovery(); err != nil { 2146 return err 2147 } 2148 2149 return nil 2150 } 2151 2152 // StopNode stop Status node. Stopped node cannot be resumed. 2153 func (b *GethStatusBackend) StopNode() error { 2154 b.mu.Lock() 2155 defer b.mu.Unlock() 2156 return b.stopNode() 2157 } 2158 2159 func (b *GethStatusBackend) stopNode() error { 2160 if b.statusNode == nil || !b.IsNodeRunning() { 2161 return nil 2162 } 2163 if !b.LocalPairingStateManager.IsPairing() { 2164 defer signal.SendNodeStopped() 2165 } 2166 2167 return b.statusNode.Stop() 2168 } 2169 2170 // RestartNode restart running Status node, fails if node is not running 2171 func (b *GethStatusBackend) RestartNode() error { 2172 b.mu.Lock() 2173 defer b.mu.Unlock() 2174 2175 if !b.IsNodeRunning() { 2176 return node.ErrNoRunningNode 2177 } 2178 2179 if err := b.stopNode(); err != nil { 2180 return err 2181 } 2182 2183 return b.startNode(b.config) 2184 } 2185 2186 // ResetChainData remove chain data from data directory. 2187 // Node is stopped, and new node is started, with clean data directory. 2188 func (b *GethStatusBackend) ResetChainData() error { 2189 b.mu.Lock() 2190 defer b.mu.Unlock() 2191 2192 if err := b.stopNode(); err != nil { 2193 return err 2194 } 2195 // config is cleaned when node is stopped 2196 if err := b.statusNode.ResetChainData(b.config); err != nil { 2197 return err 2198 } 2199 signal.SendChainDataRemoved() 2200 return b.startNode(b.config) 2201 } 2202 2203 // CallRPC executes public RPC requests on node's in-proc RPC server. 2204 func (b *GethStatusBackend) CallRPC(inputJSON string) (string, error) { 2205 client := b.statusNode.RPCClient() 2206 if client == nil { 2207 return "", ErrRPCClientUnavailable 2208 } 2209 return client.CallRaw(inputJSON), nil 2210 } 2211 2212 // CallPrivateRPC executes public and private RPC requests on node's in-proc RPC server. 2213 func (b *GethStatusBackend) CallPrivateRPC(inputJSON string) (string, error) { 2214 client := b.statusNode.RPCClient() 2215 if client == nil { 2216 return "", ErrRPCClientUnavailable 2217 } 2218 return client.CallRaw(inputJSON), nil 2219 } 2220 2221 // SendTransaction creates a new transaction and waits until it's complete. 2222 func (b *GethStatusBackend) SendTransaction(sendArgs transactions.SendTxArgs, password string) (hash types.Hash, err error) { 2223 verifiedAccount, err := b.getVerifiedWalletAccount(sendArgs.From.String(), password) 2224 if err != nil { 2225 return hash, err 2226 } 2227 2228 hash, _, err = b.transactor.SendTransaction(sendArgs, verifiedAccount, -1) 2229 return hash, err 2230 } 2231 2232 func (b *GethStatusBackend) SendTransactionWithChainID(chainID uint64, sendArgs transactions.SendTxArgs, password string) (hash types.Hash, err error) { 2233 verifiedAccount, err := b.getVerifiedWalletAccount(sendArgs.From.String(), password) 2234 if err != nil { 2235 return hash, err 2236 } 2237 2238 hash, _, err = b.transactor.SendTransactionWithChainID(chainID, sendArgs, -1, verifiedAccount) 2239 return hash, err 2240 } 2241 2242 func (b *GethStatusBackend) SendTransactionWithSignature(sendArgs transactions.SendTxArgs, sig []byte) (hash types.Hash, err error) { 2243 txWithSignature, err := b.transactor.BuildTransactionWithSignature(b.transactor.NetworkID(), sendArgs, sig) 2244 if err != nil { 2245 return hash, err 2246 } 2247 2248 return b.transactor.SendTransactionWithSignature(common.Address(sendArgs.From), sendArgs.Symbol, sendArgs.MultiTransactionID, txWithSignature) 2249 } 2250 2251 // HashTransaction validate the transaction and returns new sendArgs and the transaction hash. 2252 func (b *GethStatusBackend) HashTransaction(sendArgs transactions.SendTxArgs) (transactions.SendTxArgs, types.Hash, error) { 2253 return b.transactor.HashTransaction(sendArgs) 2254 } 2255 2256 // SignMessage checks the pwd vs the selected account and passes on the signParams 2257 // to personalAPI for message signature 2258 func (b *GethStatusBackend) SignMessage(rpcParams personal.SignParams) (types.HexBytes, error) { 2259 verifiedAccount, err := b.getVerifiedWalletAccount(rpcParams.Address, rpcParams.Password) 2260 if err != nil { 2261 return types.HexBytes{}, err 2262 } 2263 return b.personalAPI.Sign(rpcParams, verifiedAccount) 2264 } 2265 2266 // Recover calls the personalAPI to return address associated with the private 2267 // key that was used to calculate the signature in the message 2268 func (b *GethStatusBackend) Recover(rpcParams personal.RecoverParams) (types.Address, error) { 2269 return b.personalAPI.Recover(rpcParams) 2270 } 2271 2272 // SignTypedData accepts data and password. Gets verified account and signs typed data. 2273 func (b *GethStatusBackend) SignTypedData(typed typeddata.TypedData, address string, password string) (types.HexBytes, error) { 2274 account, err := b.getVerifiedWalletAccount(address, password) 2275 if err != nil { 2276 return types.HexBytes{}, err 2277 } 2278 chain := new(big.Int).SetUint64(b.StatusNode().Config().NetworkID) 2279 sig, err := typeddata.Sign(typed, account.AccountKey.PrivateKey, chain) 2280 if err != nil { 2281 return types.HexBytes{}, err 2282 } 2283 return types.HexBytes(sig), err 2284 } 2285 2286 // SignTypedDataV4 accepts data and password. Gets verified account and signs typed data. 2287 func (b *GethStatusBackend) SignTypedDataV4(typed signercore.TypedData, address string, password string) (types.HexBytes, error) { 2288 account, err := b.getVerifiedWalletAccount(address, password) 2289 if err != nil { 2290 return types.HexBytes{}, err 2291 } 2292 chain := new(big.Int).SetUint64(b.StatusNode().Config().NetworkID) 2293 sig, err := typeddata.SignTypedDataV4(typed, account.AccountKey.PrivateKey, chain) 2294 if err != nil { 2295 return types.HexBytes{}, err 2296 } 2297 return types.HexBytes(sig), err 2298 } 2299 2300 // HashTypedData generates the hash of TypedData. 2301 func (b *GethStatusBackend) HashTypedData(typed typeddata.TypedData) (types.Hash, error) { 2302 chain := new(big.Int).SetUint64(b.StatusNode().Config().NetworkID) 2303 hash, err := typeddata.ValidateAndHash(typed, chain) 2304 if err != nil { 2305 return types.Hash{}, err 2306 } 2307 return types.Hash(hash), err 2308 } 2309 2310 // HashTypedDataV4 generates the hash of TypedData. 2311 func (b *GethStatusBackend) HashTypedDataV4(typed signercore.TypedData) (types.Hash, error) { 2312 chain := new(big.Int).SetUint64(b.StatusNode().Config().NetworkID) 2313 hash, err := typeddata.HashTypedDataV4(typed, chain) 2314 if err != nil { 2315 return types.Hash{}, err 2316 } 2317 return types.Hash(hash), err 2318 } 2319 2320 func (b *GethStatusBackend) getVerifiedWalletAccount(address, password string) (*account.SelectedExtKey, error) { 2321 config := b.StatusNode().Config() 2322 db, err := accounts.NewDB(b.appDB) 2323 if err != nil { 2324 b.log.Error("failed to create new *Database instance", "error", err) 2325 return nil, err 2326 } 2327 exists, err := db.AddressExists(types.HexToAddress(address)) 2328 if err != nil { 2329 b.log.Error("failed to query db for a given address", "address", address, "error", err) 2330 return nil, err 2331 } 2332 2333 if !exists { 2334 b.log.Error("failed to get a selected account", "err", transactions.ErrInvalidTxSender) 2335 return nil, transactions.ErrAccountDoesntExist 2336 } 2337 2338 key, err := b.accountManager.VerifyAccountPassword(config.KeyStoreDir, address, password) 2339 if _, ok := err.(*account.ErrCannotLocateKeyFile); ok { 2340 key, err = b.generatePartialAccountKey(db, address, password) 2341 if err != nil { 2342 return nil, err 2343 } 2344 } 2345 2346 if err != nil { 2347 b.log.Error("failed to verify account", "account", address, "error", err) 2348 return nil, err 2349 } 2350 2351 return &account.SelectedExtKey{ 2352 Address: key.Address, 2353 AccountKey: key, 2354 }, nil 2355 } 2356 2357 func (b *GethStatusBackend) generatePartialAccountKey(db *accounts.Database, address string, password string) (*types.Key, error) { 2358 dbPath, err := db.GetPath(types.HexToAddress(address)) 2359 path := "m/" + dbPath[strings.LastIndex(dbPath, "/")+1:] 2360 if err != nil { 2361 b.log.Error("failed to get path for given account address", "account", address, "error", err) 2362 return nil, err 2363 } 2364 2365 rootAddress, err := db.GetWalletRootAddress() 2366 if err != nil { 2367 return nil, err 2368 } 2369 info, err := b.accountManager.AccountsGenerator().LoadAccount(rootAddress.Hex(), password) 2370 if err != nil { 2371 return nil, err 2372 } 2373 masterID := info.ID 2374 2375 accInfosMap, err := b.accountManager.AccountsGenerator().StoreDerivedAccounts(masterID, password, []string{path}) 2376 if err != nil { 2377 return nil, err 2378 } 2379 2380 _, key, err := b.accountManager.AddressToDecryptedAccount(accInfosMap[path].Address, password) 2381 if err != nil { 2382 return nil, err 2383 } 2384 2385 return key, nil 2386 } 2387 2388 // registerHandlers attaches Status callback handlers to running node 2389 func (b *GethStatusBackend) registerHandlers() error { 2390 var clients []*rpc.Client 2391 2392 if c := b.StatusNode().RPCClient(); c != nil { 2393 clients = append(clients, c) 2394 } else { 2395 return errors.New("RPC client unavailable") 2396 } 2397 2398 for _, client := range clients { 2399 client.RegisterHandler( 2400 params.AccountsMethodName, 2401 func(context.Context, uint64, ...interface{}) (interface{}, error) { 2402 return b.accountManager.Accounts() 2403 }, 2404 ) 2405 2406 if b.allowAllRPC { 2407 // this should only happen in unit-tests, this variable is not available outside this package 2408 continue 2409 } 2410 client.RegisterHandler(params.SendTransactionMethodName, unsupportedMethodHandler) 2411 client.RegisterHandler(params.PersonalSignMethodName, unsupportedMethodHandler) 2412 client.RegisterHandler(params.PersonalRecoverMethodName, unsupportedMethodHandler) 2413 } 2414 2415 return nil 2416 } 2417 2418 func unsupportedMethodHandler(ctx context.Context, chainID uint64, rpcParams ...interface{}) (interface{}, error) { 2419 return nil, ErrUnsupportedRPCMethod 2420 } 2421 2422 // ConnectionChange handles network state changes logic. 2423 func (b *GethStatusBackend) ConnectionChange(typ string, expensive bool) { 2424 b.mu.Lock() 2425 defer b.mu.Unlock() 2426 2427 state := connection.State{ 2428 Type: connection.NewType(typ), 2429 Expensive: expensive, 2430 } 2431 if typ == connection.None { 2432 state.Offline = true 2433 } 2434 2435 b.log.Info("Network state change", "old", b.connectionState, "new", state) 2436 2437 if b.connectionState.Offline && !state.Offline { 2438 // flush hystrix if we are going again online, since it doesn't behave 2439 // well when offline 2440 hystrix.Flush() 2441 } 2442 2443 b.connectionState = state 2444 b.statusNode.ConnectionChanged(state) 2445 2446 // logic of handling state changes here 2447 // restart node? force peers reconnect? etc 2448 } 2449 2450 // AppStateChange handles app state changes (background/foreground). 2451 // state values: see https://facebook.github.io/react-native/docs/appstate.html 2452 func (b *GethStatusBackend) AppStateChange(state string) { 2453 var messenger *protocol.Messenger 2454 s, err := parseAppState(state) 2455 if err != nil { 2456 log.Error("AppStateChange failed, ignoring", "error", err) 2457 return 2458 } 2459 2460 b.appState = s 2461 2462 if b.statusNode == nil { 2463 log.Warn("statusNode nil, not reporting app state change") 2464 return 2465 } 2466 2467 if b.statusNode.WakuExtService() != nil { 2468 messenger = b.statusNode.WakuExtService().Messenger() 2469 } 2470 2471 if b.statusNode.WakuV2ExtService() != nil { 2472 messenger = b.statusNode.WakuV2ExtService().Messenger() 2473 } 2474 2475 if messenger == nil { 2476 log.Warn("messenger nil, not reporting app state change") 2477 return 2478 } 2479 2480 if s == appStateForeground { 2481 messenger.ToForeground() 2482 } else { 2483 messenger.ToBackground() 2484 } 2485 2486 // TODO: put node in low-power mode if the app is in background (or inactive) 2487 // and normal mode if the app is in foreground. 2488 } 2489 2490 func (b *GethStatusBackend) StopLocalNotifications() error { 2491 if b.statusNode == nil { 2492 return nil 2493 } 2494 return b.statusNode.StopLocalNotifications() 2495 } 2496 2497 func (b *GethStatusBackend) StartLocalNotifications() error { 2498 if b.statusNode == nil { 2499 return nil 2500 } 2501 return b.statusNode.StartLocalNotifications() 2502 2503 } 2504 2505 // Logout clears whisper identities. 2506 func (b *GethStatusBackend) Logout() error { 2507 b.mu.Lock() 2508 defer b.mu.Unlock() 2509 2510 b.log.Debug("logging out") 2511 err := b.cleanupServices() 2512 if err != nil { 2513 return err 2514 } 2515 err = b.closeDBs() 2516 if err != nil { 2517 return err 2518 } 2519 2520 b.AccountManager().Logout() 2521 b.account = nil 2522 2523 if b.statusNode != nil { 2524 if err := b.statusNode.Stop(); err != nil { 2525 return err 2526 } 2527 b.statusNode = nil 2528 } 2529 2530 if !b.LocalPairingStateManager.IsPairing() { 2531 signal.SendNodeStopped() 2532 } 2533 2534 // re-initialize the node, at some point we should better manage the lifecycle 2535 b.initialize() 2536 2537 err = b.statusNode.StartMediaServerWithoutDB() 2538 if err != nil { 2539 b.log.Error("failed to start media server without app db", "err", err) 2540 return err 2541 } 2542 return nil 2543 } 2544 2545 // cleanupServices stops parts of services that doesn't managed by a node and removes injected data from services. 2546 func (b *GethStatusBackend) cleanupServices() error { 2547 b.selectedAccountKeyID = "" 2548 if b.statusNode == nil { 2549 return nil 2550 } 2551 return b.statusNode.Cleanup() 2552 } 2553 2554 func (b *GethStatusBackend) closeDBs() error { 2555 err := b.closeWalletDB() 2556 if err != nil { 2557 return err 2558 } 2559 return b.closeAppDB() 2560 } 2561 2562 func (b *GethStatusBackend) closeAppDB() error { 2563 if b.appDB != nil { 2564 err := b.appDB.Close() 2565 if err != nil { 2566 return err 2567 } 2568 b.appDB = nil 2569 return nil 2570 } 2571 return nil 2572 } 2573 2574 func (b *GethStatusBackend) closeWalletDB() error { 2575 if b.walletDB != nil { 2576 err := b.walletDB.Close() 2577 if err != nil { 2578 return err 2579 } 2580 b.walletDB = nil 2581 } 2582 return nil 2583 } 2584 2585 // SelectAccount selects current wallet and chat accounts, by verifying that each address has corresponding account which can be decrypted 2586 // using provided password. Once verification is done, the decrypted chat key is injected into Whisper (as a single identity, 2587 // all previous identities are removed). 2588 func (b *GethStatusBackend) SelectAccount(loginParams account.LoginParams) error { 2589 b.mu.Lock() 2590 defer b.mu.Unlock() 2591 2592 b.AccountManager().RemoveOnboarding() 2593 2594 err := b.accountManager.SelectAccount(loginParams) 2595 if err != nil { 2596 return err 2597 } 2598 2599 if loginParams.MultiAccount != nil { 2600 b.account = loginParams.MultiAccount 2601 } 2602 2603 if err := b.injectAccountsIntoServices(); err != nil { 2604 return err 2605 } 2606 2607 return nil 2608 } 2609 2610 func (b *GethStatusBackend) GetActiveAccount() (*multiaccounts.Account, error) { 2611 if b.account == nil { 2612 return nil, errors.New("master key account is nil in the GethStatusBackend") 2613 } 2614 2615 return b.account, nil 2616 } 2617 2618 func (b *GethStatusBackend) LocalPairingStarted() error { 2619 if b.account == nil { 2620 return errors.New("master key account is nil in the GethStatusBackend") 2621 } 2622 2623 accountDB, err := accounts.NewDB(b.appDB) 2624 if err != nil { 2625 return err 2626 } 2627 2628 return accountDB.MnemonicWasShown() 2629 } 2630 2631 func (b *GethStatusBackend) injectAccountsIntoWakuService(w types.WakuKeyManager, st *ext.Service) error { 2632 chatAccount, err := b.accountManager.SelectedChatAccount() 2633 if err != nil { 2634 return err 2635 } 2636 2637 identity := chatAccount.AccountKey.PrivateKey 2638 2639 acc, err := b.GetActiveAccount() 2640 if err != nil { 2641 return err 2642 } 2643 2644 if err := w.DeleteKeyPairs(); err != nil { // err is not possible; method return value is incorrect 2645 return err 2646 } 2647 b.selectedAccountKeyID, err = w.AddKeyPair(identity) 2648 if err != nil { 2649 return ErrWakuIdentityInjectionFailure 2650 } 2651 2652 if st != nil { 2653 if err := st.InitProtocol(b.statusNode.GethNode().Config().Name, identity, b.appDB, b.walletDB, b.statusNode.HTTPServer(), b.multiaccountsDB, acc, b.accountManager, b.statusNode.RPCClient(), b.statusNode.WalletService(), b.statusNode.CommunityTokensService(), b.statusNode.WakuV2Service(), logutils.ZapLogger(), b.statusNode.AccountsFeed()); err != nil { 2654 return err 2655 } 2656 // Set initial connection state 2657 st.ConnectionChanged(b.connectionState) 2658 2659 messenger := st.Messenger() 2660 // Init public status api 2661 b.statusNode.StatusPublicService().Init(messenger) 2662 b.statusNode.AccountService().Init(messenger) 2663 // Init chat service 2664 accDB, err := accounts.NewDB(b.appDB) 2665 if err != nil { 2666 return err 2667 } 2668 b.statusNode.ChatService(accDB).Init(messenger) 2669 b.statusNode.EnsService().Init(messenger.SyncEnsNamesWithDispatchMessage) 2670 b.statusNode.CommunityTokensService().Init(messenger) 2671 } 2672 2673 return nil 2674 } 2675 2676 func (b *GethStatusBackend) InstallationID() string { 2677 m := b.Messenger() 2678 if m != nil { 2679 return m.InstallationID() 2680 } 2681 return "" 2682 } 2683 2684 func (b *GethStatusBackend) KeyUID() string { 2685 m := b.Messenger() 2686 if m != nil { 2687 return m.KeyUID() 2688 } 2689 return "" 2690 } 2691 2692 func (b *GethStatusBackend) injectAccountsIntoServices() error { 2693 if b.statusNode.WakuService() != nil { 2694 return b.injectAccountsIntoWakuService(b.statusNode.WakuService(), func() *ext.Service { 2695 if b.statusNode.WakuExtService() == nil { 2696 return nil 2697 } 2698 return b.statusNode.WakuExtService().Service 2699 }()) 2700 } 2701 2702 if b.statusNode.WakuV2Service() != nil { 2703 return b.injectAccountsIntoWakuService(b.statusNode.WakuV2Service(), func() *ext.Service { 2704 if b.statusNode.WakuV2ExtService() == nil { 2705 return nil 2706 } 2707 return b.statusNode.WakuV2ExtService().Service 2708 }()) 2709 } 2710 2711 return nil 2712 } 2713 2714 // ExtractGroupMembershipSignatures extract signatures from tuples of content/signature 2715 func (b *GethStatusBackend) ExtractGroupMembershipSignatures(signaturePairs [][2]string) ([]string, error) { 2716 return crypto.ExtractSignatures(signaturePairs) 2717 } 2718 2719 // SignGroupMembership signs a piece of data containing membership information 2720 func (b *GethStatusBackend) SignGroupMembership(content string) (string, error) { 2721 selectedChatAccount, err := b.accountManager.SelectedChatAccount() 2722 if err != nil { 2723 return "", err 2724 } 2725 2726 return crypto.SignStringAsHex(content, selectedChatAccount.AccountKey.PrivateKey) 2727 } 2728 2729 func (b *GethStatusBackend) Messenger() *protocol.Messenger { 2730 node := b.StatusNode() 2731 if node != nil { 2732 accountService := node.AccountService() 2733 if accountService != nil { 2734 return accountService.GetMessenger() 2735 } 2736 } 2737 return nil 2738 } 2739 2740 // SignHash exposes vanilla ECDSA signing for signing a message for Swarm 2741 func (b *GethStatusBackend) SignHash(hexEncodedHash string) (string, error) { 2742 hash, err := hexutil.Decode(hexEncodedHash) 2743 if err != nil { 2744 return "", fmt.Errorf("SignHash: could not unmarshal the input: %v", err) 2745 } 2746 2747 chatAccount, err := b.accountManager.SelectedChatAccount() 2748 if err != nil { 2749 return "", fmt.Errorf("SignHash: could not select account: %v", err.Error()) 2750 } 2751 2752 signature, err := ethcrypto.Sign(hash, chatAccount.AccountKey.PrivateKey) 2753 if err != nil { 2754 return "", fmt.Errorf("SignHash: could not sign the hash: %v", err) 2755 } 2756 2757 hexEncodedSignature := types.EncodeHex(signature) 2758 return hexEncodedSignature, nil 2759 } 2760 2761 func (b *GethStatusBackend) SwitchFleet(fleet string, conf *params.NodeConfig) error { 2762 if b.appDB == nil { 2763 return ErrDBNotAvailable 2764 } 2765 2766 accountDB, err := accounts.NewDB(b.appDB) 2767 if err != nil { 2768 return err 2769 } 2770 2771 err = accountDB.SaveSetting("fleet", fleet) 2772 if err != nil { 2773 return err 2774 } 2775 2776 err = nodecfg.SaveNodeConfig(b.appDB, conf) 2777 if err != nil { 2778 return err 2779 } 2780 2781 return nil 2782 } 2783 2784 func (b *GethStatusBackend) getAppDBPath(keyUID string) (string, error) { 2785 if len(b.rootDataDir) == 0 { 2786 return "", errors.New("root datadir wasn't provided") 2787 } 2788 2789 return filepath.Join(b.rootDataDir, fmt.Sprintf("%s-v4.db", keyUID)), nil 2790 } 2791 2792 func (b *GethStatusBackend) getWalletDBPath(keyUID string) (string, error) { 2793 if len(b.rootDataDir) == 0 { 2794 return "", errors.New("root datadir wasn't provided") 2795 } 2796 2797 return filepath.Join(b.rootDataDir, fmt.Sprintf("%s-wallet.db", keyUID)), nil 2798 }