package transactions import ( "io/ioutil" "math/big" "os" "reflect" "testing" "github.com/stretchr/testify/suite" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/status-im/status-go/account" "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/multiaccounts" "github.com/status-im/status-go/multiaccounts/accounts" "github.com/status-im/status-go/params" "github.com/status-im/status-go/t/e2e" "github.com/status-im/status-go/t/utils" "github.com/status-im/status-go/transactions" ) type initFunc func([]byte, *transactions.SendTxArgs) func buildLoginParams(mainAccountAddress, chatAddress, password string) account.LoginParams { return account.LoginParams{ ChatAddress: types.HexToAddress(chatAddress), Password: password, MainAccount: types.HexToAddress(mainAccountAddress), } } func TestTransactionsTestSuite(t *testing.T) { utils.Init() suite.Run(t, new(TransactionsTestSuite)) } type TransactionsTestSuite struct { e2e.BackendTestSuite } func (s *TransactionsTestSuite) TestCallRPCSendTransaction() { utils.CheckTestSkipForNetworks(s.T(), params.MainNetworkID) s.StartTestBackend() defer s.StopTestBackend() utils.EnsureNodeSync(s.Backend.StatusNode().EnsureSync) s.sendTransactionUsingRPCClient(s.Backend.CallRPC) } func (s *TransactionsTestSuite) TestCallUpstreamRPCSendTransaction() { utils.CheckTestSkipForNetworks(s.T(), params.MainNetworkID, params.StatusChainNetworkID) addr, err := utils.GetRemoteURL() s.NoError(err) s.StartTestBackend(e2e.WithUpstream(addr)) defer s.StopTestBackend() s.sendTransactionUsingRPCClient(s.Backend.CallRPC) } func (s *TransactionsTestSuite) TestCallPrivateRPCSendTransaction() { utils.CheckTestSkipForNetworks(s.T(), params.MainNetworkID) s.StartTestBackend() defer s.StopTestBackend() utils.EnsureNodeSync(s.Backend.StatusNode().EnsureSync) s.sendTransactionUsingRPCClient(s.Backend.CallPrivateRPC) } func (s *TransactionsTestSuite) TestCallUpstreamPrivateRPCSendTransaction() { utils.CheckTestSkipForNetworks(s.T(), params.MainNetworkID, params.StatusChainNetworkID) addr, err := utils.GetRemoteURL() s.NoError(err) s.StartTestBackend(e2e.WithUpstream(addr)) defer s.StopTestBackend() s.sendTransactionUsingRPCClient(s.Backend.CallPrivateRPC) } func (s *TransactionsTestSuite) sendTransactionUsingRPCClient( callRPCFn func(string) (string, error), ) { err := s.Backend.SelectAccount(buildLoginParams( utils.TestConfig.Account1.WalletAddress, utils.TestConfig.Account1.ChatAddress, utils.TestConfig.Account1.Password, )) s.NoError(err) result, err := callRPCFn(`{ "jsonrpc": "2.0", "id": 1, "method": "eth_sendTransaction", "params": [{ "from": "` + utils.TestConfig.Account1.WalletAddress + `", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "value": "0x9184e72a" }] }`) s.NoError(err) s.Contains(result, `"error":{"code":-32700,"message":"method is unsupported by RPC interface"}`) } func (s *TransactionsTestSuite) TestEmptyToFieldPreserved() { utils.CheckTestSkipForNetworks(s.T(), params.MainNetworkID) tmpdir, err := ioutil.TempDir("", "transactions-tests-") s.Require().NoError(err) defer os.Remove(tmpdir) wallet := types.HexToAddress(utils.TestConfig.Account1.WalletAddress) s.StartTestBackendWithAccount(multiaccounts.Account{KeyUID: utils.TestConfig.Account1.WalletAddress}, utils.TestConfig.Account1.Password, []accounts.Account{{Address: wallet, Wallet: true, Chat: true}}, e2e.WithDataDir(tmpdir), ) defer s.LogoutAndStop() utils.EnsureNodeSync(s.Backend.StatusNode().EnsureSync) args := transactions.SendTxArgs{ From: account.FromAddress(utils.TestConfig.Account1.WalletAddress), } hash, err := s.Backend.SendTransaction(args, utils.TestConfig.Account1.Password) s.NoError(err) s.NotNil(hash) } // TestSendContractCompat tries to send transaction using the legacy "Data" // field, which is supported for backward compatibility reasons. func (s *TransactionsTestSuite) TestSendContractTxCompat() { utils.CheckTestSkipForNetworks(s.T(), params.MainNetworkID) initFunc := func(byteCode []byte, args *transactions.SendTxArgs) { args.Data = (types.HexBytes)(byteCode) } s.testSendContractTx(initFunc, nil, "") } // TestSendContractCompat tries to send transaction using both the legacy // "Data" and "Input" fields. Also makes sure that the error is returned if // they have different values. func (s *TransactionsTestSuite) TestSendContractTxCollision() { utils.CheckTestSkipForNetworks(s.T(), params.MainNetworkID) // Scenario 1: Both fields are filled and have the same value, expect success initFunc := func(byteCode []byte, args *transactions.SendTxArgs) { args.Input = (types.HexBytes)(byteCode) args.Data = (types.HexBytes)(byteCode) } s.testSendContractTx(initFunc, nil, "") // Scenario 2: Both fields are filled with different values, expect an error inverted := func(source []byte) []byte { inverse := make([]byte, len(source)) copy(inverse, source) for i, b := range inverse { inverse[i] = b ^ 0xFF } return inverse } initFunc2 := func(byteCode []byte, args *transactions.SendTxArgs) { args.Input = (types.HexBytes)(byteCode) args.Data = (types.HexBytes)(inverted(byteCode)) } s.testSendContractTx(initFunc2, transactions.ErrInvalidSendTxArgs, "expected error when invalid tx args are sent") } func (s *TransactionsTestSuite) TestSendContractTx() { utils.CheckTestSkipForNetworks(s.T(), params.MainNetworkID) initFunc := func(byteCode []byte, args *transactions.SendTxArgs) { args.Input = (types.HexBytes)(byteCode) } s.testSendContractTx(initFunc, nil, "") } func (s *TransactionsTestSuite) testSendContractTx(setInputAndDataValue initFunc, expectedError error, expectedErrorDescription string) { tmpdir, err := ioutil.TempDir("", "transactions-tests-") s.Require().NoError(err) defer os.Remove(tmpdir) wallet := types.HexToAddress(utils.TestConfig.Account1.WalletAddress) s.StartTestBackendWithAccount(multiaccounts.Account{KeyUID: utils.TestConfig.Account1.WalletAddress}, utils.TestConfig.Account1.Password, []accounts.Account{{Address: wallet, Wallet: true, Chat: true}}, e2e.WithDataDir(tmpdir), ) defer s.LogoutAndStop() utils.EnsureNodeSync(s.Backend.StatusNode().EnsureSync) // this call blocks, up until Complete Transaction is called byteCode, err := types.DecodeHex(`0x6060604052341561000c57fe5b5b60a58061001b6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680636ffa1caa14603a575bfe5b3415604157fe5b60556004808035906020019091905050606b565b6040518082815260200191505060405180910390f35b60008160020290505b9190505600a165627a7a72305820ccdadd737e4ac7039963b54cee5e5afb25fa859a275252bdcf06f653155228210029`) s.NoError(err) gas := uint64(params.DefaultGas) args := transactions.SendTxArgs{ From: account.FromAddress(utils.TestConfig.Account1.WalletAddress), To: nil, // marker, contract creation is expected //Value: (*hexutil.Big)(new(big.Int).Mul(big.NewInt(1), common.Ether)), Gas: (*hexutil.Uint64)(&gas), } setInputAndDataValue(byteCode, &args) hash, err := s.Backend.SendTransaction(args, utils.TestConfig.Account1.Password) if expectedError != nil { s.Equal(expectedError, err, expectedErrorDescription) return } s.NoError(err) s.False(reflect.DeepEqual(hash, types.Hash{})) } func (s *TransactionsTestSuite) TestSendEther() { utils.CheckTestSkipForNetworks(s.T(), params.MainNetworkID) tmpdir, err := ioutil.TempDir("", "transactions-tests-") s.Require().NoError(err) defer os.Remove(tmpdir) wallet := types.HexToAddress(utils.TestConfig.Account1.WalletAddress) s.StartTestBackendWithAccount(multiaccounts.Account{KeyUID: utils.TestConfig.Account1.WalletAddress}, utils.TestConfig.Account1.Password, []accounts.Account{{Address: wallet, Wallet: true, Chat: true}}, e2e.WithDataDir(tmpdir), ) defer s.LogoutAndStop() utils.EnsureNodeSync(s.Backend.StatusNode().EnsureSync) hash, err := s.Backend.SendTransaction(transactions.SendTxArgs{ From: account.FromAddress(utils.TestConfig.Account1.WalletAddress), To: account.ToAddress(utils.TestConfig.Account2.WalletAddress), Value: (*hexutil.Big)(big.NewInt(1000000000000)), }, utils.TestConfig.Account1.Password) s.NoError(err) s.False(reflect.DeepEqual(hash, types.Hash{})) } func (s *TransactionsTestSuite) TestSendEtherTxUpstream() { utils.CheckTestSkipForNetworks(s.T(), params.MainNetworkID, params.StatusChainNetworkID) tmpdir, err := ioutil.TempDir("", "transactions-tests-") s.Require().NoError(err) defer os.Remove(tmpdir) addr, err := utils.GetRemoteURL() s.NoError(err) wallet := types.HexToAddress(utils.TestConfig.Account1.WalletAddress) s.StartTestBackendWithAccount(multiaccounts.Account{KeyUID: utils.TestConfig.Account1.WalletAddress}, utils.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(utils.TestConfig.Account1.WalletAddress), To: account.ToAddress(utils.TestConfig.Account2.WalletAddress), GasPrice: (*hexutil.Big)(big.NewInt(28000000000)), Value: (*hexutil.Big)(big.NewInt(1000000000000)), }, utils.TestConfig.Account1.Password) s.NoError(err) s.False(reflect.DeepEqual(hash, types.Hash{})) }