mirror of
https://github.com/status-im/status-go.git
synced 2025-02-26 13:45:34 +00:00
Account's address was used as a primary key in accounts db and as a deterministic id of an account in some API calls. Also it was used as a part of the name of the account specific database. This revealed some extra information about the account and wasn't necessary. At first the hash of the address was planned to be used as a deterministic id, but we already have a keyUid which is calculated as sha256 hash of account's public key and has similar properties: - it is deterministic - doesn't reveal accounts public key or address in plain
641 lines
21 KiB
Go
641 lines
21 KiB
Go
// +build e2e_test
|
|
|
|
// This is a file with e2e tests for C bindings written in library.go.
|
|
// As a CGO file, it can't have `_test.go` suffix as it's not allowed by Go.
|
|
// At the same time, we don't want this file to be included in the binaries.
|
|
// This is why `e2e_test` tag was introduced. Without it, this file is excluded
|
|
// from the build. Providing this tag will include this file into the build
|
|
// and that's what is done while running e2e tests for `lib/` package.
|
|
|
|
package main
|
|
|
|
import (
|
|
"C"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"math/big"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
"github.com/ethereum/go-ethereum/event"
|
|
"github.com/status-im/status-go/account"
|
|
"github.com/status-im/status-go/eth-node/crypto"
|
|
"github.com/status-im/status-go/eth-node/types"
|
|
"github.com/status-im/status-go/multiaccounts/accounts"
|
|
"github.com/status-im/status-go/signal"
|
|
. "github.com/status-im/status-go/t/utils" //nolint: golint
|
|
"github.com/status-im/status-go/transactions"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
const initJS = `
|
|
var _status_catalog = {
|
|
foo: 'bar'
|
|
};`
|
|
|
|
var (
|
|
zeroHash = common.Hash{}
|
|
testChainDir string
|
|
keystoreDir string
|
|
nodeConfigJSON string
|
|
)
|
|
|
|
func buildAccountData(name, chatAddress string) *C.char {
|
|
return C.CString(fmt.Sprintf(`{
|
|
"name": "%s",
|
|
"key-uid": "%s"
|
|
}`, name, chatAddress))
|
|
}
|
|
|
|
func buildSubAccountData(chatAddress string) *C.char {
|
|
accs := []accounts.Account{
|
|
{
|
|
Wallet: true,
|
|
Chat: true,
|
|
Address: common.HexToAddress(chatAddress),
|
|
},
|
|
}
|
|
data, _ := json.Marshal(accs)
|
|
return C.CString(string(data))
|
|
}
|
|
|
|
func buildLoginParams(mainAccountAddress, chatAddress, password string) account.LoginParams {
|
|
return account.LoginParams{
|
|
ChatAddress: common.HexToAddress(chatAddress),
|
|
Password: password,
|
|
MainAccount: common.HexToAddress(mainAccountAddress),
|
|
}
|
|
}
|
|
|
|
func waitSignal(feed *event.Feed, event string, timeout time.Duration) error {
|
|
events := make(chan signal.Envelope)
|
|
sub := feed.Subscribe(events)
|
|
defer sub.Unsubscribe()
|
|
after := time.After(timeout)
|
|
for {
|
|
select {
|
|
case envelope := <-events:
|
|
if envelope.Type == event {
|
|
return nil
|
|
}
|
|
case <-after:
|
|
return fmt.Errorf("signal %v wasn't received in %v", event, timeout)
|
|
}
|
|
}
|
|
}
|
|
|
|
func createAccountAndLogin(t *testing.T, feed *event.Feed) account.Info {
|
|
account1, _, err := statusBackend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
|
require.NoError(t, err)
|
|
t.Logf("account created: {address: %s, key: %s}", account1.WalletAddress, account1.WalletPubKey)
|
|
|
|
// select account
|
|
loginResponse := APIResponse{}
|
|
rawResponse := SaveAccountAndLogin(buildAccountData("test", account1.WalletAddress), C.CString(TestConfig.Account1.Password), C.CString(nodeConfigJSON), buildSubAccountData(account1.WalletAddress))
|
|
require.NoError(t, json.Unmarshal([]byte(C.GoString(rawResponse)), &loginResponse))
|
|
require.Empty(t, loginResponse.Error)
|
|
require.NoError(t, waitSignal(feed, signal.EventLoggedIn, 5*time.Second))
|
|
return account1
|
|
}
|
|
|
|
// nolint: deadcode
|
|
func testExportedAPI(t *testing.T) {
|
|
testDir := filepath.Join(TestDataDir, TestNetworkNames[GetNetworkID()])
|
|
_ = OpenAccounts(C.CString(testDir))
|
|
// inject test accounts
|
|
testKeyDir := filepath.Join(testDir, "keystore")
|
|
_ = InitKeystore(C.CString(testKeyDir))
|
|
require.NoError(t, ImportTestAccount(testKeyDir, GetAccount1PKFile()))
|
|
require.NoError(t, ImportTestAccount(testKeyDir, GetAccount2PKFile()))
|
|
|
|
// FIXME(tiabc): All of that is done because usage of cgo is not supported in tests.
|
|
// Probably, there should be a cleaner way, for example, test cgo bindings in e2e tests
|
|
// separately from other internal tests.
|
|
// NOTE(dshulyak) tests are using same backend with same keystore. but after every test we explicitly logging out.
|
|
tests := []struct {
|
|
name string
|
|
fn func(t *testing.T, feed *event.Feed) bool
|
|
}{
|
|
{
|
|
"StopResumeNode",
|
|
testStopResumeNode,
|
|
},
|
|
|
|
{
|
|
"RPCInProc",
|
|
testCallRPC,
|
|
},
|
|
|
|
{
|
|
"RPCPrivateAPI",
|
|
testCallRPCWithPrivateAPI,
|
|
},
|
|
{
|
|
"RPCPrivateClient",
|
|
testCallPrivateRPCWithPrivateAPI,
|
|
},
|
|
{
|
|
"VerifyAccountPassword",
|
|
testVerifyAccountPassword,
|
|
},
|
|
{
|
|
"RecoverAccount",
|
|
testRecoverAccount,
|
|
},
|
|
{
|
|
"LoginKeycard",
|
|
testLoginWithKeycard,
|
|
},
|
|
{
|
|
"AccountLoout",
|
|
testAccountLogout,
|
|
},
|
|
{
|
|
"SendTransaction",
|
|
testSendTransactionWithLogin,
|
|
},
|
|
{
|
|
"SendTransactionInvalidPassword",
|
|
testSendTransactionInvalidPassword,
|
|
},
|
|
{
|
|
"SendTransactionFailed",
|
|
testFailedTransaction,
|
|
},
|
|
{
|
|
"MultiAccount/Generate/Derive/StoreDerived/Load/Reset",
|
|
testMultiAccountGenerateDeriveStoreLoadReset,
|
|
},
|
|
{
|
|
"MultiAccount/ImportMnemonic/Derive",
|
|
testMultiAccountImportMnemonicAndDerive,
|
|
},
|
|
{
|
|
"MultiAccount/GenerateAndDerive",
|
|
testMultiAccountGenerateAndDerive,
|
|
},
|
|
{
|
|
"MultiAccount/Import/Store",
|
|
testMultiAccountImportStore,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
feed := &event.Feed{}
|
|
signal.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
|
|
var envelope signal.Envelope
|
|
require.NoError(t, json.Unmarshal([]byte(jsonEvent), &envelope))
|
|
feed.Send(envelope)
|
|
})
|
|
defer func() {
|
|
if snode := statusBackend.StatusNode(); snode == nil || !snode.IsRunning() {
|
|
return
|
|
}
|
|
Logout()
|
|
waitSignal(feed, signal.EventNodeStopped, 5*time.Second)
|
|
}()
|
|
require.True(t, tc.fn(t, feed))
|
|
})
|
|
}
|
|
}
|
|
|
|
func testVerifyAccountPassword(t *testing.T, feed *event.Feed) bool {
|
|
tmpDir, err := ioutil.TempDir(os.TempDir(), "accounts")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(tmpDir) // nolint: errcheck
|
|
|
|
if err = ImportTestAccount(tmpDir, GetAccount1PKFile()); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err = ImportTestAccount(tmpDir, GetAccount2PKFile()); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// rename account file (to see that file's internals reviewed, when locating account key)
|
|
accountFilePathOriginal := filepath.Join(tmpDir, GetAccount1PKFile())
|
|
accountFilePath := filepath.Join(tmpDir, "foo"+TestConfig.Account1.WalletAddress+"bar.pk")
|
|
if err := os.Rename(accountFilePathOriginal, accountFilePath); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
response := APIResponse{}
|
|
rawResponse := VerifyAccountPassword(
|
|
C.CString(tmpDir),
|
|
C.CString(TestConfig.Account1.WalletAddress),
|
|
C.CString(TestConfig.Account1.Password))
|
|
|
|
if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &response); err != nil {
|
|
t.Errorf("cannot decode response (%s): %v", C.GoString(rawResponse), err)
|
|
return false
|
|
}
|
|
if response.Error != "" {
|
|
t.Errorf("unexpected error: %s", response.Error)
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func testStopResumeNode(t *testing.T, feed *event.Feed) bool { //nolint: gocyclo
|
|
account1 := createAccountAndLogin(t, feed)
|
|
whisperService, err := statusBackend.StatusNode().WhisperService()
|
|
require.NoError(t, err)
|
|
require.True(t, whisperService.HasKeyPair(account1.ChatPubKey), "whisper should have keypair")
|
|
|
|
response := APIResponse{}
|
|
rawResponse := StopNode()
|
|
require.NoError(t, json.Unmarshal([]byte(C.GoString(rawResponse)), &response))
|
|
require.Empty(t, response.Error)
|
|
|
|
require.NoError(t, waitSignal(feed, signal.EventNodeStopped, 3*time.Second))
|
|
response = APIResponse{}
|
|
rawResponse = StartNode(C.CString(nodeConfigJSON))
|
|
require.NoError(t, json.Unmarshal([]byte(C.GoString(rawResponse)), &response))
|
|
require.Empty(t, response.Error)
|
|
|
|
require.NoError(t, waitSignal(feed, signal.EventNodeReady, 5*time.Second))
|
|
|
|
// now, verify that we still have account logged in
|
|
whisperService, err = statusBackend.StatusNode().WhisperService()
|
|
require.NoError(t, err)
|
|
require.True(t, whisperService.HasKeyPair(account1.ChatPubKey))
|
|
return true
|
|
}
|
|
|
|
func testCallRPC(t *testing.T, feed *event.Feed) bool {
|
|
createAccountAndLogin(t, feed)
|
|
expected := `{"jsonrpc":"2.0","id":64,"result":"0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad"}`
|
|
rawResponse := CallRPC(C.CString(`{"jsonrpc":"2.0","method":"web3_sha3","params":["0x68656c6c6f20776f726c64"],"id":64}`))
|
|
received := C.GoString(rawResponse)
|
|
if expected != received {
|
|
t.Errorf("unexpected response: expected: %v, got: %v", expected, received)
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func testCallRPCWithPrivateAPI(t *testing.T, feed *event.Feed) bool {
|
|
createAccountAndLogin(t, feed)
|
|
expected := `{"jsonrpc":"2.0","id":64,"error":{"code":-32601,"message":"the method admin_nodeInfo does not exist/is not available"}}`
|
|
rawResponse := CallRPC(C.CString(`{"jsonrpc":"2.0","method":"admin_nodeInfo","params":[],"id":64}`))
|
|
require.Equal(t, expected, C.GoString(rawResponse))
|
|
return true
|
|
}
|
|
|
|
func testCallPrivateRPCWithPrivateAPI(t *testing.T, feed *event.Feed) bool {
|
|
createAccountAndLogin(t, feed)
|
|
rawResponse := CallPrivateRPC(C.CString(`{"jsonrpc":"2.0","method":"admin_nodeInfo","params":[],"id":64}`))
|
|
received := C.GoString(rawResponse)
|
|
if strings.Contains(received, "error") {
|
|
t.Errorf("unexpected response containing error: %v", received)
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func testRecoverAccount(t *testing.T, feed *event.Feed) bool { //nolint: gocyclo
|
|
keyStore := statusBackend.AccountManager().GetKeystore()
|
|
require.NotNil(t, keyStore)
|
|
// create an account
|
|
accountInfo, mnemonic, err := statusBackend.AccountManager().CreateAccount(TestConfig.Account1.Password)
|
|
if err != nil {
|
|
t.Errorf("could not create account: %v", err)
|
|
return false
|
|
}
|
|
t.Logf("Account created: {address: %s, key: %s, mnemonic:%s}", accountInfo.WalletAddress, accountInfo.WalletPubKey, mnemonic)
|
|
|
|
// try recovering using password + mnemonic
|
|
recoverAccountResponse := AccountInfo{}
|
|
rawResponse := RecoverAccount(C.CString(TestConfig.Account1.Password), C.CString(mnemonic))
|
|
|
|
if err = json.Unmarshal([]byte(C.GoString(rawResponse)), &recoverAccountResponse); err != nil {
|
|
t.Errorf("cannot decode RecoverAccount response (%s): %v", C.GoString(rawResponse), err)
|
|
return false
|
|
}
|
|
|
|
if recoverAccountResponse.Error != "" {
|
|
t.Errorf("recover account failed: %v", recoverAccountResponse.Error)
|
|
return false
|
|
}
|
|
|
|
if recoverAccountResponse.Address != recoverAccountResponse.WalletAddress ||
|
|
recoverAccountResponse.PubKey != recoverAccountResponse.WalletPubKey {
|
|
t.Error("for backward compatibility pubkey/address should be equal to walletAddress/walletPubKey")
|
|
}
|
|
|
|
walletAddressCheck, walletPubKeyCheck := recoverAccountResponse.Address, recoverAccountResponse.PubKey
|
|
chatAddressCheck, chatPubKeyCheck := recoverAccountResponse.ChatAddress, recoverAccountResponse.ChatPubKey
|
|
|
|
if accountInfo.WalletAddress != walletAddressCheck || accountInfo.WalletPubKey != walletPubKeyCheck {
|
|
t.Error("recover wallet account details failed to pull the correct details")
|
|
}
|
|
|
|
if accountInfo.ChatAddress != chatAddressCheck || accountInfo.ChatPubKey != chatPubKeyCheck {
|
|
t.Error("recover chat account details failed to pull the correct details")
|
|
}
|
|
|
|
// now test recovering, but make sure that account/key file is removed i.e. simulate recovering on a new device
|
|
account, err := account.ParseAccountString(accountInfo.WalletAddress)
|
|
if err != nil {
|
|
t.Errorf("can not get account from address: %v", err)
|
|
}
|
|
|
|
account, key, err := keyStore.AccountDecryptedKey(account, TestConfig.Account1.Password)
|
|
if err != nil {
|
|
t.Errorf("can not obtain decrypted account key: %v", err)
|
|
return false
|
|
}
|
|
extChild2String := key.ExtendedKey.String()
|
|
|
|
if err = keyStore.Delete(account, TestConfig.Account1.Password); err != nil {
|
|
t.Errorf("cannot remove account: %v", err)
|
|
}
|
|
|
|
recoverAccountResponse = AccountInfo{}
|
|
rawResponse = RecoverAccount(C.CString(TestConfig.Account1.Password), C.CString(mnemonic))
|
|
|
|
if err = json.Unmarshal([]byte(C.GoString(rawResponse)), &recoverAccountResponse); err != nil {
|
|
t.Errorf("cannot decode RecoverAccount response (%s): %v", C.GoString(rawResponse), err)
|
|
return false
|
|
}
|
|
|
|
if recoverAccountResponse.Error != "" {
|
|
t.Errorf("recover account failed (for non-cached account): %v", recoverAccountResponse.Error)
|
|
return false
|
|
}
|
|
walletAddressCheck, walletPubKeyCheck = recoverAccountResponse.Address, recoverAccountResponse.PubKey
|
|
if accountInfo.WalletAddress != walletAddressCheck || accountInfo.WalletPubKey != walletPubKeyCheck {
|
|
t.Error("recover wallet account details failed to pull the correct details (for non-cached account)")
|
|
}
|
|
|
|
chatAddressCheck, chatPubKeyCheck = recoverAccountResponse.ChatAddress, recoverAccountResponse.ChatPubKey
|
|
if accountInfo.ChatAddress != chatAddressCheck || accountInfo.ChatPubKey != chatPubKeyCheck {
|
|
t.Error("recover chat account details failed to pull the correct details (for non-cached account)")
|
|
}
|
|
|
|
// make sure that extended key exists and is imported ok too
|
|
_, key, err = keyStore.AccountDecryptedKey(account, TestConfig.Account1.Password)
|
|
if err != nil {
|
|
t.Errorf("can not obtain decrypted account key: %v", err)
|
|
return false
|
|
}
|
|
if extChild2String != key.ExtendedKey.String() {
|
|
t.Errorf("CKD#2 key mismatch, expected: %s, got: %s", extChild2String, key.ExtendedKey.String())
|
|
}
|
|
|
|
// make sure that calling import several times, just returns from cache (no error is expected)
|
|
recoverAccountResponse = AccountInfo{}
|
|
rawResponse = RecoverAccount(C.CString(TestConfig.Account1.Password), C.CString(mnemonic))
|
|
|
|
if err = json.Unmarshal([]byte(C.GoString(rawResponse)), &recoverAccountResponse); err != nil {
|
|
t.Errorf("cannot decode RecoverAccount response (%s): %v", C.GoString(rawResponse), err)
|
|
return false
|
|
}
|
|
|
|
if recoverAccountResponse.Error != "" {
|
|
t.Errorf("recover account failed (for non-cached account): %v", recoverAccountResponse.Error)
|
|
return false
|
|
}
|
|
walletAddressCheck, walletPubKeyCheck = recoverAccountResponse.Address, recoverAccountResponse.PubKey
|
|
if accountInfo.WalletAddress != walletAddressCheck || accountInfo.WalletPubKey != walletPubKeyCheck {
|
|
t.Error("recover wallet account details failed to pull the correct details (for non-cached account)")
|
|
}
|
|
|
|
chatAddressCheck, chatPubKeyCheck = recoverAccountResponse.ChatAddress, recoverAccountResponse.ChatPubKey
|
|
if accountInfo.ChatAddress != chatAddressCheck || accountInfo.ChatPubKey != chatPubKeyCheck {
|
|
t.Error("recover chat account details failed to pull the correct details (for non-cached account)")
|
|
}
|
|
|
|
loginResponse := APIResponse{}
|
|
rawResponse = SaveAccountAndLogin(buildAccountData("test", walletAddressCheck), C.CString(TestConfig.Account1.Password), C.CString(nodeConfigJSON), buildSubAccountData(walletAddressCheck))
|
|
require.NoError(t, json.Unmarshal([]byte(C.GoString(rawResponse)), &loginResponse))
|
|
require.Empty(t, loginResponse.Error)
|
|
require.NoError(t, waitSignal(feed, signal.EventLoggedIn, 5*time.Second))
|
|
|
|
// time to login with recovered data
|
|
whisperService, err := statusBackend.StatusNode().WhisperService()
|
|
if err != nil {
|
|
t.Errorf("whisper service not running: %v", err)
|
|
}
|
|
|
|
if !whisperService.HasKeyPair(chatPubKeyCheck) {
|
|
t.Errorf("identity not injected into whisper: %v", err)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func testLoginWithKeycard(t *testing.T, feed *event.Feed) bool { //nolint: gocyclo
|
|
createAccountAndLogin(t, feed)
|
|
chatPrivKey, err := crypto.GenerateKey()
|
|
if err != nil {
|
|
t.Errorf("error generating chat key")
|
|
return false
|
|
}
|
|
chatPrivKeyHex := hex.EncodeToString(crypto.FromECDSA(chatPrivKey))
|
|
|
|
encryptionPrivKey, err := crypto.GenerateKey()
|
|
if err != nil {
|
|
t.Errorf("error generating encryption key")
|
|
return false
|
|
}
|
|
encryptionPrivKeyHex := hex.EncodeToString(crypto.FromECDSA(encryptionPrivKey))
|
|
|
|
whisperService, err := statusBackend.StatusNode().WhisperService()
|
|
if err != nil {
|
|
t.Errorf("whisper service not running: %v", err)
|
|
}
|
|
|
|
chatPubKeyHex := types.EncodeHex(crypto.FromECDSAPub(&chatPrivKey.PublicKey))
|
|
if whisperService.HasKeyPair(chatPubKeyHex) {
|
|
t.Error("identity already present in whisper")
|
|
return false
|
|
}
|
|
|
|
loginResponse := APIResponse{}
|
|
rawResponse := LoginWithKeycard(C.CString(chatPrivKeyHex), C.CString(encryptionPrivKeyHex))
|
|
|
|
if err = json.Unmarshal([]byte(C.GoString(rawResponse)), &loginResponse); err != nil {
|
|
t.Errorf("cannot decode LoginWithKeycard response (%s): %v", C.GoString(rawResponse), err)
|
|
return false
|
|
}
|
|
|
|
if loginResponse.Error != "" {
|
|
t.Errorf("Test failed: could not login with keycard: %v", err)
|
|
return false
|
|
}
|
|
|
|
if !whisperService.HasKeyPair(chatPubKeyHex) {
|
|
t.Error("identity not present in whisper after logging in with keycard")
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func testAccountLogout(t *testing.T, feed *event.Feed) bool {
|
|
accountInfo := createAccountAndLogin(t, feed)
|
|
whisperService, err := statusBackend.StatusNode().WhisperService()
|
|
if err != nil {
|
|
t.Errorf("whisper service not running: %v", err)
|
|
return false
|
|
}
|
|
|
|
if !whisperService.HasKeyPair(accountInfo.ChatPubKey) {
|
|
t.Error("identity not injected into whisper")
|
|
return false
|
|
}
|
|
|
|
logoutResponse := APIResponse{}
|
|
rawResponse := Logout()
|
|
|
|
if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &logoutResponse); err != nil {
|
|
t.Errorf("cannot decode RecoverAccount response (%s): %v", C.GoString(rawResponse), err)
|
|
return false
|
|
}
|
|
|
|
if logoutResponse.Error != "" {
|
|
t.Errorf("cannot logout: %v", logoutResponse.Error)
|
|
return false
|
|
}
|
|
|
|
// now, logout and check if identity is removed indeed
|
|
if whisperService.HasKeyPair(accountInfo.ChatPubKey) {
|
|
t.Error("identity not cleared from whisper")
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
type jsonrpcAnyResponse struct {
|
|
Result json.RawMessage `json:"result"`
|
|
jsonrpcErrorResponse
|
|
}
|
|
|
|
func testSendTransactionWithLogin(t *testing.T, feed *event.Feed) bool {
|
|
loginResponse := APIResponse{}
|
|
rawResponse := SaveAccountAndLogin(buildAccountData("test", TestConfig.Account1.WalletAddress), C.CString(TestConfig.Account1.Password), C.CString(nodeConfigJSON), buildSubAccountData(TestConfig.Account1.WalletAddress))
|
|
require.NoError(t, json.Unmarshal([]byte(C.GoString(rawResponse)), &loginResponse))
|
|
require.Empty(t, loginResponse.Error)
|
|
require.NoError(t, waitSignal(feed, signal.EventLoggedIn, 5*time.Second))
|
|
EnsureNodeSync(statusBackend.StatusNode().EnsureSync)
|
|
|
|
args, err := json.Marshal(transactions.SendTxArgs{
|
|
From: account.FromAddress(TestConfig.Account1.WalletAddress),
|
|
To: account.ToAddress(TestConfig.Account2.WalletAddress),
|
|
Value: (*hexutil.Big)(big.NewInt(1000000000000)),
|
|
})
|
|
if err != nil {
|
|
t.Errorf("failed to marshal errors: %v", err)
|
|
return false
|
|
}
|
|
rawResult := SendTransaction(C.CString(string(args)), C.CString(TestConfig.Account1.Password))
|
|
|
|
var result jsonrpcAnyResponse
|
|
if err := json.Unmarshal([]byte(C.GoString(rawResult)), &result); err != nil {
|
|
t.Errorf("failed to unmarshal rawResult '%s': %v", C.GoString(rawResult), err)
|
|
return false
|
|
}
|
|
if result.Error.Message != "" {
|
|
t.Errorf("failed to send transaction: %v", result.Error)
|
|
return false
|
|
}
|
|
hash := common.BytesToHash(result.Result)
|
|
if reflect.DeepEqual(hash, common.Hash{}) {
|
|
t.Errorf("response hash empty: %s", hash.Hex())
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func testSendTransactionInvalidPassword(t *testing.T, feed *event.Feed) bool {
|
|
acc := createAccountAndLogin(t, feed)
|
|
EnsureNodeSync(statusBackend.StatusNode().EnsureSync)
|
|
|
|
args, err := json.Marshal(transactions.SendTxArgs{
|
|
From: common.HexToAddress(acc.WalletAddress),
|
|
To: account.ToAddress(TestConfig.Account2.WalletAddress),
|
|
Value: (*hexutil.Big)(big.NewInt(1000000000000)),
|
|
})
|
|
if err != nil {
|
|
t.Errorf("failed to marshal errors: %v", err)
|
|
return false
|
|
}
|
|
rawResult := SendTransaction(C.CString(string(args)), C.CString("invalid password"))
|
|
|
|
var result jsonrpcAnyResponse
|
|
if err := json.Unmarshal([]byte(C.GoString(rawResult)), &result); err != nil {
|
|
t.Errorf("failed to unmarshal rawResult '%s': %v", C.GoString(rawResult), err)
|
|
return false
|
|
}
|
|
if result.Error.Message != keystore.ErrDecrypt.Error() {
|
|
t.Errorf("invalid result: %q", result)
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func testFailedTransaction(t *testing.T, feed *event.Feed) bool {
|
|
createAccountAndLogin(t, feed)
|
|
EnsureNodeSync(statusBackend.StatusNode().EnsureSync)
|
|
|
|
args, err := json.Marshal(transactions.SendTxArgs{
|
|
From: *account.ToAddress(TestConfig.Account1.WalletAddress),
|
|
To: account.ToAddress(TestConfig.Account2.WalletAddress),
|
|
Value: (*hexutil.Big)(big.NewInt(1000000000000)),
|
|
})
|
|
if err != nil {
|
|
t.Errorf("failed to marshal errors: %v", err)
|
|
return false
|
|
}
|
|
rawResult := SendTransaction(C.CString(string(args)), C.CString(TestConfig.Account1.Password))
|
|
|
|
var result jsonrpcAnyResponse
|
|
if err := json.Unmarshal([]byte(C.GoString(rawResult)), &result); err != nil {
|
|
t.Errorf("failed to unmarshal rawResult '%s': %v", C.GoString(rawResult), err)
|
|
return false
|
|
}
|
|
|
|
if result.Error.Message != transactions.ErrAccountDoesntExist.Error() {
|
|
t.Errorf("expected error to be ErrAccountDoesntExist, got %s", result.Error.Message)
|
|
return false
|
|
}
|
|
|
|
if result.Result != nil {
|
|
t.Errorf("expected result to be nil")
|
|
return false
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
//nolint: deadcode
|
|
func testValidateNodeConfig(t *testing.T, config string, fn func(*testing.T, APIDetailedResponse)) {
|
|
result := ValidateNodeConfig(C.CString(config))
|
|
|
|
var resp APIDetailedResponse
|
|
|
|
err := json.Unmarshal([]byte(C.GoString(result)), &resp)
|
|
require.NoError(t, err)
|
|
|
|
fn(t, resp)
|
|
}
|