laconicd/x/evm/keeper/utils_test.go
yihuang ccc6f5b53d
fix: use EffectiveGasPrice in ante handler for dynamic fee tx (#817)
* Use effectiveGasPrice in ante handler for dynamic fee tx

Closes: #814

Solution:
- use effectiveGasPrice when check minimal-gas-prices, and deduct fee in ante handler
- implement an EthMempoolFeeDecorator

* add effectiveGasPrice to tx receipt

* changelog

* fix unit test

* fix comments

* add comments

* Apply suggestions from code review

Co-authored-by: Thomas Nguy <81727899+thomas-nguy@users.noreply.github.com>

* review suggestions

Co-authored-by: Thomas Nguy <81727899+thomas-nguy@users.noreply.github.com>
Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
2021-12-15 02:17:03 +00:00

441 lines
12 KiB
Go

package keeper_test
import (
"math/big"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
ethparams "github.com/ethereum/go-ethereum/params"
evmkeeper "github.com/tharsis/ethermint/x/evm/keeper"
evmtypes "github.com/tharsis/ethermint/x/evm/types"
)
func (suite *KeeperTestSuite) TestCheckSenderBalance() {
hundredInt := sdk.NewInt(100)
zeroInt := sdk.ZeroInt()
oneInt := sdk.OneInt()
fiveInt := sdk.NewInt(5)
fiftyInt := sdk.NewInt(50)
negInt := sdk.NewInt(-10)
testCases := []struct {
name string
to string
gasLimit uint64
gasPrice *sdk.Int
gasFeeCap *big.Int
gasTipCap *big.Int
cost *sdk.Int
from string
accessList *ethtypes.AccessList
expectPass bool
dynamicTxFee bool
}{
{
name: "Enough balance",
to: suite.address.String(),
gasLimit: 10,
gasPrice: &oneInt,
cost: &oneInt,
from: suite.address.String(),
accessList: &ethtypes.AccessList{},
expectPass: true,
},
{
name: "Equal balance",
to: suite.address.String(),
gasLimit: 99,
gasPrice: &oneInt,
cost: &oneInt,
from: suite.address.String(),
accessList: &ethtypes.AccessList{},
expectPass: true,
},
{
name: "negative cost",
to: suite.address.String(),
gasLimit: 1,
gasPrice: &oneInt,
cost: &negInt,
from: suite.address.String(),
accessList: &ethtypes.AccessList{},
expectPass: false,
},
{
name: "Higher gas limit, not enough balance",
to: suite.address.String(),
gasLimit: 100,
gasPrice: &oneInt,
cost: &oneInt,
from: suite.address.String(),
accessList: &ethtypes.AccessList{},
expectPass: false,
},
{
name: "Higher gas price, enough balance",
to: suite.address.String(),
gasLimit: 10,
gasPrice: &fiveInt,
cost: &oneInt,
from: suite.address.String(),
accessList: &ethtypes.AccessList{},
expectPass: true,
},
{
name: "Higher gas price, not enough balance",
to: suite.address.String(),
gasLimit: 20,
gasPrice: &fiveInt,
cost: &oneInt,
from: suite.address.String(),
accessList: &ethtypes.AccessList{},
expectPass: false,
},
{
name: "Higher cost, enough balance",
to: suite.address.String(),
gasLimit: 10,
gasPrice: &fiveInt,
cost: &fiftyInt,
from: suite.address.String(),
accessList: &ethtypes.AccessList{},
expectPass: true,
},
{
name: "Higher cost, not enough balance",
to: suite.address.String(),
gasLimit: 10,
gasPrice: &fiveInt,
cost: &hundredInt,
from: suite.address.String(),
accessList: &ethtypes.AccessList{},
expectPass: false,
},
{
name: "Enough balance w/ dynamicTxFee",
to: suite.address.String(),
gasLimit: 10,
gasFeeCap: big.NewInt(1),
cost: &oneInt,
from: suite.address.String(),
accessList: &ethtypes.AccessList{},
expectPass: true,
dynamicTxFee: true,
},
{
name: "Equal balance w/ dynamicTxFee",
to: suite.address.String(),
gasLimit: 99,
gasFeeCap: big.NewInt(1),
cost: &oneInt,
from: suite.address.String(),
accessList: &ethtypes.AccessList{},
expectPass: true,
dynamicTxFee: true,
},
{
name: "negative cost w/ dynamicTxFee",
to: suite.address.String(),
gasLimit: 1,
gasFeeCap: big.NewInt(1),
cost: &negInt,
from: suite.address.String(),
accessList: &ethtypes.AccessList{},
expectPass: false,
dynamicTxFee: true,
},
{
name: "Higher gas limit, not enough balance w/ dynamicTxFee",
to: suite.address.String(),
gasLimit: 100,
gasFeeCap: big.NewInt(1),
cost: &oneInt,
from: suite.address.String(),
accessList: &ethtypes.AccessList{},
expectPass: false,
dynamicTxFee: true,
},
{
name: "Higher gas price, enough balance w/ dynamicTxFee",
to: suite.address.String(),
gasLimit: 10,
gasFeeCap: big.NewInt(5),
cost: &oneInt,
from: suite.address.String(),
accessList: &ethtypes.AccessList{},
expectPass: true,
dynamicTxFee: true,
},
{
name: "Higher gas price, not enough balance w/ dynamicTxFee",
to: suite.address.String(),
gasLimit: 20,
gasFeeCap: big.NewInt(5),
cost: &oneInt,
from: suite.address.String(),
accessList: &ethtypes.AccessList{},
expectPass: false,
dynamicTxFee: true,
},
{
name: "Higher cost, enough balance w/ dynamicTxFee",
to: suite.address.String(),
gasLimit: 10,
gasFeeCap: big.NewInt(5),
cost: &fiftyInt,
from: suite.address.String(),
accessList: &ethtypes.AccessList{},
expectPass: true,
dynamicTxFee: true,
},
{
name: "Higher cost, not enough balance w/ dynamicTxFee",
to: suite.address.String(),
gasLimit: 10,
gasFeeCap: big.NewInt(5),
cost: &hundredInt,
from: suite.address.String(),
accessList: &ethtypes.AccessList{},
expectPass: false,
dynamicTxFee: true,
},
}
suite.app.EvmKeeper.AddBalance(suite.address, hundredInt.BigInt())
balance := suite.app.EvmKeeper.GetBalance(suite.address)
suite.Require().Equal(balance, hundredInt.BigInt())
for i, tc := range testCases {
suite.Run(tc.name, func() {
to := common.HexToAddress(tc.from)
var amount, gasPrice, gasFeeCap, gasTipCap *big.Int
if tc.cost != nil {
amount = tc.cost.BigInt()
}
if tc.dynamicTxFee {
gasFeeCap = tc.gasFeeCap
if tc.gasTipCap == nil {
gasTipCap = oneInt.BigInt()
} else {
gasTipCap = tc.gasTipCap
}
} else {
if tc.gasPrice != nil {
gasPrice = tc.gasPrice.BigInt()
}
}
tx := evmtypes.NewTx(zeroInt.BigInt(), 1, &to, amount, tc.gasLimit, gasPrice, gasFeeCap, gasTipCap, nil, tc.accessList)
tx.From = tc.from
txData, _ := evmtypes.UnpackTxData(tx.Data)
err := evmkeeper.CheckSenderBalance(
suite.app.EvmKeeper.Ctx(),
suite.app.BankKeeper,
suite.address[:],
txData,
evmtypes.DefaultEVMDenom,
)
if tc.expectPass {
suite.Require().NoError(err, "valid test %d failed", i)
} else {
suite.Require().Error(err, "invalid test %d passed", i)
}
})
}
}
func (suite *KeeperTestSuite) TestDeductTxCostsFromUserBalance() {
hundredInt := sdk.NewInt(100)
zeroInt := sdk.ZeroInt()
oneInt := sdk.NewInt(1)
fiveInt := sdk.NewInt(5)
fiftyInt := sdk.NewInt(50)
// should be enough to cover all test cases
initBalance := sdk.NewInt((ethparams.InitialBaseFee + 10) * 105)
testCases := []struct {
name string
gasLimit uint64
gasPrice *sdk.Int
gasFeeCap *big.Int
gasTipCap *big.Int
cost *sdk.Int
accessList *ethtypes.AccessList
expectPass bool
dynamicTxFee bool
}{
{
name: "Enough balance",
gasLimit: 10,
gasPrice: &oneInt,
cost: &oneInt,
accessList: &ethtypes.AccessList{},
expectPass: true,
},
{
name: "Equal balance",
gasLimit: 100,
gasPrice: &oneInt,
cost: &oneInt,
accessList: &ethtypes.AccessList{},
expectPass: true,
},
{
name: "Higher gas limit, not enough balance",
gasLimit: 105,
gasPrice: &oneInt,
cost: &oneInt,
accessList: &ethtypes.AccessList{},
expectPass: false,
},
{
name: "Higher gas price, enough balance",
gasLimit: 20,
gasPrice: &fiveInt,
cost: &oneInt,
accessList: &ethtypes.AccessList{},
expectPass: true,
},
{
name: "Higher gas price, not enough balance",
gasLimit: 20,
gasPrice: &fiftyInt,
cost: &oneInt,
accessList: &ethtypes.AccessList{},
expectPass: false,
},
// This case is expected to be true because the fees can be deducted, but the tx
// execution is going to fail because there is no more balance to pay the cost
{
name: "Higher cost, enough balance",
gasLimit: 100,
gasPrice: &oneInt,
cost: &fiftyInt,
accessList: &ethtypes.AccessList{},
expectPass: true,
},
// testcases with dynamicTxFee enabled.
{
name: "Invalid gasFeeCap w/ dynamicTxFee",
gasLimit: 10,
gasFeeCap: big.NewInt(1),
gasTipCap: big.NewInt(1),
cost: &oneInt,
accessList: &ethtypes.AccessList{},
expectPass: false,
dynamicTxFee: true,
},
{
name: "empty tip fee is valid to deduct",
gasLimit: 10,
gasFeeCap: big.NewInt(ethparams.InitialBaseFee),
gasTipCap: big.NewInt(1),
cost: &oneInt,
accessList: &ethtypes.AccessList{},
expectPass: true,
dynamicTxFee: true,
},
{
name: "effectiveTip equal to gasTipCap",
gasLimit: 100,
gasFeeCap: big.NewInt(ethparams.InitialBaseFee + 2),
cost: &oneInt,
accessList: &ethtypes.AccessList{},
expectPass: true,
dynamicTxFee: true,
},
{
name: "effectiveTip equal to (gasFeeCap - baseFee)",
gasLimit: 105,
gasFeeCap: big.NewInt(ethparams.InitialBaseFee + 1),
gasTipCap: big.NewInt(2),
cost: &oneInt,
accessList: &ethtypes.AccessList{},
expectPass: true,
dynamicTxFee: true,
},
}
for i, tc := range testCases {
suite.Run(tc.name, func() {
suite.dynamicTxFee = tc.dynamicTxFee
suite.SetupTest()
var amount, gasPrice, gasFeeCap, gasTipCap *big.Int
if tc.cost != nil {
amount = tc.cost.BigInt()
}
if suite.dynamicTxFee {
if tc.gasFeeCap != nil {
gasFeeCap = tc.gasFeeCap
}
if tc.gasTipCap == nil {
gasTipCap = oneInt.BigInt()
} else {
gasTipCap = tc.gasTipCap
}
suite.app.EvmKeeper.AddBalance(suite.address, initBalance.BigInt())
balance := suite.app.EvmKeeper.GetBalance(suite.address)
suite.Require().Equal(balance, initBalance.BigInt())
} else {
if tc.gasPrice != nil {
gasPrice = tc.gasPrice.BigInt()
}
suite.app.EvmKeeper.AddBalance(suite.address, hundredInt.BigInt())
balance := suite.app.EvmKeeper.GetBalance(suite.address)
suite.Require().Equal(balance, hundredInt.BigInt())
}
tx := evmtypes.NewTx(zeroInt.BigInt(), 1, &suite.address, amount, tc.gasLimit, gasPrice, gasFeeCap, gasTipCap, nil, tc.accessList)
tx.From = suite.address.String()
txData, _ := evmtypes.UnpackTxData(tx.Data)
fees, err := suite.app.EvmKeeper.DeductTxCostsFromUserBalance(
suite.app.EvmKeeper.Ctx(),
*tx,
txData,
evmtypes.DefaultEVMDenom,
false,
false,
suite.dynamicTxFee, // london
)
if tc.expectPass {
suite.Require().NoError(err, "valid test %d failed", i)
if tc.dynamicTxFee {
baseFee := suite.app.FeeMarketKeeper.GetBaseFee(suite.ctx)
suite.Require().Equal(
fees,
sdk.NewCoins(
sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewIntFromBigInt(txData.EffectiveFee(baseFee))),
),
"valid test %d failed, fee value is wrong ", i,
)
} else {
suite.Require().Equal(
fees,
sdk.NewCoins(
sdk.NewCoin(evmtypes.DefaultEVMDenom, tc.gasPrice.Mul(sdk.NewIntFromUint64(tc.gasLimit))),
),
"valid test %d failed, fee value is wrong ", i,
)
}
} else {
suite.Require().Error(err, "invalid test %d passed", i)
suite.Require().Nil(fees, "invalid test %d passed. fees value must be nil", i)
}
})
}
suite.dynamicTxFee = false // reset flag
}