status-im/status-go

View on GitHub
multiaccounts/accounts/database_test.go

Summary

Maintainability
A
0 mins
Test Coverage
package accounts

import (
    "database/sql"
    "encoding/json"
    "fmt"
    "testing"

    "github.com/stretchr/testify/require"

    "github.com/status-im/status-go/appdatabase"
    "github.com/status-im/status-go/eth-node/types"
    "github.com/status-im/status-go/multiaccounts/common"
    "github.com/status-im/status-go/multiaccounts/settings"
    "github.com/status-im/status-go/params"
    "github.com/status-im/status-go/t/helpers"
)

func setupTestDB(t *testing.T) (*Database, func()) {
    db, stop, err := helpers.SetupTestSQLDB(appdatabase.DbInitializer{}, "settings-tests-")
    require.NoError(t, err)
    d, err := NewDB(db)
    require.NoError(t, err)
    return d, func() { require.NoError(t, stop()) }
}

func TestGetAddresses(t *testing.T) {
    db, stop := setupTestDB(t)
    defer stop()
    accounts := []*Account{
        {Address: types.Address{0x01}, Chat: true, Wallet: true},
        {Address: types.Address{0x02}},
    }
    require.NoError(t, db.SaveOrUpdateAccounts(accounts, false))
    addresses, err := db.GetAddresses()
    require.NoError(t, err)
    require.Equal(t, []types.Address{{0x01}, {0x02}}, addresses)
}

func TestMoveWalletAccount(t *testing.T) {
    db, stop := setupTestDB(t)
    defer stop()

    networks := json.RawMessage("{}")
    setting := settings.Settings{
        Networks: &networks,
    }
    config := params.NodeConfig{}
    err := db.CreateSettings(setting, config)
    require.NoError(t, err)

    accounts := []*Account{
        {Address: types.Address{0x01}, Type: AccountTypeWatch, Position: 0},
        {Address: types.Address{0x02}, Type: AccountTypeWatch, Position: 1},
        {Address: types.Address{0x03}, Type: AccountTypeWatch, Position: 2},
        {Address: types.Address{0x04}, Type: AccountTypeWatch, Position: 3},
        {Address: types.Address{0x05}, Type: AccountTypeWatch, Position: 4},
        {Address: types.Address{0x06}, Type: AccountTypeWatch, Position: 5},
    }
    require.NoError(t, db.SaveOrUpdateAccounts(accounts, false))
    dbAccounts, err := db.GetActiveAccounts()
    require.NoError(t, err)
    require.Len(t, dbAccounts, len(accounts))
    for i := 0; i < len(accounts); i++ {
        require.True(t, SameAccounts(accounts[i], dbAccounts[i]))
    }

    clock := uint64(1000)
    err = db.MoveWalletAccount(-1, 4, clock)
    require.ErrorIs(t, err, ErrMovingAccountToWrongPosition)
    err = db.MoveWalletAccount(4, -1, clock)
    require.ErrorIs(t, err, ErrMovingAccountToWrongPosition)
    err = db.MoveWalletAccount(4, 4, clock)
    require.ErrorIs(t, err, ErrMovingAccountToWrongPosition)

    // Move down account from position 1 to position 4
    err = db.MoveWalletAccount(1, 4, clock)
    require.NoError(t, err)

    // Expected after moving down
    accounts = []*Account{
        {Address: types.Address{0x01}, Type: AccountTypeWatch, Position: 0},
        {Address: types.Address{0x03}, Type: AccountTypeWatch, Position: 1},
        {Address: types.Address{0x04}, Type: AccountTypeWatch, Position: 2},
        {Address: types.Address{0x05}, Type: AccountTypeWatch, Position: 3},
        {Address: types.Address{0x02}, Type: AccountTypeWatch, Position: 4}, // acc with addr 0x02 is at position 4 (moved from position 1)
        {Address: types.Address{0x06}, Type: AccountTypeWatch, Position: 5},
    }

    dbAccounts, err = db.GetActiveAccounts()
    require.NoError(t, err)
    for i := 0; i < len(accounts); i++ {
        require.True(t, SameAccounts(accounts[i], dbAccounts[i]))
    }

    // Check clock
    dbClock, err := db.GetClockOfLastAccountsPositionChange()
    require.NoError(t, err)
    require.Equal(t, clock, dbClock)

    // Move up account from position 5 to position 0
    clock = 2000
    err = db.MoveWalletAccount(5, 0, clock)
    require.NoError(t, err)

    // Expected after moving up
    accounts = []*Account{
        {Address: types.Address{0x06}, Type: AccountTypeWatch, Position: 0}, // acc with addr 0x06 is at position 0 (moved from position 5)
        {Address: types.Address{0x01}, Type: AccountTypeWatch, Position: 1},
        {Address: types.Address{0x03}, Type: AccountTypeWatch, Position: 2},
        {Address: types.Address{0x04}, Type: AccountTypeWatch, Position: 3},
        {Address: types.Address{0x05}, Type: AccountTypeWatch, Position: 4},
        {Address: types.Address{0x02}, Type: AccountTypeWatch, Position: 5},
    }

    dbAccounts, err = db.GetActiveAccounts()
    require.NoError(t, err)
    for i := 0; i < len(accounts); i++ {
        require.True(t, SameAccounts(accounts[i], dbAccounts[i]))
    }

    // Check clock
    dbClock, err = db.GetClockOfLastAccountsPositionChange()
    require.NoError(t, err)
    require.Equal(t, clock, dbClock)
}

