status-im/status-go

View on GitHub
api/backend_test.go

Summary

Maintainability
D
1 day
Test Coverage
package api

import (
    "context"
    "crypto/sha256"
    "database/sql"
    "encoding/hex"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "math/rand"
    "os"
    "path"
    "path/filepath"
    "strings"
    "sync"
    "testing"
    "time"

    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/require"

    gethcrypto "github.com/ethereum/go-ethereum/crypto"

    "github.com/status-im/status-go/appdatabase"
    "github.com/status-im/status-go/connection"
    "github.com/status-im/status-go/eth-node/crypto"
    "github.com/status-im/status-go/eth-node/types"
    "github.com/status-im/status-go/multiaccounts"
    "github.com/status-im/status-go/multiaccounts/accounts"
    "github.com/status-im/status-go/multiaccounts/settings"
    "github.com/status-im/status-go/node"
    "github.com/status-im/status-go/params"
    "github.com/status-im/status-go/protocol/requests"
    "github.com/status-im/status-go/rpc"
    "github.com/status-im/status-go/services/typeddata"
    walletservice "github.com/status-im/status-go/services/wallet"
    "github.com/status-im/status-go/signal"
    "github.com/status-im/status-go/sqlite"
    "github.com/status-im/status-go/t/helpers"
    "github.com/status-im/status-go/t/utils"
    "github.com/status-im/status-go/transactions"
    "github.com/status-im/status-go/walletdatabase"
)

var (
    networks     = json.RawMessage("{}")
    testSettings = settings.Settings{
        Address:           types.HexToAddress("0xeC540f3745Ff2964AFC1171a5A0DD726d1F6B472"),
        DisplayName:       "UserDisplayName",
        CurrentNetwork:    "mainnet_rpc",
        DappsAddress:      types.HexToAddress("0xe1300f99fDF7346986CbC766903245087394ecd0"),
        EIP1581Address:    types.HexToAddress("0xe1DDDE9235a541d1344550d969715CF43982de9f"),
        InstallationID:    "d3efcff6-cffa-560e-a547-21d3858cbc51",
        KeyUID:            "0x4e8129f3edfc004875be17bf468a784098a9f69b53c095be1f52deff286935ab",
        LatestDerivedPath: 0,
        Name:              "Jittery Cornflowerblue Kingbird",
        Networks:          &networks,
        PhotoPath:         "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAIAAACRXR/mAAAAjklEQVR4nOzXwQmFMBAAUZXUYh32ZB32ZB02sxYQQSZGsod55/91WFgSS0RM+SyjA56ZRZhFmEWYRRT6h+M6G16zrxv6fdJpmUWYRbxsYr13dKfanpN0WmYRZhGzXz6AWYRZRIfbaX26fT9Jk07LLMIsosPt9I/dTDotswizCG+nhFmEWYRZhFnEHQAA///z1CFkYamgfQAAAABJRU5ErkJggg==",
        PreviewPrivacy:    false,
        PublicKey:         "0x04211fe0f69772ecf7eb0b5bfc7678672508a9fb01f2d699096f0d59ef7fe1a0cb1e648a80190db1c0f5f088872444d846f2956d0bd84069f3f9f69335af852ac0",
        SigningPhrase:     "yurt joey vibe",
        WalletRootAddress: types.HexToAddress("0xeB591fd819F86D0A6a2EF2Bcb94f77807a7De1a6")}
)

func setupTestDB() (*sql.DB, func() error, error) {
    return helpers.SetupTestSQLDB(appdatabase.DbInitializer{}, "tests")
}

func setupTestWalletDB() (*sql.DB, func() error, error) {
    return helpers.SetupTestSQLDB(walletdatabase.DbInitializer{}, "tests")
}

func setupTestMultiDB() (*multiaccounts.Database, func() error, error) {
    tmpfile, err := ioutil.TempFile("", "tests")
    if err != nil {
        return nil, nil, err
    }
    db, err := multiaccounts.InitializeDB(tmpfile.Name())
    if err != nil {
        return nil, nil, err
    }
    return db, func() error {
        err := db.Close()
        if err != nil {
            return err
        }
        return os.Remove(tmpfile.Name())
    }, nil
}

func setupGethStatusBackend() (*GethStatusBackend, func() error, func() error, func() error, error) {
    db, stop1, err := setupTestDB()
    if err != nil {
        return nil, nil, nil, nil, err
    }
    backend := NewGethStatusBackend()
    backend.StatusNode().SetAppDB(db)

    ma, stop2, err := setupTestMultiDB()
    if err != nil {
        return nil, nil, nil, nil, err
    }
    backend.StatusNode().SetMultiaccountsDB(ma)

    walletDb, stop3, err := setupTestWalletDB()
    if err != nil {
        return nil, nil, nil, nil, err
    }
    backend.StatusNode().SetWalletDB(walletDb)

    return backend, stop1, stop2, stop3, err
}

