a8722655bb
* all: bump go-ethereum to v1.10.4 * build * state transition and rpc * wip rpc changes * fix refund * fixes * no base fee param * ante handler * undo change * fix test * bump deps * calculate base fee * gRPC base fee query * update RPC * fix * update' * go.mod * fix build * fix panic * rm changes in third_party * json rpc changes * reserved fields * fixes fixes fixes * rm no stringer * fixes 2 * tests wip * bump geth version * update * grpc traceTx * rm fee market from ante * fix TransactionArgs * lint * update proto * update tx args * changelog
618 lines
15 KiB
Go
618 lines
15 KiB
Go
package keeper_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"math/big"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
|
|
ethermint "github.com/tharsis/ethermint/types"
|
|
"github.com/tharsis/ethermint/x/evm/types"
|
|
)
|
|
|
|
// Not valid Ethereum address
|
|
const invalidAddress = "0x0000"
|
|
|
|
func (suite *KeeperTestSuite) TestQueryAccount() {
|
|
var (
|
|
req *types.QueryAccountRequest
|
|
expAccount *types.QueryAccountResponse
|
|
)
|
|
|
|
testCases := []struct {
|
|
msg string
|
|
malleate func()
|
|
expPass bool
|
|
}{
|
|
{
|
|
"invalid address",
|
|
func() {
|
|
expAccount = &types.QueryAccountResponse{
|
|
Balance: "0",
|
|
CodeHash: common.BytesToHash(crypto.Keccak256(nil)).Hex(),
|
|
Nonce: 0,
|
|
}
|
|
req = &types.QueryAccountRequest{
|
|
Address: invalidAddress,
|
|
}
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"success",
|
|
func() {
|
|
amt := sdk.Coins{ethermint.NewPhotonCoinInt64(100)}
|
|
err := suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, amt)
|
|
suite.Require().NoError(err)
|
|
err = suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, suite.address.Bytes(), amt)
|
|
suite.Require().NoError(err)
|
|
|
|
expAccount = &types.QueryAccountResponse{
|
|
Balance: "100",
|
|
CodeHash: common.BytesToHash(crypto.Keccak256(nil)).Hex(),
|
|
Nonce: 0,
|
|
}
|
|
req = &types.QueryAccountRequest{
|
|
Address: suite.address.String(),
|
|
}
|
|
},
|
|
true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
|
suite.SetupTest() // reset
|
|
|
|
tc.malleate()
|
|
ctx := sdk.WrapSDKContext(suite.ctx)
|
|
res, err := suite.queryClient.Account(ctx, req)
|
|
|
|
if tc.expPass {
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res)
|
|
|
|
suite.Require().Equal(expAccount, res)
|
|
} else {
|
|
suite.Require().Error(err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestQueryCosmosAccount() {
|
|
var (
|
|
req *types.QueryCosmosAccountRequest
|
|
expAccount *types.QueryCosmosAccountResponse
|
|
)
|
|
|
|
testCases := []struct {
|
|
msg string
|
|
malleate func()
|
|
expPass bool
|
|
}{
|
|
{
|
|
"invalid address",
|
|
func() {
|
|
expAccount = &types.QueryCosmosAccountResponse{
|
|
CosmosAddress: sdk.AccAddress(common.Address{}.Bytes()).String(),
|
|
}
|
|
req = &types.QueryCosmosAccountRequest{
|
|
Address: invalidAddress,
|
|
}
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"success",
|
|
func() {
|
|
expAccount = &types.QueryCosmosAccountResponse{
|
|
CosmosAddress: sdk.AccAddress(suite.address.Bytes()).String(),
|
|
Sequence: 0,
|
|
AccountNumber: 0,
|
|
}
|
|
req = &types.QueryCosmosAccountRequest{
|
|
Address: suite.address.String(),
|
|
}
|
|
},
|
|
true,
|
|
},
|
|
{
|
|
"success with seq and account number",
|
|
func() {
|
|
acc := suite.app.AccountKeeper.GetAccount(suite.ctx, suite.address.Bytes())
|
|
suite.Require().NoError(acc.SetSequence(10))
|
|
suite.Require().NoError(acc.SetAccountNumber(1))
|
|
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
|
|
|
expAccount = &types.QueryCosmosAccountResponse{
|
|
CosmosAddress: sdk.AccAddress(suite.address.Bytes()).String(),
|
|
Sequence: 10,
|
|
AccountNumber: 1,
|
|
}
|
|
req = &types.QueryCosmosAccountRequest{
|
|
Address: suite.address.String(),
|
|
}
|
|
},
|
|
true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
|
suite.SetupTest() // reset
|
|
|
|
tc.malleate()
|
|
ctx := sdk.WrapSDKContext(suite.ctx)
|
|
res, err := suite.queryClient.CosmosAccount(ctx, req)
|
|
|
|
if tc.expPass {
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res)
|
|
|
|
suite.Require().Equal(expAccount, res)
|
|
} else {
|
|
suite.Require().Error(err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestQueryBalance() {
|
|
var (
|
|
req *types.QueryBalanceRequest
|
|
expBalance string
|
|
)
|
|
|
|
testCases := []struct {
|
|
msg string
|
|
malleate func()
|
|
expPass bool
|
|
}{
|
|
{
|
|
"invalid address",
|
|
func() {
|
|
expBalance = "0"
|
|
req = &types.QueryBalanceRequest{
|
|
Address: invalidAddress,
|
|
}
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"success",
|
|
func() {
|
|
amt := sdk.Coins{ethermint.NewPhotonCoinInt64(100)}
|
|
err := suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, amt)
|
|
suite.Require().NoError(err)
|
|
err = suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, suite.address.Bytes(), amt)
|
|
suite.Require().NoError(err)
|
|
|
|
expBalance = "100"
|
|
req = &types.QueryBalanceRequest{
|
|
Address: suite.address.String(),
|
|
}
|
|
},
|
|
true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
|
suite.SetupTest() // reset
|
|
|
|
tc.malleate()
|
|
ctx := sdk.WrapSDKContext(suite.ctx)
|
|
res, err := suite.queryClient.Balance(ctx, req)
|
|
|
|
if tc.expPass {
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res)
|
|
|
|
suite.Require().Equal(expBalance, res.Balance)
|
|
} else {
|
|
suite.Require().Error(err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestQueryStorage() {
|
|
var (
|
|
req *types.QueryStorageRequest
|
|
expValue string
|
|
)
|
|
|
|
testCases := []struct {
|
|
msg string
|
|
malleate func()
|
|
expPass bool
|
|
}{
|
|
{
|
|
"invalid address",
|
|
func() {
|
|
req = &types.QueryStorageRequest{
|
|
Address: invalidAddress,
|
|
}
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"success",
|
|
func() {
|
|
key := common.BytesToHash([]byte("key"))
|
|
value := common.BytesToHash([]byte("value"))
|
|
expValue = value.String()
|
|
suite.app.EvmKeeper.SetState(suite.address, key, value)
|
|
req = &types.QueryStorageRequest{
|
|
Address: suite.address.String(),
|
|
Key: key.String(),
|
|
}
|
|
},
|
|
true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
|
suite.SetupTest() // reset
|
|
|
|
tc.malleate()
|
|
ctx := sdk.WrapSDKContext(suite.ctx)
|
|
res, err := suite.queryClient.Storage(ctx, req)
|
|
|
|
if tc.expPass {
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res)
|
|
|
|
suite.Require().Equal(expValue, res.Value)
|
|
} else {
|
|
suite.Require().Error(err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestQueryCode() {
|
|
var (
|
|
req *types.QueryCodeRequest
|
|
expCode []byte
|
|
)
|
|
|
|
testCases := []struct {
|
|
msg string
|
|
malleate func()
|
|
expPass bool
|
|
}{
|
|
{
|
|
"invalid address",
|
|
func() {
|
|
req = &types.QueryCodeRequest{
|
|
Address: invalidAddress,
|
|
}
|
|
exp := &types.QueryCodeResponse{}
|
|
expCode = exp.Code
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"success",
|
|
func() {
|
|
expCode = []byte("code")
|
|
suite.app.EvmKeeper.SetCode(suite.address, expCode)
|
|
|
|
req = &types.QueryCodeRequest{
|
|
Address: suite.address.String(),
|
|
}
|
|
},
|
|
true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
|
suite.SetupTest() // reset
|
|
|
|
tc.malleate()
|
|
ctx := sdk.WrapSDKContext(suite.ctx)
|
|
res, err := suite.queryClient.Code(ctx, req)
|
|
|
|
if tc.expPass {
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res)
|
|
|
|
suite.Require().Equal(expCode, res.Code)
|
|
} else {
|
|
suite.Require().Error(err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestQueryTxLogs() {
|
|
var (
|
|
txHash common.Hash
|
|
expLogs []*types.Log
|
|
)
|
|
|
|
testCases := []struct {
|
|
msg string
|
|
malleate func()
|
|
}{
|
|
{
|
|
"empty logs",
|
|
func() {
|
|
txHash = common.BytesToHash([]byte("hash"))
|
|
expLogs = nil
|
|
},
|
|
},
|
|
{
|
|
"success",
|
|
func() {
|
|
txHash = common.BytesToHash([]byte("tx_hash"))
|
|
|
|
expLogs = []*types.Log{
|
|
{
|
|
Address: suite.address.String(),
|
|
Topics: []string{common.BytesToHash([]byte("topic")).String()},
|
|
Data: []byte("data"),
|
|
BlockNumber: 1,
|
|
TxHash: txHash.String(),
|
|
TxIndex: 1,
|
|
BlockHash: common.BytesToHash(suite.ctx.HeaderHash()).Hex(),
|
|
Index: 0,
|
|
Removed: false,
|
|
},
|
|
}
|
|
|
|
suite.app.EvmKeeper.SetTxHashTransient(txHash)
|
|
suite.app.EvmKeeper.IncreaseTxIndexTransient()
|
|
for _, log := range types.LogsToEthereum(expLogs) {
|
|
suite.app.EvmKeeper.AddLog(log)
|
|
}
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
|
suite.SetupTest() // reset
|
|
|
|
tc.malleate()
|
|
logs := suite.app.EvmKeeper.GetTxLogsTransient(txHash)
|
|
suite.Require().Equal(expLogs, types.NewLogsFromEth(logs))
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestQueryParams() {
|
|
ctx := sdk.WrapSDKContext(suite.ctx)
|
|
expParams := types.DefaultParams()
|
|
|
|
res, err := suite.queryClient.Params(ctx, &types.QueryParamsRequest{})
|
|
suite.Require().NoError(err)
|
|
suite.Require().Equal(expParams, res.Params)
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestQueryValidatorAccount() {
|
|
var (
|
|
req *types.QueryValidatorAccountRequest
|
|
expAccount *types.QueryValidatorAccountResponse
|
|
)
|
|
|
|
testCases := []struct {
|
|
msg string
|
|
malleate func()
|
|
expPass bool
|
|
}{
|
|
{
|
|
"invalid address",
|
|
func() {
|
|
expAccount = &types.QueryValidatorAccountResponse{
|
|
AccountAddress: sdk.AccAddress(common.Address{}.Bytes()).String(),
|
|
}
|
|
req = &types.QueryValidatorAccountRequest{
|
|
ConsAddress: "",
|
|
}
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"success",
|
|
func() {
|
|
expAccount = &types.QueryValidatorAccountResponse{
|
|
AccountAddress: sdk.AccAddress(suite.address.Bytes()).String(),
|
|
Sequence: 0,
|
|
AccountNumber: 0,
|
|
}
|
|
req = &types.QueryValidatorAccountRequest{
|
|
ConsAddress: suite.consAddress.String(),
|
|
}
|
|
},
|
|
true,
|
|
},
|
|
{
|
|
"success with seq and account number",
|
|
func() {
|
|
acc := suite.app.AccountKeeper.GetAccount(suite.ctx, suite.address.Bytes())
|
|
suite.Require().NoError(acc.SetSequence(10))
|
|
suite.Require().NoError(acc.SetAccountNumber(1))
|
|
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
|
|
|
expAccount = &types.QueryValidatorAccountResponse{
|
|
AccountAddress: sdk.AccAddress(suite.address.Bytes()).String(),
|
|
Sequence: 10,
|
|
AccountNumber: 1,
|
|
}
|
|
req = &types.QueryValidatorAccountRequest{
|
|
ConsAddress: suite.consAddress.String(),
|
|
}
|
|
},
|
|
true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
|
suite.SetupTest() // reset
|
|
|
|
tc.malleate()
|
|
ctx := sdk.WrapSDKContext(suite.ctx)
|
|
res, err := suite.queryClient.ValidatorAccount(ctx, req)
|
|
|
|
if tc.expPass {
|
|
suite.Require().NoError(err)
|
|
suite.Require().NotNil(res)
|
|
|
|
suite.Require().Equal(expAccount, res)
|
|
} else {
|
|
suite.Require().Error(err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestEstimateGas() {
|
|
gasHelper := hexutil.Uint64(20000)
|
|
|
|
var (
|
|
args types.TransactionArgs
|
|
gasCap uint64
|
|
)
|
|
testCases := []struct {
|
|
msg string
|
|
malleate func()
|
|
expPass bool
|
|
expGas uint64
|
|
}{
|
|
// should success, because transfer value is zero
|
|
{"default args", func() {
|
|
args = types.TransactionArgs{To: &common.Address{}}
|
|
}, true, 21000},
|
|
// should fail, because the default From address(zero address) don't have fund
|
|
{"not enough balance", func() {
|
|
args = types.TransactionArgs{To: &common.Address{}, Value: (*hexutil.Big)(big.NewInt(100))}
|
|
}, false, 0},
|
|
// should success, enough balance now
|
|
{"enough balance", func() {
|
|
args = types.TransactionArgs{To: &common.Address{}, From: &suite.address, Value: (*hexutil.Big)(big.NewInt(100))}
|
|
}, false, 0},
|
|
// should success, because gas limit lower than 21000 is ignored
|
|
{"gas exceed allowance", func() {
|
|
args = types.TransactionArgs{To: &common.Address{}, Gas: &gasHelper}
|
|
}, true, 21000},
|
|
// should fail, invalid gas cap
|
|
{"gas exceed global allowance", func() {
|
|
args = types.TransactionArgs{To: &common.Address{}}
|
|
gasCap = 20000
|
|
}, false, 0},
|
|
// 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())
|
|
suite.Require().NoError(err)
|
|
data := append(ContractBin, ctorArgs...)
|
|
args = types.TransactionArgs{
|
|
From: &suite.address,
|
|
Data: (*hexutil.Bytes)(&data),
|
|
}
|
|
}, true, 1186778},
|
|
// estimate gas of an erc20 transfer, the exact gas number is checked with geth
|
|
{"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))
|
|
suite.Require().NoError(err)
|
|
args = types.TransactionArgs{To: &contractAddr, From: &suite.address, Data: (*hexutil.Bytes)(&transferData)}
|
|
}, true, 51880},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
|
suite.SetupTest()
|
|
gasCap = 25_000_000
|
|
tc.malleate()
|
|
|
|
args, err := json.Marshal(&args)
|
|
suite.Require().NoError(err)
|
|
req := types.EthCallRequest{
|
|
Args: args,
|
|
GasCap: gasCap,
|
|
}
|
|
|
|
rsp, err := suite.queryClient.EstimateGas(sdk.WrapSDKContext(suite.ctx), &req)
|
|
if tc.expPass {
|
|
suite.Require().NoError(err)
|
|
suite.Require().Equal(tc.expGas, rsp.Gas)
|
|
} else {
|
|
suite.Require().Error(err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestTraceTx() {
|
|
ctx := sdk.WrapSDKContext(suite.ctx)
|
|
// TODO deploy contract that triggers internal transactions
|
|
var (
|
|
txMsg *types.MsgEthereumTx
|
|
traceConfig *types.TraceConfig
|
|
)
|
|
|
|
testCases := []struct {
|
|
msg string
|
|
malleate func()
|
|
expPass bool
|
|
traceResponse []byte
|
|
}{
|
|
{
|
|
msg: "default trace",
|
|
malleate: func() {
|
|
traceConfig = nil
|
|
},
|
|
expPass: true,
|
|
traceResponse: []byte{0x7b, 0x22, 0x67, 0x61, 0x73, 0x22, 0x3a, 0x33, 0x34, 0x38, 0x32, 0x38, 0x2c, 0x22, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x22, 0x3a, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x22, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x3a, 0x22, 0x22, 0x2c, 0x22, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x22, 0x3a, 0x5b, 0x5d, 0x7d},
|
|
}, {
|
|
msg: "javascript tracer",
|
|
malleate: func() {
|
|
traceConfig = &types.TraceConfig{
|
|
Tracer: "{data: [], fault: function(log) {}, step: function(log) { if(log.op.toString() == \"CALL\") this.data.push(log.stack.peek(0)); }, result: function() { return this.data; }}",
|
|
}
|
|
},
|
|
expPass: true,
|
|
traceResponse: []byte{0x5b, 0x5d},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
|
suite.SetupTest()
|
|
// Deploy contract
|
|
contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdk.NewIntWithDecimal(1000, 18).BigInt())
|
|
suite.Commit()
|
|
// Generate token transfer transaction
|
|
txMsg = suite.TransferERC20Token(suite.T(), contractAddr, suite.address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), sdk.NewIntWithDecimal(1, 18).BigInt())
|
|
suite.Commit()
|
|
|
|
tc.malleate()
|
|
traceReq := types.QueryTraceTxRequest{
|
|
Msg: txMsg,
|
|
TraceConfig: traceConfig,
|
|
TxIndex: 1, // Can be hardcoded as this will be the only tx included in the block
|
|
}
|
|
res, err := suite.queryClient.TraceTx(ctx, &traceReq)
|
|
|
|
if tc.expPass {
|
|
suite.Require().NoError(err)
|
|
suite.Require().Equal(tc.traceResponse, res.Data)
|
|
} else {
|
|
suite.Require().Error(err)
|
|
}
|
|
})
|
|
}
|
|
}
|