func TestGetWalletAddress(t *testing.T) {
    db, stop := setupTestDB(t)
    defer stop()
    address := types.Address{0x01}
    _, err := db.GetWalletAddress()
    require.Equal(t, err, sql.ErrNoRows)
    require.NoError(t, db.SaveOrUpdateAccounts([]*Account{{Address: address, Wallet: true}}, false))
    wallet, err := db.GetWalletAddress()
    require.NoError(t, err)
    require.Equal(t, address, wallet)
}

func TestGetChatAddress(t *testing.T) {
    db, stop := setupTestDB(t)
    defer stop()
    address := types.Address{0x01}
    _, err := db.GetChatAddress()
    require.Equal(t, err, sql.ErrNoRows)
    require.NoError(t, db.SaveOrUpdateAccounts([]*Account{{Address: address, Chat: true}}, false))
    chat, err := db.GetChatAddress()
    require.NoError(t, err)
    require.Equal(t, address, chat)
}

func TestAddressExists(t *testing.T) {
    db, stop := setupTestDB(t)
    defer stop()

    accounts := []*Account{
        {Address: types.Address{0x01}, Chat: true, Wallet: true},
    }
    require.NoError(t, db.SaveOrUpdateAccounts(accounts, false))

    exists, err := db.AddressExists(accounts[0].Address)
    require.NoError(t, err)
    require.True(t, exists)
}

func TestAddressDoesntExist(t *testing.T) {
    db, stop := setupTestDB(t)
    defer stop()
    exists, err := db.AddressExists(types.Address{1, 1, 1})
    require.NoError(t, err)
    require.False(t, exists)
}

