github.com/status-im/status-go@v1.1.0/api/old_mobile_user_upgrading_from_v1_to_v2_test.go (about)

     1  package api
     2  
     3  import (
     4  	"context"
     5  	"database/sql"
     6  	"strings"
     7  	"testing"
     8  
     9  	d_common "github.com/status-im/status-go/common"
    10  
    11  	"github.com/status-im/status-go/appdatabase"
    12  	"github.com/status-im/status-go/common/dbsetup"
    13  	"github.com/status-im/status-go/sqlite"
    14  
    15  	"github.com/stretchr/testify/suite"
    16  
    17  	"github.com/status-im/status-go/eth-node/types"
    18  	"github.com/status-im/status-go/multiaccounts/accounts"
    19  	"github.com/status-im/status-go/multiaccounts/common"
    20  	"github.com/status-im/status-go/protocol/tt"
    21  	"github.com/status-im/status-go/t/utils"
    22  )
    23  
    24  const (
    25  	oldMobileUserKeyUID = "0x855ab0a932e5325daab7a550b9fcd78d2a17de5e2b7a52241f82505ea9d87629"
    26  	oldMobileUserPasswd = "0x20756cad9b728c8225fd8cedb6badaf8731e174506950219ea657cd54f35f46c" // #nosec G101
    27  
    28  	// what we have in table `accounts` before test run:
    29  	// 1. Address: 0x23A5CEF34B18920785F4B895849936F65CBDEF73
    30  	//    Wallet: 1, Chat: 0, Type: '', Path: m/44'/60'/0'/0/0, Name: 'Main account', Derived_from: '', Pubkey: 0x047B67AD2...
    31  	// 2. Address: 0x4851276E2B7DC3B8BEF1749127031BCB3578492D
    32  	//    Wallet: 0, Chat: 1, Type: '', Path: m/43'/60'/1581'/0'/0, Name: 'Cadetblue Fuzzy Flickertailsquirrel', Derived_from: '', Pubkey: 0x04F96F6F5...
    33  	// 3. Address: 0x4D26E5C2F85BA5D10BDA6B031E1C1579F8ECFA1F
    34  	//    Wallet: 0, Chat: 0, Type: 'generated', Path: m/44'/60'/0'/1, Name: 'generated', Derived_from: '', Pubkey: 0x04488EDA7...
    35  	// 4. Address: 0x516312D69737C5E6EF16F22E0097FF5D9F0C4196
    36  	//    Wallet: 0, Chat: 0, Type: 'key', Path: m/44'/60'/0'/0/0, Name: 'key', Derived_from: '', Pubkey: 0x040D5E4E3...
    37  	// 5. Address: 0x95222290DD7278AA3DDD389CC1E1D165CC4BAFE5
    38  	//    Wallet: 0, Chat: 0, Type: 'watch', Path: '', Name: 'watch-only', Derived_from: '', Pubkey: <null>
    39  	// 6. Address: 0xB7A1233D1309CE665A3A4DB088E4A046EB333545
    40  	//    Wallet: 0, Chat: 0, Type: 'seed', Path: m/44'/60'/0'/0/0, Name: 'seed', Derived_from: '', Pubkey: 0x04FDE3E5...
    41  	// seed phrase for 0xB7A1233D1309CE665A3A4DB088E4A046EB333545: vocal blouse script census island armor seek catch wool narrow peasant attract
    42  	// private key for 0x516312D69737C5E6EF16F22E0097FF5D9F0C4196: c3ad0b50652318f845565c13761e5369ce75dcbc2a94616e15b829d4b07410fe
    43  	// status account seed phrase: coin globe kit hamster notable proof orphan always mistake usual morning usage
    44  	srcFolder = "../static/test-mobile-release-1.20.x-aa6e4b2-account/"
    45  )
    46  
    47  type OldMobileUserUpgradingFromV1ToV2Test struct {
    48  	suite.Suite
    49  	tmpdir string
    50  }
    51  
    52  type PostLoginCheckCallback func(b *GethStatusBackend)
    53  
    54  func (s *OldMobileUserUpgradingFromV1ToV2Test) SetupTest() {
    55  	utils.Init()
    56  	s.tmpdir = s.T().TempDir()
    57  	copyDir(srcFolder, s.tmpdir, s.T())
    58  }
    59  
    60  func TestOldMobileUserUpgradingFromV1ToV2(t *testing.T) {
    61  	suite.Run(t, new(OldMobileUserUpgradingFromV1ToV2Test))
    62  }
    63  
    64  func (s *OldMobileUserUpgradingFromV1ToV2Test) loginMobileUser(check PostLoginCheckCallback) {
    65  	b := NewGethStatusBackend()
    66  	b.UpdateRootDataDir(s.tmpdir)
    67  	s.Require().NoError(b.OpenAccounts())
    68  	s.Require().NoError(b.Login(oldMobileUserKeyUID, oldMobileUserPasswd))
    69  
    70  	check(b)
    71  
    72  	s.Require().NoError(b.Logout())
    73  }
    74  
    75  func (s *OldMobileUserUpgradingFromV1ToV2Test) TestOptimizeMobileWakuV2SettingsForMobileV1() {
    76  	bkFunc := d_common.IsMobilePlatform
    77  	d_common.IsMobilePlatform = func() bool {
    78  		return true
    79  	}
    80  	defer func() {
    81  		d_common.IsMobilePlatform = bkFunc
    82  	}()
    83  
    84  	s.loginMobileUser(func(b *GethStatusBackend) {
    85  		nc, err := b.GetNodeConfig()
    86  		s.Require().NoError(err)
    87  		s.Require().True(nc.WakuV2Config.LightClient)
    88  		s.Require().False(nc.WakuV2Config.EnableStoreConfirmationForMessagesSent)
    89  	})
    90  }
    91  
    92  func (s *OldMobileUserUpgradingFromV1ToV2Test) TestLoginAndMigrationsStillWorkWithExistingMobileUser() {
    93  	checkAfterLogin := func(b *GethStatusBackend) {
    94  		db, err := accounts.NewDB(b.appDB)
    95  		s.Require().NoError(err)
    96  		accs, err := db.GetAllAccounts()
    97  		s.Require().NoError(err)
    98  		s.Require().True(len(accs) == 6)
    99  		kps, err := db.GetAllKeypairs()
   100  		s.Require().NoError(err)
   101  		s.Require().True(len(kps) == 3)
   102  
   103  		// Create a map to categorize keypairs by their type
   104  		keypairMap := make(map[accounts.KeypairType][]*accounts.Keypair)
   105  		for _, kp := range kps {
   106  			keypairMap[kp.Type] = append(keypairMap[kp.Type], kp)
   107  		}
   108  
   109  		// Check profile keypair
   110  		profileKps, ok := keypairMap[accounts.KeypairTypeProfile]
   111  		s.Require().True(ok, "Profile keypair not found")
   112  		s.Require().True(len(profileKps) == 1, "Unexpected number of profile keypairs")
   113  		s.Require().True(len(profileKps[0].Accounts) == 3)
   114  		for _, a := range profileKps[0].Accounts {
   115  			s.Require().Equal(a.KeyUID, oldMobileUserKeyUID)
   116  		}
   117  
   118  		generator := b.AccountManager().AccountsGenerator()
   119  		// Check seed keypair
   120  		seedKps, ok := keypairMap[accounts.KeypairTypeSeed]
   121  		s.Require().True(ok, "Seed keypair not found")
   122  		s.Require().True(len(seedKps) == 1, "Unexpected number of seed keypairs")
   123  		s.Require().True(len(seedKps[0].Accounts) == 1)
   124  		info, err := generator.LoadAccount(seedKps[0].Accounts[0].Address.Hex(), oldMobileUserPasswd)
   125  		s.Require().NoError(err)
   126  		s.Require().Equal(seedKps[0].KeyUID, info.KeyUID)
   127  		s.Require().Equal(seedKps[0].Accounts[0].KeyUID, info.KeyUID)
   128  		mnemonicNoExtraSpaces := strings.Join(strings.Fields("vocal blouse script census island armor seek catch wool narrow peasant attract"), " ")
   129  		importedSeedAccountInfo, err := generator.ImportMnemonic(mnemonicNoExtraSpaces, "")
   130  		s.Require().NoError(err)
   131  		derivedAddresses, err := generator.DeriveAddresses(importedSeedAccountInfo.ID, paths)
   132  		s.Require().NoError(err)
   133  		s.Require().Equal(derivedAddresses[pathDefaultWallet].PublicKey, "0x04fde3e58a7379161da2adf033fbee076e2ba11fca8b07c4d06610b399911a60017e4c108eae243487d19e273f99c2d6af13ff5e330783f4389212092b01cc616c")
   134  		//following line shows: we're unable to calculate the right KeyUID with the wrong public key from existing records for the imported seed account
   135  		s.Require().False(importedSeedAccountInfo.KeyUID == seedKps[0].KeyUID)
   136  
   137  		// Check key keypair
   138  		keyKps, ok := keypairMap[accounts.KeypairTypeKey]
   139  		s.Require().True(ok, "Key keypair not found")
   140  		s.Require().True(len(keyKps) == 1, "Unexpected number of key keypairs")
   141  		s.Require().True(len(keyKps[0].Accounts) == 1)
   142  		info, err = generator.LoadAccount(keyKps[0].Accounts[0].Address.Hex(), oldMobileUserPasswd)
   143  		s.Require().NoError(err)
   144  		s.Require().Equal(keyKps[0].KeyUID, info.KeyUID)
   145  		s.Require().Equal(keyKps[0].Accounts[0].KeyUID, info.KeyUID)
   146  		info, err = generator.ImportPrivateKey("c3ad0b50652318f845565c13761e5369ce75dcbc2a94616e15b829d4b07410fe")
   147  		s.Require().NoError(err)
   148  		s.Require().Equal(info.KeyUID, keyKps[0].KeyUID)
   149  	}
   150  
   151  	s.loginMobileUser(checkAfterLogin)
   152  	s.loginMobileUser(checkAfterLogin) // Login twice to catch weird errors that only appear after logout
   153  }
   154  
   155  // TestAddWalletAccount we should be able to add a wallet account after upgrading from mobile v1
   156  func (s *OldMobileUserUpgradingFromV1ToV2Test) TestAddWalletAccountAfterUpgradingFromMobileV1() {
   157  	b := NewGethStatusBackend()
   158  	b.UpdateRootDataDir(s.tmpdir)
   159  	s.Require().NoError(b.OpenAccounts())
   160  	s.Require().NoError(b.Login(oldMobileUserKeyUID, oldMobileUserPasswd))
   161  	db, _ := accounts.NewDB(b.appDB)
   162  	walletRootAddress, err := db.GetWalletRootAddress()
   163  	s.Require().NoError(err)
   164  	masterAddress, err := db.GetMasterAddress()
   165  	s.Require().NoError(err)
   166  
   167  	kps, _ := db.GetAllKeypairs()
   168  	// Create a map to categorize keypairs by their type
   169  	keypairMap := make(map[accounts.KeypairType][]*accounts.Keypair)
   170  	for _, kp := range kps {
   171  		keypairMap[kp.Type] = append(keypairMap[kp.Type], kp)
   172  	}
   173  	profileKps := keypairMap[accounts.KeypairTypeProfile]
   174  	profileKp := profileKps[0]
   175  	s.Require().True(profileKp.DerivedFrom == walletRootAddress.Hex())
   176  	s.Require().False(masterAddress.Hex() == walletRootAddress.Hex())
   177  	s.T().Logf("masterAddress: %s, walletRootAddress: %s", masterAddress.Hex(), walletRootAddress.Hex())
   178  
   179  	// simulate mobile frontend adding a wallet account
   180  	suggestedPath, err := db.ResolveSuggestedPathForKeypair(oldMobileUserKeyUID)
   181  	s.Require().NoError(err)
   182  	generator := b.AccountManager().AccountsGenerator()
   183  	accountInfo, err := generator.LoadAccount(profileKp.DerivedFrom, oldMobileUserPasswd)
   184  	s.Require().NoError(err)
   185  	infoMap, err := generator.DeriveAddresses(accountInfo.ID, []string{suggestedPath})
   186  	s.Require().NoError(err)
   187  	s.Require().Len(infoMap, 1)
   188  	deriveAccountInfo := infoMap[suggestedPath]
   189  	expectedDerivedAddress := "0xf44F8Ebc5b088e0eA8a0f7309A4a0c525AD783DA"
   190  	s.Require().Equal(expectedDerivedAddress, deriveAccountInfo.Address)
   191  	derivedAddress := types.HexToAddress(deriveAccountInfo.Address)
   192  	accountsAPI := b.StatusNode().AccountService().AccountsAPI()
   193  	err = accountsAPI.AddAccount(context.Background(), oldMobileUserPasswd, &accounts.Account{
   194  		Address:   derivedAddress,
   195  		KeyUID:    oldMobileUserKeyUID,
   196  		Wallet:    false,
   197  		Chat:      false,
   198  		Type:      accounts.AccountTypeGenerated,
   199  		Path:      suggestedPath,
   200  		PublicKey: types.Hex2Bytes(deriveAccountInfo.PublicKey),
   201  		Name:      "GeneratedAccount2",
   202  		Emoji:     "emoji",
   203  		ColorID:   common.CustomizationColorBlue,
   204  	})
   205  	s.Require().NoError(err)
   206  	// need retry since there's a possible of getting "no key for given address or file" error
   207  	err = tt.RetryWithBackOff(func() error {
   208  		return accountsAPI.DeleteAccount(context.Background(), derivedAddress)
   209  	})
   210  	s.Require().NoError(err)
   211  	s.Require().NoError(b.Logout())
   212  }
   213  
   214  func (s *OldMobileUserUpgradingFromV1ToV2Test) TestFixMissingKeyUIDForAccounts() {
   215  	db, err := sqlite.OpenDB(sqlite.InMemoryPath, "1234567890", dbsetup.ReducedKDFIterationsNumber)
   216  	s.Require().NoError(err)
   217  	tx, err := db.BeginTx(context.Background(), &sql.TxOptions{})
   218  	s.Require().NoError(err)
   219  	s.Require().ErrorContains(appdatabase.FixMissingKeyUIDForAccounts(tx), "no such table: accounts")
   220  	s.Require().NoError(tx.Rollback())
   221  
   222  	_, err = db.Exec(`
   223  create table accounts
   224  (
   225      address      VARCHAR not null primary key,
   226      wallet       BOOLEAN,
   227      chat         BOOLEAN,
   228      type         TEXT,
   229      storage      TEXT,
   230      pubkey       BLOB,
   231      path         TEXT,
   232      name         TEXT,
   233      color        TEXT,
   234      created_at   DATETIME           not null,
   235      updated_at   DATETIME           not null,
   236      hidden       BOOL default FALSE not null,
   237      emoji        TEXT default ""    not null,
   238      derived_from TEXT default ""    not null,
   239      clock        INT  default 0     not null
   240  ) without rowid;`)
   241  	s.Require().NoError(err)
   242  	tx, err = db.BeginTx(context.Background(), &sql.TxOptions{})
   243  	s.Require().NoError(err)
   244  	s.Require().ErrorContains(appdatabase.FixMissingKeyUIDForAccounts(tx), "no such table: settings")
   245  	s.Require().NoError(tx.Rollback())
   246  
   247  	_, err = db.Exec(`
   248  create table settings
   249  (
   250      address                               VARCHAR                    not null,
   251      key_uid                               VARCHAR                    not null,
   252      latest_derived_path                   UNSIGNED INT default 0,
   253      public_key                            VARCHAR                    not null,
   254      synthetic_id                          VARCHAR      default 'id'  not null primary key,
   255      wallet_root_address                   VARCHAR                    not null
   256  ) without rowid;`)
   257  	s.Require().NoError(err)
   258  	tx, err = db.BeginTx(context.Background(), &sql.TxOptions{})
   259  	s.Require().NoError(err)
   260  	// no rows in `settings` table, but we expect no error
   261  	s.Require().NoError(appdatabase.FixMissingKeyUIDForAccounts(tx))
   262  	s.Require().NoError(tx.Commit())
   263  }