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  }