func TestWatchOnlyAccounts(t *testing.T) {
    db, stop := setupTestDB(t)
    defer stop()

    // check the db
    dbAccounts, err := db.GetActiveAccounts()
    require.NoError(t, err)
    require.Equal(t, 0, len(dbAccounts))

    woAccounts := GetWatchOnlyAccountsForTest()

    // try to save keypair with watch only accounts
    kp := &Keypair{}
    kp.Accounts = append(kp.Accounts, woAccounts...)
    err = db.SaveOrUpdateKeypair(kp)
    require.Error(t, err)

    // check the db after that trying to save keypair with watch only accounts
    dbAccounts, err = db.GetActiveAccounts()
    require.NoError(t, err)
    require.Equal(t, 0, len(dbAccounts))

    // save watch only accounts
    err = db.SaveOrUpdateAccounts(woAccounts, false)
    require.NoError(t, err)
    _, err = db.GetKeypairByKeyUID(woAccounts[0].KeyUID)
    require.Error(t, err)
    require.True(t, err == ErrDbKeypairNotFound)
    dbAccounts, err = db.GetActiveAccounts()
    require.NoError(t, err)
    require.Equal(t, len(woAccounts), len(dbAccounts))
    require.Equal(t, woAccounts[0].Address, dbAccounts[0].Address)

    // try to save the same watch only account again
    err = db.SaveOrUpdateAccounts(woAccounts[:1], false)
    require.NoError(t, err)
    dbAccounts, err = db.GetActiveAccounts()
    require.NoError(t, err)
    require.Equal(t, len(woAccounts), len(dbAccounts))
    dbAcc, err := db.GetAccountByAddress(woAccounts[:1][0].Address)
    require.NoError(t, err)
    require.Equal(t, woAccounts[:1][0].Address, dbAcc.Address)

    // try to save new watch only account
    wo4 := &Account{
        Address: types.Address{0x14},
        Type:    AccountTypeWatch,
        Name:    "WatchOnlyAcc4",
        ColorID: common.CustomizationColorPrimary,
        Emoji:   "emoji-1",
    }
    err = db.SaveOrUpdateAccounts([]*Account{wo4}, false)
    require.NoError(t, err)
    dbAccounts, err = db.GetActiveAccounts()
    require.NoError(t, err)
    require.Equal(t, len(woAccounts)+1, len(dbAccounts))
    dbAcc, err = db.GetAccountByAddress(wo4.Address)
    require.NoError(t, err)
    require.Equal(t, wo4.Address, dbAcc.Address)

    // updated watch onl to save the same account after it's saved
    wo4.Name = wo4.Name + "updated"
    wo4.ColorID = common.CustomizationColorCamel
    wo4.Emoji = wo4.Emoji + "updated"
    err = db.SaveOrUpdateAccounts([]*Account{wo4}, false)
    require.NoError(t, err)
    dbAccounts, err = db.GetActiveAccounts()
    require.NoError(t, err)
    require.Equal(t, len(woAccounts)+1, len(dbAccounts))
    dbAcc, err = db.GetAccountByAddress(wo4.Address)
    require.NoError(t, err)
    require.Equal(t, wo4.Address, dbAcc.Address)

    // try to delete keypair for watch only account
    err = db.RemoveKeypair(wo4.KeyUID, 0)
    require.Error(t, err)
    require.True(t, err == ErrDbKeypairNotFound)

    // try to delete watch only account
    err = db.RemoveAccount(wo4.Address, 0)
    require.NoError(t, err)
    dbAccounts, err = db.GetActiveAccounts()
    require.NoError(t, err)
    require.Equal(t, len(woAccounts), len(dbAccounts))
    _, err = db.GetAccountByAddress(wo4.Address)
    require.Error(t, err)
    require.True(t, err == ErrDbAccountNotFound)
}

func TestUpdateKeypairName(t *testing.T) {
    db, stop := setupTestDB(t)
    defer stop()

    kp := GetProfileKeypairForTest(true, false, false)

    // check the db
    dbAccounts, err := db.GetActiveAccounts()
    require.NoError(t, err)
    require.Equal(t, 0, len(dbAccounts))

    // save keypair
    err = db.SaveOrUpdateKeypair(kp)
    require.NoError(t, err)
    dbKeypairs, err := db.GetActiveKeypairs()
    require.NoError(t, err)
    require.Equal(t, 1, len(dbKeypairs))
    require.True(t, SameKeypairs(kp, dbKeypairs[0]))

    // update keypair name
    kp.Name = kp.Name + "updated"
    kp.Accounts[0].Name = kp.Name
    err = db.UpdateKeypairName(kp.KeyUID, kp.Name, kp.Clock, true)
    require.NoError(t, err)

    // check keypair
    dbKp, err := db.GetKeypairByKeyUID(kp.KeyUID)
    require.NoError(t, err)
    require.Equal(t, len(kp.Accounts), len(dbKp.Accounts))
    require.True(t, SameKeypairs(kp, dbKp))
}

