rpc: transaction receipt test (#678)

* 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
This commit is contained in:
yihuang 2021-10-21 03:00:17 +08:00 committed by GitHub
parent c644dd6707
commit 1000461a55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 193 additions and 38 deletions

View File

@ -17,6 +17,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2.3.5 - uses: actions/checkout@v2.3.5
- uses: actions/setup-go@v2
with:
go-version: 1.17
- uses: technote-space/get-diff-action@v5 - uses: technote-space/get-diff-action@v5
id: git_diff id: git_diff
with: with:

View File

@ -81,6 +81,9 @@ jobs:
timeout-minutes: 60 timeout-minutes: 60
steps: steps:
- uses: actions/checkout@v2.3.5 - uses: actions/checkout@v2.3.5
- uses: actions/setup-go@v2
with:
go-version: 1.17
- uses: technote-space/get-diff-action@v5 - uses: technote-space/get-diff-action@v5
id: git_diff id: git_diff
with: with:

View File

@ -3,4 +3,4 @@
# prepare sloc v0.5.17 in PATH # prepare sloc v0.5.17 in PATH
solc --combined-json bin,abi --allow-paths . ./tests/solidity/suites/staking/contracts/test/mocks/StandardTokenMock.sol \ 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\"" \ | jq ".contracts.\"./tests/solidity/suites/staking/contracts/test/mocks/StandardTokenMock.sol:StandardTokenMock\"" \
> x/evm/keeper/ERC20Contract.json > x/evm/types/ERC20Contract.json

View File

@ -527,6 +527,84 @@ func TestEth_GetTransactionReceipt(t *testing.T) {
require.Equal(t, []interface{}{}, receipt["logs"].([]interface{})) 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 // deployTestContract deploys a contract that emits an event in the constructor
func deployTestContract(t *testing.T) (hexutil.Bytes, map[string]interface{}) { func deployTestContract(t *testing.T) (hexutil.Bytes, map[string]interface{}) {
param := make([]map[string]string, 1) param := make([]map[string]string, 1)
@ -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) { func TestEth_GetFilterChanges_NoTopics(t *testing.T) {
rpcRes := call(t, "eth_blockNumber", []string{}) rpcRes := call(t, "eth_blockNumber", []string{})

View File

@ -63,7 +63,7 @@ func DoBenchmark(b *testing.B, txBuilder TxBuilder) {
func BenchmarkTokenTransfer(b *testing.B) { func BenchmarkTokenTransfer(b *testing.B) {
DoBenchmark(b, func(suite *KeeperTestSuite, contract common.Address) *types.MsgEthereumTx { 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) require.NoError(b, err)
nonce := suite.app.EvmKeeper.GetNonce(suite.address) 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) return types.NewTx(suite.app.EvmKeeper.ChainID(), nonce, &contract, big.NewInt(0), 410000, big.NewInt(1), nil, nil, input, nil)
@ -72,7 +72,7 @@ func BenchmarkTokenTransfer(b *testing.B) {
func BenchmarkEmitLogs(b *testing.B) { func BenchmarkEmitLogs(b *testing.B) {
DoBenchmark(b, func(suite *KeeperTestSuite, contract common.Address) *types.MsgEthereumTx { 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) require.NoError(b, err)
nonce := suite.app.EvmKeeper.GetNonce(suite.address) 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) return types.NewTx(suite.app.EvmKeeper.ChainID(), nonce, &contract, big.NewInt(0), 4100000, big.NewInt(1), nil, nil, input, nil)
@ -81,7 +81,7 @@ func BenchmarkEmitLogs(b *testing.B) {
func BenchmarkTokenTransferFrom(b *testing.B) { func BenchmarkTokenTransferFrom(b *testing.B) {
DoBenchmark(b, func(suite *KeeperTestSuite, contract common.Address) *types.MsgEthereumTx { 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) require.NoError(b, err)
nonce := suite.app.EvmKeeper.GetNonce(suite.address) 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) return types.NewTx(suite.app.EvmKeeper.ChainID(), nonce, &contract, big.NewInt(0), 410000, big.NewInt(1), nil, nil, input, nil)
@ -90,7 +90,7 @@ func BenchmarkTokenTransferFrom(b *testing.B) {
func BenchmarkTokenMint(b *testing.B) { func BenchmarkTokenMint(b *testing.B) {
DoBenchmark(b, func(suite *KeeperTestSuite, contract common.Address) *types.MsgEthereumTx { 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) require.NoError(b, err)
nonce := suite.app.EvmKeeper.GetNonce(suite.address) 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) return types.NewTx(suite.app.EvmKeeper.ChainID(), nonce, &contract, big.NewInt(0), 410000, big.NewInt(1), nil, nil, input, nil)

View File

@ -514,9 +514,9 @@ func (suite *KeeperTestSuite) TestEstimateGas() {
}, false, 0, false}, }, false, 0, false},
// estimate gas of an erc20 contract deployment, the exact gas number is checked with geth // estimate gas of an erc20 contract deployment, the exact gas number is checked with geth
{"contract deployment", func() { {"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) suite.Require().NoError(err)
data := append(ContractBin, ctorArgs...) data := append(types.ERC20Contract.Bin, ctorArgs...)
args = types.TransactionArgs{ args = types.TransactionArgs{
From: &suite.address, From: &suite.address,
Data: (*hexutil.Bytes)(&data), Data: (*hexutil.Bytes)(&data),
@ -526,7 +526,7 @@ func (suite *KeeperTestSuite) TestEstimateGas() {
{"erc20 transfer", func() { {"erc20 transfer", func() {
contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdk.NewIntWithDecimal(1000, 18).BigInt()) contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdk.NewIntWithDecimal(1000, 18).BigInt())
suite.Commit() 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) suite.Require().NoError(err)
args = types.TransactionArgs{To: &contractAddr, From: &suite.address, Data: (*hexutil.Bytes)(&transferData)} args = types.TransactionArgs{To: &contractAddr, From: &suite.address, Data: (*hexutil.Bytes)(&transferData)}
}, true, 51880, false}, }, true, 51880, false},
@ -549,9 +549,9 @@ func (suite *KeeperTestSuite) TestEstimateGas() {
gasCap = 20000 gasCap = 20000
}, false, 0, true}, }, false, 0, true},
{"contract deployment w/ dynamicTxFee", func() { {"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) suite.Require().NoError(err)
data := append(ContractBin, ctorArgs...) data := append(types.ERC20Contract.Bin, ctorArgs...)
args = types.TransactionArgs{ args = types.TransactionArgs{
From: &suite.address, From: &suite.address,
Data: (*hexutil.Bytes)(&data), Data: (*hexutil.Bytes)(&data),
@ -560,7 +560,7 @@ func (suite *KeeperTestSuite) TestEstimateGas() {
{"erc20 transfer w/ dynamicTxFee", func() { {"erc20 transfer w/ dynamicTxFee", func() {
contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdk.NewIntWithDecimal(1000, 18).BigInt()) contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdk.NewIntWithDecimal(1000, 18).BigInt())
suite.Commit() 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) suite.Require().NoError(err)
args = types.TransactionArgs{To: &contractAddr, From: &suite.address, Data: (*hexutil.Bytes)(&transferData)} args = types.TransactionArgs{To: &contractAddr, From: &suite.address, Data: (*hexutil.Bytes)(&transferData)}
}, true, 51880, true}, }, true, 51880, true},

View File

@ -27,7 +27,6 @@ import (
ethermint "github.com/tharsis/ethermint/types" ethermint "github.com/tharsis/ethermint/types"
"github.com/tharsis/ethermint/x/evm/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"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
ethtypes "github.com/ethereum/go-ethereum/core/types" ethtypes "github.com/ethereum/go-ethereum/core/types"
@ -40,29 +39,6 @@ import (
"github.com/tendermint/tendermint/version" "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) var testTokens = sdk.NewIntWithDecimal(1000, 18)
type KeeperTestSuite struct { type KeeperTestSuite struct {
@ -193,10 +169,10 @@ func (suite *KeeperTestSuite) DeployTestContract(t require.TestingT, owner commo
ctx := sdk.WrapSDKContext(suite.ctx) ctx := sdk.WrapSDKContext(suite.ctx)
chainID := suite.app.EvmKeeper.ChainID() chainID := suite.app.EvmKeeper.ChainID()
ctorArgs, err := ContractABI.Pack("", owner, supply) ctorArgs, err := types.ERC20Contract.ABI.Pack("", owner, supply)
require.NoError(t, err) require.NoError(t, err)
data := append(ContractBin, ctorArgs...) data := append(types.ERC20Contract.Bin, ctorArgs...)
args, err := json.Marshal(&types.TransactionArgs{ args, err := json.Marshal(&types.TransactionArgs{
From: &suite.address, From: &suite.address,
Data: (*hexutil.Bytes)(&data), Data: (*hexutil.Bytes)(&data),
@ -250,7 +226,7 @@ func (suite *KeeperTestSuite) TransferERC20Token(t require.TestingT, contractAdd
ctx := sdk.WrapSDKContext(suite.ctx) ctx := sdk.WrapSDKContext(suite.ctx)
chainID := suite.app.EvmKeeper.ChainID() 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) require.NoError(t, err)
args, err := json.Marshal(&types.TransactionArgs{To: &contractAddr, From: &from, Data: (*hexutil.Bytes)(&transferData)}) args, err := json.Marshal(&types.TransactionArgs{To: &contractAddr, From: &from, Data: (*hexutil.Bytes)(&transferData)})
require.NoError(t, err) require.NoError(t, err)

View File

@ -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")
}
}