Multi account login (#1525)
* multi-account login and signing put methods count threshold back to 20 * validate login params * refactoring * use common.Address * remove unused var in test
This commit is contained in:
parent
2b96aa5456
commit
acfe9a721c
|
@ -32,6 +32,8 @@ var (
|
|||
ErrOnboardingAccountNotFound = errors.New("cannot find onboarding account with the given id")
|
||||
)
|
||||
|
||||
var zeroAddress = common.Address{}
|
||||
|
||||
// GethServiceProvider provides required geth services.
|
||||
type GethServiceProvider interface {
|
||||
AccountManager() (*accounts.Manager, error)
|
||||
|
@ -47,8 +49,9 @@ type Manager struct {
|
|||
accountsGenerator *generator.Generator
|
||||
onboarding *Onboarding
|
||||
|
||||
selectedWalletAccount *SelectedExtKey // account that was processed during the last call to SelectAccount()
|
||||
selectedChatAccount *SelectedExtKey // account that was processed during the last call to SelectAccount()
|
||||
selectedChatAccount *SelectedExtKey // account that was processed during the last call to SelectAccount()
|
||||
mainAccountAddress common.Address
|
||||
watchAddresses []common.Address
|
||||
}
|
||||
|
||||
// NewManager returns new node account manager.
|
||||
|
@ -103,66 +106,6 @@ func (m *Manager) CreateAccount(password string) (Info, string, error) {
|
|||
return info, mnemonic, nil
|
||||
}
|
||||
|
||||
// CreateChildAccount creates sub-account for an account identified by parent address.
|
||||
// CKD#2 is used as root for master accounts (when parentAddress is "").
|
||||
// Otherwise (when parentAddress != ""), child is derived directly from parent.
|
||||
func (m *Manager) CreateChildAccount(parentAddress, password string) (address, pubKey string, err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
keyStore, err := m.geth.AccountKeyStore()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if parentAddress == "" && m.selectedWalletAccount != nil { // derive from selected account by default
|
||||
parentAddress = m.selectedWalletAccount.Address.Hex()
|
||||
}
|
||||
|
||||
if parentAddress == "" {
|
||||
return "", "", ErrNoAccountSelected
|
||||
}
|
||||
|
||||
account, err := ParseAccountString(parentAddress)
|
||||
if err != nil {
|
||||
return "", "", ErrAddressToAccountMappingFailure
|
||||
}
|
||||
|
||||
// make sure that given password can decrypt key associated with a given parent address
|
||||
account, accountKey, err := keyStore.AccountDecryptedKey(account, password)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("%s: %v", ErrAccountToKeyMappingFailure.Error(), err)
|
||||
}
|
||||
|
||||
parentKey, err := extkeys.NewKeyFromString(accountKey.ExtendedKey.String())
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// derive child key
|
||||
childKey, err := parentKey.Child(accountKey.SubAccountIndex)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if err = keyStore.IncSubAccountIndex(account, password); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
accountKey.SubAccountIndex++
|
||||
|
||||
// import derived key into account keystore
|
||||
address, pubKey, err = m.importExtendedKey(extkeys.KeyPurposeWallet, childKey, password)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// update in-memory selected account
|
||||
if m.selectedWalletAccount != nil {
|
||||
m.selectedWalletAccount.AccountKey = accountKey
|
||||
}
|
||||
|
||||
return address, pubKey, nil
|
||||
}
|
||||
|
||||
// RecoverAccount re-creates master key using given details.
|
||||
// Once master key is re-generated, it is inserted into keystore (if not already there).
|
||||
func (m *Manager) RecoverAccount(password, mnemonic string) (Info, error) {
|
||||
|
@ -246,23 +189,19 @@ func (m *Manager) VerifyAccountPassword(keyStoreDir, address, password string) (
|
|||
|
||||
// SelectAccount selects current account, by verifying that address has corresponding account which can be decrypted
|
||||
// using provided password. Once verification is done, all previous identities are removed).
|
||||
func (m *Manager) SelectAccount(walletAddress, chatAddress, password string) error {
|
||||
func (m *Manager) SelectAccount(loginParams LoginParams) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
m.accountsGenerator.Reset()
|
||||
|
||||
selectedWalletAccount, err := m.unlockExtendedKey(walletAddress, password)
|
||||
selectedChatAccount, err := m.unlockExtendedKey(loginParams.ChatAddress.String(), loginParams.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
selectedChatAccount, err := m.unlockExtendedKey(chatAddress, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.selectedWalletAccount = selectedWalletAccount
|
||||
m.watchAddresses = loginParams.WatchAddresses
|
||||
m.mainAccountAddress = loginParams.MainAccount
|
||||
m.selectedChatAccount = selectedChatAccount
|
||||
|
||||
return nil
|
||||
|
@ -287,15 +226,24 @@ func (m *Manager) SetChatAccount(privKey *ecdsa.PrivateKey) {
|
|||
}
|
||||
}
|
||||
|
||||
// SelectedWalletAccount returns currently selected wallet account
|
||||
func (m *Manager) SelectedWalletAccount() (*SelectedExtKey, error) {
|
||||
// MainAccountAddress returns currently selected watch addresses.
|
||||
func (m *Manager) MainAccountAddress() (common.Address, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
if m.selectedWalletAccount == nil {
|
||||
return nil, ErrNoAccountSelected
|
||||
if m.mainAccountAddress == zeroAddress {
|
||||
return zeroAddress, ErrNoAccountSelected
|
||||
}
|
||||
return m.selectedWalletAccount, nil
|
||||
|
||||
return m.mainAccountAddress, nil
|
||||
}
|
||||
|
||||
// WatchAddresses returns currently selected watch addresses.
|
||||
func (m *Manager) WatchAddresses() []common.Address {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
return m.watchAddresses
|
||||
}
|
||||
|
||||
// SelectedChatAccount returns currently selected chat account
|
||||
|
@ -309,13 +257,14 @@ func (m *Manager) SelectedChatAccount() (*SelectedExtKey, error) {
|
|||
return m.selectedChatAccount, nil
|
||||
}
|
||||
|
||||
// Logout clears selectedWalletAccount.
|
||||
// Logout clears selected accounts.
|
||||
func (m *Manager) Logout() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
m.accountsGenerator.Reset()
|
||||
m.selectedWalletAccount = nil
|
||||
m.mainAccountAddress = zeroAddress
|
||||
m.watchAddresses = nil
|
||||
m.selectedChatAccount = nil
|
||||
}
|
||||
|
||||
|
@ -387,40 +336,12 @@ func (m *Manager) Accounts() ([]gethcommon.Address, error) {
|
|||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
am, err := m.geth.AccountManager()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
addresses := make([]gethcommon.Address, 0)
|
||||
if m.mainAccountAddress != zeroAddress {
|
||||
addresses = append(addresses, m.mainAccountAddress)
|
||||
}
|
||||
|
||||
var addresses []gethcommon.Address
|
||||
for _, wallet := range am.Wallets() {
|
||||
for _, account := range wallet.Accounts() {
|
||||
addresses = append(addresses, account.Address)
|
||||
}
|
||||
}
|
||||
|
||||
if m.selectedWalletAccount == nil {
|
||||
return []gethcommon.Address{}, nil
|
||||
}
|
||||
|
||||
m.refreshSelectedWalletAccount()
|
||||
|
||||
filtered := make([]gethcommon.Address, 0)
|
||||
for _, account := range addresses {
|
||||
// main account
|
||||
if m.selectedWalletAccount.Address.Hex() == account.Hex() {
|
||||
filtered = append(filtered, account)
|
||||
} else {
|
||||
// sub accounts
|
||||
for _, subAccount := range m.selectedWalletAccount.SubAccounts {
|
||||
if subAccount.Address.Hex() == account.Hex() {
|
||||
filtered = append(filtered, account)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filtered, nil
|
||||
return addresses, nil
|
||||
}
|
||||
|
||||
// StartOnboarding starts the onboarding process generating accountsCount accounts and returns a slice of OnboardingAccount.
|
||||
|
@ -472,63 +393,6 @@ func (m *Manager) ImportOnboardingAccount(id string, password string) (Info, str
|
|||
return info, acc.mnemonic, nil
|
||||
}
|
||||
|
||||
// refreshSelectedWalletAccount re-populates list of sub-accounts of the currently selected account (if any)
|
||||
func (m *Manager) refreshSelectedWalletAccount() {
|
||||
if m.selectedWalletAccount == nil {
|
||||
return
|
||||
}
|
||||
|
||||
accountKey := m.selectedWalletAccount.AccountKey
|
||||
if accountKey == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// re-populate list of sub-accounts
|
||||
subAccounts, err := m.findSubAccounts(accountKey.ExtendedKey, accountKey.SubAccountIndex)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
m.selectedWalletAccount = &SelectedExtKey{
|
||||
Address: m.selectedWalletAccount.Address,
|
||||
AccountKey: m.selectedWalletAccount.AccountKey,
|
||||
SubAccounts: subAccounts,
|
||||
}
|
||||
}
|
||||
|
||||
// findSubAccounts traverses cached accounts and adds as a sub-accounts any
|
||||
// that belong to the currently selected account.
|
||||
// The extKey is CKD#2 := root of sub-accounts of the main account
|
||||
func (m *Manager) findSubAccounts(extKey *extkeys.ExtendedKey, subAccountIndex uint32) ([]accounts.Account, error) {
|
||||
keyStore, err := m.geth.AccountKeyStore()
|
||||
if err != nil {
|
||||
return []accounts.Account{}, err
|
||||
}
|
||||
|
||||
subAccounts := make([]accounts.Account, 0)
|
||||
if extKey.Depth == 5 { // CKD#2 level
|
||||
// gather possible sub-account addresses
|
||||
subAccountAddresses := make([]gethcommon.Address, 0)
|
||||
for i := uint32(0); i < subAccountIndex; i++ {
|
||||
childKey, err := extKey.Child(i)
|
||||
if err != nil {
|
||||
return []accounts.Account{}, err
|
||||
}
|
||||
subAccountAddresses = append(subAccountAddresses, crypto.PubkeyToAddress(childKey.ToECDSA().PublicKey))
|
||||
}
|
||||
|
||||
// see if any of the gathered addresses actually exist in cached accounts list
|
||||
for _, cachedAccount := range keyStore.Accounts() {
|
||||
for _, possibleAddress := range subAccountAddresses {
|
||||
if possibleAddress.Hex() == cachedAccount.Address.Hex() {
|
||||
subAccounts = append(subAccounts, cachedAccount)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return subAccounts, nil
|
||||
}
|
||||
|
||||
// AddressToDecryptedAccount tries to load decrypted key for a given account.
|
||||
// The running node, has a keystore directory which is loaded on start. Key file
|
||||
// for a given address is expected to be in that directory prior to node start.
|
||||
|
@ -558,16 +422,9 @@ func (m *Manager) unlockExtendedKey(address, password string) (*SelectedExtKey,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// persist account key for easier recovery of currently selected key
|
||||
subAccounts, err := m.findSubAccounts(accountKey.ExtendedKey, accountKey.SubAccountIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
selectedExtendedKey := &SelectedExtKey{
|
||||
Address: account.Address,
|
||||
AccountKey: accountKey,
|
||||
SubAccounts: subAccounts,
|
||||
Address: account.Address,
|
||||
AccountKey: accountKey,
|
||||
}
|
||||
|
||||
return selectedExtendedKey, nil
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
gethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/golang/mock/gomock"
|
||||
|
@ -114,10 +115,7 @@ func TestVerifyAccountPasswordWithAccountBeforeEIP55(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
var (
|
||||
errKeyStore = errors.New("Can't return a key store")
|
||||
errAccManager = errors.New("Can't return an account manager")
|
||||
)
|
||||
var errKeyStore = errors.New("Can't return a key store")
|
||||
|
||||
func TestManagerTestSuite(t *testing.T) {
|
||||
gethServiceProvider := newMockGethServiceProvider(t)
|
||||
|
@ -289,21 +287,13 @@ func (s *ManagerTestSuite) TestSelectAccount() {
|
|||
s.password,
|
||||
errKeyStore,
|
||||
},
|
||||
{
|
||||
"fail_wrongWalletAddress",
|
||||
[]interface{}{s.keyStore, nil},
|
||||
"wrong-wallet-address",
|
||||
s.chatAddress,
|
||||
s.password,
|
||||
ErrAddressToAccountMappingFailure,
|
||||
},
|
||||
{
|
||||
"fail_wrongChatAddress",
|
||||
[]interface{}{s.keyStore, nil},
|
||||
s.walletAddress,
|
||||
"wrong-chat-address",
|
||||
"0x0000000000000000000000000000000000000001",
|
||||
s.password,
|
||||
ErrAddressToAccountMappingFailure,
|
||||
errors.New("cannot retrieve a valid key for a given account: no key for given address or file"),
|
||||
},
|
||||
{
|
||||
"fail_wrongPassword",
|
||||
|
@ -319,18 +309,24 @@ func (s *ManagerTestSuite) TestSelectAccount() {
|
|||
s.T().Run(testCase.name, func(t *testing.T) {
|
||||
s.reinitMock()
|
||||
s.gethServiceProvider.EXPECT().AccountKeyStore().Return(testCase.accountKeyStoreReturn...).AnyTimes()
|
||||
err := s.accManager.SelectAccount(testCase.walletAddress, testCase.chatAddress, testCase.password)
|
||||
loginParams := LoginParams{
|
||||
ChatAddress: common.HexToAddress(testCase.chatAddress),
|
||||
MainAccount: common.HexToAddress(testCase.walletAddress),
|
||||
Password: testCase.password,
|
||||
}
|
||||
err := s.accManager.SelectAccount(loginParams)
|
||||
s.Equal(testCase.expectedError, err)
|
||||
|
||||
selectedWalletAccount, walletErr := s.accManager.SelectedWalletAccount()
|
||||
selectedMainAccountAddress, walletErr := s.accManager.MainAccountAddress()
|
||||
selectedChatAccount, chatErr := s.accManager.SelectedChatAccount()
|
||||
|
||||
if testCase.expectedError == nil {
|
||||
s.Equal(selectedWalletAccount.AccountKey, selectedChatAccount.AccountKey)
|
||||
s.Equal(testCase.walletAddress, selectedMainAccountAddress.String())
|
||||
s.Equal(testCase.chatAddress, crypto.PubkeyToAddress(selectedChatAccount.AccountKey.PrivateKey.PublicKey).Hex())
|
||||
s.NoError(walletErr)
|
||||
s.NoError(chatErr)
|
||||
} else {
|
||||
s.Nil(selectedWalletAccount)
|
||||
s.Equal(common.Address{}, selectedMainAccountAddress)
|
||||
s.Nil(selectedChatAccount)
|
||||
s.Equal(walletErr, ErrNoAccountSelected)
|
||||
s.Equal(chatErr, ErrNoAccountSelected)
|
||||
|
@ -356,95 +352,28 @@ func (s *ManagerTestSuite) TestSetChatAccount() {
|
|||
s.Equal(privKey, selectedChatAccount.AccountKey.PrivateKey)
|
||||
s.Equal(address, selectedChatAccount.Address)
|
||||
|
||||
selectedWalletAccount, err := s.accManager.SelectedWalletAccount()
|
||||
selectedMainAccountAddress, err := s.accManager.MainAccountAddress()
|
||||
s.Error(err)
|
||||
s.Nil(selectedWalletAccount)
|
||||
}
|
||||
|
||||
func (s *ManagerTestSuite) TestCreateChildAccount() {
|
||||
// First, test the negative case where an account is not selected
|
||||
// and an address is not provided.
|
||||
s.accManager.selectedWalletAccount = nil
|
||||
s.T().Run("fail_noAccount", func(t *testing.T) {
|
||||
s.gethServiceProvider.EXPECT().AccountKeyStore().Return(s.keyStore, nil).AnyTimes()
|
||||
_, _, err := s.accManager.CreateChildAccount("", s.password)
|
||||
s.Equal(ErrNoAccountSelected, err)
|
||||
})
|
||||
|
||||
// Now, select the test account for rest of the test cases.
|
||||
s.reinitMock()
|
||||
s.gethServiceProvider.EXPECT().AccountKeyStore().Return(s.keyStore, nil).AnyTimes()
|
||||
err := s.accManager.SelectAccount(s.walletAddress, s.chatAddress, s.password)
|
||||
s.NoError(err)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
walletAddress string
|
||||
chatAddress string
|
||||
password string
|
||||
accountKeyStoreReturn []interface{}
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
"success",
|
||||
s.walletAddress,
|
||||
s.chatAddress,
|
||||
s.password,
|
||||
[]interface{}{s.keyStore, nil},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"fail_keyStore",
|
||||
s.walletAddress,
|
||||
s.chatAddress,
|
||||
s.password,
|
||||
[]interface{}{nil, errKeyStore},
|
||||
errKeyStore,
|
||||
},
|
||||
{
|
||||
"fail_wrongWalletAddress",
|
||||
"wrong-address",
|
||||
s.chatAddress,
|
||||
s.password,
|
||||
[]interface{}{s.keyStore, nil},
|
||||
ErrAddressToAccountMappingFailure,
|
||||
},
|
||||
{
|
||||
"fail_wrongPassword",
|
||||
s.walletAddress,
|
||||
s.chatAddress,
|
||||
"wrong-password",
|
||||
[]interface{}{s.keyStore, nil},
|
||||
errors.New("cannot retrieve a valid key for a given account: could not decrypt key with given passphrase"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
s.T().Run(testCase.name, func(t *testing.T) {
|
||||
s.reinitMock()
|
||||
s.gethServiceProvider.EXPECT().AccountKeyStore().Return(testCase.accountKeyStoreReturn...).AnyTimes()
|
||||
childAddr, childPubKey, err := s.accManager.CreateChildAccount(testCase.walletAddress, testCase.password)
|
||||
if testCase.expectedError != nil {
|
||||
s.Equal(testCase.expectedError, err)
|
||||
} else {
|
||||
s.NoError(err)
|
||||
s.NotEmpty(childAddr)
|
||||
s.NotEmpty(childPubKey)
|
||||
}
|
||||
})
|
||||
}
|
||||
s.Equal(common.Address{}, selectedMainAccountAddress)
|
||||
}
|
||||
|
||||
func (s *ManagerTestSuite) TestLogout() {
|
||||
s.accManager.Logout()
|
||||
s.Nil(s.accManager.selectedWalletAccount)
|
||||
s.Equal(common.Address{}, s.accManager.mainAccountAddress)
|
||||
s.Nil(s.accManager.selectedChatAccount)
|
||||
s.Len(s.accManager.watchAddresses, 0)
|
||||
}
|
||||
|
||||
// TestAccounts tests cases for (*Manager).Accounts.
|
||||
func (s *ManagerTestSuite) TestAccounts() {
|
||||
// Select the test account
|
||||
s.gethServiceProvider.EXPECT().AccountKeyStore().Return(s.keyStore, nil).AnyTimes()
|
||||
err := s.accManager.SelectAccount(s.walletAddress, s.chatAddress, s.password)
|
||||
loginParams := LoginParams{
|
||||
MainAccount: common.HexToAddress(s.walletAddress),
|
||||
ChatAddress: common.HexToAddress(s.chatAddress),
|
||||
Password: s.password,
|
||||
}
|
||||
err := s.accManager.SelectAccount(loginParams)
|
||||
s.NoError(err)
|
||||
|
||||
// Success
|
||||
|
@ -453,13 +382,8 @@ func (s *ManagerTestSuite) TestAccounts() {
|
|||
s.NoError(err)
|
||||
s.NotNil(accs)
|
||||
|
||||
// Can't get an account manager
|
||||
s.gethServiceProvider.EXPECT().AccountManager().Return(nil, errAccManager)
|
||||
_, err = s.accManager.Accounts()
|
||||
s.Equal(errAccManager, err)
|
||||
|
||||
// Selected account is nil but doesn't fail
|
||||
s.accManager.selectedWalletAccount = nil
|
||||
// Selected main account address is zero address but doesn't fail
|
||||
s.accManager.mainAccountAddress = common.Address{}
|
||||
s.gethServiceProvider.EXPECT().AccountManager().Return(s.gethAccManager, nil)
|
||||
accs, err = s.accManager.Accounts()
|
||||
s.NoError(err)
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package account
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
|
@ -14,6 +16,54 @@ var (
|
|||
ErrInvalidMnemonicPhraseLength = errors.New("invalid mnemonic phrase length; valid lengths are 12, 15, 18, 21, and 24")
|
||||
)
|
||||
|
||||
type LoginParams struct {
|
||||
ChatAddress common.Address `json:"chatAddress"`
|
||||
Password string `json:"password"`
|
||||
MainAccount common.Address `json:"mainAccount"`
|
||||
WatchAddresses []common.Address `json:"watchAddresses"`
|
||||
}
|
||||
|
||||
type ErrZeroAddress struct {
|
||||
field string
|
||||
}
|
||||
|
||||
func (e *ErrZeroAddress) Error() string {
|
||||
return fmt.Sprintf("%s contains an empty address", e.field)
|
||||
}
|
||||
|
||||
func newErrZeroAddress(field string) *ErrZeroAddress {
|
||||
return &ErrZeroAddress{
|
||||
field: field,
|
||||
}
|
||||
}
|
||||
|
||||
func ParseLoginParams(paramsJSON string) (LoginParams, error) {
|
||||
var (
|
||||
params LoginParams
|
||||
zeroAddress common.Address
|
||||
)
|
||||
|
||||
if err := json.Unmarshal([]byte(paramsJSON), ¶ms); err != nil {
|
||||
return params, err
|
||||
}
|
||||
|
||||
if params.ChatAddress == zeroAddress {
|
||||
return params, newErrZeroAddress("ChatAddress")
|
||||
}
|
||||
|
||||
if params.MainAccount == zeroAddress {
|
||||
return params, newErrZeroAddress("MainAccount")
|
||||
}
|
||||
|
||||
for _, address := range params.WatchAddresses {
|
||||
if address == zeroAddress {
|
||||
return params, newErrZeroAddress("WatchAddresses")
|
||||
}
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// Info contains wallet and chat addresses and public keys of an account.
|
||||
type Info struct {
|
||||
WalletAddress string
|
||||
|
|
|
@ -295,7 +295,7 @@ func (b *StatusBackend) CallPrivateRPC(inputJSON string) (string, error) {
|
|||
|
||||
// SendTransaction creates a new transaction and waits until it's complete.
|
||||
func (b *StatusBackend) SendTransaction(sendArgs transactions.SendTxArgs, password string) (hash gethcommon.Hash, err error) {
|
||||
verifiedAccount, err := b.getVerifiedWalletAccount(password)
|
||||
verifiedAccount, err := b.getVerifiedWalletAccount(sendArgs.From.String(), password)
|
||||
if err != nil {
|
||||
return hash, err
|
||||
}
|
||||
|
@ -329,7 +329,7 @@ func (b *StatusBackend) HashTransaction(sendArgs transactions.SendTxArgs) (trans
|
|||
// SignMessage checks the pwd vs the selected account and passes on the signParams
|
||||
// to personalAPI for message signature
|
||||
func (b *StatusBackend) SignMessage(rpcParams personal.SignParams) (hexutil.Bytes, error) {
|
||||
verifiedAccount, err := b.getVerifiedWalletAccount(rpcParams.Password)
|
||||
verifiedAccount, err := b.getVerifiedWalletAccount(rpcParams.Address, rpcParams.Password)
|
||||
if err != nil {
|
||||
return hexutil.Bytes{}, err
|
||||
}
|
||||
|
@ -343,8 +343,8 @@ func (b *StatusBackend) Recover(rpcParams personal.RecoverParams) (gethcommon.Ad
|
|||
}
|
||||
|
||||
// SignTypedData accepts data and password. Gets verified account and signs typed data.
|
||||
func (b *StatusBackend) SignTypedData(typed typeddata.TypedData, password string) (hexutil.Bytes, error) {
|
||||
account, err := b.getVerifiedWalletAccount(password)
|
||||
func (b *StatusBackend) SignTypedData(typed typeddata.TypedData, address string, password string) (hexutil.Bytes, error) {
|
||||
account, err := b.getVerifiedWalletAccount(address, password)
|
||||
if err != nil {
|
||||
return hexutil.Bytes{}, err
|
||||
}
|
||||
|
@ -366,19 +366,40 @@ func (b *StatusBackend) HashTypedData(typed typeddata.TypedData) (common.Hash, e
|
|||
return hash, err
|
||||
}
|
||||
|
||||
func (b *StatusBackend) getVerifiedWalletAccount(password string) (*account.SelectedExtKey, error) {
|
||||
selectedWalletAccount, err := b.accountManager.SelectedWalletAccount()
|
||||
if err != nil {
|
||||
b.log.Error("failed to get a selected account", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
func (b *StatusBackend) getVerifiedWalletAccount(address, password string) (*account.SelectedExtKey, error) {
|
||||
config := b.StatusNode().Config()
|
||||
_, err = b.accountManager.VerifyAccountPassword(config.KeyStoreDir, selectedWalletAccount.Address.String(), password)
|
||||
|
||||
var validAddress bool
|
||||
|
||||
addresses := b.accountManager.WatchAddresses()
|
||||
mainAccountAddress, err := b.accountManager.MainAccountAddress()
|
||||
if err != nil {
|
||||
b.log.Error("failed to verify account", "account", selectedWalletAccount.Address.String(), "error", err)
|
||||
return nil, err
|
||||
}
|
||||
return selectedWalletAccount, nil
|
||||
|
||||
addresses = append(addresses, mainAccountAddress)
|
||||
for _, a := range addresses {
|
||||
if a.String() == address {
|
||||
validAddress = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !validAddress {
|
||||
b.log.Error("failed to get a selected account", "err", transactions.ErrInvalidTxSender)
|
||||
return nil, transactions.ErrInvalidTxSender
|
||||
}
|
||||
|
||||
key, err := b.accountManager.VerifyAccountPassword(config.KeyStoreDir, address, password)
|
||||
if err != nil {
|
||||
b.log.Error("failed to verify account", "account", address, "error", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &account.SelectedExtKey{
|
||||
Address: key.Address,
|
||||
AccountKey: key,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// registerHandlers attaches Status callback handlers to running node
|
||||
|
@ -531,13 +552,13 @@ func (b *StatusBackend) reSelectAccount() error {
|
|||
// SelectAccount selects current wallet and chat accounts, by verifying that each address has corresponding account which can be decrypted
|
||||
// using provided password. Once verification is done, the decrypted chat key is injected into Whisper (as a single identity,
|
||||
// all previous identities are removed).
|
||||
func (b *StatusBackend) SelectAccount(walletAddress, chatAddress, password string) error {
|
||||
func (b *StatusBackend) SelectAccount(loginParams account.LoginParams) error {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
b.AccountManager().RemoveOnboarding()
|
||||
|
||||
err := b.accountManager.SelectAccount(walletAddress, chatAddress, password)
|
||||
err := b.accountManager.SelectAccount(loginParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -564,37 +585,48 @@ func (b *StatusBackend) SelectAccount(walletAddress, chatAddress, password strin
|
|||
return err
|
||||
}
|
||||
|
||||
if err := st.InitProtocolWithPassword(chatAddress, password); err != nil {
|
||||
if err := st.InitProtocolWithPassword(loginParams.ChatAddress.String(), loginParams.Password); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = b.startWallet(password)
|
||||
|
||||
err = b.startWallet(loginParams.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = b.startBrowsers(password)
|
||||
|
||||
err = b.startBrowsers(loginParams.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return b.startPermissions(password)
|
||||
|
||||
return b.startPermissions(loginParams.Password)
|
||||
}
|
||||
|
||||
func (b *StatusBackend) startWallet(password string) error {
|
||||
if !b.statusNode.Config().WalletConfig.Enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
wallet, err := b.statusNode.WalletService()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
account, err := b.accountManager.SelectedWalletAccount()
|
||||
|
||||
watchAddresses := b.accountManager.WatchAddresses()
|
||||
mainAccountAddress, err := b.accountManager.MainAccountAddress()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path := path.Join(b.statusNode.Config().DataDir, fmt.Sprintf("wallet-%x.sql", account.Address))
|
||||
|
||||
allAddresses := make([]common.Address, len(watchAddresses)+1)
|
||||
allAddresses[0] = mainAccountAddress
|
||||
copy(allAddresses[1:], watchAddresses)
|
||||
|
||||
path := path.Join(b.statusNode.Config().DataDir, fmt.Sprintf("wallet-%x.sql", mainAccountAddress))
|
||||
return wallet.StartReactor(path, password,
|
||||
b.statusNode.RPCClient().Ethclient(),
|
||||
[]common.Address{account.Address},
|
||||
allAddresses,
|
||||
new(big.Int).SetUint64(b.statusNode.Config().NetworkID))
|
||||
}
|
||||
|
||||
|
@ -606,11 +638,13 @@ func (b *StatusBackend) startBrowsers(password string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
account, err := b.accountManager.SelectedWalletAccount()
|
||||
|
||||
mainAccountAddress, err := b.accountManager.MainAccountAddress()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path := path.Join(b.statusNode.Config().DataDir, fmt.Sprintf("browsers-%x.sql", account.Address))
|
||||
|
||||
path := path.Join(b.statusNode.Config().DataDir, fmt.Sprintf("browsers-%x.sql", mainAccountAddress))
|
||||
return svc.StartDatabase(path, password)
|
||||
}
|
||||
|
||||
|
@ -622,11 +656,11 @@ func (b *StatusBackend) startPermissions(password string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
account, err := b.accountManager.SelectedWalletAccount()
|
||||
mainAccountAddress, err := b.accountManager.MainAccountAddress()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path := path.Join(b.statusNode.Config().DataDir, fmt.Sprintf("permissions-%x.sql", account.Address))
|
||||
path := path.Join(b.statusNode.Config().DataDir, fmt.Sprintf("permissions-%x.sql", mainAccountAddress))
|
||||
return svc.StartDatabase(path, password)
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/signal"
|
||||
"github.com/status-im/status-go/t/utils"
|
||||
|
@ -226,7 +228,12 @@ func initNodeAndLogin(t *testing.T, backend *StatusBackend) (string, string) {
|
|||
info, _, err := backend.AccountManager().CreateAccount(password)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, backend.AccountManager().SelectAccount(info.WalletAddress, info.ChatAddress, password))
|
||||
loginParams := account.LoginParams{
|
||||
MainAccount: common.HexToAddress(info.WalletAddress),
|
||||
ChatAddress: common.HexToAddress(info.ChatAddress),
|
||||
Password: password,
|
||||
}
|
||||
require.NoError(t, backend.AccountManager().SelectAccount(loginParams))
|
||||
|
||||
unlockFmt := `
|
||||
{
|
||||
|
|
|
@ -167,7 +167,12 @@ func TestBackendAccountsConcurrently(t *testing.T) {
|
|||
for tuple := range addressCh {
|
||||
wg.Add(1)
|
||||
go func(tuple [3]string) {
|
||||
assert.NoError(t, backend.SelectAccount(tuple[0], tuple[1], tuple[2]))
|
||||
loginParams := account.LoginParams{
|
||||
MainAccount: common.HexToAddress(tuple[0]),
|
||||
ChatAddress: common.HexToAddress(tuple[1]),
|
||||
Password: tuple[2],
|
||||
}
|
||||
assert.NoError(t, backend.SelectAccount(loginParams))
|
||||
wg.Done()
|
||||
}(tuple)
|
||||
|
||||
|
@ -219,8 +224,8 @@ func TestBackendInjectChatAccount(t *testing.T) {
|
|||
require.True(t, whisperService.HasKeyPair(chatPubKeyHex), "identity not injected into whisper")
|
||||
|
||||
// wallet account should not be selected
|
||||
walletAcc, err := backend.AccountManager().SelectedWalletAccount()
|
||||
require.Nil(t, walletAcc)
|
||||
mainAccountAddress, err := backend.AccountManager().MainAccountAddress()
|
||||
require.Equal(t, common.Address{}, mainAccountAddress)
|
||||
require.Equal(t, account.ErrNoAccountSelected, err)
|
||||
|
||||
// selected chat account should have the key injected previously
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/api"
|
||||
"github.com/status-im/status-go/exportlogs"
|
||||
"github.com/status-im/status-go/logutils"
|
||||
|
@ -225,26 +226,6 @@ func CreateAccount(password *C.char) *C.char {
|
|||
return C.CString(string(outBytes))
|
||||
}
|
||||
|
||||
//CreateChildAccount creates sub-account
|
||||
//export CreateChildAccount
|
||||
func CreateChildAccount(parentAddress, password *C.char) *C.char {
|
||||
address, pubKey, err := statusBackend.AccountManager().CreateChildAccount(C.GoString(parentAddress), C.GoString(password))
|
||||
|
||||
errString := ""
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
errString = err.Error()
|
||||
}
|
||||
|
||||
out := AccountInfo{
|
||||
Address: address,
|
||||
PubKey: pubKey,
|
||||
Error: errString,
|
||||
}
|
||||
outBytes, _ := json.Marshal(out)
|
||||
return C.CString(string(outBytes))
|
||||
}
|
||||
|
||||
//RecoverAccount re-creates master key using given details
|
||||
//export RecoverAccount
|
||||
func RecoverAccount(password, mnemonic *C.char) *C.char {
|
||||
|
@ -346,8 +327,13 @@ func VerifyAccountPassword(keyStoreDir, address, password *C.char) *C.char {
|
|||
//Login loads a key file (for a given address), tries to decrypt it using the password, to verify ownership
|
||||
// if verified, purges all the previous identities from Whisper, and injects verified key as shh identity
|
||||
//export Login
|
||||
func Login(address, password *C.char) *C.char {
|
||||
err := statusBackend.SelectAccount(C.GoString(address), C.GoString(address), C.GoString(password))
|
||||
func Login(loginParamsJSON *C.char) *C.char {
|
||||
params, err := account.ParseLoginParams(C.GoString(loginParamsJSON))
|
||||
if err != nil {
|
||||
return C.CString(prepareJSONResponseWithCode(nil, err, codeFailedParseParams))
|
||||
}
|
||||
|
||||
err = statusBackend.SelectAccount(params)
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
|
@ -473,7 +459,7 @@ func HashMessage(message *C.char) *C.char {
|
|||
// SignTypedData unmarshall data into TypedData, validate it and signs with selected account,
|
||||
// if password matches selected account.
|
||||
//export SignTypedData
|
||||
func SignTypedData(data, password *C.char) *C.char {
|
||||
func SignTypedData(data, address, password *C.char) *C.char {
|
||||
var typed typeddata.TypedData
|
||||
err := json.Unmarshal([]byte(C.GoString(data)), &typed)
|
||||
if err != nil {
|
||||
|
@ -482,7 +468,7 @@ func SignTypedData(data, password *C.char) *C.char {
|
|||
if err := typed.Validate(); err != nil {
|
||||
return C.CString(prepareJSONResponseWithCode(nil, err, codeFailedParseParams))
|
||||
}
|
||||
result, err := statusBackend.SignTypedData(typed, C.GoString(password))
|
||||
result, err := statusBackend.SignTypedData(typed, C.GoString(address), C.GoString(password))
|
||||
return C.CString(prepareJSONResponse(result.String(), err))
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import "C"
|
|||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
|
@ -46,6 +47,22 @@ var (
|
|||
nodeConfigJSON string
|
||||
)
|
||||
|
||||
func buildLoginParamsJSON(chatAddress, password string) *C.char {
|
||||
return C.CString(fmt.Sprintf(`{
|
||||
"chatAddress": "%s",
|
||||
"password": "%s",
|
||||
"mainAccount": "%s"
|
||||
}`, chatAddress, password, chatAddress))
|
||||
}
|
||||
|
||||
func buildLoginParams(mainAccountAddress, chatAddress, password string) account.LoginParams {
|
||||
return account.LoginParams{
|
||||
ChatAddress: gethcommon.HexToAddress(chatAddress),
|
||||
Password: password,
|
||||
MainAccount: gethcommon.HexToAddress(mainAccountAddress),
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
testChainDir = filepath.Join(TestDataDir, TestNetworkNames[GetNetworkID()])
|
||||
|
||||
|
@ -110,10 +127,6 @@ func testExportedAPI(t *testing.T, done chan struct{}) {
|
|||
"call private API using private RPC client",
|
||||
testCallPrivateRPCWithPrivateAPI,
|
||||
},
|
||||
{
|
||||
"create main and child accounts",
|
||||
testCreateChildAccount,
|
||||
},
|
||||
{
|
||||
"verify account password",
|
||||
testVerifyAccountPassword,
|
||||
|
@ -261,7 +274,7 @@ func testStopResumeNode(t *testing.T) bool { //nolint: gocyclo
|
|||
|
||||
// select account
|
||||
loginResponse := APIResponse{}
|
||||
rawResponse := Login(C.CString(account1.WalletAddress), C.CString(TestConfig.Account1.Password))
|
||||
rawResponse := Login(buildLoginParamsJSON(account1.WalletAddress, TestConfig.Account1.Password))
|
||||
|
||||
if err = json.Unmarshal([]byte(C.GoString(rawResponse)), &loginResponse); err != nil {
|
||||
t.Errorf("cannot decode RecoverAccount response (%s): %v", C.GoString(rawResponse), err)
|
||||
|
@ -377,149 +390,6 @@ func testCallPrivateRPCWithPrivateAPI(t *testing.T) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func testCreateChildAccount(t *testing.T) bool { //nolint: gocyclo
|
||||
// to make sure that we start with empty account (which might get populated during previous tests)
|
||||
if err := statusBackend.Logout(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
keyStore, err := statusBackend.StatusNode().AccountKeyStore()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return false
|
||||
}
|
||||
|
||||
// create an account
|
||||
createAccountResponse := AccountInfo{}
|
||||
rawResponse := CreateAccount(C.CString(TestConfig.Account1.Password))
|
||||
|
||||
if err = json.Unmarshal([]byte(C.GoString(rawResponse)), &createAccountResponse); err != nil {
|
||||
t.Errorf("cannot decode CreateAccount response (%s): %v", C.GoString(rawResponse), err)
|
||||
return false
|
||||
}
|
||||
|
||||
if createAccountResponse.Error != "" {
|
||||
t.Errorf("could not create account: %s", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if createAccountResponse.Address != createAccountResponse.WalletAddress ||
|
||||
createAccountResponse.PubKey != createAccountResponse.WalletPubKey {
|
||||
t.Error("for backward compatibility pubkey/address should be equal to walletAddress/walletPubKey")
|
||||
}
|
||||
|
||||
walletAddress, walletPubKey, chatAddress, _, mnemonic := createAccountResponse.Address, createAccountResponse.PubKey,
|
||||
createAccountResponse.ChatAddress, createAccountResponse.ChatPubKey, createAccountResponse.Mnemonic
|
||||
t.Logf("Account created: {address: %s, key: %s, mnemonic:%s}", walletAddress, walletPubKey, mnemonic)
|
||||
|
||||
acct, err := account.ParseAccountString(walletAddress)
|
||||
if err != nil {
|
||||
t.Errorf("can not get account from address: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
t.Errorf("can not obtain decrypted account key: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if key.ExtendedKey == nil {
|
||||
t.Error("CKD#2 has not been generated for new account")
|
||||
return false
|
||||
}
|
||||
|
||||
// try creating sub-account, w/o selecting main account i.e. w/o login to main account
|
||||
createSubAccountResponse := AccountInfo{}
|
||||
rawResponse = CreateChildAccount(C.CString(""), C.CString(TestConfig.Account1.Password))
|
||||
|
||||
if err = json.Unmarshal([]byte(C.GoString(rawResponse)), &createSubAccountResponse); err != nil {
|
||||
t.Errorf("cannot decode CreateChildAccount response (%s): %v", C.GoString(rawResponse), err)
|
||||
return false
|
||||
}
|
||||
|
||||
if createSubAccountResponse.Error != account.ErrNoAccountSelected.Error() {
|
||||
t.Errorf("expected error is not returned (tried to create sub-account w/o login): %v", createSubAccountResponse.Error)
|
||||
return false
|
||||
}
|
||||
|
||||
err = statusBackend.SelectAccount(walletAddress, chatAddress, TestConfig.Account1.Password)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed: could not select account: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
// try to create sub-account with wrong password
|
||||
createSubAccountResponse = AccountInfo{}
|
||||
rawResponse = CreateChildAccount(C.CString(""), C.CString("wrong password"))
|
||||
|
||||
if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &createSubAccountResponse); err != nil {
|
||||
t.Errorf("cannot decode CreateChildAccount response (%s): %v", C.GoString(rawResponse), err)
|
||||
return false
|
||||
}
|
||||
|
||||
if createSubAccountResponse.Error != "cannot retrieve a valid key for a given account: could not decrypt key with given passphrase" {
|
||||
t.Errorf("expected error is not returned (tried to create sub-account with wrong password): %v", createSubAccountResponse.Error)
|
||||
return false
|
||||
}
|
||||
|
||||
// create sub-account (from implicit parent)
|
||||
createSubAccountResponse1 := AccountInfo{}
|
||||
rawResponse = CreateChildAccount(C.CString(""), C.CString(TestConfig.Account1.Password))
|
||||
|
||||
if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &createSubAccountResponse1); err != nil {
|
||||
t.Errorf("cannot decode CreateChildAccount response (%s): %v", C.GoString(rawResponse), err)
|
||||
return false
|
||||
}
|
||||
|
||||
if createSubAccountResponse1.Error != "" {
|
||||
t.Errorf("cannot create sub-account: %v", createSubAccountResponse1.Error)
|
||||
return false
|
||||
}
|
||||
|
||||
// make sure that sub-account index automatically progresses
|
||||
createSubAccountResponse2 := AccountInfo{}
|
||||
rawResponse = CreateChildAccount(C.CString(""), C.CString(TestConfig.Account1.Password))
|
||||
|
||||
if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &createSubAccountResponse2); err != nil {
|
||||
t.Errorf("cannot decode CreateChildAccount response (%s): %v", C.GoString(rawResponse), err)
|
||||
return false
|
||||
}
|
||||
|
||||
if createSubAccountResponse2.Error != "" {
|
||||
t.Errorf("cannot create sub-account: %v", createSubAccountResponse2.Error)
|
||||
}
|
||||
|
||||
if createSubAccountResponse1.Address == createSubAccountResponse2.Address || createSubAccountResponse1.PubKey == createSubAccountResponse2.PubKey {
|
||||
t.Error("sub-account index auto-increament failed")
|
||||
return false
|
||||
}
|
||||
|
||||
// create sub-account (from explicit parent)
|
||||
createSubAccountResponse3 := AccountInfo{}
|
||||
rawResponse = CreateChildAccount(C.CString(createSubAccountResponse2.Address), C.CString(TestConfig.Account1.Password))
|
||||
|
||||
if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &createSubAccountResponse3); err != nil {
|
||||
t.Errorf("cannot decode CreateChildAccount response (%s): %v", C.GoString(rawResponse), err)
|
||||
return false
|
||||
}
|
||||
|
||||
if createSubAccountResponse3.Error != "" {
|
||||
t.Errorf("cannot create sub-account: %v", createSubAccountResponse3.Error)
|
||||
}
|
||||
|
||||
subAccount1, subAccount2, subAccount3 := createSubAccountResponse1.Address, createSubAccountResponse2.Address, createSubAccountResponse3.Address
|
||||
subPubKey1, subPubKey2, subPubKey3 := createSubAccountResponse1.PubKey, createSubAccountResponse2.PubKey, createSubAccountResponse3.PubKey
|
||||
|
||||
if subAccount1 == subAccount3 || subPubKey1 == subPubKey3 || subAccount2 == subAccount3 || subPubKey2 == subPubKey3 {
|
||||
t.Error("sub-account index auto-increament failed")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func testRecoverAccount(t *testing.T) bool { //nolint: gocyclo
|
||||
keyStore, _ := statusBackend.StatusNode().AccountKeyStore()
|
||||
|
||||
|
@ -643,7 +513,7 @@ func testRecoverAccount(t *testing.T) bool { //nolint: gocyclo
|
|||
if whisperService.HasKeyPair(chatPubKeyCheck) {
|
||||
t.Error("identity already present in whisper")
|
||||
}
|
||||
err = statusBackend.SelectAccount(walletAddressCheck, chatAddressCheck, TestConfig.Account1.Password)
|
||||
err = statusBackend.SelectAccount(buildLoginParams(walletAddressCheck, chatAddressCheck, TestConfig.Account1.Password))
|
||||
if err != nil {
|
||||
t.Errorf("Test failed: could not select account: %v", err)
|
||||
return false
|
||||
|
@ -684,7 +554,7 @@ func testAccountSelect(t *testing.T) bool { //nolint: gocyclo
|
|||
|
||||
// try selecting with wrong password
|
||||
loginResponse := APIResponse{}
|
||||
rawResponse := Login(C.CString(accountInfo1.WalletAddress), C.CString("wrongPassword"))
|
||||
rawResponse := Login(buildLoginParamsJSON(accountInfo1.WalletAddress, "wrongPassword"))
|
||||
|
||||
if err = json.Unmarshal([]byte(C.GoString(rawResponse)), &loginResponse); err != nil {
|
||||
t.Errorf("cannot decode RecoverAccount response (%s): %v", C.GoString(rawResponse), err)
|
||||
|
@ -697,7 +567,7 @@ func testAccountSelect(t *testing.T) bool { //nolint: gocyclo
|
|||
}
|
||||
|
||||
loginResponse = APIResponse{}
|
||||
rawResponse = Login(C.CString(accountInfo1.WalletAddress), C.CString(TestConfig.Account1.Password))
|
||||
rawResponse = Login(buildLoginParamsJSON(accountInfo1.WalletAddress, TestConfig.Account1.Password))
|
||||
|
||||
if err = json.Unmarshal([]byte(C.GoString(rawResponse)), &loginResponse); err != nil {
|
||||
t.Errorf("cannot decode RecoverAccount response (%s): %v", C.GoString(rawResponse), err)
|
||||
|
@ -718,7 +588,7 @@ func testAccountSelect(t *testing.T) bool { //nolint: gocyclo
|
|||
}
|
||||
|
||||
loginResponse = APIResponse{}
|
||||
rawResponse = Login(C.CString(accountInfo2.WalletAddress), C.CString(TestConfig.Account1.Password))
|
||||
rawResponse = Login(buildLoginParamsJSON(accountInfo2.WalletAddress, TestConfig.Account1.Password))
|
||||
|
||||
if err = json.Unmarshal([]byte(C.GoString(rawResponse)), &loginResponse); err != nil {
|
||||
t.Errorf("cannot decode RecoverAccount response (%s): %v", C.GoString(rawResponse), err)
|
||||
|
@ -807,7 +677,7 @@ func testAccountLogout(t *testing.T) bool {
|
|||
}
|
||||
|
||||
// select/login
|
||||
err = statusBackend.SelectAccount(accountInfo.WalletAddress, accountInfo.ChatAddress, TestConfig.Account1.Password)
|
||||
err = statusBackend.SelectAccount(buildLoginParams(accountInfo.WalletAddress, accountInfo.ChatAddress, TestConfig.Account1.Password))
|
||||
if err != nil {
|
||||
t.Errorf("Test failed: could not select account: %v", err)
|
||||
return false
|
||||
|
@ -848,7 +718,7 @@ func testSendTransaction(t *testing.T) bool {
|
|||
EnsureNodeSync(statusBackend.StatusNode().EnsureSync)
|
||||
|
||||
// log into account from which transactions will be sent
|
||||
if err := statusBackend.SelectAccount(TestConfig.Account1.WalletAddress, TestConfig.Account1.ChatAddress, TestConfig.Account1.Password); err != nil {
|
||||
if err := statusBackend.SelectAccount(buildLoginParams(TestConfig.Account1.WalletAddress, TestConfig.Account1.ChatAddress, TestConfig.Account1.Password)); err != nil {
|
||||
t.Errorf("cannot select account: %v. Error %q", TestConfig.Account1.WalletAddress, err)
|
||||
return false
|
||||
}
|
||||
|
@ -886,11 +756,11 @@ func testSendTransactionInvalidPassword(t *testing.T) bool {
|
|||
EnsureNodeSync(statusBackend.StatusNode().EnsureSync)
|
||||
|
||||
// log into account from which transactions will be sent
|
||||
if err := statusBackend.SelectAccount(
|
||||
if err := statusBackend.SelectAccount(buildLoginParams(
|
||||
TestConfig.Account1.WalletAddress,
|
||||
TestConfig.Account1.ChatAddress,
|
||||
TestConfig.Account1.Password,
|
||||
); err != nil {
|
||||
)); err != nil {
|
||||
t.Errorf("cannot select account: %v. Error %q", TestConfig.Account1.WalletAddress, err)
|
||||
return false
|
||||
}
|
||||
|
@ -923,8 +793,8 @@ func testFailedTransaction(t *testing.T) bool {
|
|||
EnsureNodeSync(statusBackend.StatusNode().EnsureSync)
|
||||
|
||||
// log into wrong account in order to get selectedAccount error
|
||||
if err := statusBackend.SelectAccount(TestConfig.Account2.WalletAddress, TestConfig.Account2.ChatAddress, TestConfig.Account2.Password); err != nil {
|
||||
t.Errorf("cannot select account: %v. Error %q", TestConfig.Account1.WalletAddress, err)
|
||||
if err := statusBackend.SelectAccount(buildLoginParams(TestConfig.Account2.WalletAddress, TestConfig.Account2.ChatAddress, TestConfig.Account2.Password)); err != nil {
|
||||
t.Errorf("cannot select account: %v. Error %q", TestConfig.Account2.WalletAddress, err)
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/api"
|
||||
"github.com/status-im/status-go/exportlogs"
|
||||
"github.com/status-im/status-go/logutils"
|
||||
|
@ -229,25 +230,6 @@ func CreateAccount(password string) string {
|
|||
return string(outBytes)
|
||||
}
|
||||
|
||||
// CreateChildAccount creates sub-account.
|
||||
func CreateChildAccount(parentAddress, password string) string {
|
||||
address, pubKey, err := statusBackend.AccountManager().CreateChildAccount(parentAddress, password)
|
||||
|
||||
errString := ""
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
errString = err.Error()
|
||||
}
|
||||
|
||||
out := AccountInfo{
|
||||
Address: address,
|
||||
PubKey: pubKey,
|
||||
Error: errString,
|
||||
}
|
||||
outBytes, _ := json.Marshal(out)
|
||||
return string(outBytes)
|
||||
}
|
||||
|
||||
// RecoverAccount re-creates master key using given details.
|
||||
func RecoverAccount(password, mnemonic string) string {
|
||||
info, err := statusBackend.AccountManager().RecoverAccount(password, mnemonic)
|
||||
|
@ -344,8 +326,13 @@ func VerifyAccountPassword(keyStoreDir, address, password string) string {
|
|||
// Login loads a key file (for a given address), tries to decrypt it using the password,
|
||||
// to verify ownership if verified, purges all the previous identities from Whisper,
|
||||
// and injects verified key as shh identity.
|
||||
func Login(address, password string) string {
|
||||
err := statusBackend.SelectAccount(address, address, password)
|
||||
func Login(loginParamsJSON string) string {
|
||||
params, err := account.ParseLoginParams(loginParamsJSON)
|
||||
if err != nil {
|
||||
return prepareJSONResponseWithCode(nil, err, codeFailedParseParams)
|
||||
}
|
||||
|
||||
err = statusBackend.SelectAccount(params)
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
|
@ -377,7 +364,7 @@ func SignMessage(rpcParams string) string {
|
|||
// SignTypedData unmarshall data into TypedData, validate it and signs with selected account,
|
||||
// if password matches selected account.
|
||||
//export SignTypedData
|
||||
func SignTypedData(data, password string) string {
|
||||
func SignTypedData(data, address, password string) string {
|
||||
var typed typeddata.TypedData
|
||||
err := json.Unmarshal([]byte(data), &typed)
|
||||
if err != nil {
|
||||
|
@ -386,7 +373,7 @@ func SignTypedData(data, password string) string {
|
|||
if err := typed.Validate(); err != nil {
|
||||
return prepareJSONResponseWithCode(nil, err, codeFailedParseParams)
|
||||
}
|
||||
result, err := statusBackend.SignTypedData(typed, password)
|
||||
result, err := statusBackend.SignTypedData(typed, address, password)
|
||||
return prepareJSONResponse(result.String(), err)
|
||||
}
|
||||
|
||||
|
|
|
@ -91,17 +91,17 @@ func (mr *MockAccountManagerMockRecorder) AddressToDecryptedAccount(arg0, arg1 i
|
|||
}
|
||||
|
||||
// SelectAccount mocks base method
|
||||
func (m *MockAccountManager) SelectAccount(walletAddress, chatAddress, password string) error {
|
||||
func (m *MockAccountManager) SelectAccount(arg0 account.LoginParams) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SelectAccount", walletAddress, chatAddress, password)
|
||||
ret := m.ctrl.Call(m, "SelectAccount", arg0)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// SelectAccount indicates an expected call of SelectAccount
|
||||
func (mr *MockAccountManagerMockRecorder) SelectAccount(walletAddress, chatAddress, password interface{}) *gomock.Call {
|
||||
func (mr *MockAccountManagerMockRecorder) SelectAccount(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectAccount", reflect.TypeOf((*MockAccountManager)(nil).SelectAccount), walletAddress, chatAddress, password)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectAccount", reflect.TypeOf((*MockAccountManager)(nil).SelectAccount), arg0)
|
||||
}
|
||||
|
||||
// CreateAccount mocks base method
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/status-im/status-go/account"
|
||||
)
|
||||
|
||||
|
@ -40,7 +41,12 @@ func (api *PublicAPI) Login(context context.Context, req LoginRequest) (res Logi
|
|||
return
|
||||
}
|
||||
|
||||
if err = api.s.am.SelectAccount(req.Addr, req.Addr, req.Password); err != nil {
|
||||
loginParams := account.LoginParams{
|
||||
ChatAddress: common.HexToAddress(req.Addr),
|
||||
Password: req.Password,
|
||||
MainAccount: common.HexToAddress(req.Addr),
|
||||
}
|
||||
if err = api.s.am.SelectAccount(loginParams); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
@ -48,9 +49,15 @@ var logintests = []struct {
|
|||
key := keystore.Key{
|
||||
PrivateKey: &ecdsa.PrivateKey{},
|
||||
}
|
||||
s.am.EXPECT().AddressToDecryptedAccount("address...", "password").Return(accounts.Account{}, &key, nil)
|
||||
s.am.EXPECT().AddressToDecryptedAccount("0x01", "password").Return(accounts.Account{}, &key, nil)
|
||||
s.w.EXPECT().AddKeyPair(key.PrivateKey).Return("addressKey", nil)
|
||||
s.am.EXPECT().SelectAccount("address...", "address...", "password").Return(nil)
|
||||
|
||||
loginParams := account.LoginParams{
|
||||
MainAccount: common.HexToAddress("0x01"),
|
||||
ChatAddress: common.HexToAddress("0x01"),
|
||||
Password: "password",
|
||||
}
|
||||
s.am.EXPECT().SelectAccount(loginParams).Return(nil)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -61,7 +68,7 @@ var logintests = []struct {
|
|||
key := keystore.Key{
|
||||
PrivateKey: &ecdsa.PrivateKey{},
|
||||
}
|
||||
s.am.EXPECT().AddressToDecryptedAccount("address...", "password").Return(accounts.Account{}, &key, errors.New("foo"))
|
||||
s.am.EXPECT().AddressToDecryptedAccount("0x01", "password").Return(accounts.Account{}, &key, errors.New("foo"))
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -72,7 +79,7 @@ var logintests = []struct {
|
|||
key := keystore.Key{
|
||||
PrivateKey: &ecdsa.PrivateKey{},
|
||||
}
|
||||
s.am.EXPECT().AddressToDecryptedAccount("address...", "password").Return(accounts.Account{}, &key, nil)
|
||||
s.am.EXPECT().AddressToDecryptedAccount("0x01", "password").Return(accounts.Account{}, &key, nil)
|
||||
s.w.EXPECT().AddKeyPair(key.PrivateKey).Return("", errors.New("foo"))
|
||||
},
|
||||
},
|
||||
|
@ -84,16 +91,22 @@ var logintests = []struct {
|
|||
key := keystore.Key{
|
||||
PrivateKey: &ecdsa.PrivateKey{},
|
||||
}
|
||||
s.am.EXPECT().AddressToDecryptedAccount("address...", "password").Return(accounts.Account{}, &key, nil)
|
||||
s.am.EXPECT().AddressToDecryptedAccount("0x01", "password").Return(accounts.Account{}, &key, nil)
|
||||
s.w.EXPECT().AddKeyPair(key.PrivateKey).Return("", nil)
|
||||
s.am.EXPECT().SelectAccount("address...", "address...", "password").Return(errors.New("foo"))
|
||||
|
||||
loginParams := account.LoginParams{
|
||||
MainAccount: common.HexToAddress("0x01"),
|
||||
ChatAddress: common.HexToAddress("0x01"),
|
||||
Password: "password",
|
||||
}
|
||||
s.am.EXPECT().SelectAccount(loginParams).Return(errors.New("foo"))
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func (s *StatusSuite) TestLogin() {
|
||||
for _, t := range logintests {
|
||||
req := LoginRequest{Addr: "address...", Password: "password"}
|
||||
req := LoginRequest{Addr: "0x01", Password: "password"}
|
||||
|
||||
t.prepareExpectations(s)
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ type WhisperService interface {
|
|||
// AccountManager interface to manage account actions
|
||||
type AccountManager interface {
|
||||
AddressToDecryptedAccount(string, string) (accounts.Account, *keystore.Key, error)
|
||||
SelectAccount(walletAddress, chatAddress, password string) error
|
||||
SelectAccount(account.LoginParams) error
|
||||
CreateAccount(password string) (accountInfo account.Info, mnemonic string, err error)
|
||||
}
|
||||
|
||||
|
|
|
@ -30,8 +30,17 @@ type TransfersSuite struct {
|
|||
}
|
||||
|
||||
func (s *TransfersSuite) SelectAccount() {
|
||||
s.Require().NoError(s.backend.SelectAccount(s.Info.WalletAddress, s.Info.ChatAddress, s.Password))
|
||||
_, err := s.backend.AccountManager().SelectedWalletAccount()
|
||||
loginParams := account.LoginParams{
|
||||
ChatAddress: common.HexToAddress(s.Info.ChatAddress),
|
||||
Password: s.Password,
|
||||
MainAccount: common.HexToAddress(s.Info.WalletAddress),
|
||||
}
|
||||
|
||||
s.Require().NoError(s.backend.SelectAccount(loginParams))
|
||||
_, err := s.backend.AccountManager().MainAccountAddress()
|
||||
s.Require().NoError(err)
|
||||
|
||||
_, err = s.backend.AccountManager().SelectedChatAccount()
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ func (s *AccountsTestSuite) TestRPCEthAccounts() {
|
|||
defer s.StopTestBackend()
|
||||
|
||||
// log into test account
|
||||
err := s.Backend.SelectAccount(TestConfig.Account1.WalletAddress, TestConfig.Account1.ChatAddress, TestConfig.Account1.Password)
|
||||
err := s.Backend.SelectAccount(buildLoginParams(TestConfig.Account1.WalletAddress, TestConfig.Account1.ChatAddress, TestConfig.Account1.Password, nil))
|
||||
s.NoError(err)
|
||||
|
||||
rpcClient := s.Backend.StatusNode().RPCClient()
|
||||
|
@ -51,7 +51,7 @@ func (s *AccountsTestSuite) TestRPCEthAccountsWithUpstream() {
|
|||
defer s.StopTestBackend()
|
||||
|
||||
// log into test account
|
||||
err = s.Backend.SelectAccount(TestConfig.Account1.WalletAddress, TestConfig.Account1.ChatAddress, TestConfig.Account1.Password)
|
||||
err = s.Backend.SelectAccount(buildLoginParams(TestConfig.Account1.WalletAddress, TestConfig.Account1.ChatAddress, TestConfig.Account1.Password, nil))
|
||||
s.NoError(err)
|
||||
|
||||
rpcClient := s.Backend.StatusNode().RPCClient()
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/extkeys"
|
||||
|
@ -14,6 +15,15 @@ import (
|
|||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
func buildLoginParams(mainAccountAddress, chatAddress, password string, watchAddresses []common.Address) account.LoginParams {
|
||||
return account.LoginParams{
|
||||
ChatAddress: common.HexToAddress(chatAddress),
|
||||
Password: password,
|
||||
MainAccount: common.HexToAddress(mainAccountAddress),
|
||||
WatchAddresses: watchAddresses,
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccountsTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(AccountsTestSuite))
|
||||
}
|
||||
|
@ -42,7 +52,7 @@ func (s *AccountsTestSuite) TestAccountsList() {
|
|||
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(accountInfo.WalletAddress, accountInfo.ChatAddress, TestConfig.Account1.Password)
|
||||
err = s.Backend.SelectAccount(buildLoginParams(accountInfo.WalletAddress, accountInfo.ChatAddress, TestConfig.Account1.Password, nil))
|
||||
s.NoError(err, "account selection failed")
|
||||
|
||||
// at this point main account should show up
|
||||
|
@ -51,36 +61,6 @@ func (s *AccountsTestSuite) TestAccountsList() {
|
|||
s.Equal(1, len(accounts), "exactly single account is expected (main account)")
|
||||
s.Equal(accounts[0].Hex(), accountInfo.WalletAddress,
|
||||
fmt.Sprintf("main account is not retured as the first key: got %s, expected %s", accounts[0].Hex(), "0x"+accountInfo.WalletAddress))
|
||||
|
||||
// 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(accounts[0].Hex(), accountInfo.WalletAddress, "main account is not retured as the first key")
|
||||
s.Equal(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(accounts[0].Hex(), accountInfo.WalletAddress, "main account is not retured as the first key")
|
||||
|
||||
subAccount1MatchesKey1 := accounts[1].Hex() != "0x"+subAccount1
|
||||
subAccount1MatchesKey2 := accounts[2].Hex() != "0x"+subAccount1
|
||||
s.False(!subAccount1MatchesKey1 && !subAccount1MatchesKey2, "subAcount1 not returned")
|
||||
|
||||
subAccount2MatchesKey1 := accounts[1].Hex() != "0x"+subAccount2
|
||||
subAccount2MatchesKey2 := accounts[2].Hex() != "0x"+subAccount2
|
||||
s.False(!subAccount2MatchesKey1 && !subAccount2MatchesKey2, "subAcount2 not returned")
|
||||
}
|
||||
|
||||
func (s *AccountsTestSuite) TestImportSingleExtendedKey() {
|
||||
|
@ -135,55 +115,6 @@ func (s *AccountsTestSuite) TestImportAccount() {
|
|||
s.True(key.ExtendedKey.IsZeroed())
|
||||
}
|
||||
|
||||
func (s *AccountsTestSuite) TestCreateChildAccount() {
|
||||
s.StartTestBackend()
|
||||
defer s.StopTestBackend()
|
||||
|
||||
keyStore, err := s.Backend.StatusNode().AccountKeyStore()
|
||||
s.NoError(err)
|
||||
s.NotNil(keyStore)
|
||||
|
||||
// create an account
|
||||
accountInfo, mnemonic, err := s.Backend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
||||
s.NoError(err)
|
||||
s.T().Logf("Account created: {walletAddress: %s, walletKey: %s, chatAddress: %s, chatKey: %s, mnemonic:%s}",
|
||||
accountInfo.WalletAddress, accountInfo.WalletPubKey, accountInfo.ChatAddress, accountInfo.ChatPubKey, mnemonic)
|
||||
|
||||
acct, err := account.ParseAccountString(accountInfo.WalletAddress)
|
||||
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(accountInfo.WalletAddress, accountInfo.ChatAddress, 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()
|
||||
|
@ -244,15 +175,15 @@ func (s *AccountsTestSuite) TestSelectAccount() {
|
|||
accountInfo2.WalletAddress, accountInfo2.WalletPubKey, accountInfo2.ChatAddress, accountInfo2.ChatPubKey)
|
||||
|
||||
// try selecting with wrong password
|
||||
err = s.Backend.SelectAccount(accountInfo1.WalletAddress, accountInfo1.ChatAddress, "wrongPassword")
|
||||
err = s.Backend.SelectAccount(buildLoginParams(accountInfo1.WalletAddress, accountInfo1.ChatAddress, "wrongPassword", nil))
|
||||
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(accountInfo1.WalletAddress, accountInfo1.ChatAddress, TestConfig.Account1.Password)
|
||||
err = s.Backend.SelectAccount(buildLoginParams(accountInfo1.WalletAddress, accountInfo1.ChatAddress, TestConfig.Account1.Password, nil))
|
||||
s.NoError(err)
|
||||
|
||||
// select another account, make sure that previous account is wiped out from Whisper cache
|
||||
s.NoError(s.Backend.SelectAccount(accountInfo2.WalletAddress, accountInfo2.ChatAddress, TestConfig.Account1.Password))
|
||||
s.NoError(s.Backend.SelectAccount(buildLoginParams(accountInfo2.WalletAddress, accountInfo2.ChatAddress, TestConfig.Account1.Password, nil)))
|
||||
}
|
||||
|
||||
func (s *AccountsTestSuite) TestSetChatAccount() {
|
||||
|
@ -285,19 +216,23 @@ func (s *AccountsTestSuite) TestSelectedAccountOnRestart() {
|
|||
s.NoError(err)
|
||||
|
||||
// make sure that no account is selected by default
|
||||
selectedWalletAccount, err := s.Backend.AccountManager().SelectedWalletAccount()
|
||||
selectedWalletAccount, err := s.Backend.AccountManager().MainAccountAddress()
|
||||
s.EqualError(account.ErrNoAccountSelected, err.Error(), "account selected, but should not be")
|
||||
s.Nil(selectedWalletAccount)
|
||||
s.Equal(common.Address{}, selectedWalletAccount)
|
||||
selectedChatAccount, err := s.Backend.AccountManager().SelectedChatAccount()
|
||||
s.EqualError(account.ErrNoAccountSelected, err.Error(), "account selected, but should not be")
|
||||
s.Nil(selectedChatAccount)
|
||||
|
||||
// select account
|
||||
err = s.Backend.SelectAccount(accountInfo1.WalletAddress, accountInfo1.ChatAddress, "wrongPassword")
|
||||
err = s.Backend.SelectAccount(buildLoginParams(accountInfo1.WalletAddress, accountInfo1.ChatAddress, "wrongPassword", nil))
|
||||
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(accountInfo2.WalletAddress, accountInfo2.ChatAddress, TestConfig.Account1.Password))
|
||||
watchAddresses := []common.Address{
|
||||
common.HexToAddress("0x00000000000000000000000000000000000001"),
|
||||
common.HexToAddress("0x00000000000000000000000000000000000002"),
|
||||
}
|
||||
s.NoError(s.Backend.SelectAccount(buildLoginParams(accountInfo2.WalletAddress, accountInfo2.ChatAddress, TestConfig.Account1.Password, watchAddresses)))
|
||||
|
||||
// stop node (and all of its sub-protocols)
|
||||
nodeConfig := s.Backend.StatusNode().Config()
|
||||
|
@ -306,27 +241,29 @@ func (s *AccountsTestSuite) TestSelectedAccountOnRestart() {
|
|||
s.NoError(s.Backend.StopNode())
|
||||
|
||||
// make sure that account is still selected
|
||||
selectedWalletAccount, err = s.Backend.AccountManager().SelectedWalletAccount()
|
||||
selectedWalletAccount, err = s.Backend.AccountManager().MainAccountAddress()
|
||||
s.Require().NoError(err)
|
||||
s.NotNil(selectedWalletAccount)
|
||||
s.Equal(selectedWalletAccount.Address.Hex(), accountInfo2.WalletAddress, "incorrect wallet address selected")
|
||||
s.Equal(selectedWalletAccount.String(), accountInfo2.WalletAddress, "incorrect wallet address selected")
|
||||
selectedChatAccount, err = s.Backend.AccountManager().SelectedChatAccount()
|
||||
s.NoError(err)
|
||||
s.NotNil(selectedChatAccount)
|
||||
s.Equal(selectedChatAccount.Address.Hex(), accountInfo2.ChatAddress, "incorrect chat address selected")
|
||||
s.Equal(watchAddresses, s.Backend.AccountManager().WatchAddresses())
|
||||
|
||||
// resume node
|
||||
s.Require().NoError(s.Backend.StartNode(&preservedNodeConfig))
|
||||
|
||||
// re-check selected account (account2 MUST be selected)
|
||||
selectedWalletAccount, err = s.Backend.AccountManager().SelectedWalletAccount()
|
||||
selectedWalletAccount, err = s.Backend.AccountManager().MainAccountAddress()
|
||||
s.NoError(err)
|
||||
s.NotNil(selectedWalletAccount)
|
||||
s.Equal(selectedWalletAccount.Address.Hex(), accountInfo2.WalletAddress, "incorrect wallet address selected")
|
||||
s.Equal(selectedWalletAccount.String(), accountInfo2.WalletAddress, "incorrect wallet address selected")
|
||||
selectedChatAccount, err = s.Backend.AccountManager().SelectedChatAccount()
|
||||
s.NoError(err)
|
||||
s.NotNil(selectedChatAccount)
|
||||
s.Equal(selectedChatAccount.Address.Hex(), accountInfo2.WalletAddress, "incorrect chat address selected")
|
||||
s.Equal(watchAddresses, s.Backend.AccountManager().WatchAddresses())
|
||||
|
||||
// now restart node using RestartNode() method, and make sure that account is still available
|
||||
s.RestartTestNode()
|
||||
|
@ -336,10 +273,11 @@ func (s *AccountsTestSuite) TestSelectedAccountOnRestart() {
|
|||
s.NoError(s.Backend.Logout())
|
||||
s.RestartTestNode()
|
||||
|
||||
selectedWalletAccount, err = s.Backend.AccountManager().SelectedWalletAccount()
|
||||
selectedWalletAccount, err = s.Backend.AccountManager().MainAccountAddress()
|
||||
s.EqualError(account.ErrNoAccountSelected, err.Error())
|
||||
s.Nil(selectedWalletAccount)
|
||||
s.Equal(common.Address{}, selectedWalletAccount)
|
||||
selectedChatAccount, err = s.Backend.AccountManager().SelectedChatAccount()
|
||||
s.EqualError(account.ErrNoAccountSelected, err.Error())
|
||||
s.Nil(selectedChatAccount)
|
||||
s.Len(s.Backend.AccountManager().WatchAddresses(), 0)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
gethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/status-im/status-go/account"
|
||||
|
@ -17,6 +18,14 @@ import (
|
|||
|
||||
type initFunc func([]byte, *transactions.SendTxArgs)
|
||||
|
||||
func buildLoginParams(mainAccountAddress, chatAddress, password string) account.LoginParams {
|
||||
return account.LoginParams{
|
||||
ChatAddress: common.HexToAddress(chatAddress),
|
||||
Password: password,
|
||||
MainAccount: common.HexToAddress(mainAccountAddress),
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransactionsTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(TransactionsTestSuite))
|
||||
}
|
||||
|
@ -70,7 +79,7 @@ func (s *TransactionsTestSuite) TestCallUpstreamPrivateRPCSendTransaction() {
|
|||
func (s *TransactionsTestSuite) sendTransactionUsingRPCClient(
|
||||
callRPCFn func(string) (string, error),
|
||||
) {
|
||||
err := s.Backend.SelectAccount(TestConfig.Account1.WalletAddress, TestConfig.Account1.ChatAddress, TestConfig.Account1.Password)
|
||||
err := s.Backend.SelectAccount(buildLoginParams(TestConfig.Account1.WalletAddress, TestConfig.Account1.ChatAddress, TestConfig.Account1.Password))
|
||||
s.NoError(err)
|
||||
|
||||
result, err := callRPCFn(`{
|
||||
|
@ -94,7 +103,7 @@ func (s *TransactionsTestSuite) TestEmptyToFieldPreserved() {
|
|||
defer s.StopTestBackend()
|
||||
|
||||
EnsureNodeSync(s.Backend.StatusNode().EnsureSync)
|
||||
err := s.Backend.SelectAccount(TestConfig.Account1.WalletAddress, TestConfig.Account1.ChatAddress, TestConfig.Account1.Password)
|
||||
err := s.Backend.SelectAccount(buildLoginParams(TestConfig.Account1.WalletAddress, TestConfig.Account1.ChatAddress, TestConfig.Account1.Password))
|
||||
s.NoError(err)
|
||||
|
||||
args := transactions.SendTxArgs{
|
||||
|
@ -162,7 +171,7 @@ func (s *TransactionsTestSuite) testSendContractTx(setInputAndDataValue initFunc
|
|||
|
||||
EnsureNodeSync(s.Backend.StatusNode().EnsureSync)
|
||||
|
||||
err := s.Backend.AccountManager().SelectAccount(TestConfig.Account1.WalletAddress, TestConfig.Account1.ChatAddress, TestConfig.Account1.Password)
|
||||
err := s.Backend.AccountManager().SelectAccount(buildLoginParams(TestConfig.Account1.WalletAddress, TestConfig.Account1.ChatAddress, TestConfig.Account1.Password))
|
||||
s.NoError(err)
|
||||
|
||||
// this call blocks, up until Complete Transaction is called
|
||||
|
@ -196,7 +205,7 @@ func (s *TransactionsTestSuite) TestSendEther() {
|
|||
|
||||
EnsureNodeSync(s.Backend.StatusNode().EnsureSync)
|
||||
|
||||
err := s.Backend.AccountManager().SelectAccount(TestConfig.Account1.WalletAddress, TestConfig.Account1.ChatAddress, TestConfig.Account1.Password)
|
||||
err := s.Backend.AccountManager().SelectAccount(buildLoginParams(TestConfig.Account1.WalletAddress, TestConfig.Account1.ChatAddress, TestConfig.Account1.Password))
|
||||
s.NoError(err)
|
||||
|
||||
hash, err := s.Backend.SendTransaction(transactions.SendTxArgs{
|
||||
|
@ -216,7 +225,7 @@ func (s *TransactionsTestSuite) TestSendEtherTxUpstream() {
|
|||
s.StartTestBackend(e2e.WithUpstream(addr))
|
||||
defer s.StopTestBackend()
|
||||
|
||||
err = s.Backend.SelectAccount(TestConfig.Account1.WalletAddress, TestConfig.Account1.ChatAddress, TestConfig.Account1.Password)
|
||||
err = s.Backend.SelectAccount(buildLoginParams(TestConfig.Account1.WalletAddress, TestConfig.Account1.ChatAddress, TestConfig.Account1.Password))
|
||||
s.NoError(err)
|
||||
|
||||
hash, err := s.Backend.SendTransaction(transactions.SendTxArgs{
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/status-im/status-go/account"
|
||||
|
@ -22,6 +23,14 @@ type WhisperTestSuite struct {
|
|||
e2e.BackendTestSuite
|
||||
}
|
||||
|
||||
func buildLoginParams(mainAccountAddress, chatAddress, password string) account.LoginParams {
|
||||
return account.LoginParams{
|
||||
ChatAddress: common.HexToAddress(chatAddress),
|
||||
Password: password,
|
||||
MainAccount: common.HexToAddress(mainAccountAddress),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(adam): can anyone explain what this test is testing?
|
||||
// I don't see any race condition testing here.
|
||||
func (s *WhisperTestSuite) TestWhisperFilterRace() {
|
||||
|
@ -109,17 +118,17 @@ func (s *WhisperTestSuite) TestSelectAccount() {
|
|||
s.False(whisperService.HasKeyPair(accountInfo2.ChatPubKey), "identity already present in whisper")
|
||||
|
||||
// try selecting with wrong password
|
||||
err = s.Backend.SelectAccount(accountInfo1.WalletAddress, accountInfo1.ChatAddress, "wrongpassword")
|
||||
err = s.Backend.SelectAccount(buildLoginParams(accountInfo1.WalletAddress, accountInfo1.ChatAddress, "wrongpassword"))
|
||||
s.NotNil(err)
|
||||
|
||||
// select account 1
|
||||
err = s.Backend.SelectAccount(accountInfo1.WalletAddress, accountInfo1.ChatAddress, TestConfig.Account1.Password)
|
||||
err = s.Backend.SelectAccount(buildLoginParams(accountInfo1.WalletAddress, accountInfo1.ChatAddress, TestConfig.Account1.Password))
|
||||
s.NoError(err)
|
||||
s.True(whisperService.HasKeyPair(accountInfo1.ChatPubKey), "identity not injected in whisper")
|
||||
|
||||
// select account 2, make sure that previous account is wiped out from Whisper cache
|
||||
s.False(whisperService.HasKeyPair(accountInfo2.ChatPubKey), "identity already present in whisper")
|
||||
s.NoError(s.Backend.SelectAccount(accountInfo2.WalletAddress, accountInfo2.ChatAddress, TestConfig.Account2.Password))
|
||||
s.NoError(s.Backend.SelectAccount(buildLoginParams(accountInfo2.WalletAddress, accountInfo2.ChatAddress, TestConfig.Account2.Password)))
|
||||
s.True(whisperService.HasKeyPair(accountInfo2.ChatPubKey), "identity not injected into whisper")
|
||||
}
|
||||
|
||||
|
@ -136,7 +145,7 @@ func (s *WhisperTestSuite) TestLogout() {
|
|||
|
||||
// make sure that identity doesn't exist (yet) in Whisper
|
||||
s.False(whisperService.HasKeyPair(accountInfo.ChatPubKey), "identity already present in whisper")
|
||||
s.NoError(s.Backend.SelectAccount(accountInfo.WalletAddress, accountInfo.ChatAddress, TestConfig.Account1.Password))
|
||||
s.NoError(s.Backend.SelectAccount(buildLoginParams(accountInfo.WalletAddress, accountInfo.ChatAddress, TestConfig.Account1.Password)))
|
||||
s.True(whisperService.HasKeyPair(accountInfo.ChatPubKey), "identity not injected into whisper")
|
||||
|
||||
s.NoError(s.Backend.Logout())
|
||||
|
@ -159,9 +168,9 @@ func (s *WhisperTestSuite) TestSelectedAccountOnRestart() {
|
|||
s.False(whisperService.HasKeyPair(accountInfo1.ChatPubKey), "identity already present in whisper")
|
||||
|
||||
// make sure that no wallet account is selected by default
|
||||
selectedWalletAccount, err := s.Backend.AccountManager().SelectedWalletAccount()
|
||||
selectedWalletAccount, err := s.Backend.AccountManager().MainAccountAddress()
|
||||
s.EqualError(account.ErrNoAccountSelected, err.Error(), "account selected, but should not be")
|
||||
s.Nil(selectedWalletAccount)
|
||||
s.Equal(common.Address{}, selectedWalletAccount)
|
||||
|
||||
// make sure that no chat account is selected by default
|
||||
selectedChatAccount, err := s.Backend.AccountManager().SelectedChatAccount()
|
||||
|
@ -169,12 +178,12 @@ func (s *WhisperTestSuite) TestSelectedAccountOnRestart() {
|
|||
s.Nil(selectedChatAccount)
|
||||
|
||||
// select account with wrong password
|
||||
err = s.Backend.SelectAccount(accountInfo1.WalletAddress, accountInfo1.ChatAddress, "wrongPassword")
|
||||
err = s.Backend.SelectAccount(buildLoginParams(accountInfo1.WalletAddress, accountInfo1.ChatAddress, "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 with right password
|
||||
s.NoError(s.Backend.SelectAccount(accountInfo1.WalletAddress, accountInfo1.ChatAddress, TestConfig.Account1.Password))
|
||||
s.NoError(s.Backend.SelectAccount(buildLoginParams(accountInfo1.WalletAddress, accountInfo1.ChatAddress, TestConfig.Account1.Password)))
|
||||
selectedChatAccount1, err := s.Backend.AccountManager().SelectedChatAccount()
|
||||
s.NoError(err)
|
||||
selectedChatPubKey1 := hexutil.Encode(crypto.FromECDSAPub(&selectedChatAccount1.AccountKey.PrivateKey.PublicKey))
|
||||
|
@ -183,7 +192,7 @@ func (s *WhisperTestSuite) TestSelectedAccountOnRestart() {
|
|||
|
||||
// select another account, make sure that previous account is wiped out from Whisper cache
|
||||
s.False(whisperService.HasKeyPair(accountInfo2.ChatPubKey), "identity already present in whisper")
|
||||
s.NoError(s.Backend.SelectAccount(accountInfo2.WalletAddress, accountInfo2.ChatAddress, TestConfig.Account2.Password))
|
||||
s.NoError(s.Backend.SelectAccount(buildLoginParams(accountInfo2.WalletAddress, accountInfo2.ChatAddress, TestConfig.Account2.Password)))
|
||||
selectedChatAccount2, err := s.Backend.AccountManager().SelectedChatAccount()
|
||||
s.NoError(err)
|
||||
selectedChatPubKey2 := hexutil.Encode(crypto.FromECDSAPub(&selectedChatAccount2.AccountKey.PrivateKey.PublicKey))
|
||||
|
@ -201,10 +210,10 @@ func (s *WhisperTestSuite) TestSelectedAccountOnRestart() {
|
|||
s.Require().NoError(s.Backend.StartNode(&preservedNodeConfig))
|
||||
|
||||
// re-check selected account (account2 MUST be selected)
|
||||
selectedWalletAccount, err = s.Backend.AccountManager().SelectedWalletAccount()
|
||||
selectedWalletAccount, err = s.Backend.AccountManager().MainAccountAddress()
|
||||
s.NoError(err)
|
||||
s.NotNil(selectedWalletAccount)
|
||||
s.Equal(selectedWalletAccount.Address.Hex(), accountInfo2.WalletAddress, "incorrect wallet address selected")
|
||||
s.Equal(selectedWalletAccount.String(), accountInfo2.WalletAddress, "incorrect wallet address selected")
|
||||
selectedChatAccount, err = s.Backend.AccountManager().SelectedChatAccount()
|
||||
s.NoError(err)
|
||||
s.NotNil(selectedChatAccount)
|
||||
|
@ -230,9 +239,9 @@ func (s *WhisperTestSuite) TestSelectedAccountOnRestart() {
|
|||
s.False(whisperService.HasKeyPair(selectedChatPubKey2), "identity not injected into whisper")
|
||||
s.False(whisperService.HasKeyPair(selectedChatPubKey1), "identity should not be present, but it is still present in whisper")
|
||||
|
||||
selectedWalletAccount, err = s.Backend.AccountManager().SelectedWalletAccount()
|
||||
selectedWalletAccount, err = s.Backend.AccountManager().MainAccountAddress()
|
||||
s.EqualError(account.ErrNoAccountSelected, err.Error())
|
||||
s.Nil(selectedWalletAccount)
|
||||
s.Equal(common.Address{}, selectedWalletAccount)
|
||||
|
||||
selectedChatAccount, err = s.Backend.AccountManager().SelectedChatAccount()
|
||||
s.EqualError(account.ErrNoAccountSelected, err.Error())
|
||||
|
@ -251,7 +260,7 @@ func (s *WhisperTestSuite) TestSelectedChatKeyIsUsedInWhisper() {
|
|||
s.NoError(err)
|
||||
|
||||
// select account
|
||||
s.NoError(s.Backend.SelectAccount(accountInfo.WalletAddress, accountInfo.ChatAddress, TestConfig.Account1.Password))
|
||||
s.NoError(s.Backend.SelectAccount(buildLoginParams(accountInfo.WalletAddress, accountInfo.ChatAddress, TestConfig.Account1.Password)))
|
||||
|
||||
// Get the chat account
|
||||
selectedChatAccount, err := s.Backend.AccountManager().SelectedChatAccount()
|
||||
|
|
Loading…
Reference in New Issue