func TestKeypairs(t *testing.T) {
    keypairs := []*Keypair{
        GetProfileKeypairForTest(true, true, true),
        GetSeedImportedKeypair1ForTest(),
        GetPrivKeyImportedKeypairForTest(), // in this context (when testing db functions) there is not limitations for private key imported keypair
    }

    for _, kp := range keypairs {
        t.Run("test keypair "+kp.Name, func(t *testing.T) {
            db, stop := setupTestDB(t)
            defer stop()

            // check the db
            dbKeypairs, err := db.GetActiveKeypairs()
            require.NoError(t, err)
            require.Equal(t, 0, len(dbKeypairs))
            dbAccounts, err := db.GetActiveAccounts()
            require.NoError(t, err)
            require.Equal(t, 0, len(dbAccounts))

            expectedLastUsedDerivationIndex := uint64(len(kp.Accounts) - 1)
            if kp.Type == KeypairTypeProfile {
                expectedLastUsedDerivationIndex-- // subtract one more in case of profile keypair because of chat account
            }

            // save keypair
            err = db.SaveOrUpdateKeypair(kp)
            require.NoError(t, err)
            dbKeypairs, err = db.GetActiveKeypairs()
            require.NoError(t, err)
            require.Equal(t, 1, len(dbKeypairs))
            dbKp, err := db.GetKeypairByKeyUID(kp.KeyUID)
            require.NoError(t, err)
            require.Equal(t, len(kp.Accounts), len(dbKp.Accounts))
            kp.LastUsedDerivationIndex = expectedLastUsedDerivationIndex
            require.Equal(t, kp.KeyUID, dbKp.KeyUID)
            dbAccounts, err = db.GetActiveAccounts()
            require.NoError(t, err)
            require.Equal(t, len(kp.Accounts), len(dbAccounts))

            // delete keypair
            err = db.RemoveKeypair(kp.KeyUID, 0)
            require.NoError(t, err)
            _, err = db.GetKeypairByKeyUID(kp.KeyUID)
            require.Error(t, err)
            require.True(t, err == ErrDbKeypairNotFound)

            // save keypair again to test the flow below
            err = db.SaveOrUpdateKeypair(kp)
            require.NoError(t, err)
            dbKeypairs, err = db.GetActiveKeypairs()
            require.NoError(t, err)
            require.Equal(t, 1, len(dbKeypairs))

            ind := len(kp.Accounts) - 1
            accToUpdate := kp.Accounts[ind]

            // try to save the same account again
            err = db.SaveOrUpdateAccounts([]*Account{accToUpdate}, false)
            require.NoError(t, err)
            dbKp, err = db.GetKeypairByKeyUID(kp.KeyUID)
            require.NoError(t, err)
            require.Equal(t, len(kp.Accounts), len(dbKp.Accounts))
            require.Equal(t, kp.KeyUID, dbKp.KeyUID)
            dbAccounts, err = db.GetActiveAccounts()
            require.NoError(t, err)
            require.Equal(t, len(kp.Accounts), len(dbAccounts))

            // update an existing account
            accToUpdate.Name = accToUpdate.Name + "updated"
            accToUpdate.ColorID = common.CustomizationColorBrown
            accToUpdate.Emoji = accToUpdate.Emoji + "updated"

            err = db.SaveOrUpdateAccounts([]*Account{accToUpdate}, false)
            require.NoError(t, err)
            dbKp, err = db.GetKeypairByKeyUID(kp.KeyUID)
            require.NoError(t, err)
            require.Equal(t, len(kp.Accounts), len(dbKp.Accounts))
            dbAccounts, err = db.GetActiveAccounts()
            require.NoError(t, err)
            require.Equal(t, len(kp.Accounts), len(dbAccounts))
            dbAcc, err := db.GetAccountByAddress(accToUpdate.Address)
            require.NoError(t, err)
            require.Equal(t, accToUpdate.Address, dbAcc.Address)

            // update keypair name
            kpToUpdate := kp
            kpToUpdate.Name = kpToUpdate.Name + "updated"
            err = db.SaveOrUpdateKeypair(kp)
            require.NoError(t, err)
            dbKeypairs, err = db.GetActiveKeypairs()
            require.NoError(t, err)
            require.Equal(t, 1, len(dbKeypairs))
            dbKp, err = db.GetKeypairByKeyUID(kp.KeyUID)
            require.NoError(t, err)
            require.Equal(t, len(kp.Accounts), len(dbKp.Accounts))
            require.Equal(t, kpToUpdate.KeyUID, dbKp.KeyUID)

            // save new account to an existing keypair which is out of the default Status' derivation root path
            accToAdd := kp.Accounts[ind]
            accToAdd.Address = types.Address{0x08}
            accToAdd.Path = "m/44'/60'/0'/0/10"
            accToAdd.PublicKey = types.Hex2Bytes("0x000000008")
            accToAdd.Name = "Generated Acc 8"

            err = db.SaveOrUpdateAccounts([]*Account{accToAdd}, false)
            require.NoError(t, err)
            dbKp, err = db.GetKeypairByKeyUID(kp.KeyUID)
            require.NoError(t, err)
            require.Equal(t, len(kp.Accounts)+1, len(dbKp.Accounts))
            require.Equal(t, kp.LastUsedDerivationIndex, dbKp.LastUsedDerivationIndex)
            dbAccounts, err = db.GetActiveAccounts()
            require.NoError(t, err)
            require.Equal(t, len(kp.Accounts)+1, len(dbAccounts))
            dbAcc, err = db.GetAccountByAddress(accToUpdate.Address)
            require.NoError(t, err)
            require.Equal(t, accToAdd.Address, dbAcc.Address)

            // save new account to an existing keypair which follows Status' default derivation root path
            accToAdd = kp.Accounts[ind]
            accToAdd.Address = types.Address{0x09}
            accToAdd.Path = "m/44'/60'/0'/0/3"
            accToAdd.PublicKey = types.Hex2Bytes("0x000000009")
            accToAdd.Name = "Generated Acc 9"

            expectedLastUsedDerivationIndex = 3
            if kp.Type == KeypairTypeSeed {
                accToAdd.Path = "m/44'/60'/0'/0/2"
                expectedLastUsedDerivationIndex = 2
            } else if kp.Type == KeypairTypeKey {
                accToAdd.Path = "m/44'/60'/0'/0/1"
                expectedLastUsedDerivationIndex = 1
            }

            err = db.SaveOrUpdateAccounts([]*Account{accToAdd}, false)
            require.NoError(t, err)
            dbKp, err = db.GetKeypairByKeyUID(kp.KeyUID)
            require.NoError(t, err)
            require.Equal(t, len(kp.Accounts)+2, len(dbKp.Accounts))
            require.Equal(t, expectedLastUsedDerivationIndex, dbKp.LastUsedDerivationIndex)
            dbAccounts, err = db.GetActiveAccounts()
            require.NoError(t, err)
            require.Equal(t, len(kp.Accounts)+2, len(dbAccounts))
            dbAcc, err = db.GetAccountByAddress(accToUpdate.Address)
            require.NoError(t, err)
            require.Equal(t, accToAdd.Address, dbAcc.Address)

            // delete account
            err = db.RemoveAccount(accToAdd.Address, 0)
            require.NoError(t, err)
            dbAccounts, err = db.GetActiveAccounts()
            require.NoError(t, err)
            require.Equal(t, len(kp.Accounts)+1, len(dbAccounts))
            _, err = db.GetAccountByAddress(accToAdd.Address)
            require.Error(t, err)
            require.True(t, err == ErrDbAccountNotFound)

            for _, acc := range dbAccounts {
                err = db.RemoveAccount(acc.Address, 0)
                require.NoError(t, err)
            }

            _, err = db.GetKeypairByKeyUID(kp.KeyUID)
            require.Error(t, err)
            require.True(t, err == ErrDbKeypairNotFound)
        })
    }
}

