392d1dd8cf
* Problem: missing json rpc of eth_feeHistory #685 add oracle backend space ready structure ok refactoring return feehistory data flow ok basefee set gas used ratio computing reward add testing add gas used prepare data fill reward increase coin fixing api add mac add launch gas used ratio ok print element reward workes reward working fix panic value correct remove debugging log tidy up tidy up remove oracle tidy up fix handler crash add unit test tidy up add limit check reformat fix lint fix lint fix lint fix lint Update rpc/ethereum/backend/feebackend.go thanks Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Update rpc/ethereum/backend/feebackend.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Update rpc/ethereum/backend/feebackend.go thanks Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Update rpc/ethereum/backend/feebackend.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> fix compile error split lines remove temporary string conversion return error if gaslimit is 0 move OneFeeHistory to types add comment only err check Update rpc/ethereum/backend/feebackend.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Update rpc/ethereum/backend/feebackend.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> tidy up add feehistory-cap * Apply suggestions from code review * changelog Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Federico Kunze Küllmer <federico.kunze94@gmail.com>
372 lines
11 KiB
Go
372 lines
11 KiB
Go
package keeper_test
|
|
|
|
import (
|
|
_ "embed"
|
|
"encoding/json"
|
|
"math/big"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/stretchr/testify/suite"
|
|
|
|
"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"
|
|
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
|
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
|
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
|
tmjson "github.com/tendermint/tendermint/libs/json"
|
|
feemarkettypes "github.com/tharsis/ethermint/x/feemarket/types"
|
|
|
|
"github.com/tharsis/ethermint/app"
|
|
"github.com/tharsis/ethermint/crypto/ethsecp256k1"
|
|
"github.com/tharsis/ethermint/encoding"
|
|
"github.com/tharsis/ethermint/server/config"
|
|
"github.com/tharsis/ethermint/tests"
|
|
ethermint "github.com/tharsis/ethermint/types"
|
|
"github.com/tharsis/ethermint/x/evm/types"
|
|
|
|
"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"
|
|
"github.com/ethereum/go-ethereum/params"
|
|
|
|
abci "github.com/tendermint/tendermint/abci/types"
|
|
"github.com/tendermint/tendermint/crypto/tmhash"
|
|
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
|
tmversion "github.com/tendermint/tendermint/proto/tendermint/version"
|
|
"github.com/tendermint/tendermint/version"
|
|
)
|
|
|
|
var testTokens = sdk.NewIntWithDecimal(1000, 18)
|
|
|
|
type KeeperTestSuite struct {
|
|
suite.Suite
|
|
|
|
ctx sdk.Context
|
|
app *app.EthermintApp
|
|
queryClient types.QueryClient
|
|
address common.Address
|
|
consAddress sdk.ConsAddress
|
|
|
|
// for generate test tx
|
|
clientCtx client.Context
|
|
ethSigner ethtypes.Signer
|
|
|
|
appCodec codec.Codec
|
|
signer keyring.Signer
|
|
|
|
dynamicTxFee bool
|
|
mintFeeCollector bool
|
|
}
|
|
|
|
/// DoSetupTest setup test environment, it uses`require.TestingT` to support both `testing.T` and `testing.B`.
|
|
func (suite *KeeperTestSuite) DoSetupTest(t require.TestingT) {
|
|
checkTx := false
|
|
|
|
// account key
|
|
priv, err := ethsecp256k1.GenerateKey()
|
|
require.NoError(t, err)
|
|
suite.address = common.BytesToAddress(priv.PubKey().Address().Bytes())
|
|
suite.signer = tests.NewSigner(priv)
|
|
|
|
// consensus key
|
|
priv, err = ethsecp256k1.GenerateKey()
|
|
require.NoError(t, err)
|
|
suite.consAddress = sdk.ConsAddress(priv.PubKey().Address())
|
|
|
|
if suite.dynamicTxFee {
|
|
// setup feemarketGenesis params
|
|
feemarketGenesis := feemarkettypes.DefaultGenesisState()
|
|
feemarketGenesis.Params.EnableHeight = 1
|
|
feemarketGenesis.Params.NoBaseFee = false
|
|
feemarketGenesis.BaseFee = sdk.NewInt(feemarketGenesis.Params.InitialBaseFee)
|
|
suite.app = app.Setup(checkTx, feemarketGenesis)
|
|
} else {
|
|
suite.app = app.Setup(checkTx, nil)
|
|
}
|
|
|
|
if suite.mintFeeCollector {
|
|
// mint some coin to fee collector
|
|
coins := sdk.NewCoins(sdk.NewCoin(types.DefaultEVMDenom, sdk.NewInt(int64(params.TxGas)-1)))
|
|
genesisState := app.ModuleBasics.DefaultGenesis(suite.app.AppCodec())
|
|
balances := []banktypes.Balance{
|
|
{
|
|
Address: suite.app.AccountKeeper.GetModuleAddress(authtypes.FeeCollectorName).String(),
|
|
Coins: coins,
|
|
},
|
|
}
|
|
// update total supply
|
|
bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, sdk.NewCoins(sdk.NewCoin(types.DefaultEVMDenom, sdk.NewInt((int64(params.TxGas)-1)))), []banktypes.Metadata{})
|
|
bz := suite.app.AppCodec().MustMarshalJSON(bankGenesis)
|
|
require.NotNil(t, bz)
|
|
genesisState[banktypes.ModuleName] = suite.app.AppCodec().MustMarshalJSON(bankGenesis)
|
|
|
|
// we marshal the genesisState of all module to a byte array
|
|
stateBytes, err := tmjson.MarshalIndent(genesisState, "", " ")
|
|
require.NoError(t, err)
|
|
|
|
// Initialize the chain
|
|
suite.app.InitChain(
|
|
abci.RequestInitChain{
|
|
ChainId: "ethermint_9000-1",
|
|
Validators: []abci.ValidatorUpdate{},
|
|
ConsensusParams: simapp.DefaultConsensusParams,
|
|
AppStateBytes: stateBytes,
|
|
},
|
|
)
|
|
}
|
|
|
|
suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{
|
|
Height: 1,
|
|
ChainID: "ethermint_9000-1",
|
|
Time: time.Now().UTC(),
|
|
ProposerAddress: suite.consAddress.Bytes(),
|
|
Version: tmversion.Consensus{
|
|
Block: version.BlockProtocol,
|
|
},
|
|
LastBlockId: tmproto.BlockID{
|
|
Hash: tmhash.Sum([]byte("block_id")),
|
|
PartSetHeader: tmproto.PartSetHeader{
|
|
Total: 11,
|
|
Hash: tmhash.Sum([]byte("partset_header")),
|
|
},
|
|
},
|
|
AppHash: tmhash.Sum([]byte("app")),
|
|
DataHash: tmhash.Sum([]byte("data")),
|
|
EvidenceHash: tmhash.Sum([]byte("evidence")),
|
|
ValidatorsHash: tmhash.Sum([]byte("validators")),
|
|
NextValidatorsHash: tmhash.Sum([]byte("next_validators")),
|
|
ConsensusHash: tmhash.Sum([]byte("consensus")),
|
|
LastResultsHash: tmhash.Sum([]byte("last_result")),
|
|
})
|
|
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)
|
|
|
|
acc := ðermint.EthAccount{
|
|
BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(suite.address.Bytes()), nil, 0, 0),
|
|
CodeHash: common.BytesToHash(crypto.Keccak256(nil)).String(),
|
|
}
|
|
|
|
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
|
|
|
valAddr := sdk.ValAddress(suite.address.Bytes())
|
|
validator, err := stakingtypes.NewValidator(valAddr, priv.PubKey(), stakingtypes.Description{})
|
|
err = suite.app.StakingKeeper.SetValidatorByConsAddr(suite.ctx, validator)
|
|
require.NoError(t, err)
|
|
err = suite.app.StakingKeeper.SetValidatorByConsAddr(suite.ctx, validator)
|
|
require.NoError(t, err)
|
|
suite.app.StakingKeeper.SetValidator(suite.ctx, validator)
|
|
|
|
encodingConfig := encoding.MakeConfig(app.ModuleBasics)
|
|
suite.clientCtx = client.Context{}.WithTxConfig(encodingConfig.TxConfig)
|
|
suite.ethSigner = ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID())
|
|
suite.appCodec = encodingConfig.Marshaler
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) SetupTest() {
|
|
suite.DoSetupTest(suite.T())
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) EvmDenom() string {
|
|
ctx := sdk.WrapSDKContext(suite.ctx)
|
|
rsp, _ := suite.queryClient.Params(ctx, &types.QueryParamsRequest{})
|
|
return rsp.Params.EvmDenom
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
// DeployTestContract deploy a test erc20 contract and returns the contract address
|
|
func (suite *KeeperTestSuite) DeployTestContract(t require.TestingT, owner common.Address, supply *big.Int) common.Address {
|
|
ctx := sdk.WrapSDKContext(suite.ctx)
|
|
chainID := suite.app.EvmKeeper.ChainID()
|
|
|
|
ctorArgs, err := types.ERC20Contract.ABI.Pack("", owner, supply)
|
|
require.NoError(t, err)
|
|
|
|
data := append(types.ERC20Contract.Bin, ctorArgs...)
|
|
args, err := json.Marshal(&types.TransactionArgs{
|
|
From: &suite.address,
|
|
Data: (*hexutil.Bytes)(&data),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
res, err := suite.queryClient.EstimateGas(ctx, &types.EthCallRequest{
|
|
Args: args,
|
|
GasCap: uint64(config.DefaultGasCap),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
nonce := suite.app.EvmKeeper.GetNonce(suite.address)
|
|
|
|
var erc20DeployTx *types.MsgEthereumTx
|
|
if suite.dynamicTxFee {
|
|
erc20DeployTx = types.NewTxContract(
|
|
chainID,
|
|
nonce,
|
|
nil, // amount
|
|
res.Gas, // gasLimit
|
|
nil, // gasPrice
|
|
suite.app.FeeMarketKeeper.GetBaseFee(suite.ctx),
|
|
big.NewInt(1),
|
|
data, // input
|
|
ðtypes.AccessList{}, // accesses
|
|
)
|
|
} else {
|
|
erc20DeployTx = types.NewTxContract(
|
|
chainID,
|
|
nonce,
|
|
nil, // amount
|
|
res.Gas, // gasLimit
|
|
nil, // gasPrice
|
|
nil, nil,
|
|
data, // input
|
|
nil, // accesses
|
|
)
|
|
}
|
|
|
|
erc20DeployTx.From = suite.address.Hex()
|
|
err = erc20DeployTx.Sign(ethtypes.LatestSignerForChainID(chainID), suite.signer)
|
|
require.NoError(t, err)
|
|
rsp, err := suite.app.EvmKeeper.EthereumTx(ctx, erc20DeployTx)
|
|
require.NoError(t, err)
|
|
require.Empty(t, rsp.VmError)
|
|
return crypto.CreateAddress(suite.address, nonce)
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TransferERC20Token(t require.TestingT, contractAddr, from, to common.Address, amount *big.Int) *types.MsgEthereumTx {
|
|
ctx := sdk.WrapSDKContext(suite.ctx)
|
|
chainID := suite.app.EvmKeeper.ChainID()
|
|
|
|
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)
|
|
res, err := suite.queryClient.EstimateGas(ctx, &types.EthCallRequest{
|
|
Args: args,
|
|
GasCap: 25_000_000,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
nonce := suite.app.EvmKeeper.GetNonce(suite.address)
|
|
|
|
var ercTransferTx *types.MsgEthereumTx
|
|
if suite.dynamicTxFee {
|
|
ercTransferTx = types.NewTx(
|
|
chainID,
|
|
nonce,
|
|
&contractAddr,
|
|
nil,
|
|
res.Gas,
|
|
nil,
|
|
suite.app.FeeMarketKeeper.GetBaseFee(suite.ctx),
|
|
big.NewInt(1),
|
|
transferData,
|
|
ðtypes.AccessList{}, // accesses
|
|
)
|
|
} else {
|
|
ercTransferTx = types.NewTx(
|
|
chainID,
|
|
nonce,
|
|
&contractAddr,
|
|
nil,
|
|
res.Gas,
|
|
nil,
|
|
nil, nil,
|
|
transferData,
|
|
nil,
|
|
)
|
|
}
|
|
|
|
ercTransferTx.From = suite.address.Hex()
|
|
err = ercTransferTx.Sign(ethtypes.LatestSignerForChainID(chainID), suite.signer)
|
|
require.NoError(t, err)
|
|
rsp, err := suite.app.EvmKeeper.EthereumTx(ctx, ercTransferTx)
|
|
require.NoError(t, err)
|
|
require.Empty(t, rsp.VmError)
|
|
return ercTransferTx
|
|
}
|
|
|
|
// DeployTestMessageCall deploy a test erc20 contract and returns the contract address
|
|
func (suite *KeeperTestSuite) DeployTestMessageCall(t require.TestingT) common.Address {
|
|
ctx := sdk.WrapSDKContext(suite.ctx)
|
|
chainID := suite.app.EvmKeeper.ChainID()
|
|
|
|
data := types.TestMessageCall.Bin
|
|
args, err := json.Marshal(&types.TransactionArgs{
|
|
From: &suite.address,
|
|
Data: (*hexutil.Bytes)(&data),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
res, err := suite.queryClient.EstimateGas(ctx, &types.EthCallRequest{
|
|
Args: args,
|
|
GasCap: uint64(config.DefaultGasCap),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
nonce := suite.app.EvmKeeper.GetNonce(suite.address)
|
|
|
|
var erc20DeployTx *types.MsgEthereumTx
|
|
if suite.dynamicTxFee {
|
|
erc20DeployTx = types.NewTxContract(
|
|
chainID,
|
|
nonce,
|
|
nil, // amount
|
|
res.Gas, // gasLimit
|
|
nil, // gasPrice
|
|
suite.app.FeeMarketKeeper.GetBaseFee(suite.ctx),
|
|
big.NewInt(1),
|
|
data, // input
|
|
ðtypes.AccessList{}, // accesses
|
|
)
|
|
} else {
|
|
erc20DeployTx = types.NewTxContract(
|
|
chainID,
|
|
nonce,
|
|
nil, // amount
|
|
res.Gas, // gasLimit
|
|
nil, // gasPrice
|
|
nil, nil,
|
|
data, // input
|
|
nil, // accesses
|
|
)
|
|
}
|
|
|
|
erc20DeployTx.From = suite.address.Hex()
|
|
err = erc20DeployTx.Sign(ethtypes.LatestSignerForChainID(chainID), suite.signer)
|
|
require.NoError(t, err)
|
|
rsp, err := suite.app.EvmKeeper.EthereumTx(ctx, erc20DeployTx)
|
|
require.NoError(t, err)
|
|
require.Empty(t, rsp.VmError)
|
|
return crypto.CreateAddress(suite.address, nonce)
|
|
}
|
|
|
|
func TestKeeperTestSuite(t *testing.T) {
|
|
suite.Run(t, new(KeeperTestSuite))
|
|
}
|