github.com/status-im/status-go@v1.1.0/multiaccounts/accounts/database.go (about) 1 package accounts 2 3 import ( 4 "database/sql" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "strconv" 9 "strings" 10 11 "github.com/status-im/status-go/eth-node/types" 12 "github.com/status-im/status-go/multiaccounts/common" 13 "github.com/status-im/status-go/multiaccounts/settings" 14 notificationssettings "github.com/status-im/status-go/multiaccounts/settings_notifications" 15 walletsettings "github.com/status-im/status-go/multiaccounts/settings_wallet" 16 "github.com/status-im/status-go/nodecfg" 17 "github.com/status-im/status-go/params" 18 ) 19 20 const ( 21 statusChatPath = "m/43'/60'/1581'/0'/0" 22 statusWalletRootPath = "m/44'/60'/0'/0/" 23 zeroAddress = "0x0000000000000000000000000000000000000000" 24 SyncedFromBackup = "backup" // means a keypair is coming from backed up data 25 ThirtyDaysInMilliseconds = 30 * 24 * 60 * 60 * 1000 26 27 maxNumOfGeneratedAddresses = uint64(100) 28 numOfGeneratedAddressesRegularKeypair = maxNumOfGeneratedAddresses 29 numOfGeneratedAddressesKeycardKeypair = uint64(10) 30 ) 31 32 var ( 33 errDbPassedParameterIsNil = errors.New("accounts: passed parameter is nil") 34 errDbTransactionIsNil = errors.New("accounts: database transaction is nil") 35 ErrDbKeypairNotFound = errors.New("accounts: keypair is not found") 36 ErrCannotRemoveProfileKeypair = errors.New("accounts: cannot remove profile keypair") 37 ErrDbAccountNotFound = errors.New("accounts: account is not found") 38 ErrCannotRemoveProfileAccount = errors.New("accounts: cannot remove profile account") 39 ErrCannotRemoveDefaultWalletAccount = errors.New("accounts: cannot remove default wallet account") 40 ErrAccountWrongPosition = errors.New("accounts: trying to set wrong position to account") 41 ErrNotTheSameNumberOdAccountsToApplyReordering = errors.New("accounts: there is different number of accounts between received sync message and db accounts") 42 ErrNotTheSameAccountsToApplyReordering = errors.New("accounts: there are differences between accounts in received sync message and db accounts") 43 ErrMovingAccountToWrongPosition = errors.New("accounts: trying to move account to a wrong position") 44 ErrKeypairDifferentAccountsKeyUID = errors.New("cannot store keypair with different accounts' key uid than keypair's key uid") 45 ErrKeypairWithoutAccounts = errors.New("cannot store keypair without accounts") 46 ) 47 48 type Keypair struct { 49 KeyUID string `json:"key-uid"` 50 Name string `json:"name"` 51 Type KeypairType `json:"type"` 52 DerivedFrom string `json:"derived-from"` 53 LastUsedDerivationIndex uint64 `json:"last-used-derivation-index,omitempty"` 54 SyncedFrom string `json:"synced-from,omitempty"` // keeps an info which device this keypair is added from can be one of two values defined in constants or device name (custom) 55 Clock uint64 `json:"clock,omitempty"` 56 Accounts []*Account `json:"accounts,omitempty"` 57 Keycards []*Keycard `json:"keycards,omitempty"` 58 Removed bool `json:"removed,omitempty"` 59 } 60 61 type Account struct { 62 Address types.Address `json:"address"` 63 KeyUID string `json:"key-uid"` 64 Wallet bool `json:"wallet"` 65 AddressWasNotShown bool `json:"address-was-not-shown,omitempty"` 66 Chat bool `json:"chat"` 67 Type AccountType `json:"type,omitempty"` 68 Path string `json:"path,omitempty"` 69 PublicKey types.HexBytes `json:"public-key,omitempty"` 70 Name string `json:"name"` 71 Emoji string `json:"emoji"` 72 ColorID common.CustomizationColor `json:"colorId,omitempty"` 73 Hidden bool `json:"hidden"` 74 Clock uint64 `json:"clock,omitempty"` 75 Removed bool `json:"removed,omitempty"` 76 Operable AccountOperable `json:"operable"` // describes an account's operability (check AccountOperable type constants for details) 77 CreatedAt int64 `json:"createdAt"` 78 Position int64 `json:"position"` 79 ProdPreferredChainIDs string `json:"prodPreferredChainIds"` 80 TestPreferredChainIDs string `json:"testPreferredChainIds"` 81 } 82 83 type KeypairType string 84 type AccountType string 85 type AccountOperable string 86 87 func (a KeypairType) String() string { 88 return string(a) 89 } 90 91 func (a AccountType) String() string { 92 return string(a) 93 } 94 95 func (a AccountOperable) String() string { 96 return string(a) 97 } 98 99 const ( 100 KeypairTypeProfile KeypairType = "profile" 101 KeypairTypeKey KeypairType = "key" 102 KeypairTypeSeed KeypairType = "seed" 103 ) 104 105 const ( 106 AccountTypeGenerated AccountType = "generated" 107 AccountTypeKey AccountType = "key" 108 AccountTypeSeed AccountType = "seed" 109 AccountTypeWatch AccountType = "watch" 110 ) 111 112 const ( 113 AccountNonOperable AccountOperable = "no" // an account is non operable it is not a keycard account and there is no keystore file for it and no keystore file for the address it is derived from 114 AccountPartiallyOperable AccountOperable = "partially" // an account is partially operable if it is not a keycard account and there is created keystore file for the address it is derived from 115 AccountFullyOperable AccountOperable = "fully" // an account is fully operable if it is not a keycard account and there is a keystore file for it 116 117 ProdPreferredChainIDsDefault = "1:10:42161" 118 TestPreferredChainIDsDefault = "5:420:421613" 119 TestSepoliaPreferredChainIDsDefault = "11155111:11155420:421614" 120 ) 121 122 // Returns true if an account is a wallet account that logged in user has a control over, otherwise returns false. 123 func (a *Account) IsWalletNonWatchOnlyAccount() bool { 124 return !a.Chat && len(a.Type) > 0 && a.Type != AccountTypeWatch 125 } 126 127 // Returns true if an account is a wallet account that is ready for sending transactions, otherwise returns false. 128 func (a *Account) IsWalletAccountReadyForTransaction() bool { 129 return a.IsWalletNonWatchOnlyAccount() && a.Operable != AccountNonOperable 130 } 131 132 func (a *Account) MarshalJSON() ([]byte, error) { 133 item := struct { 134 Address types.Address `json:"address"` 135 MixedcaseAddress string `json:"mixedcase-address"` 136 KeyUID string `json:"key-uid"` 137 Wallet bool `json:"wallet"` 138 Chat bool `json:"chat"` 139 Type AccountType `json:"type"` 140 Path string `json:"path"` 141 PublicKey types.HexBytes `json:"public-key"` 142 Name string `json:"name"` 143 Emoji string `json:"emoji"` 144 ColorID common.CustomizationColor `json:"colorId"` 145 Hidden bool `json:"hidden"` 146 Clock uint64 `json:"clock"` 147 Removed bool `json:"removed"` 148 Operable AccountOperable `json:"operable"` 149 CreatedAt int64 `json:"createdAt"` 150 Position int64 `json:"position"` 151 ProdPreferredChainIDs string `json:"prodPreferredChainIds"` 152 TestPreferredChainIDs string `json:"testPreferredChainIds"` 153 }{ 154 Address: a.Address, 155 MixedcaseAddress: a.Address.Hex(), 156 KeyUID: a.KeyUID, 157 Wallet: a.Wallet, 158 Chat: a.Chat, 159 Type: a.Type, 160 Path: a.Path, 161 PublicKey: a.PublicKey, 162 Name: a.Name, 163 Emoji: a.Emoji, 164 ColorID: a.ColorID, 165 Hidden: a.Hidden, 166 Clock: a.Clock, 167 Removed: a.Removed, 168 Operable: a.Operable, 169 CreatedAt: a.CreatedAt, 170 Position: a.Position, 171 ProdPreferredChainIDs: a.ProdPreferredChainIDs, 172 TestPreferredChainIDs: a.TestPreferredChainIDs, 173 } 174 175 return json.Marshal(item) 176 } 177 178 func (a *Keypair) MarshalJSON() ([]byte, error) { 179 item := struct { 180 KeyUID string `json:"key-uid"` 181 Name string `json:"name"` 182 Type KeypairType `json:"type"` 183 DerivedFrom string `json:"derived-from"` 184 LastUsedDerivationIndex uint64 `json:"last-used-derivation-index"` 185 SyncedFrom string `json:"synced-from"` 186 Clock uint64 `json:"clock"` 187 Accounts []*Account `json:"accounts"` 188 Keycards []*Keycard `json:"keycards"` 189 Removed bool `json:"removed"` 190 }{ 191 KeyUID: a.KeyUID, 192 Name: a.Name, 193 Type: a.Type, 194 DerivedFrom: a.DerivedFrom, 195 LastUsedDerivationIndex: a.LastUsedDerivationIndex, 196 SyncedFrom: a.SyncedFrom, 197 Clock: a.Clock, 198 Accounts: a.Accounts, 199 Keycards: a.Keycards, 200 Removed: a.Removed, 201 } 202 203 return json.Marshal(item) 204 } 205 206 func (a *Keypair) CopyKeypair() *Keypair { 207 kp := &Keypair{ 208 Clock: a.Clock, 209 KeyUID: a.KeyUID, 210 Name: a.Name, 211 Type: a.Type, 212 DerivedFrom: a.DerivedFrom, 213 LastUsedDerivationIndex: a.LastUsedDerivationIndex, 214 SyncedFrom: a.SyncedFrom, 215 Accounts: make([]*Account, len(a.Accounts)), 216 Keycards: make([]*Keycard, len(a.Keycards)), 217 Removed: a.Removed, 218 } 219 220 for i, acc := range a.Accounts { 221 kp.Accounts[i] = &Account{ 222 Address: acc.Address, 223 KeyUID: acc.KeyUID, 224 Wallet: acc.Wallet, 225 Chat: acc.Chat, 226 Type: acc.Type, 227 Path: acc.Path, 228 PublicKey: acc.PublicKey, 229 Name: acc.Name, 230 Emoji: acc.Emoji, 231 ColorID: acc.ColorID, 232 Hidden: acc.Hidden, 233 Clock: acc.Clock, 234 Removed: acc.Removed, 235 Operable: acc.Operable, 236 CreatedAt: acc.CreatedAt, 237 Position: acc.Position, 238 ProdPreferredChainIDs: acc.ProdPreferredChainIDs, 239 TestPreferredChainIDs: acc.TestPreferredChainIDs, 240 } 241 } 242 243 for i, kc := range a.Keycards { 244 kp.Keycards[i] = &Keycard{ 245 KeycardUID: kc.KeycardUID, 246 KeycardName: kc.KeycardName, 247 KeycardLocked: kc.KeycardLocked, 248 AccountsAddresses: kc.AccountsAddresses, 249 KeyUID: kc.KeyUID, 250 } 251 } 252 253 return kp 254 } 255 256 func (a *Keypair) GetChatPublicKey() types.HexBytes { 257 for _, acc := range a.Accounts { 258 if acc.Chat { 259 return acc.PublicKey 260 } 261 } 262 263 return nil 264 } 265 266 func (a *Keypair) MigratedToKeycard() bool { 267 return len(a.Keycards) > 0 268 } 269 270 // Returns operability of a keypair: 271 // - if any of keypair's account is not operable, then a keyapir is considered as non operable 272 // - if any of keypair's account is partially operable, then a keyapir is considered as partially operable 273 // - if all accounts are fully operable, then a keyapir is considered as fully operable 274 func (a *Keypair) Operability() AccountOperable { 275 for _, acc := range a.Accounts { 276 if acc.Operable == AccountNonOperable { 277 return AccountNonOperable 278 } 279 if acc.Operable == AccountPartiallyOperable { 280 return AccountPartiallyOperable 281 } 282 } 283 284 return AccountFullyOperable 285 } 286 287 // TODO: implement clean full interface. This might require refactoring Database methods 288 type AccountsStorage interface { 289 GetKeypairByKeyUID(keyUID string) (*Keypair, error) 290 GetAccountByAddress(address types.Address) (*Account, error) 291 AddressExists(address types.Address) (bool, error) 292 } 293 294 // Database sql wrapper for operations with browser objects. 295 type Database struct { 296 settings.DatabaseSettingsManager 297 *notificationssettings.NotificationsSettings 298 *walletsettings.WalletSettings 299 db *sql.DB 300 } 301 302 // NewDB returns a new instance of *Database 303 func NewDB(db *sql.DB) (*Database, error) { 304 if db == nil { 305 return nil, errDbPassedParameterIsNil 306 } 307 308 sDB, err := settings.MakeNewDB(db) 309 if err != nil { 310 return nil, err 311 } 312 sn := notificationssettings.NewNotificationsSettings(db) 313 sw := walletsettings.NewWalletSettings(db) 314 315 return &Database{sDB, sn, sw, db}, nil 316 } 317 318 // DB Gets db sql.DB 319 func (db *Database) DB() *sql.DB { 320 return db.db 321 } 322 323 // Close closes database. 324 func (db *Database) Close() error { 325 return db.db.Close() 326 } 327 328 func GetAccountTypeForKeypairType(kpType KeypairType) AccountType { 329 switch kpType { 330 case KeypairTypeProfile: 331 return AccountTypeGenerated 332 case KeypairTypeKey: 333 return AccountTypeKey 334 case KeypairTypeSeed: 335 return AccountTypeSeed 336 default: 337 return AccountTypeWatch 338 } 339 } 340 341 func (db *Database) processRows(rows *sql.Rows) ([]*Keypair, []*Account, error) { 342 keypairMap := make(map[string]*Keypair) 343 allAccounts := []*Account{} 344 345 var ( 346 kpKeyUID sql.NullString 347 kpName sql.NullString 348 kpType sql.NullString 349 kpDerivedFrom sql.NullString 350 kpLastUsedDerivationIndex sql.NullInt64 351 kpSyncedFrom sql.NullString 352 kpClock sql.NullInt64 353 kpRemoved sql.NullBool 354 ) 355 356 var ( 357 accAddress sql.NullString 358 accKeyUID sql.NullString 359 accPath sql.NullString 360 accName sql.NullString 361 accColorID sql.NullString 362 accEmoji sql.NullString 363 accWallet sql.NullBool 364 accChat sql.NullBool 365 accHidden sql.NullBool 366 accOperable sql.NullString 367 accClock sql.NullInt64 368 accCreatedAt sql.NullTime 369 accPosition sql.NullInt64 370 accRemoved sql.NullBool 371 accProdPreferredChainIDs sql.NullString 372 accTestPreferredChainIDs sql.NullString 373 accAddressWasNotShown sql.NullBool 374 ) 375 376 for rows.Next() { 377 kp := &Keypair{} 378 acc := &Account{} 379 pubkey := []byte{} 380 err := rows.Scan( 381 &kpKeyUID, &kpName, &kpType, &kpDerivedFrom, &kpLastUsedDerivationIndex, &kpSyncedFrom, &kpClock, &kpRemoved, 382 &accAddress, &accKeyUID, &pubkey, &accPath, &accName, &accColorID, &accEmoji, 383 &accWallet, &accChat, &accHidden, &accOperable, &accClock, &accCreatedAt, &accPosition, &accRemoved, 384 &accProdPreferredChainIDs, &accTestPreferredChainIDs, &accAddressWasNotShown) 385 if err != nil { 386 return nil, nil, err 387 } 388 389 // check keypair fields 390 if kpKeyUID.Valid { 391 kp.KeyUID = kpKeyUID.String 392 } 393 if kpName.Valid { 394 kp.Name = kpName.String 395 } 396 if kpType.Valid { 397 kp.Type = KeypairType(kpType.String) 398 } 399 if kpDerivedFrom.Valid { 400 kp.DerivedFrom = kpDerivedFrom.String 401 } 402 if kpLastUsedDerivationIndex.Valid { 403 kp.LastUsedDerivationIndex = uint64(kpLastUsedDerivationIndex.Int64) 404 } 405 if kpSyncedFrom.Valid { 406 kp.SyncedFrom = kpSyncedFrom.String 407 } 408 if kpClock.Valid { 409 kp.Clock = uint64(kpClock.Int64) 410 } 411 if kpRemoved.Valid { 412 kp.Removed = kpRemoved.Bool 413 } 414 // check keypair accounts fields 415 if accAddress.Valid { 416 acc.Address = types.BytesToAddress([]byte(accAddress.String)) 417 } 418 if accKeyUID.Valid { 419 acc.KeyUID = accKeyUID.String 420 } 421 if accPath.Valid { 422 acc.Path = accPath.String 423 } 424 if accName.Valid { 425 acc.Name = accName.String 426 } 427 if accColorID.Valid { 428 acc.ColorID = common.CustomizationColor(accColorID.String) 429 } 430 if accEmoji.Valid { 431 acc.Emoji = accEmoji.String 432 } 433 if accWallet.Valid { 434 acc.Wallet = accWallet.Bool 435 } 436 if accChat.Valid { 437 acc.Chat = accChat.Bool 438 } 439 if accHidden.Valid { 440 acc.Hidden = accHidden.Bool 441 } 442 if accOperable.Valid { 443 acc.Operable = AccountOperable(accOperable.String) 444 } 445 if accClock.Valid { 446 acc.Clock = uint64(accClock.Int64) 447 } 448 if accCreatedAt.Valid { 449 acc.CreatedAt = accCreatedAt.Time.UnixMilli() 450 } 451 if accPosition.Valid { 452 acc.Position = accPosition.Int64 453 } 454 if accProdPreferredChainIDs.Valid { 455 acc.ProdPreferredChainIDs = accProdPreferredChainIDs.String 456 } 457 if accTestPreferredChainIDs.Valid { 458 acc.TestPreferredChainIDs = accTestPreferredChainIDs.String 459 } 460 if accAddressWasNotShown.Valid { 461 acc.AddressWasNotShown = accAddressWasNotShown.Bool 462 } 463 if lth := len(pubkey); lth > 0 { 464 acc.PublicKey = make(types.HexBytes, lth) 465 copy(acc.PublicKey, pubkey) 466 } 467 if accRemoved.Valid { 468 acc.Removed = accRemoved.Bool 469 } 470 acc.Type = GetAccountTypeForKeypairType(kp.Type) 471 472 if kp.KeyUID != "" { 473 if _, ok := keypairMap[kp.KeyUID]; !ok { 474 keypairMap[kp.KeyUID] = kp 475 } 476 keypairMap[kp.KeyUID].Accounts = append(keypairMap[kp.KeyUID].Accounts, acc) 477 } 478 allAccounts = append(allAccounts, acc) 479 } 480 481 if err := rows.Err(); err != nil { 482 return nil, nil, err 483 } 484 485 // Convert map to list 486 keypairs := make([]*Keypair, 0, len(keypairMap)) 487 for _, keypair := range keypairMap { 488 keypairs = append(keypairs, keypair) 489 } 490 491 return keypairs, allAccounts, nil 492 } 493 494 // If `includeRemoved` is false and `keyUID` is not empty, then keypairs which are not flagged as removed and match the `keyUID` will be returned. 495 // If `includeRemoved` is true and `keyUID` is not empty, then keypairs which match the `keyUID` will be returned (regardless how they are flagged). 496 // If `includeRemoved` is false and `keyUID` is empty, then all keypairs which are not flagged as removed will be returned. 497 // If `includeRemoved` is true and `keyUID` is empty, then all keypairs will be returned (regardless how they are flagged). 498 func (db *Database) getKeypairs(tx *sql.Tx, keyUID string, includeRemoved bool) ([]*Keypair, error) { 499 var ( 500 rows *sql.Rows 501 err error 502 mainQueryWhere string 503 subQueryWhere string 504 ) 505 if tx == nil { 506 tx, err = db.db.Begin() 507 if err != nil { 508 return nil, err 509 } 510 defer func() { 511 if err == nil { 512 err = tx.Commit() 513 return 514 } 515 _ = tx.Rollback() 516 }() 517 } 518 519 if keyUID != "" { 520 mainQueryWhere = "WHERE k.key_uid = ?" 521 if !includeRemoved { 522 mainQueryWhere += " AND k.removed = 0" 523 } 524 } else if !includeRemoved { 525 mainQueryWhere = "WHERE k.removed = 0" 526 } 527 528 if !includeRemoved { 529 subQueryWhere = "WHERE removed = 0" 530 } 531 532 query := fmt.Sprintf( // nolint: gosec 533 ` 534 SELECT 535 k.*, 536 ka.address, 537 ka.key_uid, 538 ka.pubkey, 539 ka.path, 540 ka.name, 541 ka.color, 542 ka.emoji, 543 ka.wallet, 544 ka.chat, 545 ka.hidden, 546 ka.operable, 547 ka.clock, 548 ka.created_at, 549 ka.position, 550 ka.removed, 551 ka.prod_preferred_chain_ids, 552 ka.test_preferred_chain_ids, 553 ka.address_was_not_shown 554 FROM 555 keypairs k 556 LEFT JOIN 557 ( 558 SELECT * 559 FROM 560 keypairs_accounts 561 %s 562 ) AS ka 563 ON 564 k.key_uid = ka.key_uid 565 %s 566 ORDER BY 567 ka.position`, subQueryWhere, mainQueryWhere) 568 569 stmt, err := tx.Prepare(query) 570 if err != nil { 571 return nil, err 572 } 573 defer stmt.Close() 574 575 if keyUID != "" { 576 rows, err = stmt.Query(keyUID) 577 } else { 578 rows, err = stmt.Query() 579 } 580 if err != nil { 581 return nil, err 582 } 583 584 defer rows.Close() 585 586 keypairs, _, err := db.processRows(rows) 587 if err != nil { 588 return nil, err 589 } 590 591 for _, kp := range keypairs { 592 keycards, err := db.getKeycards(tx, kp.KeyUID, "") 593 if err != nil { 594 return nil, err 595 } 596 597 kp.Keycards = keycards 598 } 599 600 return keypairs, nil 601 } 602 603 func (db *Database) getKeypairByKeyUID(tx *sql.Tx, keyUID string, includeRemoved bool) (*Keypair, error) { 604 keypairs, err := db.getKeypairs(tx, keyUID, includeRemoved) 605 if err != nil && err != sql.ErrNoRows { 606 return nil, err 607 } 608 609 if len(keypairs) == 0 { 610 return nil, ErrDbKeypairNotFound 611 } 612 return keypairs[0], nil 613 } 614 615 // If `includeRemoved` is false and `address` is not zero address, then accounts which are not flagged as removed and match the `address` will be returned. 616 // If `includeRemoved` is true and `address` is not zero address, then accounts which match the `address` will be returned (regardless how they are flagged). 617 // If `includeRemoved` is false and `address` is zero address, then all accounts which are not flagged as removed will be returned. 618 // If `includeRemoved` is true and `address` is zero address, then all accounts will be returned (regardless how they are flagged). 619 func (db *Database) getAccounts(tx *sql.Tx, address types.Address, includeRemoved bool) ([]*Account, error) { 620 var ( 621 rows *sql.Rows 622 err error 623 where string 624 ) 625 filterByAddress := address.String() != zeroAddress 626 if filterByAddress { 627 where = "WHERE ka.address = ?" 628 if !includeRemoved { 629 where += " AND ka.removed = 0" 630 } 631 } else if !includeRemoved { 632 where = "WHERE ka.removed = 0" 633 } 634 635 query := fmt.Sprintf( // nolint: gosec 636 ` 637 SELECT 638 k.*, 639 ka.address, 640 ka.key_uid, 641 ka.pubkey, 642 ka.path, 643 ka.name, 644 ka.color, 645 ka.emoji, 646 ka.wallet, 647 ka.chat, 648 ka.hidden, 649 ka.operable, 650 ka.clock, 651 ka.created_at, 652 ka.position, 653 ka.removed, 654 ka.prod_preferred_chain_ids, 655 ka.test_preferred_chain_ids, 656 ka.address_was_not_shown 657 FROM 658 keypairs_accounts ka 659 LEFT JOIN 660 keypairs k 661 ON 662 ka.key_uid = k.key_uid 663 %s 664 ORDER BY 665 ka.position`, where) 666 667 if tx == nil { 668 if filterByAddress { 669 rows, err = db.db.Query(query, address) 670 } else { 671 rows, err = db.db.Query(query) 672 } 673 if err != nil { 674 return nil, err 675 } 676 } else { 677 stmt, err := tx.Prepare(query) 678 if err != nil { 679 return nil, err 680 } 681 defer stmt.Close() 682 683 if filterByAddress { 684 rows, err = stmt.Query(address) 685 } else { 686 rows, err = stmt.Query() 687 } 688 if err != nil { 689 return nil, err 690 } 691 } 692 693 defer rows.Close() 694 _, allAccounts, err := db.processRows(rows) 695 if err != nil { 696 return nil, err 697 } 698 699 return allAccounts, nil 700 } 701 702 func (db *Database) getAccountByAddress(tx *sql.Tx, address types.Address) (*Account, error) { 703 accounts, err := db.getAccounts(tx, address, false) 704 if err != nil && err != sql.ErrNoRows { 705 return nil, err 706 } 707 708 if len(accounts) == 0 { 709 return nil, ErrDbAccountNotFound 710 } 711 712 return accounts[0], nil 713 } 714 715 func (db *Database) markAccountRemoved(tx *sql.Tx, address types.Address, clock uint64) error { 716 if tx == nil { 717 return errDbTransactionIsNil 718 } 719 720 _, err := db.getAccountByAddress(tx, address) 721 if err != nil { 722 return err 723 } 724 725 query, err := tx.Prepare(` 726 UPDATE 727 keypairs_accounts 728 SET 729 removed = 1, 730 clock = ? 731 WHERE 732 address = ? 733 `) 734 if err != nil { 735 return err 736 } 737 defer query.Close() 738 739 _, err = query.Exec(clock, address) 740 return err 741 } 742 743 // Marking keypair as removed, will delete related keycards. 744 func (db *Database) markKeypairRemoved(tx *sql.Tx, keyUID string, clock uint64) error { 745 if tx == nil { 746 return errDbTransactionIsNil 747 } 748 749 keypair, err := db.getKeypairByKeyUID(tx, keyUID, false) 750 if err != nil { 751 return err 752 } 753 754 for _, acc := range keypair.Accounts { 755 if acc.Removed { 756 continue 757 } 758 err = db.markAccountRemoved(tx, acc.Address, clock) 759 if err != nil { 760 return err 761 } 762 } 763 764 query := ` 765 UPDATE 766 keypairs 767 SET 768 removed = 1, 769 clock = ? 770 WHERE 771 key_uid = ? 772 ` 773 774 stmt, err := tx.Prepare(query) 775 if err != nil { 776 return err 777 } 778 defer stmt.Close() 779 780 _, err = stmt.Exec(clock, keyUID) 781 if err != nil { 782 return err 783 } 784 785 err = db.deleteAllKeycardsWithKeyUID(tx, keyUID) 786 return err 787 } 788 789 // Returns active keypairs (excluding removed and excluding removed accounts). 790 func (db *Database) GetActiveKeypairs() ([]*Keypair, error) { 791 return db.getKeypairs(nil, "", false) 792 } 793 794 // Returns all keypairs (including removed and removed accounts). 795 func (db *Database) GetAllKeypairs() ([]*Keypair, error) { 796 return db.getKeypairs(nil, "", true) 797 } 798 799 // Returns keypair if it is not marked as removed and its accounts which are not marked as removed. 800 func (db *Database) GetKeypairByKeyUID(keyUID string) (*Keypair, error) { 801 return db.getKeypairByKeyUID(nil, keyUID, false) 802 } 803 804 // Returns active accounts (excluding removed). 805 func (db *Database) GetActiveAccounts() ([]*Account, error) { 806 return db.getAccounts(nil, types.Address{}, false) 807 } 808 809 // Returns all accounts (including removed). 810 func (db *Database) GetAllAccounts() ([]*Account, error) { 811 return db.getAccounts(nil, types.Address{}, true) 812 } 813 814 // Returns account if it is not marked as removed. 815 func (db *Database) GetAccountByAddress(address types.Address) (*Account, error) { 816 return db.getAccountByAddress(nil, address) 817 } 818 819 // Returns active watch only accounts (excluding removed). 820 func (db *Database) GetActiveWatchOnlyAccounts() (res []*Account, err error) { 821 accounts, err := db.getAccounts(nil, types.Address{}, false) 822 if err != nil { 823 return nil, err 824 } 825 for _, acc := range accounts { 826 if acc.Type == AccountTypeWatch { 827 res = append(res, acc) 828 } 829 } 830 return 831 } 832 833 // Returns all watch only accounts (including removed). 834 func (db *Database) GetAllWatchOnlyAccounts() (res []*Account, err error) { 835 accounts, err := db.getAccounts(nil, types.Address{}, true) 836 if err != nil { 837 return nil, err 838 } 839 for _, acc := range accounts { 840 if acc.Type == AccountTypeWatch { 841 res = append(res, acc) 842 } 843 } 844 return 845 } 846 847 func (db *Database) IsAnyAccountPartiallyOrFullyOperableForKeyUID(keyUID string) (bool, error) { 848 kp, err := db.getKeypairByKeyUID(nil, keyUID, false) 849 if err != nil { 850 return false, err 851 } 852 853 for _, acc := range kp.Accounts { 854 if acc.Operable != AccountNonOperable { 855 return true, nil 856 } 857 } 858 return false, nil 859 } 860 861 func (db *Database) RemoveKeypair(keyUID string, clock uint64) error { 862 tx, err := db.db.Begin() 863 if err != nil { 864 return err 865 } 866 defer func() { 867 if err == nil { 868 err = tx.Commit() 869 return 870 } 871 _ = tx.Rollback() 872 }() 873 874 return db.markKeypairRemoved(tx, keyUID, clock) 875 } 876 877 func (db *Database) RemoveAccount(address types.Address, clock uint64) error { 878 tx, err := db.db.Begin() 879 if err != nil { 880 return err 881 } 882 883 defer func() { 884 if err == nil { 885 err = tx.Commit() 886 return 887 } 888 _ = tx.Rollback() 889 }() 890 891 acc, err := db.getAccountByAddress(tx, address) 892 if err != nil { 893 return err 894 } 895 896 kp, err := db.getKeypairByKeyUID(tx, acc.KeyUID, false) 897 if err != nil && err != ErrDbKeypairNotFound { 898 return err 899 } 900 901 if kp != nil { 902 lastAccOfKepairToBeRemoved := true 903 for _, kpAcc := range kp.Accounts { 904 if !kpAcc.Removed && kpAcc.Address != address { 905 lastAccOfKepairToBeRemoved = false 906 } 907 } 908 909 if lastAccOfKepairToBeRemoved { 910 return db.markKeypairRemoved(tx, acc.KeyUID, clock) 911 } 912 } 913 914 err = db.markAccountRemoved(tx, address, clock) 915 if err != nil { 916 return err 917 } 918 919 // Update keypair clock if any but the watch only account was deleted. 920 if kp != nil { 921 err = db.updateKeypairClock(tx, acc.KeyUID, clock) 922 return err 923 } 924 925 return nil 926 } 927 928 func updateKeypairLastUsedIndex(tx *sql.Tx, keyUID string, index uint64, clock uint64, updateKeypairClock bool) error { 929 if tx == nil { 930 return errDbTransactionIsNil 931 } 932 var ( 933 err error 934 setClock string 935 ) 936 if updateKeypairClock { 937 setClock = ", clock = ?" 938 } 939 940 query := fmt.Sprintf( // nolint: gosec 941 ` 942 UPDATE 943 keypairs 944 SET 945 last_used_derivation_index = ? 946 %s 947 WHERE 948 key_uid = ?`, setClock) 949 950 if setClock != "" { 951 _, err = tx.Exec(query, index, clock, keyUID) 952 } else { 953 _, err = tx.Exec(query, index, keyUID) 954 } 955 956 return err 957 } 958 959 func (db *Database) updateKeypairClock(tx *sql.Tx, keyUID string, clock uint64) error { 960 if tx == nil { 961 return errDbTransactionIsNil 962 } 963 964 _, err := tx.Exec(` 965 UPDATE 966 keypairs 967 SET 968 clock = ? 969 WHERE 970 key_uid = ?`, 971 clock, keyUID) 972 973 return err 974 } 975 976 func (db *Database) saveOrUpdateAccounts(tx *sql.Tx, accounts []*Account, updateKeypairClock, isGoerliEnabled bool) (err error) { 977 if tx == nil { 978 return errDbTransactionIsNil 979 } 980 981 for _, acc := range accounts { 982 var relatedKeypair *Keypair 983 // only watch only accounts have an empty `KeyUID` field 984 var keyUID *string 985 if acc.KeyUID != "" { 986 relatedKeypair, err = db.getKeypairByKeyUID(tx, acc.KeyUID, true) 987 if err != nil { 988 if err == sql.ErrNoRows { 989 // all accounts, except watch only accounts, must have a row in `keypairs` table with the same key uid 990 continue 991 } 992 return err 993 } 994 keyUID = &acc.KeyUID 995 } 996 var exists bool 997 err = tx.QueryRow("SELECT EXISTS (SELECT 1 FROM keypairs_accounts WHERE address = ? AND removed = 0)", acc.Address).Scan(&exists) 998 if err != nil { 999 return err 1000 } 1001 1002 // Apply default values if account is new and not a watch only 1003 if !exists && acc.Type != AccountTypeWatch { 1004 if acc.ProdPreferredChainIDs == "" { 1005 acc.ProdPreferredChainIDs = ProdPreferredChainIDsDefault 1006 } 1007 1008 if acc.TestPreferredChainIDs == "" { 1009 if isGoerliEnabled { 1010 acc.TestPreferredChainIDs = TestPreferredChainIDsDefault 1011 } else { 1012 acc.TestPreferredChainIDs = TestSepoliaPreferredChainIDsDefault 1013 } 1014 } 1015 } 1016 1017 _, err = tx.Exec(` 1018 INSERT OR IGNORE INTO 1019 keypairs_accounts (address, key_uid, pubkey, path, wallet, address_was_not_shown, chat, created_at, updated_at) 1020 VALUES 1021 (?, ?, ?, ?, ?, ?, ?, datetime('now'), datetime('now')); 1022 1023 UPDATE 1024 keypairs_accounts 1025 SET 1026 name = ?, 1027 color = ?, 1028 emoji = ?, 1029 hidden = ?, 1030 operable = ?, 1031 clock = ?, 1032 position = ?, 1033 updated_at = datetime('now'), 1034 removed = ?, 1035 prod_preferred_chain_ids = ?, 1036 test_preferred_chain_ids = ? 1037 WHERE 1038 address = ?; 1039 `, 1040 acc.Address, keyUID, acc.PublicKey, acc.Path, acc.Wallet, acc.AddressWasNotShown, acc.Chat, 1041 acc.Name, acc.ColorID, acc.Emoji, acc.Hidden, acc.Operable, acc.Clock, acc.Position, acc.Removed, 1042 acc.ProdPreferredChainIDs, acc.TestPreferredChainIDs, acc.Address) 1043 1044 if err != nil { 1045 return err 1046 } 1047 1048 // Update positions change clock when adding new/updating account 1049 err = db.setClockOfLastAccountsPositionChange(tx, acc.Clock) 1050 if err != nil { 1051 return err 1052 } 1053 1054 // Update keypair clock if any but the watch only account has changed. 1055 if relatedKeypair != nil && updateKeypairClock { 1056 err = db.updateKeypairClock(tx, acc.KeyUID, acc.Clock) 1057 if err != nil { 1058 return err 1059 } 1060 } 1061 1062 if !acc.Removed && strings.HasPrefix(acc.Path, statusWalletRootPath) { 1063 accIndex, err := strconv.ParseUint(acc.Path[len(statusWalletRootPath):], 0, 64) 1064 if err != nil { 1065 return err 1066 } 1067 1068 accountsContainPath := func(accounts []*Account, path string) bool { 1069 for _, acc := range accounts { 1070 if acc.Path == path { 1071 return true 1072 } 1073 } 1074 return false 1075 } 1076 1077 expectedNewKeypairIndex := uint64(0) 1078 if relatedKeypair != nil { 1079 expectedNewKeypairIndex = relatedKeypair.LastUsedDerivationIndex 1080 for { 1081 expectedNewKeypairIndex++ 1082 if !accountsContainPath(relatedKeypair.Accounts, statusWalletRootPath+strconv.FormatUint(expectedNewKeypairIndex, 10)) { 1083 break 1084 } 1085 } 1086 } 1087 1088 if accIndex == expectedNewKeypairIndex { 1089 err = updateKeypairLastUsedIndex(tx, acc.KeyUID, accIndex, acc.Clock, updateKeypairClock) 1090 if err != nil { 1091 return err 1092 } 1093 } 1094 } 1095 } 1096 1097 return nil 1098 } 1099 1100 // Saves accounts, if an account already exists, it will be updated. 1101 func (db *Database) SaveOrUpdateAccounts(accounts []*Account, updateKeypairClock bool) error { 1102 if len(accounts) == 0 { 1103 return errors.New("no provided accounts to save/update") 1104 } 1105 isGoerliEnabled, err := db.GetIsGoerliEnabled() 1106 if err != nil { 1107 return err 1108 } 1109 1110 tx, err := db.db.Begin() 1111 if err != nil { 1112 return err 1113 } 1114 defer func() { 1115 if err == nil { 1116 err = tx.Commit() 1117 return 1118 } 1119 _ = tx.Rollback() 1120 }() 1121 err = db.saveOrUpdateAccounts(tx, accounts, updateKeypairClock, isGoerliEnabled) 1122 return err 1123 } 1124 1125 // Saves a keypair and its accounts, if a keypair with `key_uid` already exists, it will be updated, 1126 // if any of its accounts exists it will be updated as well, otherwise it will be added. 1127 // Since keypair type contains `Keycards` as well, they are excluded from the saving/updating this way regardless they 1128 // are set or not. 1129 func (db *Database) SaveOrUpdateKeypair(keypair *Keypair) error { 1130 if keypair == nil { 1131 return errDbPassedParameterIsNil 1132 } 1133 1134 isGoerliEnabled, err := db.GetIsGoerliEnabled() 1135 if err != nil { 1136 return err 1137 } 1138 1139 tx, err := db.db.Begin() 1140 if err != nil { 1141 return err 1142 } 1143 defer func() { 1144 if err == nil { 1145 err = tx.Commit() 1146 return 1147 } 1148 _ = tx.Rollback() 1149 }() 1150 1151 // If keypair is being saved, not updated, then it must be at least one account and all accounts must have the same key uid. 1152 dbKeypair, err := db.getKeypairByKeyUID(tx, keypair.KeyUID, true) 1153 if err != nil && err != ErrDbKeypairNotFound { 1154 return err 1155 } 1156 if dbKeypair == nil { 1157 if len(keypair.Accounts) == 0 { 1158 return ErrKeypairWithoutAccounts 1159 } 1160 for _, acc := range keypair.Accounts { 1161 if acc.KeyUID == "" || acc.KeyUID != keypair.KeyUID { 1162 return ErrKeypairDifferentAccountsKeyUID 1163 } 1164 } 1165 } 1166 1167 _, err = tx.Exec(` 1168 INSERT OR IGNORE INTO 1169 keypairs (key_uid, type, derived_from) 1170 VALUES 1171 (?, ?, ?); 1172 1173 UPDATE 1174 keypairs 1175 SET 1176 name = ?, 1177 last_used_derivation_index = ?, 1178 synced_from = ?, 1179 clock = ?, 1180 removed = ? 1181 WHERE 1182 key_uid = ?; 1183 `, keypair.KeyUID, keypair.Type, keypair.DerivedFrom, 1184 keypair.Name, keypair.LastUsedDerivationIndex, keypair.SyncedFrom, keypair.Clock, keypair.Removed, keypair.KeyUID) 1185 if err != nil { 1186 return err 1187 } 1188 return db.saveOrUpdateAccounts(tx, keypair.Accounts, false, isGoerliEnabled) 1189 } 1190 1191 func (db *Database) UpdateKeypairName(keyUID string, name string, clock uint64, updateChatAccountName bool) error { 1192 tx, err := db.db.Begin() 1193 if err != nil { 1194 return err 1195 } 1196 defer func() { 1197 if err == nil { 1198 err = tx.Commit() 1199 return 1200 } 1201 _ = tx.Rollback() 1202 }() 1203 1204 _, err = db.getKeypairByKeyUID(tx, keyUID, false) 1205 if err != nil { 1206 return err 1207 } 1208 1209 _, err = tx.Exec(` 1210 UPDATE 1211 keypairs 1212 SET 1213 name = ?, 1214 clock = ? 1215 WHERE 1216 key_uid = ?; 1217 `, name, clock, keyUID) 1218 if err != nil { 1219 return err 1220 } 1221 1222 if updateChatAccountName { 1223 _, err = tx.Exec(` 1224 UPDATE 1225 keypairs_accounts 1226 SET 1227 name = ?, 1228 clock = ? 1229 WHERE 1230 key_uid = ? 1231 AND 1232 path = ?; 1233 `, name, clock, keyUID, statusChatPath) 1234 return err 1235 } 1236 1237 return nil 1238 } 1239 1240 func (db *Database) GetWalletAddress() (rst types.Address, err error) { 1241 err = db.db.QueryRow("SELECT address FROM keypairs_accounts WHERE wallet = 1").Scan(&rst) 1242 return 1243 } 1244 1245 func (db *Database) GetProfileKeypair() (*Keypair, error) { 1246 keypairs, err := db.getKeypairs(nil, "", false) 1247 if err != nil { 1248 return nil, err 1249 } 1250 1251 for _, kp := range keypairs { 1252 if kp.Type == KeypairTypeProfile { 1253 return kp, nil 1254 } 1255 } 1256 1257 panic("no profile keypair among known keypairs") 1258 } 1259 1260 func (db *Database) GetWalletAddresses() (rst []types.Address, err error) { 1261 rows, err := db.db.Query("SELECT address FROM keypairs_accounts WHERE chat = 0 AND removed = 0 ORDER BY created_at") 1262 if err != nil { 1263 return nil, err 1264 } 1265 defer rows.Close() 1266 1267 for rows.Next() { 1268 addr := types.Address{} 1269 err = rows.Scan(&addr) 1270 if err != nil { 1271 return nil, err 1272 } 1273 rst = append(rst, addr) 1274 } 1275 1276 if err := rows.Err(); err != nil { 1277 return nil, err 1278 } 1279 1280 return rst, nil 1281 } 1282 1283 func (db *Database) GetChatAddress() (rst types.Address, err error) { 1284 err = db.db.QueryRow("SELECT address FROM keypairs_accounts WHERE chat = 1").Scan(&rst) 1285 return 1286 } 1287 1288 func (db *Database) GetAddresses() (rst []types.Address, err error) { 1289 rows, err := db.db.Query("SELECT address FROM keypairs_accounts WHERE removed = 0 ORDER BY created_at") 1290 if err != nil { 1291 return nil, err 1292 } 1293 defer rows.Close() 1294 1295 for rows.Next() { 1296 addr := types.Address{} 1297 err = rows.Scan(&addr) 1298 if err != nil { 1299 return nil, err 1300 } 1301 rst = append(rst, addr) 1302 } 1303 1304 if err := rows.Err(); err != nil { 1305 return nil, err 1306 } 1307 1308 return rst, nil 1309 } 1310 1311 func (db *Database) keypairExists(tx *sql.Tx, keyUID string) (exists bool, err error) { 1312 query := `SELECT EXISTS (SELECT 1 FROM keypairs WHERE key_uid = ? AND removed = 0)` 1313 1314 if tx == nil { 1315 err = db.db.QueryRow(query, keyUID).Scan(&exists) 1316 } else { 1317 err = tx.QueryRow(query, keyUID).Scan(&exists) 1318 } 1319 1320 return exists, err 1321 } 1322 1323 // KeypairExists returns true if given address is stored in database. 1324 func (db *Database) KeypairExists(keyUID string) (exists bool, err error) { 1325 return db.keypairExists(nil, keyUID) 1326 } 1327 1328 // AddressExists returns true if given address is stored in database. 1329 func (db *Database) AddressExists(address types.Address) (exists bool, err error) { 1330 err = db.db.QueryRow("SELECT EXISTS (SELECT 1 FROM keypairs_accounts WHERE address = ? AND removed = 0)", address).Scan(&exists) 1331 return exists, err 1332 } 1333 1334 // GetPath returns true if account with given address was recently key and doesn't have a key yet 1335 func (db *Database) GetPath(address types.Address) (path string, err error) { 1336 err = db.db.QueryRow("SELECT path FROM keypairs_accounts WHERE address = ? AND removed = 0", address).Scan(&path) 1337 return path, err 1338 } 1339 1340 // NOTE: This should not be used to retrieve `Networks`. 1341 // NetworkManager should be used instead, otherwise RPCURL will be empty 1342 func (db *Database) GetNodeConfig() (*params.NodeConfig, error) { 1343 return nodecfg.GetNodeConfigFromDB(db.db) 1344 } 1345 1346 // Basically this function should not update the clock, cause it marks keypair/accounts locally. But... 1347 // we need to cover the case when user recovers a Status account from waku, then pairs another device via 1348 // local pairing and then imports seed/private key for the non profile keypair on one of those two devices 1349 // to make that keypair fully operable. In that case we need to inform other device about the change, that 1350 // other device may offer other options for importing that keypair on it. 1351 // If the clock is set to -1, do not update it. 1352 func (db *Database) MarkKeypairFullyOperable(keyUID string, clock uint64, updateKeypairClock bool) (err error) { 1353 tx, err := db.db.Begin() 1354 if err != nil { 1355 return err 1356 } 1357 defer func() { 1358 if err == nil { 1359 err = tx.Commit() 1360 return 1361 } 1362 _ = tx.Rollback() 1363 }() 1364 1365 kp, err := db.getKeypairByKeyUID(tx, keyUID, false) 1366 if err != nil { 1367 return err 1368 } 1369 1370 for _, acc := range kp.Accounts { 1371 _, err = tx.Exec(`UPDATE keypairs_accounts SET operable = ? WHERE address = ?`, AccountFullyOperable, acc.Address) 1372 if err != nil { 1373 return err 1374 } 1375 } 1376 1377 _, err = tx.Exec(`UPDATE keypairs SET synced_from = "" WHERE key_uid = ?`, keyUID) 1378 if err != nil { 1379 return err 1380 } 1381 1382 if updateKeypairClock { 1383 return db.updateKeypairClock(tx, keyUID, clock) 1384 } 1385 1386 return nil 1387 } 1388 1389 func (db *Database) MarkAccountFullyOperable(address types.Address) (err error) { 1390 _, err = db.db.Exec(`UPDATE keypairs_accounts SET operable = ? WHERE address = ?`, AccountFullyOperable, address) 1391 return err 1392 } 1393 1394 // This function should not update the clock, cause it marks a keypair locally. 1395 func (db *Database) SetKeypairSyncedFrom(address types.Address, operable AccountOperable) (err error) { 1396 tx, err := db.db.Begin() 1397 if err != nil { 1398 return err 1399 } 1400 defer func() { 1401 if err == nil { 1402 err = tx.Commit() 1403 return 1404 } 1405 _ = tx.Rollback() 1406 }() 1407 1408 _, err = db.getAccountByAddress(tx, address) 1409 if err != nil { 1410 return err 1411 } 1412 1413 _, err = tx.Exec(`UPDATE keypairs_accounts SET operable = ? WHERE address = ?`, operable, address) 1414 return err 1415 } 1416 1417 func (db *Database) GetPositionForNextNewAccount() (int64, error) { 1418 var pos sql.NullInt64 1419 err := db.db.QueryRow("SELECT MAX(position) FROM keypairs_accounts WHERE removed = 0").Scan(&pos) 1420 if err != nil { 1421 return 0, err 1422 } 1423 if pos.Valid { 1424 return pos.Int64 + 1, nil 1425 } 1426 return 0, nil 1427 } 1428 1429 // This function should not be used directly, it is called from the functions which reorders accounts. 1430 func (db *Database) setClockOfLastAccountsPositionChange(tx *sql.Tx, clock uint64) error { 1431 if tx == nil { 1432 return nil 1433 } 1434 _, err := tx.Exec("UPDATE settings SET wallet_accounts_position_change_clock = ? WHERE synthetic_id = 'id'", clock) 1435 return err 1436 } 1437 1438 func (db *Database) GetClockOfLastAccountsPositionChange() (result uint64, err error) { 1439 query := "SELECT wallet_accounts_position_change_clock FROM settings WHERE synthetic_id = 'id'" 1440 err = db.db.QueryRow(query).Scan(&result) 1441 if err != nil { 1442 return 0, err 1443 } 1444 return result, err 1445 } 1446 1447 // Updates positions of accounts respecting current order. 1448 func (db *Database) ResolveAccountsPositions(clock uint64) (err error) { 1449 tx, err := db.db.Begin() 1450 if err != nil { 1451 return err 1452 } 1453 defer func() { 1454 if err == nil { 1455 err = tx.Commit() 1456 return 1457 } 1458 _ = tx.Rollback() 1459 }() 1460 1461 // returns all accounts ordered by position 1462 dbAccounts, err := db.getAccounts(tx, types.Address{}, false) 1463 if err != nil { 1464 return err 1465 } 1466 1467 // starting from -1, cause `getAccounts` returns chat account as well 1468 for i := 0; i < len(dbAccounts); i++ { 1469 expectedPosition := int64(i - 1) 1470 if dbAccounts[i].Position != expectedPosition { 1471 _, err = tx.Exec("UPDATE keypairs_accounts SET position = ? WHERE address = ?", expectedPosition, dbAccounts[i].Address) 1472 if err != nil { 1473 return err 1474 } 1475 } 1476 } 1477 1478 return db.setClockOfLastAccountsPositionChange(tx, clock) 1479 } 1480 1481 // Sets positions for passed accounts. 1482 func (db *Database) SetWalletAccountsPositions(accounts []*Account, clock uint64) (err error) { 1483 if len(accounts) == 0 { 1484 return nil 1485 } 1486 for _, acc := range accounts { 1487 if acc.Position < 0 { 1488 return ErrAccountWrongPosition 1489 } 1490 } 1491 tx, err := db.db.Begin() 1492 if err != nil { 1493 return err 1494 } 1495 defer func() { 1496 if err == nil { 1497 err = tx.Commit() 1498 return 1499 } 1500 _ = tx.Rollback() 1501 }() 1502 1503 dbAccounts, err := db.getAccounts(tx, types.Address{}, false) 1504 if err != nil { 1505 return err 1506 } 1507 1508 // we need to subtract 1, because of the chat account 1509 if len(dbAccounts)-1 != len(accounts) { 1510 return ErrNotTheSameNumberOdAccountsToApplyReordering 1511 } 1512 1513 for _, dbAcc := range dbAccounts { 1514 if dbAcc.Chat { 1515 continue 1516 } 1517 found := false 1518 for _, acc := range accounts { 1519 if dbAcc.Address == acc.Address { 1520 found = true 1521 break 1522 } 1523 } 1524 if !found { 1525 return ErrNotTheSameAccountsToApplyReordering 1526 } 1527 } 1528 1529 for _, acc := range accounts { 1530 _, err = tx.Exec("UPDATE keypairs_accounts SET position = ? WHERE address = ?", acc.Position, acc.Address) 1531 if err != nil { 1532 return err 1533 } 1534 } 1535 1536 return db.setClockOfLastAccountsPositionChange(tx, clock) 1537 } 1538 1539 // Moves wallet account fromPosition to toPosition. 1540 func (db *Database) MoveWalletAccount(fromPosition int64, toPosition int64, clock uint64) (err error) { 1541 if fromPosition < 0 || toPosition < 0 || fromPosition == toPosition { 1542 return ErrMovingAccountToWrongPosition 1543 } 1544 tx, err := db.db.Begin() 1545 if err != nil { 1546 return err 1547 } 1548 defer func() { 1549 if err == nil { 1550 err = tx.Commit() 1551 return 1552 } 1553 _ = tx.Rollback() 1554 }() 1555 1556 var ( 1557 newMaxPosition int64 1558 newMinPosition int64 1559 ) 1560 err = tx.QueryRow("SELECT MAX(position), MIN(position) FROM keypairs_accounts WHERE removed = 0").Scan(&newMaxPosition, &newMinPosition) 1561 if err != nil { 1562 return err 1563 } 1564 newMaxPosition++ 1565 newMinPosition-- 1566 1567 if toPosition > fromPosition { 1568 _, err = tx.Exec("UPDATE keypairs_accounts SET position = ? WHERE position = ? AND removed = 0", newMaxPosition, fromPosition) 1569 if err != nil { 1570 return err 1571 } 1572 for i := fromPosition + 1; i <= toPosition; i++ { 1573 _, err = tx.Exec("UPDATE keypairs_accounts SET position = ? WHERE position = ? AND removed = 0", i-1, i) 1574 if err != nil { 1575 return err 1576 } 1577 } 1578 _, err = tx.Exec("UPDATE keypairs_accounts SET position = ? WHERE position = ? AND removed = 0", toPosition, newMaxPosition) 1579 if err != nil { 1580 return err 1581 } 1582 } else { 1583 _, err = tx.Exec("UPDATE keypairs_accounts SET position = ? WHERE position = ? AND removed = 0", newMinPosition, fromPosition) 1584 if err != nil { 1585 return err 1586 } 1587 for i := fromPosition - 1; i >= toPosition; i-- { 1588 _, err = tx.Exec("UPDATE keypairs_accounts SET position = ? WHERE position = ? AND removed = 0", i+1, i) 1589 if err != nil { 1590 return err 1591 } 1592 } 1593 _, err = tx.Exec("UPDATE keypairs_accounts SET position = ? WHERE position = ? AND removed = 0", toPosition, newMinPosition) 1594 if err != nil { 1595 return err 1596 } 1597 } 1598 1599 return db.setClockOfLastAccountsPositionChange(tx, clock) 1600 } 1601 1602 func (db *Database) CheckAndDeleteExpiredKeypairsAndAccounts(time uint64) error { 1603 tx, err := db.db.Begin() 1604 if err != nil { 1605 return err 1606 } 1607 defer func() { 1608 if err == nil { 1609 err = tx.Commit() 1610 return 1611 } 1612 _ = tx.Rollback() 1613 }() 1614 1615 // Check keypairs first 1616 dbKeypairs, err := db.getKeypairs(tx, "", true) 1617 if err != nil { 1618 return err 1619 } 1620 1621 for _, dbKp := range dbKeypairs { 1622 if dbKp.Type == KeypairTypeProfile || 1623 !dbKp.Removed || 1624 time-dbKp.Clock < ThirtyDaysInMilliseconds { 1625 continue 1626 } 1627 query := ` 1628 DELETE 1629 FROM 1630 keypairs 1631 WHERE 1632 key_uid = ? 1633 ` 1634 _, err := tx.Exec(query, dbKp.KeyUID) 1635 if err != nil { 1636 return err 1637 } 1638 } 1639 1640 // Check accounts (keypair related and watch only as well) 1641 dbAccounts, err := db.getAccounts(tx, types.Address{}, true) 1642 if err != nil { 1643 return err 1644 } 1645 1646 for _, dbAcc := range dbAccounts { 1647 if dbAcc.Chat || 1648 dbAcc.Wallet || 1649 !dbAcc.Removed || 1650 time-dbAcc.Clock < ThirtyDaysInMilliseconds { 1651 continue 1652 } 1653 1654 query := ` 1655 DELETE 1656 FROM 1657 keypairs_accounts 1658 WHERE 1659 address = ? 1660 ` 1661 _, err := tx.Exec(query, dbAcc.Address) 1662 if err != nil { 1663 return err 1664 } 1665 } 1666 1667 return nil 1668 } 1669 1670 func (db *Database) AddressWasShown(address types.Address) error { 1671 tx, err := db.db.Begin() 1672 if err != nil { 1673 return err 1674 } 1675 defer func() { 1676 if err == nil { 1677 err = tx.Commit() 1678 return 1679 } 1680 _ = tx.Rollback() 1681 }() 1682 1683 _, err = tx.Exec(`UPDATE keypairs_accounts SET address_was_not_shown = 0 WHERE address = ?`, address) 1684 return err 1685 } 1686 1687 func (db *Database) resolveNumOfAddressesToGenerate(keypair *Keypair) uint64 { 1688 if keypair == nil { 1689 return maxNumOfGeneratedAddresses 1690 } 1691 1692 if !keypair.MigratedToKeycard() { 1693 return numOfGeneratedAddressesRegularKeypair 1694 } 1695 1696 final := numOfGeneratedAddressesKeycardKeypair + keypair.LastUsedDerivationIndex 1697 if final < maxNumOfGeneratedAddresses { 1698 return final 1699 } 1700 return maxNumOfGeneratedAddresses 1701 } 1702 1703 func (db *Database) GetNumOfAddressesToGenerateForKeypair(keyUID string) (uint64, error) { 1704 kp, err := db.GetKeypairByKeyUID(keyUID) 1705 if err != nil { 1706 if err == ErrDbKeypairNotFound { 1707 return maxNumOfGeneratedAddresses, nil 1708 } 1709 return 0, err 1710 } 1711 1712 return db.resolveNumOfAddressesToGenerate(kp), nil 1713 } 1714 1715 func (db *Database) ResolveSuggestedPathForKeypair(keyUID string) (suggestedPath string, err error) { 1716 var tx *sql.Tx 1717 tx, err = db.db.Begin() 1718 if err != nil { 1719 return "", err 1720 } 1721 1722 defer func() { 1723 if err == nil { 1724 err = tx.Commit() 1725 return 1726 } 1727 _ = tx.Rollback() 1728 }() 1729 1730 var kp *Keypair 1731 kp, err = db.getKeypairByKeyUID(tx, keyUID, true) 1732 if err != nil { 1733 if err == ErrDbKeypairNotFound { 1734 return fmt.Sprintf("%s0", statusWalletRootPath), nil 1735 } 1736 return "", err 1737 } 1738 1739 numOfAddressesToGenerater := db.resolveNumOfAddressesToGenerate(kp) 1740 1741 nextIndex := kp.LastUsedDerivationIndex + 1 1742 for i := nextIndex; i < numOfAddressesToGenerater; i++ { 1743 suggestedPath = fmt.Sprintf("%s%d", statusWalletRootPath, i) 1744 relativePath := fmt.Sprintf("m/%d", i) 1745 found := false 1746 for _, acc := range kp.Accounts { 1747 if acc.Path != suggestedPath && acc.Path != relativePath { 1748 continue 1749 } 1750 found = true 1751 break 1752 } 1753 if !found { 1754 // relate custom migration https://github.com/status-im/status-go/pull/5279/files#diff-e62600839ff3a2748953a173d3627e2c48a252b0a962a25a873b778313f81494 1755 if kp.Type == KeypairTypeProfile { 1756 walletRootAddress, err := db.GetWalletRootAddress() 1757 if err != nil { 1758 return relativePath, err 1759 } 1760 if kp.DerivedFrom == walletRootAddress.Hex() { // derive from wallet root 1761 return relativePath, nil 1762 } 1763 } 1764 return suggestedPath, nil 1765 } 1766 } 1767 1768 return "", errors.New("couldn't find available path for new account") 1769 }