diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4850ff9f..b65ede5b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,7 +15,7 @@ jobs: # - uses: actions/checkout@v2.3.4 # - uses: actions/setup-go@v2.1.3 # with: - # go-version: 1.15 + # go-version: 1.16 # - uses: technote-space/get-diff-action@v4.2 # id: git_diff # with: @@ -62,7 +62,7 @@ jobs: - uses: actions/checkout@v2.3.4 - uses: actions/setup-go@v2.1.3 with: - go-version: 1.15 + go-version: 1.16 - uses: technote-space/get-diff-action@v4.2 id: git_diff with: @@ -101,7 +101,7 @@ jobs: - uses: actions/checkout@v2.3.4 - uses: actions/setup-go@v2.1.3 with: - go-version: 1.15 + go-version: 1.16 - uses: technote-space/get-diff-action@v4.2 id: git_diff with: @@ -140,7 +140,7 @@ jobs: - uses: actions/checkout@v2.3.4 - uses: actions/setup-go@v2.1.3 with: - go-version: 1.15 + go-version: 1.16 - uses: technote-space/get-diff-action@v4.2 id: git_diff with: @@ -179,7 +179,7 @@ jobs: - uses: actions/checkout@v2.3.4 - uses: actions/setup-go@v2.1.3 with: - go-version: 1.15 + go-version: 1.16 - uses: technote-space/get-diff-action@v4.2 id: git_diff with: @@ -218,7 +218,7 @@ jobs: # - uses: actions/checkout@v2.3.4 # - uses: actions/setup-go@v2.1.3 # with: - # go-version: 1.15 + # go-version: 1.16 # - uses: technote-space/get-diff-action@v4.2 # id: git_diff # with: diff --git a/go.mod b/go.mod index 3251b58b..879c0888 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/tharsis/ethermint -go 1.15 +go 1.16 require ( github.com/armon/go-metrics v0.3.9 diff --git a/scripts/gen-tests-artifacts.sh b/scripts/gen-tests-artifacts.sh new file mode 100755 index 00000000..b8c3f9ab --- /dev/null +++ b/scripts/gen-tests-artifacts.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +# 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 diff --git a/x/evm/keeper/ERC20Contract.json b/x/evm/keeper/ERC20Contract.json new file mode 100644 index 00000000..6ab40fd2 --- /dev/null +++ b/x/evm/keeper/ERC20Contract.json @@ -0,0 +1,4 @@ +{ + "abi": "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"initialAccount\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"initialBalance\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_subtractedValue\",\"type\":\"uint256\"}],\"name\":\"decreaseApproval\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_addedValue\",\"type\":\"uint256\"}],\"name\":\"increaseApproval\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + "bin": "608060405234801561001057600080fd5b506040516113913803806113918339818101604052604081101561003357600080fd5b810190808051906020019092919080519060200190929190505050806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508060028190555050506112e8806100a96000396000f3fe608060405234801561001057600080fd5b50600436106100935760003560e01c8063661884631161006657806366188463146101f057806370a0823114610256578063a9059cbb146102ae578063d73dd62314610314578063dd62ed3e1461037a57610093565b8063095ea7b31461009857806318160ddd146100fe57806323b872dd1461011c57806340c10f19146101a2575b600080fd5b6100e4600480360360408110156100ae57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506103f2565b604051808215151515815260200191505060405180910390f35b6101066104e4565b6040518082815260200191505060405180910390f35b6101886004803603606081101561013257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506104ee565b604051808215151515815260200191505060405180910390f35b6101ee600480360360408110156101b857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506108a3565b005b61023c6004803603604081101561020657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610955565b604051808215151515815260200191505060405180910390f35b6102986004803603602081101561026c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610be5565b6040518082815260200191505060405180910390f35b6102fa600480360360408110156102c457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610c2d565b604051808215151515815260200191505060405180910390f35b6103606004803603604081101561032a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610e49565b604051808215151515815260200191505060405180910390f35b6103dc6004803603604081101561039057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611045565b6040518082815260200191505060405180910390f35b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b6000600254905090565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482111561053b57600080fd5b600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548211156105c457600080fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156105fe57600080fd5b61064f826000808773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546110cc90919063ffffffff16565b6000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506106e2826000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546111c090919063ffffffff16565b6000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506107b382600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546110cc90919063ffffffff16565b600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509392505050565b6108f4816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546111c090919063ffffffff16565b6000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061094b816002546111c090919063ffffffff16565b6002819055505050565b600080600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050808310610a65576000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610af9565b610a7883826110cc90919063ffffffff16565b600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b8373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546040518082815260200191505060405180910390a3600191505092915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60008060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054821115610c7a57600080fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610cb457600080fd5b610d05826000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546110cc90919063ffffffff16565b6000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610d98826000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546111c090919063ffffffff16565b6000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000610eda82600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546111c090919063ffffffff16565b600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546040518082815260200191505060405180910390a36001905092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b6000828211156040518060400160405280601281526020017f4d4154485f5355425f554e444552464c4f570000000000000000000000000000815250906111ae576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015611173578082015181840152602081019050611158565b50505050905090810190601f1680156111a05780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50600082840390508091505092915050565b6000808284019050838110156040518060400160405280601181526020017f4d4154485f4144445f4f564552464c4f57000000000000000000000000000000815250906112a8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561126d578082015181840152602081019050611252565b50505050905090810190601f16801561129a5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50809150509291505056fea265627a7a72315820d78a33bfcaf696ab45393e8e57e6cd526c981fcdef7b30c0f5c93baca905bf3764736f6c63430005110032" +} diff --git a/x/evm/keeper/grpc_query.go b/x/evm/keeper/grpc_query.go index 98b39a3e..22ddae5c 100644 --- a/x/evm/keeper/grpc_query.go +++ b/x/evm/keeper/grpc_query.go @@ -406,6 +406,10 @@ func (k Keeper) EstimateGas(c context.Context, req *types.EthCallRequest) (*type ctx := sdk.UnwrapSDKContext(c) k.WithContext(ctx) + if req.GasCap < ethparams.TxGas { + return nil, status.Error(codes.InvalidArgument, "gas cap cannot be lower than 21,000") + } + var args types.CallArgs err := json.Unmarshal(req.Args, &args) if err != nil { diff --git a/x/evm/keeper/grpc_query_test.go b/x/evm/keeper/grpc_query_test.go index 99bc4142..5e9d73eb 100644 --- a/x/evm/keeper/grpc_query_test.go +++ b/x/evm/keeper/grpc_query_test.go @@ -1,13 +1,19 @@ package keeper_test import ( + _ "embed" + "encoding/json" "fmt" + "math/big" "google.golang.org/grpc/metadata" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" ethcmn "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" ethcrypto "github.com/ethereum/go-ethereum/crypto" sdk "github.com/cosmos/cosmos-sdk/types" @@ -21,6 +27,29 @@ import ( //Not valid Ethereum address const invalidAddress = "0x0000" +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) + } +} + func (suite *KeeperTestSuite) TestQueryAccount() { var ( req *types.QueryAccountRequest @@ -686,3 +715,122 @@ func (suite *KeeperTestSuite) TestQueryValidatorAccount() { }) } } + +// DeployTestContract deploy a test erc20 contract and returns the contract address +func (suite *KeeperTestSuite) deployTestContract(owner common.Address, supply *big.Int) common.Address { + ctx := sdk.WrapSDKContext(suite.ctx) + chainID := suite.app.EvmKeeper.ChainID() + + ctorArgs, err := contractABI.Pack("", owner, supply) + suite.Require().NoError(err) + + data := append(contractBin, ctorArgs...) + args, err := json.Marshal(&types.CallArgs{ + From: &suite.address, + Data: (*hexutil.Bytes)(&data), + }) + suite.Require().NoError(err) + + res, err := suite.queryClient.EstimateGas(ctx, &types.EthCallRequest{ + Args: args, + GasCap: uint64(ethermint.DefaultRPCGasLimit), + }) + suite.Require().NoError(err) + + nonce := suite.app.EvmKeeper.GetNonce(suite.address) + erc20DeployTx := types.NewTxContract( + chainID, + nonce, + nil, // amount + res.Gas, // gasLimit + nil, // gasPrice + data, // input + nil, // accesses + ) + erc20DeployTx.From = suite.address.Hex() + err = erc20DeployTx.Sign(ethtypes.LatestSignerForChainID(chainID), suite.signer) + suite.Require().NoError(err) + rsp, err := suite.app.EvmKeeper.EthereumTx(ctx, erc20DeployTx) + suite.Require().NoError(err) + suite.Require().Empty(rsp.VmError) + return crypto.CreateAddress(suite.address, nonce) +} + +func (suite *KeeperTestSuite) TestEstimateGas() { + ctx := sdk.WrapSDKContext(suite.ctx) + gasHelper := hexutil.Uint64(20000) + + var ( + args types.CallArgs + gasCap uint64 + ) + testCases := []struct { + msg string + malleate func() + expPass bool + expGas uint64 + }{ + // should success, because transfer value is zero + {"default args", func() { + args = types.CallArgs{To: &common.Address{}} + }, true, 21000}, + // should fail, because the default From address(zero address) don't have fund + {"not enough balance", func() { + args = types.CallArgs{To: &common.Address{}, Value: (*hexutil.Big)(big.NewInt(100))} + }, false, 0}, + // should success, enough balance now + {"enough balance", func() { + args = types.CallArgs{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.CallArgs{To: &common.Address{}, Gas: &gasHelper} + }, true, 21000}, + // should fail, invalid gas cap + {"gas exceed global allowance", func() { + args = types.CallArgs{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.CallArgs{ + From: &suite.address, + Data: (*hexutil.Bytes)(&data), + } + }, true, 1144643}, + // estimate gas of an erc20 transfer, the exact gas number is checked with geth + {"erc20 transfer", func() { + contractAddr := suite.deployTestContract(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.CallArgs{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 = ethermint.DefaultRPCGasLimit + tc.malleate() + + args, err := json.Marshal(&args) + suite.Require().NoError(err) + req := types.EthCallRequest{ + Args: args, + GasCap: gasCap, + } + + rsp, err := suite.queryClient.EstimateGas(ctx, &req) + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(tc.expGas, rsp.Gas) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/x/evm/keeper/keeper_test.go b/x/evm/keeper/keeper_test.go index a404265d..44917cac 100644 --- a/x/evm/keeper/keeper_test.go +++ b/x/evm/keeper/keeper_test.go @@ -8,13 +8,19 @@ import ( "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/tharsis/ethermint/app" "github.com/tharsis/ethermint/crypto/ethsecp256k1" "github.com/tharsis/ethermint/encoding" + "github.com/tharsis/ethermint/tests" ethermint "github.com/tharsis/ethermint/types" "github.com/tharsis/ethermint/x/evm/types" @@ -23,15 +29,11 @@ import ( ethtypes "github.com/ethereum/go-ethereum/core/types" ethcrypto "github.com/ethereum/go-ethereum/crypto" + abci "github.com/tendermint/tendermint/abci/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" ) -const addrHex = "0x756F45E3FA69347A9A973A725E3C98bC4db0b4c1" -const hex = "0x0d87a3a5f73140f46aac1bf419263e4e94e87c292f25007700ab7f2060e2af68" - -var ( - hash = ethcmn.FromHex(hex) -) +var testTokens = sdk.NewIntWithDecimal(1000, 18) type KeeperTestSuite struct { suite.Suite @@ -45,16 +47,33 @@ type KeeperTestSuite struct { // for generate test tx clientCtx client.Context ethSigner ethtypes.Signer + + appCodec codec.Codec + signer keyring.Signer } func (suite *KeeperTestSuite) SetupTest() { checkTx := false - suite.app = app.Setup(checkTx) - suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1, ChainID: "ethermint-1", Time: time.Now().UTC()}) - suite.app.EvmKeeper.WithContext(suite.ctx) + // account key + priv, err := ethsecp256k1.GenerateKey() + suite.Require().NoError(err) + suite.address = ethcmn.BytesToAddress(priv.PubKey().Address().Bytes()) + suite.signer = tests.NewSigner(priv) - suite.address = ethcmn.HexToAddress(addrHex) + // consensus key + priv, err = ethsecp256k1.GenerateKey() + suite.Require().NoError(err) + suite.consAddress = sdk.ConsAddress(priv.PubKey().Address()) + + suite.app = app.Setup(checkTx) + suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{ + Height: 1, + ChainID: "ethermint-1", + Time: time.Now().UTC(), + ProposerAddress: suite.consAddress.Bytes(), + }) + suite.app.EvmKeeper.WithContext(suite.ctx) queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry()) types.RegisterQueryServer(queryHelper, suite.app.EvmKeeper) @@ -67,17 +86,64 @@ func (suite *KeeperTestSuite) SetupTest() { suite.app.AccountKeeper.SetAccount(suite.ctx, acc) - priv, err := ethsecp256k1.GenerateKey() - suite.Require().NoError(err) valAddr := sdk.ValAddress(suite.address.Bytes()) validator, err := stakingtypes.NewValidator(valAddr, priv.PubKey(), stakingtypes.Description{}) - suite.app.StakingKeeper.SetValidatorByConsAddr(suite.ctx, validator) + err = suite.app.StakingKeeper.SetValidatorByConsAddr(suite.ctx, validator) + suite.Require().NoError(err) + err = suite.app.StakingKeeper.SetValidatorByConsAddr(suite.ctx, validator) + suite.Require().NoError(err) suite.app.StakingKeeper.SetValidator(suite.ctx, validator) - suite.consAddress = sdk.ConsAddress(priv.PubKey().Address()) encodingConfig := encoding.MakeConfig(app.ModuleBasics) suite.clientCtx = client.Context{}.WithTxConfig(encodingConfig.TxConfig) suite.ethSigner = ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID()) + suite.appCodec = encodingConfig.Marshaler + + // mint some tokens to coinbase address + _, bankKeeper := suite.initKeepersWithmAccPerms() + ctx := sdk.WrapSDKContext(suite.ctx) + rsp, err := suite.queryClient.Params(ctx, &types.QueryParamsRequest{}) + suite.Require().NoError(err) + initCoin := sdk.NewCoins(sdk.NewCoin(rsp.Params.EvmDenom, testTokens)) + + err = simapp.FundAccount(bankKeeper, suite.ctx, acc.GetAddress(), initCoin) + suite.Require().NoError(err) +} + +// Commit and begin new block +func (suite *KeeperTestSuite) Commit() { + suite.app.Commit() + header := suite.ctx.BlockHeader() + header.Height += 1 + suite.app.BeginBlock(abci.RequestBeginBlock{ + Header: header, + }) + + // update ctx + suite.ctx = suite.app.BaseApp.NewContext(false, header) + suite.app.EvmKeeper.WithContext(suite.ctx) + + queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry()) + types.RegisterQueryServer(queryHelper, suite.app.EvmKeeper) + suite.queryClient = types.NewQueryClient(queryHelper) +} + +// initKeepersWithmAccPerms construct a bank keeper that can mint tokens out of thin air +func (suite *KeeperTestSuite) initKeepersWithmAccPerms() (authkeeper.AccountKeeper, bankkeeper.BaseKeeper) { + maccPerms := app.GetMaccPerms() + + maccPerms[authtypes.Burner] = []string{authtypes.Burner} + maccPerms[authtypes.Minter] = []string{authtypes.Minter} + authKeeper := authkeeper.NewAccountKeeper( + suite.appCodec, suite.app.GetKey(types.StoreKey), suite.app.GetSubspace(types.ModuleName), + authtypes.ProtoBaseAccount, maccPerms, + ) + keeper := bankkeeper.NewBaseKeeper( + suite.appCodec, suite.app.GetKey(types.StoreKey), authKeeper, + suite.app.GetSubspace(types.ModuleName), map[string]bool{}, + ) + + return authKeeper, keeper } func TestKeeperTestSuite(t *testing.T) { diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index 2bd2c8e9..05a18c24 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -20,7 +20,12 @@ func (suite *KeeperTestSuite) TestGetCoinbaseAddress() { }{ { "validator not found", - func() {}, + func() { + header := suite.ctx.BlockHeader() + header.ProposerAddress = []byte{} + suite.ctx = suite.ctx.WithBlockHeader(header) + suite.app.EvmKeeper.WithContext(suite.ctx) + }, false, }, {