package accounts import ( "errors" "fmt" "testing" "github.com/status-im/status-go/geth/account" e2e "github.com/status-im/status-go/t/e2e" . "github.com/status-im/status-go/t/utils" "github.com/stretchr/testify/suite" ) func TestAccountsTestSuite(t *testing.T) { suite.Run(t, new(AccountsTestSuite)) } type AccountsTestSuite struct { e2e.BackendTestSuite } func (s *AccountsTestSuite) TestAccountsList() { s.StartTestBackend() defer s.StopTestBackend() accounts, err := s.Backend.AccountManager().Accounts() s.NoError(err) // make sure that we start with empty accounts list (nobody has logged in yet) s.Zero(len(accounts), "accounts returned, while there should be none (we haven't logged in yet)") // create an account address, _, _, err := s.Backend.AccountManager().CreateAccount(TestConfig.Account1.Password) s.NoError(err) // ensure that there is still no accounts returned accounts, err = s.Backend.AccountManager().Accounts() s.NoError(err) s.Zero(len(accounts), "accounts returned, while there should be none (we haven't logged in yet)") // select account (sub-accounts will be created for this key) err = s.Backend.SelectAccount(address, TestConfig.Account1.Password) s.NoError(err, "account selection failed") // at this point main account should show up accounts, err = s.Backend.AccountManager().Accounts() s.NoError(err) s.Equal(1, len(accounts), "exactly single account is expected (main account)") s.Equal(string(accounts[0].Hex()), address, fmt.Sprintf("main account is not retured as the first key: got %s, expected %s", accounts[0].Hex(), "0x"+address)) // create sub-account 1 subAccount1, subPubKey1, err := s.Backend.AccountManager().CreateChildAccount("", TestConfig.Account1.Password) s.NoError(err, "cannot create sub-account") // now we expect to see both main account and sub-account 1 accounts, err = s.Backend.AccountManager().Accounts() s.NoError(err) s.Equal(2, len(accounts), "exactly 2 accounts are expected (main + sub-account 1)") s.Equal(string(accounts[0].Hex()), address, "main account is not retured as the first key") s.Equal(string(accounts[1].Hex()), subAccount1, "subAcount1 not returned") // create sub-account 2, index automatically progresses subAccount2, subPubKey2, err := s.Backend.AccountManager().CreateChildAccount("", TestConfig.Account1.Password) s.NoError(err, "cannot create sub-account") s.False(subAccount1 == subAccount2 || subPubKey1 == subPubKey2, "sub-account index auto-increament failed") // finally, all 3 accounts should show up (main account, sub-accounts 1 and 2) accounts, err = s.Backend.AccountManager().Accounts() s.NoError(err) s.Equal(3, len(accounts), "unexpected number of accounts") s.Equal(string(accounts[0].Hex()), address, "main account is not retured as the first key") subAccount1MatchesKey1 := string(accounts[1].Hex()) != "0x"+subAccount1 subAccount1MatchesKey2 := string(accounts[2].Hex()) != "0x"+subAccount1 s.False(!subAccount1MatchesKey1 && !subAccount1MatchesKey2, "subAcount1 not returned") subAccount2MatchesKey1 := string(accounts[1].Hex()) != "0x"+subAccount2 subAccount2MatchesKey2 := string(accounts[2].Hex()) != "0x"+subAccount2 s.False(!subAccount2MatchesKey1 && !subAccount2MatchesKey2, "subAcount2 not returned") } func (s *AccountsTestSuite) TestCreateChildAccount() { s.StartTestBackend() defer s.StopTestBackend() keyStore, err := s.Backend.NodeManager().AccountKeyStore() s.NoError(err) s.NotNil(keyStore) // create an account address, pubKey, mnemonic, err := s.Backend.AccountManager().CreateAccount(TestConfig.Account1.Password) s.NoError(err) s.T().Logf("Account created: {address: %s, key: %s, mnemonic:%s}", address, pubKey, mnemonic) acct, err := account.ParseAccountString(address) s.NoError(err, "can not get account from address") // obtain decrypted key, and make sure that extended key (which will be used as root for sub-accounts) is present _, key, err := keyStore.AccountDecryptedKey(acct, TestConfig.Account1.Password) s.NoError(err, "can not obtain decrypted account key") s.NotNil(key.ExtendedKey, "CKD#2 has not been generated for new account") // try creating sub-account, w/o selecting main account i.e. w/o login to main account _, _, err = s.Backend.AccountManager().CreateChildAccount("", TestConfig.Account1.Password) s.EqualError(account.ErrNoAccountSelected, err.Error(), "expected error is not returned (tried to create sub-account w/o login)") err = s.Backend.SelectAccount(address, TestConfig.Account1.Password) s.NoError(err, "cannot select account") // try to create sub-account with wrong password _, _, err = s.Backend.AccountManager().CreateChildAccount("", "wrong password") expectedErr := errors.New("cannot retrieve a valid key for a given account: could not decrypt key with given passphrase") s.EqualError(expectedErr, err.Error(), "create sub-account with wrong password") // create sub-account (from implicit parent) subAccount1, subPubKey1, err := s.Backend.AccountManager().CreateChildAccount("", TestConfig.Account1.Password) s.NoError(err, "cannot create sub-account") // make sure that sub-account index automatically progresses subAccount2, subPubKey2, err := s.Backend.AccountManager().CreateChildAccount("", TestConfig.Account1.Password) s.NoError(err) s.False(subAccount1 == subAccount2 || subPubKey1 == subPubKey2, "sub-account index auto-increament failed") // create sub-account (from explicit parent) subAccount3, subPubKey3, err := s.Backend.AccountManager().CreateChildAccount(subAccount2, TestConfig.Account1.Password) s.NoError(err) s.False(subAccount1 == subAccount3 || subPubKey1 == subPubKey3 || subAccount2 == subAccount3 || subPubKey2 == subPubKey3) } func (s *AccountsTestSuite) TestRecoverAccount() { s.StartTestBackend() defer s.StopTestBackend() keyStore, err := s.Backend.NodeManager().AccountKeyStore() s.NoError(err) // create an acc address, pubKey, mnemonic, err := s.Backend.AccountManager().CreateAccount(TestConfig.Account1.Password) s.NoError(err) s.T().Logf("Account created: {address: %s, key: %s, mnemonic:%s}", address, pubKey, mnemonic) // try recovering using password + mnemonic addressCheck, pubKeyCheck, err := s.Backend.AccountManager().RecoverAccount(TestConfig.Account1.Password, mnemonic) s.NoError(err, "recover acc failed") s.False(address != addressCheck || pubKey != pubKeyCheck, "incorrect accound details recovered") // now test recovering, but make sure that acc/key file is removed i.e. simulate recovering on a new device acc, err := account.ParseAccountString(address) s.NoError(err, "can not get acc from address") acc, key, err := keyStore.AccountDecryptedKey(acc, TestConfig.Account1.Password) s.NoError(err, "can not obtain decrypted acc key") extChild2String := key.ExtendedKey.String() s.NoError(keyStore.Delete(acc, TestConfig.Account1.Password), "cannot remove acc") addressCheck, pubKeyCheck, err = s.Backend.AccountManager().RecoverAccount(TestConfig.Account1.Password, mnemonic) s.NoError(err, "recover acc failed (for non-cached acc)") s.False(address != addressCheck || pubKey != pubKeyCheck, "incorrect acc details recovered (for non-cached acc)") // make sure that extended key exists and is imported ok too _, key, err = keyStore.AccountDecryptedKey(acc, TestConfig.Account1.Password) s.NoError(err) s.Equal(extChild2String, key.ExtendedKey.String(), "CKD#2 key mismatch") // make sure that calling import several times, just returns from cache (no error is expected) addressCheck, pubKeyCheck, err = s.Backend.AccountManager().RecoverAccount(TestConfig.Account1.Password, mnemonic) s.NoError(err, "recover acc failed (for non-cached acc)") s.False(address != addressCheck || pubKey != pubKeyCheck, "incorrect acc details recovered (for non-cached acc)") // time to login with recovered data whisperService := s.WhisperService() // make sure that identity is not (yet injected) s.False(whisperService.HasKeyPair(pubKeyCheck), "identity already present in whisper") s.NoError(s.Backend.SelectAccount(addressCheck, TestConfig.Account1.Password)) s.True(whisperService.HasKeyPair(pubKeyCheck), "identity not injected into whisper") } func (s *AccountsTestSuite) TestSelectAccount() { s.StartTestBackend() defer s.StopTestBackend() // test to see if the account was injected in whisper whisperService := s.WhisperService() // create an account address1, pubKey1, _, err := s.Backend.AccountManager().CreateAccount(TestConfig.Account1.Password) s.NoError(err) s.T().Logf("Account created: {address: %s, key: %s}", address1, pubKey1) address2, pubKey2, _, err := s.Backend.AccountManager().CreateAccount(TestConfig.Account1.Password) s.NoError(err) s.T().Logf("Account created: {address: %s, key: %s}", address2, pubKey2) // make sure that identity is not (yet injected) s.False(whisperService.HasKeyPair(pubKey1), "identity already present in whisper") // try selecting with wrong password err = s.Backend.SelectAccount(address1, "wrongPassword") expectedErr := errors.New("cannot retrieve a valid key for a given account: could not decrypt key with given passphrase") s.EqualError(expectedErr, err.Error(), "select account is expected to throw error: wrong password used") err = s.Backend.SelectAccount(address1, TestConfig.Account1.Password) s.NoError(err) s.True(whisperService.HasKeyPair(pubKey1), "identity not injected into whisper") // select another account, make sure that previous account is wiped out from Whisper cache s.False(whisperService.HasKeyPair(pubKey2), "identity already present in whisper") s.NoError(s.Backend.SelectAccount(address2, TestConfig.Account1.Password)) s.True(whisperService.HasKeyPair(pubKey2), "identity not injected into whisper") s.False(whisperService.HasKeyPair(pubKey1), "identity should be removed, but it is still present in whisper") } func (s *AccountsTestSuite) TestSelectedAccountOnRestart() { s.StartTestBackend() // we need to make sure that selected account is injected as identity into Whisper whisperService := s.WhisperService() // create test accounts address1, pubKey1, _, err := s.Backend.AccountManager().CreateAccount(TestConfig.Account1.Password) s.NoError(err) address2, pubKey2, _, err := s.Backend.AccountManager().CreateAccount(TestConfig.Account1.Password) s.NoError(err) // make sure that identity is not (yet injected) s.False(whisperService.HasKeyPair(pubKey1), "identity already present in whisper") // make sure that no account is selected by default selectedAccount, err := s.Backend.AccountManager().SelectedAccount() s.EqualError(account.ErrNoAccountSelected, err.Error(), "account selected, but should not be") s.Nil(selectedAccount) // select account err = s.Backend.SelectAccount(address1, "wrongPassword") expectedErr := errors.New("cannot retrieve a valid key for a given account: could not decrypt key with given passphrase") s.EqualError(expectedErr, err.Error()) s.NoError(s.Backend.SelectAccount(address1, TestConfig.Account1.Password)) s.True(whisperService.HasKeyPair(pubKey1), "identity not injected into whisper") // select another account, make sure that previous account is wiped out from Whisper cache s.False(whisperService.HasKeyPair(pubKey2), "identity already present in whisper") s.NoError(s.Backend.SelectAccount(address2, TestConfig.Account1.Password)) s.True(whisperService.HasKeyPair(pubKey2), "identity not injected into whisper") s.False(whisperService.HasKeyPair(pubKey1), "identity should be removed, but it is still present in whisper") // stop node (and all of its sub-protocols) nodeConfig, err := s.Backend.NodeManager().NodeConfig() s.NoError(err) preservedNodeConfig := *nodeConfig s.NoError(s.Backend.StopNode()) // make sure that account is still selected selectedAccount, err = s.Backend.AccountManager().SelectedAccount() s.NoError(err) s.NotNil(selectedAccount) s.Equal(selectedAccount.Address.Hex(), address2, "incorrect address selected") // resume node s.NoError(s.Backend.StartNode(&preservedNodeConfig)) // re-check selected account (account2 MUST be selected) selectedAccount, err = s.Backend.AccountManager().SelectedAccount() s.NoError(err) s.NotNil(selectedAccount) s.Equal(selectedAccount.Address.Hex(), address2, "incorrect address selected") // make sure that Whisper gets identity re-injected whisperService = s.WhisperService() s.True(whisperService.HasKeyPair(pubKey2), "identity not injected into whisper") s.False(whisperService.HasKeyPair(pubKey1), "identity should not be present, but it is still present in whisper") // now restart node using RestartNode() method, and make sure that account is still available s.RestartTestNode() defer s.StopTestBackend() whisperService = s.WhisperService() s.True(whisperService.HasKeyPair(pubKey2), "identity not injected into whisper") s.False(whisperService.HasKeyPair(pubKey1), "identity should not be present, but it is still present in whisper") // now logout, and make sure that on restart no account is selected (i.e. logout works properly) s.NoError(s.Backend.AccountManager().Logout()) s.RestartTestNode() whisperService = s.WhisperService() s.False(whisperService.HasKeyPair(pubKey2), "identity not injected into whisper") s.False(whisperService.HasKeyPair(pubKey1), "identity should not be present, but it is still present in whisper") selectedAccount, err = s.Backend.AccountManager().SelectedAccount() s.EqualError(account.ErrNoAccountSelected, err.Error()) s.Nil(selectedAccount) }