status-go/geth/account/accounts_test.go

492 lines
13 KiB
Go
Raw Normal View History

2017-12-19 15:45:30 +00:00
package account
import (
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"testing"
2017-12-19 17:20:53 +00:00
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
gethcommon "github.com/ethereum/go-ethereum/common"
2017-12-18 14:08:31 +00:00
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
"github.com/golang/mock/gomock"
"github.com/status-im/status-go/geth/common"
. "github.com/status-im/status-go/testing"
"github.com/stretchr/testify/require"
2017-12-19 13:07:31 +00:00
"github.com/stretchr/testify/suite"
)
func TestVerifyAccountPassword(t *testing.T) {
2017-12-19 15:45:30 +00:00
accManager := NewManager(nil)
keyStoreDir, err := ioutil.TempDir(os.TempDir(), "accounts")
require.NoError(t, err)
defer os.RemoveAll(keyStoreDir) //nolint: errcheck
emptyKeyStoreDir, err := ioutil.TempDir(os.TempDir(), "accounts_empty")
require.NoError(t, err)
defer os.RemoveAll(emptyKeyStoreDir) //nolint: errcheck
// import account keys
require.NoError(t, common.ImportTestAccount(keyStoreDir, GetAccount1PKFile()))
require.NoError(t, common.ImportTestAccount(keyStoreDir, GetAccount2PKFile()))
account1Address := gethcommon.BytesToAddress(gethcommon.FromHex(TestConfig.Account1.Address))
testCases := []struct {
name string
keyPath string
address string
password string
expectedError error
}{
{
"correct address, correct password (decrypt should succeed)",
keyStoreDir,
TestConfig.Account1.Address,
TestConfig.Account1.Password,
nil,
},
{
"correct address, correct password, non-existent key store",
filepath.Join(keyStoreDir, "non-existent-folder"),
TestConfig.Account1.Address,
TestConfig.Account1.Password,
fmt.Errorf("cannot traverse key store folder: lstat %s/non-existent-folder: no such file or directory", keyStoreDir),
},
{
"correct address, correct password, empty key store (pk is not there)",
emptyKeyStoreDir,
TestConfig.Account1.Address,
TestConfig.Account1.Password,
2017-10-10 09:38:49 +00:00
fmt.Errorf("cannot locate account for address: %s", account1Address.Hex()),
},
{
"wrong address, correct password",
keyStoreDir,
"0x79791d3e8f2daa1f7fec29649d152c0ada3cc535",
TestConfig.Account1.Password,
2017-10-10 09:38:49 +00:00
fmt.Errorf("cannot locate account for address: %s", "0x79791d3E8F2dAa1F7FeC29649d152c0aDA3cc535"),
},
{
"correct address, wrong password",
keyStoreDir,
TestConfig.Account1.Address,
"wrong password", // wrong password
errors.New("could not decrypt key with given passphrase"),
},
}
for _, testCase := range testCases {
2017-12-19 15:45:30 +00:00
accountKey, err := accManager.VerifyAccountPassword(testCase.keyPath, testCase.address, testCase.password)
if !reflect.DeepEqual(err, testCase.expectedError) {
require.FailNow(t, fmt.Sprintf("unexpected error: expected \n'%v', got \n'%v'", testCase.expectedError, err))
}
if err == nil {
if accountKey == nil {
require.Fail(t, "no error reported, but account key is missing")
}
accountAddress := gethcommon.BytesToAddress(gethcommon.FromHex(testCase.address))
if accountKey.Address != accountAddress {
require.Fail(t, "account mismatch: have %s, want %s", accountKey.Address.Hex(), accountAddress.Hex())
}
}
}
}
2017-10-10 09:38:49 +00:00
// TestVerifyAccountPasswordWithAccountBeforeEIP55 verifies if VerifyAccountPassword
// can handle accounts before introduction of EIP55.
func TestVerifyAccountPasswordWithAccountBeforeEIP55(t *testing.T) {
2017-10-10 09:38:49 +00:00
keyStoreDir, err := ioutil.TempDir("", "status-accounts-test")
require.NoError(t, err)
defer os.RemoveAll(keyStoreDir) //nolint: errcheck
2017-10-10 09:38:49 +00:00
// Import keys and make sure one was created before EIP55 introduction.
err = common.ImportTestAccount(keyStoreDir, "test-account3-before-eip55.pk")
require.NoError(t, err)
2017-10-10 09:38:49 +00:00
2017-12-19 15:45:30 +00:00
accManager := NewManager(nil)
2017-10-10 09:38:49 +00:00
address := gethcommon.HexToAddress(TestConfig.Account3.Address)
2017-12-19 15:45:30 +00:00
_, err = accManager.VerifyAccountPassword(keyStoreDir, address.Hex(), TestConfig.Account3.Password)
require.NoError(t, err)
2017-10-10 09:38:49 +00:00
}
2017-12-19 16:59:43 +00:00
var (
testErrWhisper = errors.New("Can't return a whisper service")
testErrKeyStore = errors.New("Can't return a key store")
)
2017-12-19 13:07:31 +00:00
func TestManagerTestSuite(t *testing.T) {
2017-12-19 15:45:30 +00:00
nodeManager := newMockNodeManager(t)
accManager := NewManager(nodeManager)
2017-12-19 13:07:31 +00:00
keyStoreDir, err := ioutil.TempDir(os.TempDir(), "accounts")
require.NoError(t, err)
2017-12-19 13:07:31 +00:00
keyStore := keystore.NewKeyStore(keyStoreDir, keystore.LightScryptN, keystore.LightScryptP)
defer os.RemoveAll(keyStoreDir) //nolint: errcheck
2017-12-14 13:48:01 +00:00
testPassword := "test-password"
// Initial test - create test account
nodeManager.EXPECT().AccountKeyStore().Return(keyStore, nil)
addr, pubKey, mnemonic, err := accManager.CreateAccount(testPassword)
require.NoError(t, err)
2017-12-26 13:32:41 +00:00
require.NotEmpty(t, addr)
require.NotEmpty(t, pubKey)
require.NotEmpty(t, mnemonic)
s := &ManagerTestSuite{
testAccount: testAccount{
"test-password",
addr,
pubKey,
mnemonic,
},
2017-12-19 17:20:53 +00:00
nodeManager: nodeManager,
accManager: accManager,
2017-12-19 17:20:53 +00:00
keyStore: keyStore,
shh: whisper.New(nil),
gethAccManager: accounts.NewManager(),
}
suite.Run(t, s)
2017-12-19 13:07:31 +00:00
}
2017-12-19 15:45:30 +00:00
func newMockNodeManager(t *testing.T) *common.MockNodeManager {
ctrl := gomock.NewController(t)
return common.NewMockNodeManager(ctrl)
}
2017-12-19 13:07:31 +00:00
type ManagerTestSuite struct {
suite.Suite
testAccount
2017-12-19 17:20:53 +00:00
nodeManager *common.MockNodeManager
accManager *Manager
keyStore *keystore.KeyStore
shh *whisper.Whisper
gethAccManager *accounts.Manager
2017-12-19 13:07:31 +00:00
}
type testAccount struct {
password string
address string
pubKey string
mnemonic string
}
2017-12-19 15:45:30 +00:00
// reinitMock is for reassigning a new mock node manager to account manager.
// Stating the amount of times for mock calls kills the flexibility for
// development so this is a good workaround to use with EXPECT().AnyTimes()
func (s *ManagerTestSuite) reinitMock() {
s.nodeManager = newMockNodeManager(s.T())
s.accManager.nodeManager = s.nodeManager
}
2017-12-28 13:40:56 +00:00
func (s *ManagerTestSuite) TestCreateAccount() {
2017-12-19 15:45:30 +00:00
s.reinitMock()
2017-12-14 13:50:12 +00:00
// Don't fail on empty password
2017-12-19 15:45:30 +00:00
s.nodeManager.EXPECT().AccountKeyStore().Return(s.keyStore, nil)
_, _, _, err := s.accManager.CreateAccount(s.password)
2017-12-19 13:07:31 +00:00
s.NoError(err)
2017-12-14 13:50:12 +00:00
2017-12-28 13:40:56 +00:00
s.nodeManager.EXPECT().AccountKeyStore().Return(nil, testErrKeyStore)
_, _, _, err = s.accManager.CreateAccount(s.password)
s.Equal(err, testErrKeyStore)
}
func (s *ManagerTestSuite) TestRecoverAccount() {
s.reinitMock()
2017-12-19 15:45:30 +00:00
s.nodeManager.EXPECT().AccountKeyStore().Return(s.keyStore, nil)
addr, pubKey, err := s.accManager.RecoverAccount(s.password, s.mnemonic)
2017-12-19 13:07:31 +00:00
s.NoError(err)
s.Equal(s.address, addr)
s.Equal(s.pubKey, pubKey)
2017-12-14 13:48:01 +00:00
s.nodeManager.EXPECT().AccountKeyStore().Return(nil, testErrKeyStore)
_, _, err = s.accManager.RecoverAccount(s.password, s.mnemonic)
s.Equal(err, testErrKeyStore)
}
2017-12-18 14:08:31 +00:00
2017-12-19 13:07:31 +00:00
func (s *ManagerTestSuite) TestSelectAccount() {
2017-12-19 15:45:30 +00:00
s.reinitMock()
2017-12-18 14:08:31 +00:00
testCases := []struct {
name string
accountKeyStoreReturn []interface{}
whisperServiceReturn []interface{}
address string
password string
fail bool
}{
{
"success",
2017-12-19 15:45:30 +00:00
[]interface{}{s.keyStore, nil},
[]interface{}{s.shh, nil},
s.address,
2017-12-19 15:45:30 +00:00
s.password,
2017-12-18 14:08:31 +00:00
false,
},
{
"fail_keyStore",
2017-12-19 16:59:43 +00:00
[]interface{}{nil, testErrKeyStore},
2017-12-19 15:45:30 +00:00
[]interface{}{s.shh, nil},
s.address,
2017-12-19 15:45:30 +00:00
s.password,
2017-12-18 14:08:31 +00:00
true,
},
{
"fail_whisperService",
2017-12-19 15:45:30 +00:00
[]interface{}{s.keyStore, nil},
2017-12-19 16:59:43 +00:00
[]interface{}{nil, testErrWhisper},
s.address,
2017-12-19 15:45:30 +00:00
s.password,
2017-12-18 14:08:31 +00:00
true,
},
{
"fail_wrongAddress",
2017-12-19 15:45:30 +00:00
[]interface{}{s.keyStore, nil},
[]interface{}{s.shh, nil},
2017-12-18 14:08:31 +00:00
"wrong-address",
2017-12-19 15:45:30 +00:00
s.password,
2017-12-18 14:08:31 +00:00
true,
},
{
"fail_wrongPassword",
2017-12-19 15:45:30 +00:00
[]interface{}{s.keyStore, nil},
[]interface{}{s.shh, nil},
s.address,
2017-12-18 14:08:31 +00:00
"wrong-password",
true,
},
}
for _, testCase := range testCases {
2017-12-19 13:07:31 +00:00
s.T().Run(testCase.name, func(t *testing.T) {
2017-12-19 15:45:30 +00:00
s.reinitMock()
s.nodeManager.EXPECT().AccountKeyStore().Return(testCase.accountKeyStoreReturn...).AnyTimes()
s.nodeManager.EXPECT().WhisperService().Return(testCase.whisperServiceReturn...).AnyTimes()
err := s.accManager.SelectAccount(testCase.address, testCase.password)
2017-12-19 15:45:30 +00:00
if testCase.fail {
s.Error(err)
} else {
s.NoError(err)
}
})
}
}
func (s *ManagerTestSuite) TestCreateChildAccount() {
s.reinitMock()
// First, test the negative case where an account is not selected
// and an address is not provided.
s.accManager.selectedAccount = nil
s.T().Run("fail_noAccount", func(t *testing.T) {
s.nodeManager.EXPECT().AccountKeyStore().Return(s.keyStore, nil).AnyTimes()
_, _, err := s.accManager.CreateChildAccount("", s.password)
s.Error(err)
})
// Now, select the test account for rest of the test cases.
2017-12-19 15:45:30 +00:00
s.reinitMock()
s.nodeManager.EXPECT().AccountKeyStore().Return(s.keyStore, nil).AnyTimes()
s.nodeManager.EXPECT().WhisperService().Return(s.shh, nil).AnyTimes()
err := s.accManager.SelectAccount(s.address, s.password)
2017-12-19 15:45:30 +00:00
s.NoError(err)
testCases := []struct {
name string
address string
password string
accountKeyStoreReturn []interface{}
fail bool
}{
{
"success",
s.address,
2017-12-19 15:45:30 +00:00
s.password,
[]interface{}{s.keyStore, nil},
false,
},
{
"fail_keyStore",
s.address,
2017-12-19 15:45:30 +00:00
s.password,
2017-12-19 16:59:43 +00:00
[]interface{}{nil, testErrKeyStore},
2017-12-19 15:45:30 +00:00
true,
},
2017-12-19 15:52:13 +00:00
{
"fail_wrongAddress",
"wrong-address",
s.password,
[]interface{}{s.keyStore, nil},
true,
},
{
"fail_wrongPassword",
s.address,
2017-12-19 15:52:13 +00:00
"wrong-password",
[]interface{}{s.keyStore, nil},
true,
},
2017-12-19 15:45:30 +00:00
}
for _, testCase := range testCases {
s.T().Run(testCase.name, func(t *testing.T) {
s.reinitMock()
s.nodeManager.EXPECT().AccountKeyStore().Return(testCase.accountKeyStoreReturn...).AnyTimes()
childAddr, childPubKey, err := s.accManager.CreateChildAccount(testCase.address, testCase.password)
2017-12-18 14:08:31 +00:00
if testCase.fail {
2017-12-19 13:07:31 +00:00
s.Error(err)
2017-12-18 14:08:31 +00:00
} else {
2017-12-19 13:07:31 +00:00
s.NoError(err)
2017-12-26 13:32:41 +00:00
s.NotEmpty(childAddr)
s.NotEmpty(childPubKey)
2017-12-18 14:08:31 +00:00
}
})
}
}
2017-12-19 16:11:46 +00:00
2017-12-19 16:47:32 +00:00
func (s *ManagerTestSuite) TestSelectedAndReSelectAccount() {
2017-12-19 16:11:46 +00:00
s.reinitMock()
// Select the test account
2017-12-19 16:11:46 +00:00
s.nodeManager.EXPECT().AccountKeyStore().Return(s.keyStore, nil).AnyTimes()
s.nodeManager.EXPECT().WhisperService().Return(s.shh, nil).AnyTimes()
err := s.accManager.SelectAccount(s.address, s.password)
2017-12-19 16:11:46 +00:00
s.NoError(err)
s.T().Run("success", func(t *testing.T) {
acc, err := s.accManager.SelectedAccount()
s.NoError(err)
s.NotNil(acc)
2017-12-19 16:47:32 +00:00
err = s.accManager.ReSelectAccount()
s.NoError(err)
})
s.T().Run("ReSelect_fail_whisper", func(t *testing.T) {
2017-12-19 16:47:32 +00:00
s.reinitMock()
2017-12-19 16:59:43 +00:00
s.nodeManager.EXPECT().WhisperService().Return(nil, testErrWhisper).AnyTimes()
2017-12-19 16:47:32 +00:00
err = s.accManager.ReSelectAccount()
s.Error(err)
2017-12-19 16:11:46 +00:00
})
s.accManager.selectedAccount = nil
2017-12-19 16:47:32 +00:00
s.reinitMock()
s.nodeManager.EXPECT().AccountKeyStore().Return(s.keyStore, nil).AnyTimes()
s.nodeManager.EXPECT().WhisperService().Return(s.shh, nil).AnyTimes()
2017-12-19 16:11:46 +00:00
s.T().Run("Selected_fail_noAccount", func(t *testing.T) {
2017-12-19 16:11:46 +00:00
_, err := s.accManager.SelectedAccount()
s.Equal(ErrNoAccountSelected, err)
})
2017-12-19 16:47:32 +00:00
s.T().Run("ReSelect_success_noAccount", func(t *testing.T) {
err = s.accManager.ReSelectAccount()
s.NoError(err)
})
2017-12-19 16:11:46 +00:00
}
2017-12-19 16:59:43 +00:00
func (s *ManagerTestSuite) TestLogout() {
s.reinitMock()
s.nodeManager.EXPECT().WhisperService().Return(s.shh, nil)
err := s.accManager.Logout()
s.NoError(err)
s.nodeManager.EXPECT().WhisperService().Return(nil, testErrWhisper)
err = s.accManager.Logout()
s.Error(err)
}
2017-12-19 17:20:53 +00:00
func (s *ManagerTestSuite) TestAccounts() {
s.reinitMock()
// Select the test account
2017-12-19 17:20:53 +00:00
s.nodeManager.EXPECT().AccountKeyStore().Return(s.keyStore, nil).AnyTimes()
s.nodeManager.EXPECT().WhisperService().Return(s.shh, nil).AnyTimes()
err := s.accManager.SelectAccount(s.address, s.password)
2017-12-19 17:20:53 +00:00
s.NoError(err)
// Success
s.nodeManager.EXPECT().AccountManager().Return(s.gethAccManager, nil)
accs, err := s.accManager.Accounts()
s.NoError(err)
s.NotNil(accs)
// Can't get an account manager
s.nodeManager.EXPECT().AccountManager().Return(nil, errors.New("Can't return an account manager"))
_, err = s.accManager.Accounts()
s.Error(err)
// Selected account is nil but doesn't fail
s.accManager.selectedAccount = nil
s.nodeManager.EXPECT().AccountManager().Return(s.gethAccManager, nil)
accs, err = s.accManager.Accounts()
s.NoError(err)
s.NotNil(accs)
}
2017-12-19 17:32:10 +00:00
func (s *ManagerTestSuite) TestAddressToDecryptedAccount() {
s.reinitMock()
testCases := []struct {
name string
accountKeyStoreReturn []interface{}
address string
password string
fail bool
}{
{
"success",
[]interface{}{s.keyStore, nil},
s.address,
2017-12-19 17:32:10 +00:00
s.password,
false,
},
{
"fail_keyStore",
[]interface{}{nil, testErrKeyStore},
s.address,
2017-12-19 17:32:10 +00:00
s.password,
true,
},
{
"fail_wrongAddress",
[]interface{}{s.keyStore, nil},
"wrong-address",
s.password,
true,
},
{
"fail_wrongPassword",
[]interface{}{s.keyStore, nil},
s.address,
2017-12-19 17:32:10 +00:00
"wrong-password",
true,
},
}
for _, testCase := range testCases {
s.T().Run(testCase.name, func(t *testing.T) {
s.reinitMock()
s.nodeManager.EXPECT().AccountKeyStore().Return(testCase.accountKeyStoreReturn...).AnyTimes()
acc, key, err := s.accManager.AddressToDecryptedAccount(testCase.address, testCase.password)
2017-12-19 17:32:10 +00:00
if testCase.fail {
s.Error(err)
} else {
s.NoError(err)
s.NotNil(acc)
s.NotNil(key)
s.Equal(acc.Address, key.Address)
2017-12-19 17:32:10 +00:00
}
})
}
}