func TestBackendStartNodeConcurrently(t *testing.T) {
    utils.Init()

    backend, stop1, stop2, stop3, err := setupGethStatusBackend()
    defer func() {
        err := stop1()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    defer func() {
        err := stop2()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    defer func() {
        err := stop3()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    require.NoError(t, err)

    config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID)
    require.NoError(t, err)
    require.NoError(t, backend.AccountManager().InitKeystore(config.KeyStoreDir))
    count := 2
    resultCh := make(chan error)

    var wg sync.WaitGroup
    wg.Add(count)

    for i := 0; i < count; i++ {
        go func() {
            resultCh <- backend.StartNode(config)
            wg.Done()
        }()
    }

    // close channel as otherwise for loop never finishes
    go func() { wg.Wait(); close(resultCh) }()

    var results []error
    for err := range resultCh {
        results = append(results, err)
    }

    require.Contains(t, results, nil)
    require.Contains(t, results, node.ErrNodeRunning)

    err = backend.StopNode()
    require.NoError(t, err)
}

func TestBackendRestartNodeConcurrently(t *testing.T) {
    utils.Init()

    backend, stop1, stop2, stopWallet, err := setupGethStatusBackend()
    defer func() {
        err := stop1()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    defer func() {
        err := stop2()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    defer func() {
        err := stopWallet()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    require.NoError(t, err)

    config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID)
    require.NoError(t, err)
    count := 3
    require.NoError(t, backend.AccountManager().InitKeystore(config.KeyStoreDir))
    require.NoError(t, backend.StartNode(config))
    defer func() {
        require.NoError(t, backend.StopNode())
    }()

    var wg sync.WaitGroup
    wg.Add(count)

    for i := 0; i < count; i++ {
        go func(idx int) {
            assert.NoError(t, backend.RestartNode())
            wg.Done()
        }(i)
    }

    wg.Wait()
}

// TODO(adam): add concurrent tests for ResetChainData()

func TestBackendGettersConcurrently(t *testing.T) {
    utils.Init()

    backend, stop1, stop2, stopWallet, err := setupGethStatusBackend()
    defer func() {
        err := stop1()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    defer func() {
        err := stop2()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    defer func() {
        err := stopWallet()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    require.NoError(t, err)

    config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID)
    require.NoError(t, err)
    require.NoError(t, backend.AccountManager().InitKeystore(config.KeyStoreDir))
    err = backend.StartNode(config)
    require.NoError(t, err)
    defer func() {
        require.NoError(t, backend.StopNode())
    }()

    var wg sync.WaitGroup

    wg.Add(1)
    go func() {
        assert.NotNil(t, backend.StatusNode())
        wg.Done()
    }()

    wg.Add(1)
    go func() {
        assert.NotNil(t, backend.AccountManager())
        wg.Done()
    }()

    wg.Add(1)
    go func() {
        assert.NotNil(t, backend.personalAPI)
        wg.Done()
    }()

    wg.Add(1)
    go func() {
        assert.NotNil(t, backend.Transactor())
        wg.Done()
    }()

    wg.Add(1)
    go func() {
        assert.True(t, backend.IsNodeRunning())
        wg.Done()
    }()

    wg.Add(1)
    go func() {
        assert.True(t, backend.IsNodeRunning())
        wg.Done()
    }()

    wg.Wait()
}

func TestBackendConnectionChangesConcurrently(t *testing.T) {
    connections := [...]string{connection.Wifi, connection.Cellular, connection.Unknown}
    backend := NewGethStatusBackend()
    count := 3

    var wg sync.WaitGroup

    for i := 0; i < count; i++ {
        wg.Add(1)
        go func() {
            connIdx := rand.Intn(len(connections)) // nolint: gosec
            backend.ConnectionChange(connections[connIdx], false)
            wg.Done()
        }()
    }

    wg.Wait()
}

func TestBackendConnectionChangesToOffline(t *testing.T) {
    b := NewGethStatusBackend()
    b.ConnectionChange(connection.None, false)
    assert.True(t, b.connectionState.Offline)

    b.ConnectionChange(connection.Wifi, false)
    assert.False(t, b.connectionState.Offline)

    b.ConnectionChange("unknown-state", false)
    assert.False(t, b.connectionState.Offline)
}

func TestBackendCallRPCConcurrently(t *testing.T) {
    utils.Init()

    backend, stop1, stop2, stopWallet, err := setupGethStatusBackend()
    defer func() {
        err := stop1()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    defer func() {
        err := stop2()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    defer func() {
        err := stopWallet()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    require.NoError(t, err)

    config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID)
    require.NoError(t, err)
    require.NoError(t, backend.AccountManager().InitKeystore(config.KeyStoreDir))
    count := 3

    err = backend.StartNode(config)
    require.NoError(t, err)
    defer func() {
        require.NoError(t, backend.StopNode())
    }()

    var wg sync.WaitGroup

    for i := 0; i < count; i++ {
        wg.Add(1)
        go func(idx int) {
            result, err := backend.CallRPC(fmt.Sprintf(
                `{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":%d}`,
                idx+1,
            ))
            assert.NoError(t, err)
            assert.NotContains(t, result, "error")
            wg.Done()
        }(i)

        wg.Add(1)
        go func(idx int) {
            result, err := backend.CallPrivateRPC(fmt.Sprintf(
                `{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":%d}`,
                idx+1,
            ))
            assert.NoError(t, err)
            assert.NotContains(t, result, "error")
            wg.Done()
        }(i)
    }

    wg.Wait()
}

func TestAppStateChange(t *testing.T) {
    backend := NewGethStatusBackend()

    var testCases = []struct {
        name          string
        fromState     appState
        toState       appState
        expectedState appState
    }{
        {
            name:          "success",
            fromState:     appStateInactive,
            toState:       appStateBackground,
            expectedState: appStateBackground,
        },
        {
            name:          "invalid state",
            fromState:     appStateInactive,
            toState:       "unexisting",
            expectedState: appStateInactive,
        },
    }

    for _, tc := range testCases {
        t.Run(tc.name, func(t *testing.T) {
            backend.appState = tc.fromState
            backend.AppStateChange(tc.toState.String())
            assert.Equal(t, tc.expectedState.String(), backend.appState.String())
        })
    }
}

func TestBlockedRPCMethods(t *testing.T) {
    utils.Init()

    backend, stop1, stop2, stopWallet, err := setupGethStatusBackend()
    defer func() {
        err := stop1()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    defer func() {
        err := stop2()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    defer func() {
        err := stopWallet()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    require.NoError(t, err)

    config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID)
    require.NoError(t, err)
    require.NoError(t, backend.AccountManager().InitKeystore(config.KeyStoreDir))
    err = backend.StartNode(config)
    require.NoError(t, err)
    defer func() { require.NoError(t, backend.StopNode()) }()

    for idx, m := range rpc.BlockedMethods() {
        result, err := backend.CallRPC(fmt.Sprintf(
            `{"jsonrpc":"2.0","method":"%s","params":[],"id":%d}`,
            m,
            idx+1,
        ))
        assert.NoError(t, err)
        assert.Contains(t, result, fmt.Sprintf(`{"code":-32700,"message":"%s"}`, rpc.ErrMethodNotFound))
    }
}

func TestCallRPCWithStoppedNode(t *testing.T) {
    backend := NewGethStatusBackend()

    resp, err := backend.CallRPC(
        `{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":1}`,
    )
    assert.Equal(t, ErrRPCClientUnavailable, err)
    assert.Equal(t, "", resp)

    resp, err = backend.CallPrivateRPC(
        `{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":1}`,
    )
    assert.Equal(t, ErrRPCClientUnavailable, err)
    assert.Equal(t, "", resp)
}

// TODO(adam): add concurrent tests for: SendTransaction

func TestStartStopMultipleTimes(t *testing.T) {
    utils.Init()

    backend, stop1, stop2, stopWallet, err := setupGethStatusBackend()
    defer func() {
        err := stop1()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    defer func() {
        err := stop2()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    defer func() {
        err := stopWallet()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    require.NoError(t, err)

    config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID)
    require.NoError(t, err)
    require.NoError(t, backend.AccountManager().InitKeystore(config.KeyStoreDir))
    config.NoDiscovery = false
    // doesn't have to be running. just any valid enode to bypass validation.
    config.ClusterConfig.BootNodes = []string{
        "enode://e8a7c03b58911e98bbd66accb2a55d57683f35b23bf9dfca89e5e244eb5cc3f25018b4112db507faca34fb69ffb44b362f79eda97a669a8df29c72e654416784@0.0.0.0:30404",
    }
    require.NoError(t, err)
    require.NoError(t, backend.StartNode(config))
    require.NoError(t, backend.StopNode())
    require.NoError(t, backend.StartNode(config))
    require.NoError(t, backend.StopNode())
}

func TestHashTypedData(t *testing.T) {
    utils.Init()

    backend, stop1, stop2, stopWallet, err := setupGethStatusBackend()
    defer func() {
        err := stop1()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    defer func() {
        err := stop2()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    defer func() {
        err := stopWallet()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    require.NoError(t, err)

    config, err := utils.MakeTestNodeConfig(params.StatusChainNetworkID)
    require.NoError(t, err)
    require.NoError(t, backend.AccountManager().InitKeystore(config.KeyStoreDir))
    err = backend.StartNode(config)
    require.NoError(t, err)
    defer func() {
        require.NoError(t, backend.StopNode())
    }()

    eip712Domain := "EIP712Domain"
    mytypes := typeddata.Types{
        eip712Domain: []typeddata.Field{
            {Name: "name", Type: "string"},
            {Name: "version", Type: "string"},
            {Name: "chainId", Type: "uint256"},
            {Name: "verifyingContract", Type: "address"},
        },
        "Text": []typeddata.Field{
            {Name: "body", Type: "string"},
        },
    }

    domain := map[string]json.RawMessage{
        "name":              json.RawMessage(`"Ether Text"`),
        "version":           json.RawMessage(`"1"`),
        "chainId":           json.RawMessage(fmt.Sprintf("%d", params.StatusChainNetworkID)),
        "verifyingContract": json.RawMessage(`"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"`),
    }
    msg := map[string]json.RawMessage{
        "body": json.RawMessage(`"Hello, Bob!"`),
    }

    typed := typeddata.TypedData{
        Types:       mytypes,
        PrimaryType: "Text",
        Domain:      domain,
        Message:     msg,
    }

    hash, err := backend.HashTypedData(typed)
    require.NoError(t, err)
    assert.NotEqual(t, types.Hash{}, hash)
}

func TestBackendGetVerifiedAccount(t *testing.T) {
    utils.Init()

    password := "test"
    backend, defers, err := setupWalletTest(t, password)
    require.NoError(t, err)
    defer defers()

    t.Run("AccountDoesntExist", func(t *testing.T) {
        pkey, err := gethcrypto.GenerateKey()
        require.NoError(t, err)
        address := gethcrypto.PubkeyToAddress(pkey.PublicKey)
        key, err := backend.getVerifiedWalletAccount(address.String(), password)
        require.EqualError(t, err, transactions.ErrAccountDoesntExist.Error())
        require.Nil(t, key)
    })

    t.Run("PasswordDoesntMatch", func(t *testing.T) {
        pkey, err := crypto.GenerateKey()
        require.NoError(t, err)
        address := crypto.PubkeyToAddress(pkey.PublicKey)
        keyUIDHex := sha256.Sum256(gethcrypto.FromECDSAPub(&pkey.PublicKey))
        keyUID := types.EncodeHex(keyUIDHex[:])

        db, err := accounts.NewDB(backend.appDB)

        require.NoError(t, err)
        _, err = backend.AccountManager().ImportAccount(pkey, password)
        require.NoError(t, err)
        require.NoError(t, db.SaveOrUpdateKeypair(&accounts.Keypair{
            KeyUID: keyUID,
            Name:   "private key keypair",
            Type:   accounts.KeypairTypeKey,
            Accounts: []*accounts.Account{
                &accounts.Account{
                    Address: address,
                    KeyUID:  keyUID,
                },
            },
        }))
        key, err := backend.getVerifiedWalletAccount(address.String(), "wrong-password")
        require.EqualError(t, err, "could not decrypt key with given password")
        require.Nil(t, key)
    })

    t.Run("PartialAccount", func(t *testing.T) {
        // Create a derived wallet account without storing the keys
        db, err := accounts.NewDB(backend.appDB)
        require.NoError(t, err)
        newPath := "m/0"
        walletRootAddress, err := db.GetWalletRootAddress()
        require.NoError(t, err)

        walletInfo, err := backend.AccountManager().AccountsGenerator().LoadAccount(walletRootAddress.String(), password)
        require.NoError(t, err)
        derivedInfos, err := backend.AccountManager().AccountsGenerator().DeriveAddresses(walletInfo.ID, []string{newPath})
        require.NoError(t, err)
        derivedInfo := derivedInfos[newPath]

        keypair := &accounts.Keypair{
            KeyUID: walletInfo.KeyUID,
            Name:   "profile keypair",
            Type:   accounts.KeypairTypeProfile,
            Accounts: []*accounts.Account{
                &accounts.Account{
                    Address:   types.HexToAddress(derivedInfo.Address),
                    KeyUID:    walletInfo.KeyUID,
                    Type:      accounts.AccountTypeGenerated,
                    PublicKey: types.Hex2Bytes(derivedInfo.PublicKey),
                    Path:      newPath,
                    Wallet:    false,
                    Name:      "PartialAccount",
                },
            },
        }
        require.NoError(t, db.SaveOrUpdateKeypair(keypair))

        // With partial account we need to dynamically generate private key
        key, err := backend.getVerifiedWalletAccount(keypair.Accounts[0].Address.Hex(), password)
        require.NoError(t, err)
        require.Equal(t, keypair.Accounts[0].Address, key.Address)
    })

    t.Run("Success", func(t *testing.T) {
        pkey, err := crypto.GenerateKey()
        require.NoError(t, err)
        address := crypto.PubkeyToAddress(pkey.PublicKey)
        keyUIDHex := sha256.Sum256(gethcrypto.FromECDSAPub(&pkey.PublicKey))
        keyUID := types.EncodeHex(keyUIDHex[:])

        db, err := accounts.NewDB(backend.appDB)
        require.NoError(t, err)
        defer db.Close()
        _, err = backend.AccountManager().ImportAccount(pkey, password)
        require.NoError(t, err)
        require.NoError(t, db.SaveOrUpdateKeypair(&accounts.Keypair{
            KeyUID: keyUID,
            Name:   "private key keypair",
            Type:   accounts.KeypairTypeKey,
            Accounts: []*accounts.Account{
                &accounts.Account{
                    Address: address,
                    KeyUID:  keyUID,
                },
            },
        }))
        key, err := backend.getVerifiedWalletAccount(address.String(), password)
        require.NoError(t, err)
        require.Equal(t, address, key.Address)
    })
}

func TestRuntimeLogLevelIsNotWrittenToDatabase(t *testing.T) {
    utils.Init()

    b := NewGethStatusBackend()
    chatKey, err := gethcrypto.GenerateKey()
    require.NoError(t, err)
    walletKey, err := gethcrypto.GenerateKey()
    require.NoError(t, err)
    keyUIDHex := sha256.Sum256(gethcrypto.FromECDSAPub(&chatKey.PublicKey))
    keyUID := types.EncodeHex(keyUIDHex[:])
    main := multiaccounts.Account{
        KeyUID: keyUID,
    }

    tmpdir := t.TempDir()

    json := `{
        "NetworkId": 3,
        "DataDir": "` + tmpdir + `",
        "KeyStoreDir": "` + tmpdir + `",
        "KeycardPairingDataFile": "` + path.Join(tmpdir, "keycard/pairings.json") + `",
        "NoDiscovery": true,
        "TorrentConfig": {
            "Port": 9025,
            "Enabled": false,
            "DataDir": "` + tmpdir + `/archivedata",
            "TorrentDir": "` + tmpdir + `/torrents"
        },
        "RuntimeLogLevel": "INFO",
        "LogLevel": "DEBUG"
    }`

    conf, err := params.NewConfigFromJSON(json)
    require.NoError(t, err)
    require.Equal(t, "INFO", conf.RuntimeLogLevel)
    keyhex := hex.EncodeToString(gethcrypto.FromECDSA(chatKey))

    require.NoError(t, b.AccountManager().InitKeystore(conf.KeyStoreDir))
    b.UpdateRootDataDir(conf.DataDir)
    require.NoError(t, b.OpenAccounts())
    require.NotNil(t, b.statusNode.HTTPServer())

    address := crypto.PubkeyToAddress(walletKey.PublicKey)

    settings := testSettings
    settings.KeyUID = keyUID
    settings.Address = crypto.PubkeyToAddress(walletKey.PublicKey)

    chatPubKey := crypto.FromECDSAPub(&chatKey.PublicKey)
    require.NoError(t, b.SaveAccountAndStartNodeWithKey(main, "test-pass", settings, conf,
        []*accounts.Account{
            {Address: address, KeyUID: keyUID, Wallet: true},
            {Address: crypto.PubkeyToAddress(chatKey.PublicKey), KeyUID: keyUID, Chat: true, PublicKey: chatPubKey}}, keyhex))
    require.NoError(t, b.Logout())
    require.NoError(t, b.StopNode())

    require.NoError(t, b.StartNodeWithKey(main, "test-pass", keyhex, conf))
    defer func() {
        assert.NoError(t, b.Logout())
        assert.NoError(t, b.StopNode())
    }()

    c, err := b.GetNodeConfig()
    require.NoError(t, err)
    require.Equal(t, "", c.RuntimeLogLevel)
    require.Equal(t, "DEBUG", c.LogLevel)
}

func TestLoginWithKey(t *testing.T) {
    utils.Init()

    b := NewGethStatusBackend()
    chatKey, err := gethcrypto.GenerateKey()
    require.NoError(t, err)
    walletKey, err := gethcrypto.GenerateKey()
    require.NoError(t, err)
    keyUIDHex := sha256.Sum256(gethcrypto.FromECDSAPub(&chatKey.PublicKey))
    keyUID := types.EncodeHex(keyUIDHex[:])
    main := multiaccounts.Account{
        KeyUID: keyUID,
    }
    tmpdir := t.TempDir()
    conf, err := params.NewNodeConfig(tmpdir, 1777)
    require.NoError(t, err)
    keyhex := hex.EncodeToString(gethcrypto.FromECDSA(chatKey))

    require.NoError(t, b.AccountManager().InitKeystore(conf.KeyStoreDir))
    b.UpdateRootDataDir(conf.DataDir)
    require.NoError(t, b.OpenAccounts())
    require.NotNil(t, b.statusNode.HTTPServer())

    address := crypto.PubkeyToAddress(walletKey.PublicKey)

    settings := testSettings
    settings.KeyUID = keyUID
    settings.Address = crypto.PubkeyToAddress(walletKey.PublicKey)

    chatPubKey := crypto.FromECDSAPub(&chatKey.PublicKey)
    require.NoError(t, b.SaveAccountAndStartNodeWithKey(main, "test-pass", settings, conf,
        []*accounts.Account{
            {Address: address, KeyUID: keyUID, Wallet: true},
            {Address: crypto.PubkeyToAddress(chatKey.PublicKey), KeyUID: keyUID, Chat: true, PublicKey: chatPubKey}}, keyhex))
    require.NoError(t, b.Logout())
    require.NoError(t, b.StopNode())

    require.NoError(t, b.AccountManager().InitKeystore(conf.KeyStoreDir))
    b.UpdateRootDataDir(conf.DataDir)
    require.NoError(t, b.OpenAccounts())

    require.NoError(t, b.StartNodeWithKey(main, "test-pass", keyhex, conf))
    defer func() {
        assert.NoError(t, b.Logout())
        assert.NoError(t, b.StopNode())
    }()
    extkey, err := b.accountManager.SelectedChatAccount()
    require.NoError(t, err)
    require.Equal(t, crypto.PubkeyToAddress(chatKey.PublicKey), extkey.Address)

    activeAccount, err := b.GetActiveAccount()
    require.NoError(t, err)
    require.NotNil(t, activeAccount.ColorHash)
}

func TestLoginAccount(t *testing.T) {
    utils.Init()
    password := "some-password"
    tmpdir := t.TempDir()
    nameserver := "8.8.8.8"

    b := NewGethStatusBackend()
    createAccountRequest := &requests.CreateAccount{
        DisplayName:        "some-display-name",
        CustomizationColor: "#ffffff",
        Emoji:              "some",
        Password:           password,
        RootDataDir:        tmpdir,
        LogFilePath:        tmpdir + "/log",
        WakuV2Nameserver:   &nameserver,
    }
    c := make(chan interface{}, 10)
    signal.SetMobileSignalHandler(func(data []byte) {
        if strings.Contains(string(data), signal.EventLoggedIn) {
            c <- struct{}{}
        }
    })
    waitForLogin := func(chan interface{}) {
        select {
        case <-c:
            break
        case <-time.After(5 * time.Second):
            t.FailNow()
        }
    }

    acc, err := b.CreateAccountAndLogin(createAccountRequest)
    require.NoError(t, err)
    require.Equal(t, nameserver, b.config.WakuV2Config.Nameserver)

    waitForLogin(c)
    require.NoError(t, b.Logout())
    require.NoError(t, b.StopNode())

    accounts, err := b.GetAccounts()
    require.NoError(t, err)
    require.Len(t, accounts, 1)

    require.NotEmpty(t, accounts[0].KeyUID)
    require.Equal(t, acc.KeyUID, accounts[0].KeyUID)

    loginAccountRequest := &requests.Login{
        KeyUID:           accounts[0].KeyUID,
        Password:         password,
        WakuV2Nameserver: nameserver,
    }
    err = b.LoginAccount(loginAccountRequest)
    require.NoError(t, err)
    waitForLogin(c)

    require.Equal(t, nameserver, b.config.WakuV2Config.Nameserver)
}

func TestVerifyDatabasePassword(t *testing.T) {
    utils.Init()

    b := NewGethStatusBackend()
    chatKey, err := gethcrypto.GenerateKey()
    require.NoError(t, err)
    walletKey, err := gethcrypto.GenerateKey()
    require.NoError(t, err)
    keyUIDHex := sha256.Sum256(gethcrypto.FromECDSAPub(&chatKey.PublicKey))
    keyUID := types.EncodeHex(keyUIDHex[:])
    main := multiaccounts.Account{
        KeyUID: keyUID,
    }
    tmpdir := t.TempDir()
    conf, err := params.NewNodeConfig(tmpdir, 1777)
    require.NoError(t, err)
    keyhex := hex.EncodeToString(gethcrypto.FromECDSA(chatKey))

    require.NoError(t, b.AccountManager().InitKeystore(conf.KeyStoreDir))
    b.UpdateRootDataDir(conf.DataDir)
    require.NoError(t, b.OpenAccounts())

    address := crypto.PubkeyToAddress(walletKey.PublicKey)

    settings := testSettings
    settings.KeyUID = keyUID
    settings.Address = crypto.PubkeyToAddress(walletKey.PublicKey)

    chatPubKey := crypto.FromECDSAPub(&chatKey.PublicKey)

    require.NoError(t, b.SaveAccountAndStartNodeWithKey(main, "test-pass", settings, conf, []*accounts.Account{
        {Address: address, KeyUID: keyUID, Wallet: true},
        {Address: crypto.PubkeyToAddress(chatKey.PublicKey), KeyUID: keyUID, Chat: true, PublicKey: chatPubKey}}, keyhex))
    require.NoError(t, b.Logout())
    require.NoError(t, b.StopNode())

    require.Error(t, b.VerifyDatabasePassword(main.KeyUID, "wrong-pass"))
    require.NoError(t, b.VerifyDatabasePassword(main.KeyUID, "test-pass"))
}

func TestDeleteMultiaccount(t *testing.T) {
    backend := NewGethStatusBackend()

    rootDataDir := t.TempDir()

    keyStoreDir := filepath.Join(rootDataDir, "keystore")

    backend.rootDataDir = rootDataDir

    err := backend.AccountManager().InitKeystore(keyStoreDir)
    require.NoError(t, err)

    backend.AccountManager()
    accs, err := backend.AccountManager().
        AccountsGenerator().
        GenerateAndDeriveAddresses(12, 1, "", []string{"m/44'/60'/0'/0"})
    require.NoError(t, err)

    generateAccount := accs[0]
    accountInfo, err := backend.AccountManager().
        AccountsGenerator().
        StoreAccount(generateAccount.ID, "123123")
    require.NoError(t, err)

    account := multiaccounts.Account{
        Name:           "foo",
        Timestamp:      1,
        KeycardPairing: "pairing",
        KeyUID:         generateAccount.KeyUID,
    }

    err = backend.ensureAppDBOpened(account, "123123")
    require.NoError(t, err)

    s := settings.Settings{
        Address:           types.HexToAddress(accountInfo.Address),
        CurrentNetwork:    "mainnet_rpc",
        DappsAddress:      types.HexToAddress(accountInfo.Address),
        EIP1581Address:    types.HexToAddress(accountInfo.Address),
        InstallationID:    "d3efcff6-cffa-560e-a547-21d3858cbc51",
        KeyUID:            account.KeyUID,
        LatestDerivedPath: 0,
        Name:              "Jittery Cornflowerblue Kingbird",
        Networks:          &networks,
        PhotoPath:         "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAIAAACRXR/mAAAAjklEQVR4nOzXwQmFMBAAUZXUYh32ZB32ZB02sxYQQSZGsod55/91WFgSS0RM+SyjA56ZRZhFmEWYRRT6h+M6G16zrxv6fdJpmUWYRbxsYr13dKfanpN0WmYRZhGzXz6AWYRZRIfbaX26fT9Jk07LLMIsosPt9I/dTDotswizCG+nhFmEWYRZhFnEHQAA///z1CFkYamgfQAAAABJRU5ErkJggg==",
        PreviewPrivacy:    false,
        PublicKey:         accountInfo.PublicKey,
        SigningPhrase:     "yurt joey vibe",
        WalletRootAddress: types.HexToAddress(accountInfo.Address)}

    err = backend.saveAccountsAndSettings(
        s,
        &params.NodeConfig{},
        nil)
    require.Error(t, err)
    require.True(t, err == accounts.ErrKeypairWithoutAccounts)

    err = backend.OpenAccounts()
    require.NoError(t, err)

    err = backend.SaveAccount(account)
    require.NoError(t, err)

    files, err := ioutil.ReadDir(rootDataDir)
    require.NoError(t, err)
    require.NotEqual(t, 3, len(files))

    err = backend.DeleteMultiaccount(account.KeyUID, keyStoreDir)
    require.NoError(t, err)

    files, err = ioutil.ReadDir(rootDataDir)
    require.NoError(t, err)
    require.Equal(t, 3, len(files))
}

func TestConvertAccount(t *testing.T) {
    const mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
    const password = "111111"        // represents password for a regular user
    const keycardPassword = "222222" // represents password for a keycard user
    const keycardUID = "1234"
    const pathEIP1581Root = "m/43'/60'/1581'"
    const pathEIP1581Chat = pathEIP1581Root + "/0'/0"
    const pathWalletRoot = "m/44'/60'/0'/0"
    const pathDefaultWalletAccount = pathWalletRoot + "/0"
    const customWalletPath1 = pathWalletRoot + "/1"
    const customWalletPath2 = pathWalletRoot + "/2"
    var allGeneratedPaths []string
    allGeneratedPaths = append(allGeneratedPaths, pathEIP1581Root, pathEIP1581Chat, pathWalletRoot, pathDefaultWalletAccount, customWalletPath1, customWalletPath2)

    var err error

    keystoreContainsFileForAccount := func(keyStoreDir string, hexAddress string) bool {
        addrWithoutPrefix := strings.ToLower(hexAddress[2:])
        found := false
        err = filepath.Walk(keyStoreDir, func(path string, fileInfo os.FileInfo, err error) error {
            if err != nil {
                return err
            }
            if !fileInfo.IsDir() && strings.Contains(strings.ToUpper(path), strings.ToUpper(addrWithoutPrefix)) {
                found = true
            }
            return nil
        })
        return found
    }

    rootDataDir := t.TempDir()

    keyStoreDir := filepath.Join(rootDataDir, "keystore")

    utils.Init()

    backend, stop1, stop2, stopWallet, err := setupGethStatusBackend()
    defer func() {
        err := stop1()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    defer func() {
        err := stop2()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    defer func() {
        err := stopWallet()
        if err != nil {
            require.NoError(t, backend.StopNode())
        }
    }()
    require.NoError(t, err)

    backend.rootDataDir = rootDataDir
    require.NoError(t, backend.AccountManager().InitKeystore(keyStoreDir))
    err = backend.OpenAccounts()
    require.NoError(t, err)

    genAccInfo, err := backend.AccountManager().AccountsGenerator().ImportMnemonic(mnemonic, "")
    assert.NoError(t, err)

    masterAddress := genAccInfo.Address

    accountInfo, err := backend.AccountManager().AccountsGenerator().StoreAccount(genAccInfo.ID, password)
    assert.NoError(t, err)

    found := keystoreContainsFileForAccount(keyStoreDir, accountInfo.Address)
    require.True(t, found)

    derivedAccounts, err := backend.AccountManager().AccountsGenerator().StoreDerivedAccounts(genAccInfo.ID, password, allGeneratedPaths)
    assert.NoError(t, err)

    chatKey := derivedAccounts[pathEIP1581Chat].PrivateKey[2:]
    chatAddress := derivedAccounts[pathEIP1581Chat].Address
    found = keystoreContainsFileForAccount(keyStoreDir, chatAddress)
    require.True(t, found)

    defaultSettings, err := defaultSettings(genAccInfo, derivedAccounts)
    require.NoError(t, err)
    nodeConfig, err := defaultNodeConfig(defaultSettings.InstallationID, &requests.CreateAccount{
        LogLevel: defaultSettings.LogLevel,
    })
    require.NoError(t, err)
    nodeConfig.DataDir = rootDataDir
    nodeConfig.KeyStoreDir = keyStoreDir

    profileKeypair := &accounts.Keypair{
        KeyUID:      genAccInfo.KeyUID,
        Name:        "Profile Name",
        Type:        accounts.KeypairTypeProfile,
        DerivedFrom: masterAddress,
    }

    profileKeypair.Accounts = append(profileKeypair.Accounts, &accounts.Account{
        Address:   types.HexToAddress(chatAddress),
        KeyUID:    profileKeypair.KeyUID,
        Type:      accounts.AccountTypeGenerated,
        PublicKey: types.Hex2Bytes(accountInfo.PublicKey),
        Path:      pathEIP1581Chat,
        Wallet:    false,
        Chat:      true,
        Name:      "GeneratedAccount",
    })

    for p, dAccInfo := range derivedAccounts {
        found = keystoreContainsFileForAccount(keyStoreDir, dAccInfo.Address)
        require.NoError(t, err)
        require.True(t, found)

        if p == pathDefaultWalletAccount ||
            p == customWalletPath1 ||
            p == customWalletPath2 {
            wAcc := &accounts.Account{
                Address: types.HexToAddress(dAccInfo.Address),
                KeyUID:  genAccInfo.KeyUID,
                Wallet:  false,
                Chat:    false,
                Type:    accounts.AccountTypeGenerated,
                Path:    p,
                Name:    "derivacc" + p,
                Hidden:  false,
                Removed: false,
            }
            if p == pathDefaultWalletAccount {
                wAcc.Wallet = true
            }
            profileKeypair.Accounts = append(profileKeypair.Accounts, wAcc)
        }
    }

    account := multiaccounts.Account{
        Name:      profileKeypair.Name,
        Timestamp: 1,
        KeyUID:    profileKeypair.KeyUID,
    }

    err = backend.ensureAppDBOpened(account, password)
    require.NoError(t, err)

    err = backend.StartNodeWithAccountAndInitialConfig(account, password, *defaultSettings, nodeConfig, profileKeypair.Accounts)
    require.NoError(t, err)
    multiaccounts, err := backend.GetAccounts()
    require.NoError(t, err)
    require.NotEmpty(t, multiaccounts[0].ColorHash)
    serverMessenger := backend.Messenger()
    require.NotNil(t, serverMessenger)

    files, err := ioutil.ReadDir(rootDataDir)
    require.NoError(t, err)
    require.NotEqual(t, 3, len(files))

    keycardAccount := account
    keycardAccount.KeycardPairing = "pairing"

    keycardSettings := settings.Settings{
        KeycardInstanceUID: "0xdeadbeef",
        KeycardPairedOn:    1,
        KeycardPairing:     "pairing",
    }

    // Ensure we're able to open the DB
    err = backend.ensureAppDBOpened(keycardAccount, keycardPassword)
    require.NoError(t, err)

    // db creation
    db, err := accounts.NewDB(backend.appDB)
    require.NoError(t, err)

    // Check that there is no registered keycards
    keycards, err := db.GetKeycardsWithSameKeyUID(genAccInfo.KeyUID)
    require.NoError(t, err)
    require.Equal(t, 0, len(keycards))

    // Converting to a keycard account
    err = backend.ConvertToKeycardAccount(keycardAccount, keycardSettings, keycardUID, password, keycardPassword)
    require.NoError(t, err)

    // Validating results of converting to a keycard account.
    // All keystore files for the account which is migrated need to be removed.
    found = keystoreContainsFileForAccount(keyStoreDir, masterAddress)
    require.False(t, found)

    for _, dAccInfo := range derivedAccounts {
        found = keystoreContainsFileForAccount(keyStoreDir, dAccInfo.Address)
        require.False(t, found)
    }

    require.NoError(t, backend.Logout())
    require.NoError(t, backend.StopNode())

    require.NoError(t, backend.AccountManager().InitKeystore(keyStoreDir))
    require.NoError(t, backend.OpenAccounts())

    require.NoError(t, backend.StartNodeWithKey(account, keycardPassword, chatKey, nodeConfig))
    defer func() {
        assert.NoError(t, backend.Logout())
        assert.NoError(t, backend.StopNode())
    }()

    // Ensure we're able to open the DB
    err = backend.ensureAppDBOpened(keycardAccount, keycardPassword)
    require.NoError(t, err)

    // db creation after re-encryption
    db1, err := accounts.NewDB(backend.appDB)
    require.NoError(t, err)

    // Check that there is a registered keycard
    keycards, err = db1.GetKeycardsWithSameKeyUID(genAccInfo.KeyUID)
    require.NoError(t, err)
    require.Equal(t, 1, len(keycards))

    // Converting to a regular account
    err = backend.ConvertToRegularAccount(mnemonic, keycardPassword, password)
    require.NoError(t, err)

    // Validating results of converting to a regular account.
    // All keystore files for need to be created.
    found = keystoreContainsFileForAccount(keyStoreDir, accountInfo.Address)
    require.True(t, found)

    for _, dAccInfo := range derivedAccounts {
        found = keystoreContainsFileForAccount(keyStoreDir, dAccInfo.Address)
        require.True(t, found)
    }

    found = keystoreContainsFileForAccount(keyStoreDir, masterAddress)
    require.True(t, found)

    // Ensure we're able to open the DB
    err = backend.ensureAppDBOpened(keycardAccount, password)
    require.NoError(t, err)

    // db creation after re-encryption
    db2, err := accounts.NewDB(backend.appDB)
    require.NoError(t, err)

    // Check that there is no registered keycards
    keycards, err = db2.GetKeycardsWithSameKeyUID(genAccInfo.KeyUID)
    require.NoError(t, err)
    require.Equal(t, 0, len(keycards))
}

func copyFile(srcFolder string, dstFolder string, fileName string, t *testing.T) {
    data, err := ioutil.ReadFile(path.Join(srcFolder, fileName))
    if err != nil {
        t.Fail()
    }

    err = ioutil.WriteFile(path.Join(dstFolder, fileName), data, 0600)
    if err != nil {
        t.Fail()
    }
}

func copyDir(srcFolder string, dstFolder string, t *testing.T) {
    files, err := ioutil.ReadDir(srcFolder)
    require.NoError(t, err)
    for _, file := range files {
        if !file.IsDir() {
            copyFile(srcFolder, dstFolder, file.Name(), t)
        } else {
            childFolder := path.Join(srcFolder, file.Name())
            newFolder := path.Join(dstFolder, file.Name())
            err = os.MkdirAll(newFolder, os.ModePerm)
            require.NoError(t, err)
            copyDir(childFolder, newFolder, t)
        }
    }
}

func loginDesktopUser(t *testing.T, conf *params.NodeConfig) {
    // The following passwords and DB used in this test unit are only
    // used to determine if login process works correctly after a migration

    // Expected account data:
    keyUID := "0x7c46c8f6f059ab72d524f2a6d356904db30bb0392636172ab3929a6bd2220f84" // #nosec G101
    username := "TestUser"
    passwd := "0xC888C9CE9E098D5864D3DED6EBCC140A12142263BACE3A23A36F9905F12BD64A" // #nosec G101

    b := NewGethStatusBackend()

    require.NoError(t, b.AccountManager().InitKeystore(conf.KeyStoreDir))
    b.UpdateRootDataDir(conf.DataDir)

    require.NoError(t, b.OpenAccounts())

    accounts, err := b.GetAccounts()
    require.NoError(t, err)

    require.Len(t, accounts, 1)
    require.Equal(t, username, accounts[0].Name)
    require.Equal(t, keyUID, accounts[0].KeyUID)

    wg := sync.WaitGroup{}
    wg.Add(1)
    go func() {
        defer wg.Done()
        err := b.StartNodeWithAccount(accounts[0], passwd, conf)
        require.NoError(t, err)
    }()

    wg.Wait()
    require.NoError(t, b.Logout())
    require.NotNil(t, b.statusNode.HTTPServer())
    require.NoError(t, b.StopNode())

}

func TestLoginAndMigrationsStillWorkWithExistingDesktopUser(t *testing.T) {
    utils.Init()

    srcFolder := "../static/test-0.132.0-account/"

    tmpdir := t.TempDir()

    copyDir(srcFolder, tmpdir, t)

    conf, err := params.NewNodeConfig(tmpdir, 1777)
    require.NoError(t, err)

    loginDesktopUser(t, conf)
    loginDesktopUser(t, conf) // Login twice to catch weird errors that only appear after logout
}

func TestChangeDatabasePassword(t *testing.T) {
    oldPassword := "password"
    newPassword := "newPassword"

    backend := NewGethStatusBackend()
    backend.UpdateRootDataDir(t.TempDir())

    // Setup keystore to test decryption of it
    keyStoreDir := t.TempDir()
    require.NoError(t, backend.accountManager.InitKeystore(keyStoreDir))

    _, accountInfo, _, err := backend.accountManager.CreateAccount(oldPassword)
    require.NoError(t, err)

    account := multiaccounts.Account{
        Name:          "TestAccount",
        Timestamp:     1,
        KeyUID:        "0x7c46c8f6f059ab72d524f2a6d356904db30bb0392636172ab3929a6bd2220f84",
        KDFIterations: 1,
    }

    // Initialize accounts DB
    err = backend.OpenAccounts()
    require.NoError(t, err)
    err = backend.SaveAccount(account)
    require.NoError(t, err)

    // Created DBs with old password
    err = backend.ensureDBsOpened(account, oldPassword)
    require.NoError(t, err)

    // Change password
    err = backend.ChangeDatabasePassword(account.KeyUID, oldPassword, newPassword)
    require.NoError(t, err)

    // Test that DBs can be opened with new password
    appDbPath, err := backend.getAppDBPath(account.KeyUID)
    require.NoError(t, err)
    appDb, err := sqlite.OpenDB(appDbPath, newPassword, account.KDFIterations)
    require.NoError(t, err)
    appDb.Close()

    walletDbPath, err := backend.getWalletDBPath(account.KeyUID)
    require.NoError(t, err)
    walletDb, err := sqlite.OpenDB(walletDbPath, newPassword, account.KDFIterations)
    require.NoError(t, err)
    walletDb.Close()

    // Test that keystore can be decrypted with the new password
    acc, key, err := backend.accountManager.AddressToDecryptedAccount(accountInfo.WalletAddress, newPassword)
    require.NoError(t, err)
    require.NotNil(t, acc)
    require.NotNil(t, key)
    require.Equal(t, acc.Address, key.Address)
}

func TestCreateWallet(t *testing.T) {
    utils.Init()
    password := "some-password2" // nolint: goconst
    tmpdir := t.TempDir()

    b := NewGethStatusBackend()
    defer func() {
        require.NoError(t, b.StopNode())
    }()

    createAccountRequest := &requests.CreateAccount{
        DisplayName:        "some-display-name",
        CustomizationColor: "#ffffff",
        Emoji:              "emoji",
        Password:           password,
        RootDataDir:        tmpdir,
        LogFilePath:        tmpdir + "/log",
    }
    c := make(chan interface{}, 10)
    signal.SetMobileSignalHandler(func(data []byte) {
        if strings.Contains(string(data), "node.login") {
            c <- struct{}{}
        }
    })

    account, err := b.CreateAccountAndLogin(createAccountRequest)
    require.NoError(t, err)
    statusNode := b.statusNode
    require.NotNil(t, statusNode)

    walletService := statusNode.WalletService()
    require.NotNil(t, walletService)
    walletAPI := walletservice.NewAPI(walletService)

    paths := []string{"m/44'/60'/0'/0/1"}

    db, err := accounts.NewDB(b.appDB)
    require.NoError(t, err)
    walletRootAddress, err := db.GetWalletRootAddress()
    require.NoError(t, err)

    mnemonic, err := db.Mnemonic()
    require.NoError(t, err)
    require.NotEmpty(t, mnemonic)

    derivedAddress, err := walletAPI.GetDerivedAddresses(context.Background(), password, walletRootAddress.String(), paths)
    require.NoError(t, err)
    require.Len(t, derivedAddress, 1)

    accountsService := statusNode.AccountService()
    require.NotNil(t, accountsService)
    accountsAPI := accountsService.AccountsAPI()

    err = accountsAPI.AddAccount(context.Background(), password, &accounts.Account{
        KeyUID:    account.KeyUID,
        Type:      accounts.AccountTypeGenerated,
        PublicKey: derivedAddress[0].PublicKey,
        Emoji:     "some",
        ColorID:   "so",
        Name:      "some name",
        Path:      derivedAddress[0].Path,
    })
    require.NoError(t, err)
}

func TestSetFleet(t *testing.T) {
    utils.Init()
    password := "some-password2" // nolint: goconst
    tmpdir := t.TempDir()

    b := NewGethStatusBackend()
    createAccountRequest := &requests.CreateAccount{
        DisplayName:        "some-display-name",
        CustomizationColor: "#ffffff",
        Password:           password,
        RootDataDir:        tmpdir,
        LogFilePath:        tmpdir + "/log",
        Emoji:              "some",
    }
    c := make(chan interface{}, 10)
    signal.SetMobileSignalHandler(func(data []byte) {
        if strings.Contains(string(data), "node.login") {
            c <- struct{}{}
        }
    })

    newAccount, err := b.CreateAccountAndLogin(createAccountRequest)
    require.NoError(t, err)
    statusNode := b.statusNode
    require.NotNil(t, statusNode)

    savedSettings, err := b.GetSettings()
    require.NoError(t, err)
    require.Empty(t, savedSettings.Fleet)

    accountsDB, err := b.accountsDB()
    require.NoError(t, err)
    err = accountsDB.SaveSettingField(settings.Fleet, params.FleetShardsTest)
    require.NoError(t, err)

    savedSettings, err = b.GetSettings()
    require.NoError(t, err)
    require.NotEmpty(t, savedSettings.Fleet)
    require.Equal(t, params.FleetShardsTest, *savedSettings.Fleet)

    require.NoError(t, b.Logout())

    loginAccountRequest := &requests.Login{
        KeyUID:   newAccount.KeyUID,
        Password: password,
    }
    require.NoError(t, b.LoginAccount(loginAccountRequest))
    select {
    case <-c:
        break
    case <-time.After(5 * time.Second):
        t.FailNow()
    }
    // Check is using the right fleet
    require.Equal(t, b.config.ClusterConfig.WakuNodes, params.DefaultWakuNodes(params.FleetShardsTest))

    require.NoError(t, b.Logout())
}

func TestWalletConfigOnLoginAccount(t *testing.T) {
    utils.Init()
    password := "some-password2" // nolint: goconst
    tmpdir := t.TempDir()
    poktToken := "grove-token"    // nolint: goconst
    infuraToken := "infura-token" // nolint: goconst
    alchemyEthereumMainnetToken := "alchemy-ethereum-mainnet-token"
    alchemyEthereumSepoliaToken := "alchemy-ethereum-sepolia-token"
    alchemyArbitrumMainnetToken := "alchemy-arbitrum-mainnet-token"
    alchemyArbitrumSepoliaToken := "alchemy-arbitrum-sepolia-token"
    alchemyOptimismMainnetToken := "alchemy-optimism-mainnet-token"
    alchemyOptimismSepoliaToken := "alchemy-optimism-sepolia-token"
    raribleMainnetAPIKey := "rarible-mainnet-api-key" // nolint: gosec
    raribleTestnetAPIKey := "rarible-testnet-api-key" // nolint: gosec

    b := NewGethStatusBackend()
    createAccountRequest := &requests.CreateAccount{
        DisplayName:        "some-display-name",
        CustomizationColor: "#ffffff",
        Password:           password,
        RootDataDir:        tmpdir,
        LogFilePath:        tmpdir + "/log",
        Emoji:              "some",
    }
    c := make(chan interface{}, 10)
    signal.SetMobileSignalHandler(func(data []byte) {
        if strings.Contains(string(data), "node.login") {
            c <- struct{}{}
        }
    })

    newAccount, err := b.CreateAccountAndLogin(createAccountRequest)
    require.NoError(t, err)
    statusNode := b.statusNode
    require.NotNil(t, statusNode)

    require.NoError(t, b.Logout())

    loginAccountRequest := &requests.Login{
        KeyUID:   newAccount.KeyUID,
        Password: password,
        WalletSecretsConfig: requests.WalletSecretsConfig{
            PoktToken:                   poktToken,
            InfuraToken:                 infuraToken,
            AlchemyEthereumMainnetToken: alchemyEthereumMainnetToken,
            AlchemyEthereumSepoliaToken: alchemyEthereumSepoliaToken,
            AlchemyArbitrumMainnetToken: alchemyArbitrumMainnetToken,
            AlchemyArbitrumSepoliaToken: alchemyArbitrumSepoliaToken,
            AlchemyOptimismMainnetToken: alchemyOptimismMainnetToken,
            AlchemyOptimismSepoliaToken: alchemyOptimismSepoliaToken,
            RaribleMainnetAPIKey:        raribleMainnetAPIKey,
            RaribleTestnetAPIKey:        raribleTestnetAPIKey,
        },
    }

    require.NoError(t, b.LoginAccount(loginAccountRequest))
    select {
    case <-c:
        break
    case <-time.After(5 * time.Second):
        t.FailNow()
    }

    require.Equal(t, b.config.WalletConfig.InfuraAPIKey, infuraToken)
    require.Equal(t, b.config.WalletConfig.AlchemyAPIKeys[mainnetChainID], alchemyEthereumMainnetToken)
    require.Equal(t, b.config.WalletConfig.AlchemyAPIKeys[sepoliaChainID], alchemyEthereumSepoliaToken)
    require.Equal(t, b.config.WalletConfig.AlchemyAPIKeys[arbitrumChainID], alchemyArbitrumMainnetToken)
    require.Equal(t, b.config.WalletConfig.AlchemyAPIKeys[arbitrumSepoliaChainID], alchemyArbitrumSepoliaToken)
    require.Equal(t, b.config.WalletConfig.AlchemyAPIKeys[optimismChainID], alchemyOptimismMainnetToken)
    require.Equal(t, b.config.WalletConfig.AlchemyAPIKeys[optimismSepoliaChainID], alchemyOptimismSepoliaToken)
    require.Equal(t, b.config.WalletConfig.RaribleMainnetAPIKey, raribleMainnetAPIKey)
    require.Equal(t, b.config.WalletConfig.RaribleTestnetAPIKey, raribleTestnetAPIKey)

    require.NoError(t, b.Logout())
}

func TestTestnetEnabledSettingOnCreateAccount(t *testing.T) {
    utils.Init()
    tmpdir := t.TempDir()

    b := NewGethStatusBackend()

    // Creating an account with test networks enabled
    createAccountRequest1 := &requests.CreateAccount{
        DisplayName:         "User-1",
        CustomizationColor:  "#ffffff",
        Emoji:               "some",
        Password:            "password123",
        RootDataDir:         tmpdir,
        LogFilePath:         tmpdir + "/log",
        TestNetworksEnabled: true,
    }
    _, err := b.CreateAccountAndLogin(createAccountRequest1)
    require.NoError(t, err)
    statusNode := b.statusNode
    require.NotNil(t, statusNode)

    settings, err := b.GetSettings()
    require.NoError(t, err)
    require.True(t, settings.TestNetworksEnabled)

    require.NoError(t, b.Logout())

    // Creating an account with test networks disabled
    createAccountRequest2 := &requests.CreateAccount{
        DisplayName:        "User-2",
        CustomizationColor: "#ffffff",
        Emoji:              "some",
        Password:           "password",
        RootDataDir:        tmpdir,
        LogFilePath:        tmpdir + "/log",
    }
    _, err = b.CreateAccountAndLogin(createAccountRequest2)
    require.NoError(t, err)
    statusNode = b.statusNode
    require.NotNil(t, statusNode)

    settings, err = b.GetSettings()
    require.NoError(t, err)
    require.False(t, settings.TestNetworksEnabled)

    require.NoError(t, b.Logout())
}

func TestRestoreAccountAndLogin(t *testing.T) {
    utils.Init()
    tmpdir := t.TempDir()

    backend := NewGethStatusBackend()

    // Test case 1: Valid restore account request
    restoreRequest := &requests.RestoreAccount{
        Mnemonic:    "test test test test test test test test test test test test",
        FetchBackup: false,
        CreateAccount: requests.CreateAccount{
            DisplayName:           "Account1",
            DeviceName:            "StatusIM",
            Password:              "password",
            CustomizationColor:    "0x000000",
            BackupDisabledDataDir: tmpdir,
        },
    }
    account, err := backend.RestoreAccountAndLogin(restoreRequest)
    require.NoError(t, err)
    require.NotNil(t, account)

    // Test case 2: Invalid restore account request
    invalidRequest := &requests.RestoreAccount{}
    _, err = backend.RestoreAccountAndLogin(invalidRequest)
    require.Error(t, err)

    db, err := accounts.NewDB(backend.appDB)
    require.NoError(t, err)
    mnemonic, err := db.Mnemonic()
    require.NoError(t, err)
    require.Empty(t, mnemonic)
}

func TestCreateAccountPathsValidation(t *testing.T) {
    tmpdir := t.TempDir()

    validation := &requests.CreateAccountValidation{
        AllowEmptyDisplayName: false,
    }

    request := &requests.CreateAccount{
        DisplayName:        "User-1",
        Password:           "password123",
        CustomizationColor: "#ffffff",
        RootDataDir:        tmpdir,
    }

    err := request.Validate(validation)
    require.NoError(t, err)

    request.RootDataDir = ""
    request.BackupDisabledDataDir = tmpdir
    err = request.Validate(validation)
    require.NoError(t, err)
    require.Equal(t, tmpdir, request.RootDataDir)
}