func TestResolvingSuggestedDerivationPath(t *testing.T) {
    kp := GetProfileKeypairForTest(true, true, true)
    totalNumOfAccounts := len(kp.Accounts)

    db, stop := setupTestDB(t)
    defer stop()

    // check the db
    dbKp, err := db.GetKeypairByKeyUID(kp.KeyUID)
    require.Error(t, err)
    require.True(t, err == ErrDbKeypairNotFound)
    require.Nil(t, dbKp)

    expectedLastUsedDerivationIndex := uint64(2)

    // save keypair
    err = db.SaveOrUpdateKeypair(kp)
    require.NoError(t, err)
    dbKp, err = db.GetKeypairByKeyUID(kp.KeyUID)
    require.NoError(t, err)
    require.Equal(t, totalNumOfAccounts, len(dbKp.Accounts))
    require.Equal(t, expectedLastUsedDerivationIndex, dbKp.LastUsedDerivationIndex)

    // check number of addresses to generate
    numOfAddresses, err := db.GetNumOfAddressesToGenerateForKeypair(kp.KeyUID)
    require.NoError(t, err)
    require.Equal(t, maxNumOfGeneratedAddresses, numOfAddresses)

    // check suggested path
    suggestedPath, err := db.ResolveSuggestedPathForKeypair(kp.KeyUID)
    require.NoError(t, err)
    require.Equal(t, fmt.Sprintf("%s%d", statusWalletRootPath, expectedLastUsedDerivationIndex+1), suggestedPath)

    // prepare new account with the next suggested path
    generatedWalletAccountThatWillBeRemovedLater := &Account{
        Address:               types.Address{0x05},
        KeyUID:                kp.KeyUID,
        Wallet:                false,
        Chat:                  false,
        Type:                  AccountTypeGenerated,
        Path:                  suggestedPath,
        PublicKey:             types.Hex2Bytes("0x000000005"),
        Name:                  "Generated Acc 4",
        Emoji:                 "emoji-4",
        ColorID:               common.CustomizationColorPrimary,
        Hidden:                false,
        Clock:                 0,
        Removed:               false,
        Operable:              AccountFullyOperable,
        ProdPreferredChainIDs: "1",
        TestPreferredChainIDs: "5",
    }

    // add new account with the next suggested path
    err = db.SaveOrUpdateAccounts([]*Account{generatedWalletAccountThatWillBeRemovedLater}, false)
    require.NoError(t, err)

    totalNumOfAccounts++
    expectedLastUsedDerivationIndex++

    dbKp, err = db.GetKeypairByKeyUID(kp.KeyUID)
    require.NoError(t, err)
    require.Equal(t, totalNumOfAccounts, len(dbKp.Accounts))
    require.Equal(t, expectedLastUsedDerivationIndex, dbKp.LastUsedDerivationIndex)

    // check suggested path
    suggestedPath, err = db.ResolveSuggestedPathForKeypair(kp.KeyUID)
    require.NoError(t, err)
    require.Equal(t, fmt.Sprintf("%s%d", statusWalletRootPath, expectedLastUsedDerivationIndex+1), suggestedPath)

    customSuggestedPath := fmt.Sprintf("%s%d", statusWalletRootPath, expectedLastUsedDerivationIndex+1+1)

    // prepare new account with the custom suggested path
    generatedWalletAccount := &Account{
        Address:               types.Address{0x07},
        KeyUID:                kp.KeyUID,
        Wallet:                false,
        Chat:                  false,
        Type:                  AccountTypeGenerated,
        Path:                  customSuggestedPath,
        PublicKey:             types.Hex2Bytes("0x000000007"),
        Name:                  "Generated Acc 6",
        Emoji:                 "emoji-6",
        ColorID:               common.CustomizationColorPrimary,
        Hidden:                false,
        Clock:                 0,
        Removed:               false,
        Operable:              AccountFullyOperable,
        ProdPreferredChainIDs: "1",
        TestPreferredChainIDs: "5",
    }

    // add new account with the next suggested path
    err = db.SaveOrUpdateAccounts([]*Account{generatedWalletAccount}, false)
    require.NoError(t, err)

    totalNumOfAccounts++

    dbKp, err = db.GetKeypairByKeyUID(kp.KeyUID)
    require.NoError(t, err)
    require.Equal(t, totalNumOfAccounts, len(dbKp.Accounts))
    require.Equal(t, expectedLastUsedDerivationIndex, dbKp.LastUsedDerivationIndex)

    // check suggested path
    suggestedPath, err = db.ResolveSuggestedPathForKeypair(kp.KeyUID)
    require.NoError(t, err)
    require.Equal(t, fmt.Sprintf("%s%d", statusWalletRootPath, expectedLastUsedDerivationIndex+1), suggestedPath)

    // prepare new account with the next suggested path
    generatedWalletAccount = &Account{
        Address:               types.Address{0x06},
        KeyUID:                kp.KeyUID,
        Wallet:                false,
        Chat:                  false,
        Type:                  AccountTypeGenerated,
        Path:                  suggestedPath,
        PublicKey:             types.Hex2Bytes("0x000000006"),
        Name:                  "Generated Acc 5",
        Emoji:                 "emoji-5",
        ColorID:               common.CustomizationColorPrimary,
        Hidden:                false,
        Clock:                 0,
        Removed:               false,
        Operable:              AccountFullyOperable,
        ProdPreferredChainIDs: "1",
        TestPreferredChainIDs: "5",
    }

    // add new account with the next suggested path
    err = db.SaveOrUpdateAccounts([]*Account{generatedWalletAccount}, false)
    require.NoError(t, err)

    totalNumOfAccounts++
    expectedLastUsedDerivationIndex++

    dbKp, err = db.GetKeypairByKeyUID(kp.KeyUID)
    require.NoError(t, err)
    require.Equal(t, totalNumOfAccounts, len(dbKp.Accounts))
    require.Equal(t, expectedLastUsedDerivationIndex, dbKp.LastUsedDerivationIndex)

    // check suggested path
    suggestedPath, err = db.ResolveSuggestedPathForKeypair(kp.KeyUID)
    require.NoError(t, err)
    require.Equal(t, fmt.Sprintf("%s%d", statusWalletRootPath, expectedLastUsedDerivationIndex+2), suggestedPath)

    // prepare new account with the next suggested path
    generatedWalletAccount = &Account{
        Address:               types.Address{0x08},
        KeyUID:                kp.KeyUID,
        Wallet:                false,
        Chat:                  false,
        Type:                  AccountTypeGenerated,
        Path:                  suggestedPath,
        PublicKey:             types.Hex2Bytes("0x000000008"),
        Name:                  "Generated Acc 7",
        Emoji:                 "emoji-7",
        ColorID:               common.CustomizationColorPrimary,
        Hidden:                false,
        Clock:                 0,
        Removed:               false,
        Operable:              AccountFullyOperable,
        ProdPreferredChainIDs: "1",
        TestPreferredChainIDs: "5",
    }

    // add new account with the next suggested path
    err = db.SaveOrUpdateAccounts([]*Account{generatedWalletAccount}, false)
    require.NoError(t, err)

    totalNumOfAccounts++
    expectedLastUsedDerivationIndex += 2

    dbKp, err = db.GetKeypairByKeyUID(kp.KeyUID)
    require.NoError(t, err)
    require.Equal(t, totalNumOfAccounts, len(dbKp.Accounts))
    require.Equal(t, expectedLastUsedDerivationIndex, dbKp.LastUsedDerivationIndex)

    // check suggested path
    suggestedPath, err = db.ResolveSuggestedPathForKeypair(kp.KeyUID)
    require.NoError(t, err)
    require.Equal(t, fmt.Sprintf("%s%d", statusWalletRootPath, expectedLastUsedDerivationIndex+1), suggestedPath)

    // remove account
    err = db.RemoveAccount(generatedWalletAccountThatWillBeRemovedLater.Address, 0)
    require.NoError(t, err)

    totalNumOfAccounts--

    dbKp, err = db.GetKeypairByKeyUID(kp.KeyUID)
    require.NoError(t, err)
    require.Equal(t, totalNumOfAccounts, len(dbKp.Accounts))
    require.Equal(t, expectedLastUsedDerivationIndex, dbKp.LastUsedDerivationIndex)

    _, err = db.GetAccountByAddress(generatedWalletAccountThatWillBeRemovedLater.Address)
    require.Error(t, err)
    require.True(t, err == ErrDbAccountNotFound)

    // check suggested path after removing account
    suggestedPath, err = db.ResolveSuggestedPathForKeypair(kp.KeyUID)
    require.NoError(t, err)
    require.Equal(t, fmt.Sprintf("%s%d", statusWalletRootPath, expectedLastUsedDerivationIndex+1), suggestedPath)

    // remove kaypair
    err = db.RemoveKeypair(kp.KeyUID, 0)
    require.NoError(t, err)
    _, err = db.GetKeypairByKeyUID(kp.KeyUID)
    require.Error(t, err)
    require.True(t, err == ErrDbKeypairNotFound)

    // check suggested path after removing keypair when adding the same keypair again
    suggestedPath, err = db.ResolveSuggestedPathForKeypair(kp.KeyUID)
    require.NoError(t, err)
    require.Equal(t, fmt.Sprintf("%s%d", statusWalletRootPath, 0), suggestedPath)
}