Skip to content

Commit be00d15

Browse files
author
yihuang
authored
Problem: fee deduction not compatible with parallel execution (#447)
* Problem: fee deduction not compatible with parallel execution Solution: - use virtual send * Update CHANGELOG.md Signed-off-by: yihuang <huang@crypto.com> * fix build * fix test * cleanup --------- Signed-off-by: yihuang <huang@crypto.com>
1 parent 59586a5 commit be00d15

File tree

9 files changed

+67
-9
lines changed

9 files changed

+67
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
4444
* (rpc) [#434](https://github.com/crypto-org-chain/ethermint/pull/434) No need gasPrice when patch gasUsed for `eth_getTransactionReceipt`.
4545
* (rpc) [#439](https://github.com/crypto-org-chain/ethermint/pull/439), [#441](https://github.com/crypto-org-chain/ethermint/pull/441) Align trace response for failed tx with go-ethereum.
4646
* (statedb) [#446](https://github.com/crypto-org-chain/ethermint/pull/446) Re-use the cache store implementation with sdk.
47+
* (evm) [#447](https://github.com/crypto-org-chain/ethermint/pull/447) Deduct fee through virtual bank transfer.
4748

4849
### State Machine Breaking
4950

app/ante/nativefee.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/cosmos/cosmos-sdk/x/auth/types"
1313

1414
evmkeeper "github.com/evmos/ethermint/x/evm/keeper"
15+
evmtypes "github.com/evmos/ethermint/x/evm/types"
1516
)
1617

1718
// DeductFeeDecorator deducts fees from the fee payer. The fee payer is the fee granter (if specified) or first signer of the tx.
@@ -20,12 +21,12 @@ import (
2021
// CONTRACT: Tx must implement FeeTx interface to use DeductFeeDecorator
2122
type DeductFeeDecorator struct {
2223
accountKeeper ante.AccountKeeper
23-
bankKeeper types.BankKeeper
24+
bankKeeper evmtypes.BankKeeper
2425
feegrantKeeper ante.FeegrantKeeper
2526
txFeeChecker ante.TxFeeChecker
2627
}
2728

28-
func NewDeductFeeDecorator(ak ante.AccountKeeper, bk types.BankKeeper, fk ante.FeegrantKeeper, tfc ante.TxFeeChecker) DeductFeeDecorator {
29+
func NewDeductFeeDecorator(ak ante.AccountKeeper, bk evmtypes.BankKeeper, fk ante.FeegrantKeeper, tfc ante.TxFeeChecker) DeductFeeDecorator {
2930
if tfc == nil {
3031
tfc = checkTxFeeWithValidatorMinGasPrices
3132
}

app/app.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,7 @@ func NewEthermintApp(
662662

663663
// NOTE: fee market module must go last in order to retrieve the block gas used.
664664
app.ModuleManager.SetOrderEndBlockers(
665+
banktypes.ModuleName,
665666
crisistypes.ModuleName,
666667
govtypes.ModuleName,
667668
stakingtypes.ModuleName,
@@ -672,7 +673,6 @@ func NewEthermintApp(
672673
ibcexported.ModuleName,
673674
ibctransfertypes.ModuleName,
674675
authtypes.ModuleName,
675-
banktypes.ModuleName,
676676
distrtypes.ModuleName,
677677
slashingtypes.ModuleName,
678678
minttypes.ModuleName,
@@ -1033,6 +1033,23 @@ func (app *EthermintApp) RegisterNodeService(clientCtx client.Context, cfg confi
10331033
node.RegisterNodeService(clientCtx, app.GRPCQueryRouter(), cfg)
10341034
}
10351035

1036+
// GetStoreKey is used by unit test
1037+
func (app *EthermintApp) GetStoreKey(name string) storetypes.StoreKey {
1038+
key, ok := app.keys[name]
1039+
if ok {
1040+
return key
1041+
}
1042+
tkey, ok := app.tkeys[name]
1043+
if ok {
1044+
return tkey
1045+
}
1046+
mkey, ok := app.memKeys[name]
1047+
if ok {
1048+
return mkey
1049+
}
1050+
return app.okeys[name]
1051+
}
1052+
10361053
// RegisterSwaggerAPI registers swagger route with API Server
10371054
func RegisterSwaggerAPI(_ client.Context, rtr *mux.Router) {
10381055
root, err := fs.Sub(docs.SwaggerUI, "swagger-ui")

testutil/base_test_suite.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package testutil
22

33
import (
4+
"encoding/binary"
45
"encoding/json"
56
"math/big"
67
"time"
78

89
coreheader "cosmossdk.io/core/header"
910
sdkmath "cosmossdk.io/math"
11+
storetypes "cosmossdk.io/store/types"
1012
abci "github.com/cometbft/cometbft/abci/types"
1113
tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
1214
"github.com/cosmos/cosmos-sdk/baseapp"
@@ -17,6 +19,7 @@ import (
1719
sdk "github.com/cosmos/cosmos-sdk/types"
1820
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
1921
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
22+
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
2023
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
2124
"github.com/ethereum/go-ethereum/common"
2225
"github.com/ethereum/go-ethereum/common/hexutil"
@@ -41,6 +44,30 @@ type BaseTestSuite struct {
4144
App *app.EthermintApp
4245
}
4346

47+
func (suite *BaseTestSuite) MintFeeCollectorVirtual(coins sdk.Coins) {
48+
// add some virtual balance to the fee collector for refunding
49+
addVirtualCoins(
50+
suite.Ctx.ObjectStore(suite.App.GetStoreKey(banktypes.ObjectStoreKey)),
51+
suite.Ctx.TxIndex(),
52+
authtypes.NewModuleAddress(authtypes.FeeCollectorName),
53+
coins,
54+
)
55+
}
56+
57+
func addVirtualCoins(store storetypes.ObjKVStore, txIndex int, addr sdk.AccAddress, amt sdk.Coins) {
58+
key := make([]byte, len(addr)+8)
59+
copy(key, addr)
60+
binary.BigEndian.PutUint64(key[len(addr):], uint64(txIndex))
61+
62+
var coins sdk.Coins
63+
value := store.Get(key)
64+
if value != nil {
65+
coins = value.(sdk.Coins)
66+
}
67+
coins = coins.Add(amt...)
68+
store.Set(key, coins)
69+
}
70+
4471
func (suite *BaseTestSuite) SetupTest() {
4572
suite.SetupTestWithCb(suite.T(), nil)
4673
}

x/evm/handler_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,10 @@ func TestHandlerTestSuite(t *testing.T) {
4040
}
4141

4242
func (suite *HandlerTestSuite) SetupTest() {
43+
coins := sdk.NewCoins(sdk.NewCoin(types.DefaultEVMDenom, sdkmath.NewInt(100000000000000)))
44+
4345
t := suite.T()
4446
suite.SetupTestWithCb(t, func(app *app.EthermintApp, genesis app.GenesisState) app.GenesisState {
45-
coins := sdk.NewCoins(sdk.NewCoin(types.DefaultEVMDenom, sdkmath.NewInt(100000000000000)))
4647
b32address := sdk.MustBech32ifyAddressBytes(sdk.GetConfig().GetBech32AccountAddrPrefix(), suite.ConsPubKey.Address().Bytes())
4748
balances := []banktypes.Balance{
4849
{
@@ -72,6 +73,10 @@ func (suite *HandlerTestSuite) SetupTest() {
7273
genesis[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(&authGenesis)
7374
return genesis
7475
})
76+
77+
// add some virtual balance to the fee collector for refunding
78+
suite.MintFeeCollectorVirtual(coins)
79+
7580
suite.ethSigner = ethtypes.LatestSignerForChainID(suite.App.EvmKeeper.ChainID())
7681
}
7782

x/evm/keeper/gas.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func (k *Keeper) RefundGas(ctx sdk.Context, msg core.Message, leftoverGas uint64
5858

5959
// refund to sender from the fee collector module account, which is the escrow account in charge of collecting tx fees
6060

61-
err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, authtypes.FeeCollectorName, msg.From.Bytes(), refundedCoins)
61+
err := k.bankKeeper.SendCoinsFromModuleToAccountVirtual(ctx, authtypes.FeeCollectorName, msg.From.Bytes(), refundedCoins)
6262
if err != nil {
6363
err = errorsmod.Wrapf(errortypes.ErrInsufficientFunds, "fee collector account failed to refund fees: %s", err.Error())
6464
return errorsmod.Wrapf(err, "failed to refund %d leftover gas (%s)", leftoverGas, refundedCoins.String())

x/evm/keeper/state_transition_test.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,10 @@ type StateTransitionTestSuite struct {
4040
}
4141

4242
func (suite *StateTransitionTestSuite) SetupTest() {
43+
coins := sdk.NewCoins(sdk.NewCoin(types.DefaultEVMDenom, sdkmath.NewInt(int64(params.TxGas)-1)))
44+
4345
t := suite.T()
44-
suite.EVMTestSuiteWithAccountAndQueryClient.SetupTestWithCb(t, func(a *app.EthermintApp, genesis app.GenesisState) app.GenesisState {
46+
suite.SetupTestWithCb(t, func(a *app.EthermintApp, genesis app.GenesisState) app.GenesisState {
4547
feemarketGenesis := feemarkettypes.DefaultGenesisState()
4648
feemarketGenesis.Params.NoBaseFee = true
4749
genesis[feemarkettypes.ModuleName] = a.AppCodec().MustMarshalJSON(feemarketGenesis)
@@ -57,7 +59,6 @@ func (suite *StateTransitionTestSuite) SetupTest() {
5759
genesis[authtypes.ModuleName] = a.AppCodec().MustMarshalJSON(&authGenesis)
5860
if suite.mintFeeCollector {
5961
// mint some coin to fee collector
60-
coins := sdk.NewCoins(sdk.NewCoin(types.DefaultEVMDenom, sdkmath.NewInt(int64(params.TxGas)-1)))
6162
balances := []banktypes.Balance{
6263
{
6364
Address: suite.App.AccountKeeper.GetModuleAddress(authtypes.FeeCollectorName).String(),
@@ -73,6 +74,10 @@ func (suite *StateTransitionTestSuite) SetupTest() {
7374
}
7475
return genesis
7576
})
77+
78+
if suite.mintFeeCollector {
79+
suite.MintFeeCollectorVirtual(coins)
80+
}
7681
}
7782

7883
func TestStateTransitionTestSuite(t *testing.T) {

x/evm/keeper/utils.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,12 +161,12 @@ func CheckSenderBalance(
161161
}
162162

163163
// DeductFees deducts fees from the given account.
164-
func DeductFees(bankKeeper authtypes.BankKeeper, ctx sdk.Context, acc sdk.AccountI, fees sdk.Coins) error {
164+
func DeductFees(bankKeeper types.BankKeeper, ctx sdk.Context, acc sdk.AccountI, fees sdk.Coins) error {
165165
if !fees.IsValid() {
166166
return errorsmod.Wrapf(errortypes.ErrInsufficientFee, "invalid fee amount: %s", fees)
167167
}
168168

169-
err := bankKeeper.SendCoinsFromAccountToModule(ctx, acc.GetAddress(), authtypes.FeeCollectorName, fees)
169+
err := bankKeeper.SendCoinsFromAccountToModuleVirtual(ctx, acc.GetAddress(), authtypes.FeeCollectorName, fees)
170170
if err != nil {
171171
return errorsmod.Wrapf(errortypes.ErrInsufficientFunds, err.Error())
172172
}

x/evm/types/interfaces.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ type BankKeeper interface {
4747
authtypes.BankKeeper
4848
GetBalance(ctx context.Context, addr sdk.AccAddress, denom string) sdk.Coin
4949
SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
50+
SendCoinsFromModuleToAccountVirtual(ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
51+
SendCoinsFromAccountToModuleVirtual(ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
5052
MintCoins(ctx context.Context, moduleName string, amt sdk.Coins) error
5153
BurnCoins(ctx context.Context, moduleName string, amt sdk.Coins) error
5254
BlockedAddr(addr sdk.AccAddress) bool

0 commit comments

Comments
 (0)