Verify valid sub-accounts using database

This commit is contained in:
dmitry 2019-08-29 11:06:22 +03:00 committed by Dmitry Shulyak
parent b8ea79a3f0
commit bf95a71f6f
8 changed files with 171 additions and 56 deletions

View File

@ -557,25 +557,16 @@ func (b *StatusBackend) HashTypedData(typed typeddata.TypedData) (common.Hash, e
func (b *StatusBackend) getVerifiedWalletAccount(address, password string) (*account.SelectedExtKey, error) {
config := b.StatusNode().Config()
var validAddress bool
addresses := b.accountManager.WatchAddresses()
mainAccountAddress, err := b.accountManager.MainAccountAddress()
db := accounts.NewDB(b.appDB)
exists, err := db.AddressExists(common.HexToAddress(address))
if err != nil {
b.log.Error("failed to query db for a given address", "address", address, "error", err)
return nil, err
}
addresses = append(addresses, mainAccountAddress)
for _, a := range addresses {
if a.String() == address {
validAddress = true
break
}
}
if !validAddress {
if !exists {
b.log.Error("failed to get a selected account", "err", transactions.ErrInvalidTxSender)
return nil, transactions.ErrInvalidTxSender
return nil, transactions.ErrAccountDoesntExist
}
key, err := b.accountManager.VerifyAccountPassword(config.KeyStoreDir, address, password)

View File

@ -4,7 +4,10 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"math/rand"
"os"
"path/filepath"
"sync"
"testing"
@ -12,11 +15,14 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/multiaccounts"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/node"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/rpc"
"github.com/status-im/status-go/services/typeddata"
"github.com/status-im/status-go/t/utils"
"github.com/status-im/status-go/transactions"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -478,3 +484,56 @@ func TestHashTypedData(t *testing.T) {
require.NoError(t, err)
assert.NotEqual(t, common.Hash{}, hash)
}
func TestBackendGetVerifiedAccount(t *testing.T) {
password := "test"
tmpdir, err := ioutil.TempDir("", "verified-account-test-")
require.NoError(t, err)
defer os.Remove(tmpdir)
backend := NewStatusBackend()
backend.UpdateRootDataDir(tmpdir)
require.NoError(t, backend.accountManager.InitKeystore(filepath.Join(tmpdir, "keystore")))
require.NoError(t, backend.ensureAppDBOpened(multiaccounts.Account{Address: common.Address{1, 1, 1}}, password))
config, err := params.NewNodeConfig(tmpdir, 178733)
require.NoError(t, err)
// this is for StatusNode().Config() call inside of the getVerifiedWalletAccount
require.NoError(t, backend.StartNode(config))
defer func() {
require.NoError(t, backend.StopNode())
}()
t.Run("AccountDoesntExist", func(t *testing.T) {
pkey, err := crypto.GenerateKey()
require.NoError(t, err)
address := crypto.PubkeyToAddress(pkey.PublicKey)
key, err := backend.getVerifiedWalletAccount(address.String(), password)
require.EqualError(t, err, transactions.ErrAccountDoesntExist.Error())
require.Nil(t, key)
})
t.Run("PasswordDoesntMatch", func(t *testing.T) {
pkey, err := crypto.GenerateKey()
require.NoError(t, err)
address := crypto.PubkeyToAddress(pkey.PublicKey)
db := accounts.NewDB(backend.appDB)
_, err = backend.accountManager.ImportAccount(pkey, password)
require.NoError(t, err)
require.NoError(t, db.SaveAccounts([]accounts.Account{{Address: address}}))
key, err := backend.getVerifiedWalletAccount(address.String(), "wrong-password")
require.EqualError(t, err, "could not decrypt key with given passphrase")
require.Nil(t, key)
})
t.Run("Success", func(t *testing.T) {
pkey, err := crypto.GenerateKey()
require.NoError(t, err)
address := crypto.PubkeyToAddress(pkey.PublicKey)
db := accounts.NewDB(backend.appDB)
_, err = backend.accountManager.ImportAccount(pkey, password)
require.NoError(t, err)
require.NoError(t, db.SaveAccounts([]accounts.Account{{Address: address}}))
key, err := backend.getVerifiedWalletAccount(address.String(), password)
require.NoError(t, err)
require.Equal(t, address, key.Address)
})
}

View File

@ -38,7 +38,10 @@ import (
"github.com/ethereum/go-ethereum/event"
)
import "github.com/status-im/status-go/multiaccounts/accounts"
import (
"github.com/ethereum/go-ethereum/common"
"github.com/status-im/status-go/multiaccounts/accounts"
)
const initJS = `
var _status_catalog = {
@ -594,21 +597,11 @@ func testSendTransactionWithLogin(t *testing.T, feed *event.Feed) bool {
}
func testSendTransactionInvalidPassword(t *testing.T, feed *event.Feed) bool {
createAccountAndLogin(t, feed)
acc := createAccountAndLogin(t, feed)
EnsureNodeSync(statusBackend.StatusNode().EnsureSync)
// log into account from which transactions will be sent
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
}
args, err := json.Marshal(transactions.SendTxArgs{
From: account.FromAddress(TestConfig.Account1.WalletAddress),
From: common.HexToAddress(acc.WalletAddress),
To: account.ToAddress(TestConfig.Account2.WalletAddress),
Value: (*hexutil.Big)(big.NewInt(1000000000000)),
})
@ -635,14 +628,8 @@ func testFailedTransaction(t *testing.T, feed *event.Feed) bool {
createAccountAndLogin(t, feed)
EnsureNodeSync(statusBackend.StatusNode().EnsureSync)
// log into wrong account in order to get selectedAccount error
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
}
args, err := json.Marshal(transactions.SendTxArgs{
From: account.FromAddress(TestConfig.Account1.WalletAddress),
From: *account.ToAddress(TestConfig.Account1.WalletAddress),
To: account.ToAddress(TestConfig.Account2.WalletAddress),
Value: (*hexutil.Big)(big.NewInt(1000000000000)),
})
@ -658,8 +645,8 @@ func testFailedTransaction(t *testing.T, feed *event.Feed) bool {
return false
}
if result.Error.Message != transactions.ErrInvalidTxSender.Error() {
t.Errorf("expected error to be ErrInvalidTxSender, got %s", result.Error.Message)
if result.Error.Message != transactions.ErrAccountDoesntExist.Error() {
t.Errorf("expected error to be ErrAccountDoesntExist, got %s", result.Error.Message)
return false
}

View File

@ -186,3 +186,9 @@ func (db *Database) GetAddresses() (rst []common.Address, err error) {
}
return rst, nil
}
// AddressExists returns true if given address is stored in database.
func (db *Database) AddressExists(address common.Address) (exists bool, err error) {
err = db.db.QueryRow("SELECT EXISTS (SELECT 1 FROM accounts WHERE address = ?)", address).Scan(&exists)
return exists, err
}

View File

@ -175,3 +175,25 @@ func TestGetAccounts(t *testing.T) {
require.NoError(t, err)
require.Equal(t, accounts, rst)
}
func TestAddressExists(t *testing.T) {
db, stop := setupTestDB(t)
defer stop()
accounts := []Account{
{Address: common.Address{0x01}, Chat: true, Wallet: true},
}
require.NoError(t, db.SaveAccounts(accounts))
exists, err := db.AddressExists(accounts[0].Address)
require.NoError(t, err)
require.True(t, exists)
}
func TestAddressDoesntExist(t *testing.T) {
db, stop := setupTestDB(t)
defer stop()
exists, err := db.AddressExists(common.Address{1, 1, 1})
require.NoError(t, err)
require.False(t, exists)
}

View File

@ -6,6 +6,8 @@ import (
whisper "github.com/status-im/whisper/whisperv6"
"github.com/status-im/status-go/api"
"github.com/status-im/status-go/multiaccounts"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/node"
"github.com/status-im/status-go/signal"
. "github.com/status-im/status-go/t/utils" //nolint: golint
@ -100,6 +102,28 @@ func (s *BackendTestSuite) StartTestBackend(opts ...TestNodeOption) {
s.True(s.Backend.IsNodeRunning())
}
func (s *BackendTestSuite) StartTestBackendWithAccount(account multiaccounts.Account, password string, subaccs []accounts.Account, opts ...TestNodeOption) {
nodeConfig, err := MakeTestNodeConfig(GetNetworkID())
s.Require().NoError(err)
// Apply any options altering node config.
for i := range opts {
opts[i](nodeConfig)
}
// accounts must be imported before keystore is initialized
s.NoError(importTestAccounts(nodeConfig.KeyStoreDir))
s.Backend.UpdateRootDataDir(nodeConfig.DataDir)
s.NoError(s.Backend.OpenAccounts())
s.NoError(s.Backend.AccountManager().InitKeystore(nodeConfig.KeyStoreDir))
s.Require().NoError(s.Backend.StartNodeWithAccountAndConfig(account, password, nodeConfig, subaccs))
}
func (s *BackendTestSuite) LogoutAndStop() {
s.NoError(s.Backend.Logout())
s.StopTestBackend()
}
// StopTestBackend stops the node.
func (s *BackendTestSuite) StopTestBackend() {
s.True(s.Backend.IsNodeRunning())

View File

@ -1,7 +1,9 @@
package transactions
import (
"io/ioutil"
"math/big"
"os"
"reflect"
"testing"
@ -9,6 +11,8 @@ import (
gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/multiaccounts"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/params"
e2e "github.com/status-im/status-go/t/e2e"
. "github.com/status-im/status-go/t/utils"
@ -99,12 +103,18 @@ func (s *TransactionsTestSuite) sendTransactionUsingRPCClient(
func (s *TransactionsTestSuite) TestEmptyToFieldPreserved() {
CheckTestSkipForNetworks(s.T(), params.MainNetworkID)
s.StartTestBackend()
defer s.StopTestBackend()
tmpdir, err := ioutil.TempDir("", "transactions-tests-")
s.Require().NoError(err)
defer os.Remove(tmpdir)
wallet := common.HexToAddress(TestConfig.Account1.WalletAddress)
s.StartTestBackendWithAccount(multiaccounts.Account{Address: wallet}, TestConfig.Account1.Password,
[]accounts.Account{{Address: wallet, Wallet: true, Chat: true}},
e2e.WithDataDir(tmpdir),
)
defer s.LogoutAndStop()
EnsureNodeSync(s.Backend.StatusNode().EnsureSync)
err := s.Backend.SelectAccount(buildLoginParams(TestConfig.Account1.WalletAddress, TestConfig.Account1.ChatAddress, TestConfig.Account1.Password))
s.NoError(err)
args := transactions.SendTxArgs{
From: account.FromAddress(TestConfig.Account1.WalletAddress),
@ -166,14 +176,19 @@ func (s *TransactionsTestSuite) TestSendContractTx() {
}
func (s *TransactionsTestSuite) testSendContractTx(setInputAndDataValue initFunc, expectedError error, expectedErrorDescription string) {
s.StartTestBackend()
defer s.StopTestBackend()
tmpdir, err := ioutil.TempDir("", "transactions-tests-")
s.Require().NoError(err)
defer os.Remove(tmpdir)
wallet := common.HexToAddress(TestConfig.Account1.WalletAddress)
s.StartTestBackendWithAccount(multiaccounts.Account{Address: wallet}, TestConfig.Account1.Password,
[]accounts.Account{{Address: wallet, Wallet: true, Chat: true}},
e2e.WithDataDir(tmpdir),
)
defer s.LogoutAndStop()
EnsureNodeSync(s.Backend.StatusNode().EnsureSync)
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
byteCode, err := hexutil.Decode(`0x6060604052341561000c57fe5b5b60a58061001b6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680636ffa1caa14603a575bfe5b3415604157fe5b60556004808035906020019091905050606b565b6040518082815260200191505060405180910390f35b60008160020290505b9190505600a165627a7a72305820ccdadd737e4ac7039963b54cee5e5afb25fa859a275252bdcf06f653155228210029`)
s.NoError(err)
@ -194,20 +209,23 @@ func (s *TransactionsTestSuite) testSendContractTx(setInputAndDataValue initFunc
}
s.NoError(err)
s.False(reflect.DeepEqual(hash, gethcommon.Hash{}))
s.NoError(s.Backend.Logout())
}
func (s *TransactionsTestSuite) TestSendEther() {
CheckTestSkipForNetworks(s.T(), params.MainNetworkID)
tmpdir, err := ioutil.TempDir("", "transactions-tests-")
s.Require().NoError(err)
defer os.Remove(tmpdir)
s.StartTestBackend()
defer s.StopTestBackend()
wallet := common.HexToAddress(TestConfig.Account1.WalletAddress)
s.StartTestBackendWithAccount(multiaccounts.Account{Address: wallet}, TestConfig.Account1.Password,
[]accounts.Account{{Address: wallet, Wallet: true, Chat: true}},
e2e.WithDataDir(tmpdir),
)
defer s.LogoutAndStop()
EnsureNodeSync(s.Backend.StatusNode().EnsureSync)
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{
From: account.FromAddress(TestConfig.Account1.WalletAddress),
To: account.ToAddress(TestConfig.Account2.WalletAddress),
@ -220,13 +238,19 @@ func (s *TransactionsTestSuite) TestSendEther() {
func (s *TransactionsTestSuite) TestSendEtherTxUpstream() {
CheckTestSkipForNetworks(s.T(), params.MainNetworkID, params.StatusChainNetworkID)
tmpdir, err := ioutil.TempDir("", "transactions-tests-")
s.Require().NoError(err)
defer os.Remove(tmpdir)
addr, err := GetRemoteURL()
s.NoError(err)
s.StartTestBackend(e2e.WithUpstream(addr))
defer s.StopTestBackend()
err = s.Backend.SelectAccount(buildLoginParams(TestConfig.Account1.WalletAddress, TestConfig.Account1.ChatAddress, TestConfig.Account1.Password))
s.NoError(err)
wallet := common.HexToAddress(TestConfig.Account1.WalletAddress)
s.StartTestBackendWithAccount(multiaccounts.Account{Address: wallet}, TestConfig.Account1.Password,
[]accounts.Account{{Address: wallet, Wallet: true, Chat: true}},
e2e.WithUpstream(addr),
e2e.WithDataDir(tmpdir),
)
defer s.LogoutAndStop()
hash, err := s.Backend.SendTransaction(transactions.SendTxArgs{
From: account.FromAddress(TestConfig.Account1.WalletAddress),

View File

@ -15,8 +15,10 @@ var (
ErrInvalidSendTxArgs = errors.New("transaction arguments are invalid")
// ErrUnexpectedArgs is returned when args are of unexpected length.
ErrUnexpectedArgs = errors.New("unexpected args")
//ErrInvalidTxSender is returned when selected account is different tham From field.
//ErrInvalidTxSender is returned when selected account is different than From field.
ErrInvalidTxSender = errors.New("transaction can only be send by its creator")
//ErrAccountDoesntExist is sent when provided sub-account is not stored in database.
ErrAccountDoesntExist = errors.New("account doesn't exist")
)
// PendingNonceProvider provides information about nonces.