0g-chain/x/evmutil/testutil/suite.go
drklee3 f757d7ab15
feat: upgrade to Cosmos v0.46 (#1477)
* Update cosmos-sdk to v0.45.10-kava

* Add RegisterNodeService to app

* Update cosmos proto files

* Update cosmos proto files

* Use tagged v0.45.10-kava-v0.19-0.21 cosmos version

* update x/auth/legacy to x/auth/migrations

* Delete rest packages and registration

* Remove rest from proposal handlers

* Remove legacy types referencing removed sdk types

* Remove legacy tx broadcast handler

* Update incentive staking hooks to return error

* Remove grpc replace directive, use new grpc version

* Fix storetypes import

* Update tally_handler with updated gov types

* Delete legacy types

* Use new gov default config

* Update RegisterTendermintService params

Signed-off-by: drklee3 <derrick@dlee.dev>

* Replace sdk.StoreKey with storetypes.StoreKey

* Replace sdk.Int#ToDec with sdk.NewDecFromInt

* Replace sdk.NewUintFromBigInt with sdkmath.NewUintFromBigInt

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update most intances of govtypes to govv1beta1

* Unpack coin slice for Coins#Sub and Coins#SafeSub

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update committee gov codec registration

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update migrate utils period_vesting Coins#Sub

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update Coin#Sub in community proposal handler

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update Coin#Sub, FundModuleAccount/FundAccount in banktestutil

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update community, earn, kavadist proposal gov registration

* Update evm cli client EthSecp256k1Type check

* AccAddressFromHex to AccAddressFromHexUnsafe

* Add mint DefaultInflationCalculationFn to earn test

* Update use of removed staking.NewHandler

* Rename FlagIAVLFastNode -> FlagDisableIAVLFastNode

* cmd: Update new snapshot app option

Signed-off-by: drklee3 <derrick@dlee.dev>

* cmd: Add tendermint default config, use cosmos rpc status command

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update ethermint import path

github.com/tharsis/ethermint -> github.com/evmos/ethermint

* Upgrade ibc-go to v6

* Update proto dependencies

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update Tally handler test with new gov types

* Update helpers.GenTx -> helpers.GenSignedMockTx

* Update evmkeeper.NewKeeper params

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update ante authz, tests

* Add feemarket transient key, pass subspaces to evm/feemarket keepers

* Update new ante decorators

* Add new addModuleInitFlags to server commands

* Pass codec to keyring.New in genaccounts

* Pass codec to client keys add

* Add SendCoins to evmutil bank_keeper

* Use github.com/cosmos/iavl@v0.19.5

* Add ante HandlerOptions

* Add unimplemented SendCoins to evmutil bank keeper

Ethermint x/evm does not use this method

* Update init-new-chain script to disable post-london blocks

* Modify test genesis states to append 1 validator

* Update tally handler test to use string values

* Prevent querying balance for empty sdk.AccAddress in auction bidding test

* Set default bond denom to ukava

* Remove overwritten bank genesis total supply in committee proposal test

Signed-off-by: drklee3 <derrick@dlee.dev>

* Use ukava for testing staked balance

* Disable minting in community proposal handler test

Previously stake denom is used, which resulted in 0 minted coins

* Update hard APYToSPY test expected value

Increased iterations in sdk.ApproxRoot, updated closer to real value

* Fix NewDecCoinsFromCoins bug in incentive collectDerivativeStakingRewards

* Allow bkava earn incentive test values to match within small margin for rounding

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update invalid denom in issuance message coin validation

Colons are now valid in denoms

Signed-off-by: drklee3 <derrick@dlee.dev>

* Remove genesis validator in incentive delegation tests

* Update pricefeed market test for invalid denom

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update incentive delegator rewards test without genesis validator

Signed-off-by: drklee3 <derrick@dlee.dev>

* Add validator to export test

* Clear bank state in minting tests

Signed-off-by: drklee3 <derrick@dlee.dev>

* Remove validator for no stake tally test

Signed-off-by: drklee3 <derrick@dlee.dev>

* Clear incentive state before InitGenesis in incentive genesis export test

* Update swagger

Signed-off-by: drklee3 <derrick@dlee.dev>

* Update ethermint version to match replaced version

* Remove legacy swagger

* Add NewEthEmitEventDecorator

* Remove redundant func for AddModuleInitFlags

* Remove unused addBankBalanceForAddress func

* Add SetIAVLLazyLoading option to app cmd

* Use legacy.RegisterAminoMsg for committee msg concrete registration

* Remove unnecessary Amino field

* Add evm_util bankkeeper SendCoins comment

* Update test method ResetBankState to DeleteGenesisValidatorCoins to be more clear

* Validate incentive params.RewardsPerSecond to be non-zero

* Validate swap pools to disallow colons in token denoms

* Register all legacy amino types on gov modulecdc

* Remove redundant Comittee interface registration

* Pin goleveldb to v1.0.1-0.20210819022825-2ae1ddf74ef7

Causes failed to load state at height errors

* Update ethermint to new pinned version with minGasPrices parse error fix

* Update cosmos fork dependcy commit to include reverted account constructor patch

* Update Cosmos v0.46.11 and cometbft v0.34.27

* Bump minimum go version to 1.19

* Update tendermint proto

* Update internal testnet genesis

* Move NewCanTransferDecorator before NewEthGasConsumeDecorator

* Add hard borrow store tests (#1514)

* add store tests for Borrow type

* refactor Deposit tests to match

* Fix old bep3 tests (#1515)

* Update Ethermint to 1b17445 to fix duplicate proto registration

* Add custom status command to use snake_case and stdout

* Add SetInflation helper

* Reduce ambiguity with evm CanSignEthTx error

* Remove init genesis validator claim in test

* Add disabled evmante.NewMinGasPriceDecorator with x/feemarket note

* chore: use tagged versions for Cosmos and Ethermint forks

* update kvtool & increase wait for ibc transfer test

---------

Signed-off-by: drklee3 <derrick@dlee.dev>
Co-authored-by: Ruaridh <rhuairahrighairidh@users.noreply.github.com>
Co-authored-by: Robert Pirtle <astropirtle@gmail.com>
2023-04-03 20:08:45 -04:00

405 lines
13 KiB
Go

package testutil
import (
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"math/big"
"reflect"
"time"
"github.com/cosmos/cosmos-sdk/baseapp"
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/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"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/evmos/ethermint/crypto/ethsecp256k1"
"github.com/evmos/ethermint/server/config"
etherminttests "github.com/evmos/ethermint/tests"
etherminttypes "github.com/evmos/ethermint/types"
evmtypes "github.com/evmos/ethermint/x/evm/types"
feemarkettypes "github.com/evmos/ethermint/x/feemarket/types"
"github.com/stretchr/testify/suite"
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"
tmtime "github.com/tendermint/tendermint/types/time"
"github.com/tendermint/tendermint/version"
"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/evmutil/keeper"
"github.com/kava-labs/kava/x/evmutil/types"
)
type Suite struct {
suite.Suite
App app.TestApp
Ctx sdk.Context
Address common.Address
BankKeeper bankkeeper.Keeper
AccountKeeper authkeeper.AccountKeeper
Keeper keeper.Keeper
EvmBankKeeper keeper.EvmBankKeeper
Addrs []sdk.AccAddress
EvmModuleAddr sdk.AccAddress
QueryClient types.QueryClient
QueryClientEvm evmtypes.QueryClient
Key1 *ethsecp256k1.PrivKey
Key1Addr types.InternalEVMAddress
Key2 *ethsecp256k1.PrivKey
}
func (suite *Suite) SetupTest() {
tApp := app.NewTestApp()
suite.Ctx = tApp.NewContext(true, tmproto.Header{Height: 1, Time: tmtime.Now()})
suite.App = tApp
suite.BankKeeper = tApp.GetBankKeeper()
suite.AccountKeeper = tApp.GetAccountKeeper()
suite.Keeper = tApp.GetEvmutilKeeper()
suite.EvmBankKeeper = keeper.NewEvmBankKeeper(tApp.GetEvmutilKeeper(), suite.BankKeeper, suite.AccountKeeper)
suite.EvmModuleAddr = suite.AccountKeeper.GetModuleAddress(evmtypes.ModuleName)
// test evm user keys that have no minting permissions
key1, err := ethsecp256k1.GenerateKey()
suite.Require().NoError(err)
suite.Key1 = key1
suite.Key1Addr = types.NewInternalEVMAddress(common.BytesToAddress(suite.Key1.PubKey().Address()))
suite.Key2, err = ethsecp256k1.GenerateKey()
suite.Require().NoError(err)
_, addrs := app.GeneratePrivKeyAddressPairs(4)
suite.Addrs = addrs
evmGenesis := evmtypes.DefaultGenesisState()
evmGenesis.Params.EvmDenom = "akava"
feemarketGenesis := feemarkettypes.DefaultGenesisState()
feemarketGenesis.Params.EnableHeight = 1
feemarketGenesis.Params.NoBaseFee = false
cdc := suite.App.AppCodec()
coins := sdk.NewCoins(sdk.NewInt64Coin("ukava", 1000_000_000_000_000_000))
authGS := app.NewFundedGenStateWithSameCoins(cdc, coins, []sdk.AccAddress{
sdk.AccAddress(suite.Key1.PubKey().Address()),
sdk.AccAddress(suite.Key2.PubKey().Address()),
})
gs := app.GenesisState{
evmtypes.ModuleName: cdc.MustMarshalJSON(evmGenesis),
feemarkettypes.ModuleName: cdc.MustMarshalJSON(feemarketGenesis),
}
suite.App.InitializeFromGenesisStates(authGS, gs)
// consensus key - needed to set up evm module
consPriv, err := ethsecp256k1.GenerateKey()
suite.Require().NoError(err)
consAddress := sdk.ConsAddress(consPriv.PubKey().Address())
// InitializeFromGenesisStates commits first block so we start at 2 here
suite.Ctx = suite.App.NewContext(false, tmproto.Header{
Height: suite.App.LastBlockHeight() + 1,
ChainID: "kavatest_1-1",
Time: time.Now().UTC(),
ProposerAddress: 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")),
})
// We need to set the validator as calling the EVM looks up the validator address
// https://github.com/evmos/ethermint/blob/f21592ebfe74da7590eb42ed926dae970b2a9a3f/x/evm/keeper/state_transition.go#L487
// evmkeeper.EVMConfig() will return error "failed to load evm config" if not set
acc := &etherminttypes.EthAccount{
BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(suite.Address.Bytes()), nil, 0, 0),
CodeHash: common.BytesToHash(crypto.Keccak256(nil)).String(),
}
suite.AccountKeeper.SetAccount(suite.Ctx, acc)
valAddr := sdk.ValAddress(suite.Address.Bytes())
validator, err := stakingtypes.NewValidator(valAddr, consPriv.PubKey(), stakingtypes.Description{})
suite.Require().NoError(err)
err = suite.App.GetStakingKeeper().SetValidatorByConsAddr(suite.Ctx, validator)
suite.Require().NoError(err)
suite.App.GetStakingKeeper().SetValidator(suite.Ctx, validator)
// add conversion pair for first module deployed contract to evmutil params
suite.Keeper.SetParams(suite.Ctx, types.NewParams(
types.NewConversionPairs(
types.NewConversionPair(
// First contract this module deploys
MustNewInternalEVMAddressFromString("0x15932E26f5BD4923d46a2b205191C4b5d5f43FE3"),
"erc20/usdc",
),
),
))
queryHelper := baseapp.NewQueryServerTestHelper(suite.Ctx, suite.App.InterfaceRegistry())
evmtypes.RegisterQueryServer(queryHelper, suite.App.GetEvmKeeper())
suite.QueryClientEvm = evmtypes.NewQueryClient(queryHelper)
types.RegisterQueryServer(queryHelper, keeper.NewQueryServerImpl(suite.App.GetEvmutilKeeper()))
suite.QueryClient = types.NewQueryClient(queryHelper)
// We need to commit so that the ethermint feemarket beginblock runs to set the minfee
// feeMarketKeeper.GetBaseFee() will return nil otherwise
suite.Commit()
}
func (suite *Suite) Commit() {
_ = suite.App.Commit()
header := suite.Ctx.BlockHeader()
header.Height += 1
suite.App.BeginBlock(abci.RequestBeginBlock{
Header: header,
})
// update ctx
suite.Ctx = suite.App.NewContext(false, header)
}
func (suite *Suite) FundAccountWithKava(addr sdk.AccAddress, coins sdk.Coins) {
ukava := coins.AmountOf("ukava")
if ukava.IsPositive() {
err := suite.App.FundAccount(suite.Ctx, addr, sdk.NewCoins(sdk.NewCoin("ukava", ukava)))
suite.Require().NoError(err)
}
akava := coins.AmountOf("akava")
if akava.IsPositive() {
err := suite.Keeper.SetBalance(suite.Ctx, addr, akava)
suite.Require().NoError(err)
}
}
func (suite *Suite) FundModuleAccountWithKava(moduleName string, coins sdk.Coins) {
ukava := coins.AmountOf("ukava")
if ukava.IsPositive() {
err := suite.App.FundModuleAccount(suite.Ctx, moduleName, sdk.NewCoins(sdk.NewCoin("ukava", ukava)))
suite.Require().NoError(err)
}
akava := coins.AmountOf("akava")
if akava.IsPositive() {
addr := suite.AccountKeeper.GetModuleAddress(moduleName)
err := suite.Keeper.SetBalance(suite.Ctx, addr, akava)
suite.Require().NoError(err)
}
}
func (suite *Suite) DeployERC20() types.InternalEVMAddress {
// make sure module account is created
// qq: any better ways to do this?
suite.App.FundModuleAccount(
suite.Ctx,
types.ModuleName,
sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(0))),
)
contractAddr, err := suite.Keeper.DeployTestMintableERC20Contract(suite.Ctx, "USDC", "USDC", uint8(18))
suite.Require().NoError(err)
suite.Require().Greater(len(contractAddr.Address), 0)
return contractAddr
}
func (suite *Suite) GetERC20BalanceOf(
contractAbi abi.ABI,
contractAddr types.InternalEVMAddress,
accountAddr types.InternalEVMAddress,
) *big.Int {
// Query ERC20.balanceOf()
addr := common.BytesToAddress(suite.Key1.PubKey().Address())
res, err := suite.QueryContract(
types.ERC20MintableBurnableContract.ABI,
addr,
suite.Key1,
contractAddr,
"balanceOf",
accountAddr.Address,
)
suite.Require().NoError(err)
suite.Require().Len(res, 1)
balance, ok := res[0].(*big.Int)
suite.Require().True(ok, "balanceOf should respond with *big.Int")
return balance
}
func (suite *Suite) QueryContract(
contractAbi abi.ABI,
from common.Address,
fromKey *ethsecp256k1.PrivKey,
contract types.InternalEVMAddress,
method string,
args ...interface{},
) ([]interface{}, error) {
// Pack query args
data, err := contractAbi.Pack(method, args...)
suite.Require().NoError(err)
// Send TX
res, err := suite.SendTx(contract, from, fromKey, data)
suite.Require().NoError(err)
// Check for VM errors and unpack returned data
switch res.VmError {
case vm.ErrExecutionReverted.Error():
response, err := abi.UnpackRevert(res.Ret)
suite.Require().NoError(err)
return nil, errors.New(response)
case "": // No error, continue
default:
panic(fmt.Sprintf("unhandled vm error response: %v", res.VmError))
}
// Unpack response
unpackedRes, err := contractAbi.Unpack(method, res.Ret)
suite.Require().NoErrorf(err, "failed to unpack method %v response", method)
return unpackedRes, nil
}
// SendTx submits a transaction to the block.
func (suite *Suite) SendTx(
contractAddr types.InternalEVMAddress,
from common.Address,
signerKey *ethsecp256k1.PrivKey,
transferData []byte,
) (*evmtypes.MsgEthereumTxResponse, error) {
ctx := sdk.WrapSDKContext(suite.Ctx)
chainID := suite.App.GetEvmKeeper().ChainID()
args, err := json.Marshal(&evmtypes.TransactionArgs{
To: &contractAddr.Address,
From: &from,
Data: (*hexutil.Bytes)(&transferData),
})
if err != nil {
return nil, err
}
gasRes, err := suite.QueryClientEvm.EstimateGas(ctx, &evmtypes.EthCallRequest{
Args: args,
GasCap: config.DefaultGasCap,
})
if err != nil {
return nil, err
}
nonce := suite.App.GetEvmKeeper().GetNonce(suite.Ctx, suite.Address)
baseFee := suite.App.GetFeeMarketKeeper().GetBaseFee(suite.Ctx)
suite.Require().NotNil(baseFee, "base fee is nil")
// Mint the max gas to the FeeCollector to ensure balance in case of refund
suite.MintFeeCollector(sdk.NewCoins(
sdk.NewCoin(
"ukava",
sdk.NewInt(baseFee.Int64()*int64(gasRes.Gas*2)),
)))
ercTransferTx := evmtypes.NewTx(
chainID,
nonce,
&contractAddr.Address,
nil, // amount
gasRes.Gas*2, // gasLimit, TODO: runs out of gas with just res.Gas, ex: estimated was 21572 but used 24814
nil, // gasPrice
suite.App.GetFeeMarketKeeper().GetBaseFee(suite.Ctx), // gasFeeCap
big.NewInt(1), // gasTipCap
transferData,
&ethtypes.AccessList{}, // accesses
)
ercTransferTx.From = hex.EncodeToString(signerKey.PubKey().Address())
err = ercTransferTx.Sign(ethtypes.LatestSignerForChainID(chainID), etherminttests.NewSigner(signerKey))
if err != nil {
return nil, err
}
rsp, err := suite.App.GetEvmKeeper().EthereumTx(ctx, ercTransferTx)
if err != nil {
return nil, err
}
// Do not check vm error here since we want to check for errors later
return rsp, nil
}
func (suite *Suite) MintFeeCollector(coins sdk.Coins) {
err := suite.App.FundModuleAccount(suite.Ctx, authtypes.FeeCollectorName, coins)
suite.Require().NoError(err)
}
// GetEvents returns emitted events on the sdk context
func (suite *Suite) GetEvents() sdk.Events {
return suite.Ctx.EventManager().Events()
}
// EventsContains asserts that the expected event is in the provided events
func (suite *Suite) EventsContains(events sdk.Events, expectedEvent sdk.Event) {
foundMatch := false
for _, event := range events {
if event.Type == expectedEvent.Type {
if reflect.DeepEqual(attrsToMap(expectedEvent.Attributes), attrsToMap(event.Attributes)) {
foundMatch = true
}
}
}
suite.Truef(foundMatch, "event of type %s not found or did not match", expectedEvent.Type)
}
// EventsDoNotContain asserts that the event is **not** is in the provided events
func (suite *Suite) EventsDoNotContain(events sdk.Events, eventType string) {
foundMatch := false
for _, event := range events {
if event.Type == eventType {
foundMatch = true
}
}
suite.Falsef(foundMatch, "event of type %s should not be found, but was found", eventType)
}
func attrsToMap(attrs []abci.EventAttribute) []sdk.Attribute {
out := []sdk.Attribute{}
for _, attr := range attrs {
out = append(out, sdk.NewAttribute(string(attr.Key), string(attr.Value)))
}
return out
}
// MustNewInternalEVMAddressFromString returns a new InternalEVMAddress from a
// hex string. This will panic if the input hex string is invalid.
func MustNewInternalEVMAddressFromString(addrStr string) types.InternalEVMAddress {
addr, err := types.NewInternalEVMAddressFromString(addrStr)
if err != nil {
panic(err)
}
return addr
}