Skip to content
This repository has been archived by the owner on Apr 4, 2024. It is now read-only.

Commit

Permalink
rpc: transaction receipt test (#678)
Browse files Browse the repository at this point in the history
* Problem: No test on the transaction receipt api

Closes: #582

- add receipt rpc test for erc20 transfer logs

* lower gas fee

* build with go 1.17 in CI

* use go 1.17 in test-solidity

* fix merge
  • Loading branch information
yihuang authored Oct 20, 2021
1 parent c644dd6 commit 1000461
Show file tree
Hide file tree
Showing 9 changed files with 193 additions and 38 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.3.5
- uses: actions/setup-go@v2
with:
go-version: 1.17
- uses: technote-space/get-diff-action@v5
id: git_diff
with:
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ jobs:
timeout-minutes: 60
steps:
- uses: actions/checkout@v2.3.5
- uses: actions/setup-go@v2
with:
go-version: 1.17
- uses: technote-space/get-diff-action@v5
id: git_diff
with:
Expand Down
2 changes: 1 addition & 1 deletion scripts/gen-tests-artifacts.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
# prepare sloc v0.5.17 in PATH
solc --combined-json bin,abi --allow-paths . ./tests/solidity/suites/staking/contracts/test/mocks/StandardTokenMock.sol \
| jq ".contracts.\"./tests/solidity/suites/staking/contracts/test/mocks/StandardTokenMock.sol:StandardTokenMock\"" \
> x/evm/keeper/ERC20Contract.json
> x/evm/types/ERC20Contract.json
85 changes: 85 additions & 0 deletions tests/rpc/rpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,84 @@ func TestEth_GetTransactionReceipt(t *testing.T) {
require.Equal(t, []interface{}{}, receipt["logs"].([]interface{}))
}

// deployTestERC20Contract deploys a contract that emits an event in the constructor
func deployTestERC20Contract(t *testing.T) common.Address {
param := make([]map[string]string, 1)
param[0] = make(map[string]string)
param[0]["from"] = "0x" + fmt.Sprintf("%x", from)

ctorArgs, err := evmtypes.ERC20Contract.ABI.Pack("", common.BytesToAddress(from), big.NewInt(100000000))
require.NoError(t, err)
data := append(evmtypes.ERC20Contract.Bin, ctorArgs...)
param[0]["data"] = hexutil.Encode(data)

param[0]["gas"] = "0x200000"
param[0]["gasPrice"] = "0x1"

rpcRes := call(t, "eth_sendTransaction", param)

var hash hexutil.Bytes
err = json.Unmarshal(rpcRes.Result, &hash)
require.NoError(t, err)

receipt := expectSuccessReceipt(t, hash)
contractAddress := common.HexToAddress(receipt["contractAddress"].(string))
require.NotEqual(t, common.Address{}, contractAddress)

require.NotNil(t, receipt["logs"])

return contractAddress
}

// sendTestERC20Transaction sends a typical erc20 transfer transaction
func sendTestERC20Transaction(t *testing.T, contract common.Address, amount *big.Int) hexutil.Bytes {
// transfer
param := make([]map[string]string, 1)
param[0] = make(map[string]string)
param[0]["from"] = "0x" + fmt.Sprintf("%x", from)
param[0]["to"] = contract.Hex()
data, err := evmtypes.ERC20Contract.ABI.Pack("transfer", common.BigToAddress(big.NewInt(1)), amount)
require.NoError(t, err)
param[0]["data"] = hexutil.Encode(data)
param[0]["gas"] = "0x50000"
param[0]["gasPrice"] = "0x1"

rpcRes := call(t, "eth_sendTransaction", param)

var hash hexutil.Bytes
err = json.Unmarshal(rpcRes.Result, &hash)
require.NoError(t, err)
return hash
}

func TestEth_GetTransactionReceipt_ERC20Transfer(t *testing.T) {
// deploy erc20 contract
contract := deployTestERC20Contract(t)
amount := big.NewInt(10)
hash := sendTestERC20Transaction(t, contract, amount)
receipt := expectSuccessReceipt(t, hash)

require.Equal(t, 1, len(receipt["logs"].([]interface{})))
log := receipt["logs"].([]interface{})[0].(map[string]interface{})

require.Equal(t, contract, common.HexToAddress(log["address"].(string)))

valueBz, err := hexutil.Decode(log["data"].(string))
require.NoError(t, err)
require.Equal(t, amount, big.NewInt(0).SetBytes(valueBz))

require.Equal(t, false, log["removed"].(bool))
require.Equal(t, "0x0", log["logIndex"].(string))
require.Equal(t, "0x0", log["transactionIndex"].(string))

expectedTopics := []interface{}{
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000" + fmt.Sprintf("%x", from),
"0x0000000000000000000000000000000000000000000000000000000000000001",
}
require.Equal(t, expectedTopics, log["topics"].([]interface{}))
}

// deployTestContract deploys a contract that emits an event in the constructor
func deployTestContract(t *testing.T) (hexutil.Bytes, map[string]interface{}) {
param := make([]map[string]string, 1)
Expand Down Expand Up @@ -592,6 +670,13 @@ func waitForReceipt(t *testing.T, hash hexutil.Bytes) map[string]interface{} {
}
}

func expectSuccessReceipt(t *testing.T, hash hexutil.Bytes) map[string]interface{} {
receipt := waitForReceipt(t, hash)
require.NotNil(t, receipt, "transaction failed")
require.Equal(t, "0x1", receipt["status"].(string))
return receipt
}

func TestEth_GetFilterChanges_NoTopics(t *testing.T) {
rpcRes := call(t, "eth_blockNumber", []string{})

Expand Down
8 changes: 4 additions & 4 deletions x/evm/keeper/benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func DoBenchmark(b *testing.B, txBuilder TxBuilder) {

func BenchmarkTokenTransfer(b *testing.B) {
DoBenchmark(b, func(suite *KeeperTestSuite, contract common.Address) *types.MsgEthereumTx {
input, err := ContractABI.Pack("transfer", common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(1000))
input, err := types.ERC20Contract.ABI.Pack("transfer", common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(1000))
require.NoError(b, err)
nonce := suite.app.EvmKeeper.GetNonce(suite.address)
return types.NewTx(suite.app.EvmKeeper.ChainID(), nonce, &contract, big.NewInt(0), 410000, big.NewInt(1), nil, nil, input, nil)
Expand All @@ -72,7 +72,7 @@ func BenchmarkTokenTransfer(b *testing.B) {

func BenchmarkEmitLogs(b *testing.B) {
DoBenchmark(b, func(suite *KeeperTestSuite, contract common.Address) *types.MsgEthereumTx {
input, err := ContractABI.Pack("benchmarkLogs", big.NewInt(1000))
input, err := types.ERC20Contract.ABI.Pack("benchmarkLogs", big.NewInt(1000))
require.NoError(b, err)
nonce := suite.app.EvmKeeper.GetNonce(suite.address)
return types.NewTx(suite.app.EvmKeeper.ChainID(), nonce, &contract, big.NewInt(0), 4100000, big.NewInt(1), nil, nil, input, nil)
Expand All @@ -81,7 +81,7 @@ func BenchmarkEmitLogs(b *testing.B) {

func BenchmarkTokenTransferFrom(b *testing.B) {
DoBenchmark(b, func(suite *KeeperTestSuite, contract common.Address) *types.MsgEthereumTx {
input, err := ContractABI.Pack("transferFrom", suite.address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(0))
input, err := types.ERC20Contract.ABI.Pack("transferFrom", suite.address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(0))
require.NoError(b, err)
nonce := suite.app.EvmKeeper.GetNonce(suite.address)
return types.NewTx(suite.app.EvmKeeper.ChainID(), nonce, &contract, big.NewInt(0), 410000, big.NewInt(1), nil, nil, input, nil)
Expand All @@ -90,7 +90,7 @@ func BenchmarkTokenTransferFrom(b *testing.B) {

func BenchmarkTokenMint(b *testing.B) {
DoBenchmark(b, func(suite *KeeperTestSuite, contract common.Address) *types.MsgEthereumTx {
input, err := ContractABI.Pack("mint", common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(1000))
input, err := types.ERC20Contract.ABI.Pack("mint", common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(1000))
require.NoError(b, err)
nonce := suite.app.EvmKeeper.GetNonce(suite.address)
return types.NewTx(suite.app.EvmKeeper.ChainID(), nonce, &contract, big.NewInt(0), 410000, big.NewInt(1), nil, nil, input, nil)
Expand Down
12 changes: 6 additions & 6 deletions x/evm/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -514,9 +514,9 @@ func (suite *KeeperTestSuite) TestEstimateGas() {
}, false, 0, false},
// estimate gas of an erc20 contract deployment, the exact gas number is checked with geth
{"contract deployment", func() {
ctorArgs, err := ContractABI.Pack("", &suite.address, sdk.NewIntWithDecimal(1000, 18).BigInt())
ctorArgs, err := types.ERC20Contract.ABI.Pack("", &suite.address, sdk.NewIntWithDecimal(1000, 18).BigInt())
suite.Require().NoError(err)
data := append(ContractBin, ctorArgs...)
data := append(types.ERC20Contract.Bin, ctorArgs...)
args = types.TransactionArgs{
From: &suite.address,
Data: (*hexutil.Bytes)(&data),
Expand All @@ -526,7 +526,7 @@ func (suite *KeeperTestSuite) TestEstimateGas() {
{"erc20 transfer", func() {
contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdk.NewIntWithDecimal(1000, 18).BigInt())
suite.Commit()
transferData, err := ContractABI.Pack("transfer", common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(1000))
transferData, err := types.ERC20Contract.ABI.Pack("transfer", common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(1000))
suite.Require().NoError(err)
args = types.TransactionArgs{To: &contractAddr, From: &suite.address, Data: (*hexutil.Bytes)(&transferData)}
}, true, 51880, false},
Expand All @@ -549,9 +549,9 @@ func (suite *KeeperTestSuite) TestEstimateGas() {
gasCap = 20000
}, false, 0, true},
{"contract deployment w/ dynamicTxFee", func() {
ctorArgs, err := ContractABI.Pack("", &suite.address, sdk.NewIntWithDecimal(1000, 18).BigInt())
ctorArgs, err := types.ERC20Contract.ABI.Pack("", &suite.address, sdk.NewIntWithDecimal(1000, 18).BigInt())
suite.Require().NoError(err)
data := append(ContractBin, ctorArgs...)
data := append(types.ERC20Contract.Bin, ctorArgs...)
args = types.TransactionArgs{
From: &suite.address,
Data: (*hexutil.Bytes)(&data),
Expand All @@ -560,7 +560,7 @@ func (suite *KeeperTestSuite) TestEstimateGas() {
{"erc20 transfer w/ dynamicTxFee", func() {
contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdk.NewIntWithDecimal(1000, 18).BigInt())
suite.Commit()
transferData, err := ContractABI.Pack("transfer", common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(1000))
transferData, err := types.ERC20Contract.ABI.Pack("transfer", common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(1000))
suite.Require().NoError(err)
args = types.TransactionArgs{To: &contractAddr, From: &suite.address, Data: (*hexutil.Bytes)(&transferData)}
}, true, 51880, true},
Expand Down
30 changes: 3 additions & 27 deletions x/evm/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import (
ethermint "github.com/tharsis/ethermint/types"
"github.com/tharsis/ethermint/x/evm/types"

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
ethtypes "github.com/ethereum/go-ethereum/core/types"
Expand All @@ -40,29 +39,6 @@ import (
"github.com/tendermint/tendermint/version"
)

var (
//go:embed ERC20Contract.json
compiledContractJSON []byte
ContractBin []byte
ContractABI abi.ABI
)

func init() {
var tmp struct {
Abi string
Bin string
}
err := json.Unmarshal(compiledContractJSON, &tmp)
if err != nil {
panic(err)
}
ContractBin = common.FromHex(tmp.Bin)
err = json.Unmarshal([]byte(tmp.Abi), &ContractABI)
if err != nil {
panic(err)
}
}

var testTokens = sdk.NewIntWithDecimal(1000, 18)

type KeeperTestSuite struct {
Expand Down Expand Up @@ -193,10 +169,10 @@ func (suite *KeeperTestSuite) DeployTestContract(t require.TestingT, owner commo
ctx := sdk.WrapSDKContext(suite.ctx)
chainID := suite.app.EvmKeeper.ChainID()

ctorArgs, err := ContractABI.Pack("", owner, supply)
ctorArgs, err := types.ERC20Contract.ABI.Pack("", owner, supply)
require.NoError(t, err)

data := append(ContractBin, ctorArgs...)
data := append(types.ERC20Contract.Bin, ctorArgs...)
args, err := json.Marshal(&types.TransactionArgs{
From: &suite.address,
Data: (*hexutil.Bytes)(&data),
Expand Down Expand Up @@ -250,7 +226,7 @@ func (suite *KeeperTestSuite) TransferERC20Token(t require.TestingT, contractAdd
ctx := sdk.WrapSDKContext(suite.ctx)
chainID := suite.app.EvmKeeper.ChainID()

transferData, err := ContractABI.Pack("transfer", to, amount)
transferData, err := types.ERC20Contract.ABI.Pack("transfer", to, amount)
require.NoError(t, err)
args, err := json.Marshal(&types.TransactionArgs{To: &contractAddr, From: &from, Data: (*hexutil.Bytes)(&transferData)})
require.NoError(t, err)
Expand Down
File renamed without changes.
88 changes: 88 additions & 0 deletions x/evm/types/compiled_contract.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package types

import (
// embed compiled smart contract
_ "embed"
"encoding/hex"
"encoding/json"
"fmt"

"github.com/ethereum/go-ethereum/accounts/abi"
)

// HexString is a byte array that serializes to hex
type HexString []byte

// MarshalJSON serializes ByteArray to hex
func (s HexString) MarshalJSON() ([]byte, error) {
return json.Marshal(fmt.Sprintf("%x", string(s)))
}

// UnmarshalJSON deserializes ByteArray to hex
func (s *HexString) UnmarshalJSON(data []byte) error {
var x string
if err := json.Unmarshal(data, &x); err != nil {
return err
}
str, err := hex.DecodeString(x)
if err != nil {
return err
}
*s = str
return nil
}

// CompiledContract contains compiled bytecode and abi
type CompiledContract struct {
ABI abi.ABI
Bin HexString
}

type jsonCompiledContract struct {
ABI string
Bin HexString
}

// MarshalJSON serializes ByteArray to hex
func (s CompiledContract) MarshalJSON() ([]byte, error) {
abi1, err := json.Marshal(s.ABI)
if err != nil {
return nil, err
}
return json.Marshal(jsonCompiledContract{ABI: string(abi1), Bin: s.Bin})
}

// UnmarshalJSON deserializes ByteArray to hex
func (s *CompiledContract) UnmarshalJSON(data []byte) error {
var x jsonCompiledContract
if err := json.Unmarshal(data, &x); err != nil {
return err
}

s.Bin = x.Bin
if err := json.Unmarshal([]byte(x.ABI), &s.ABI); err != nil {
fmt.Println("unmarshal abi fail", x.ABI, string(data))
return err
}

return nil
}

var (
//go:embed ERC20Contract.json
erc20JSON []byte

// ERC20Contract is the compiled test erc20 contract
ERC20Contract CompiledContract
)

func init() {
err := json.Unmarshal(erc20JSON, &ERC20Contract)
if err != nil {
panic(err)
}

if len(ERC20Contract.Bin) == 0 {
panic("load contract failed")
}
}

0 comments on commit 1000461

Please sign in to comment.