Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: make gas refunds error free and panic safe #96

Merged
merged 3 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ func (app *MinitiaApp) SetCheckTx(handler blockchecktx.CheckTx) {

func (app *MinitiaApp) setPostHandler() {
app.SetPostHandler(posthandler.NewPostHandler(
app.AccountKeeper,
app.Logger(),
app.EVMKeeper,
))
}
Expand Down
47 changes: 35 additions & 12 deletions app/posthandler/gasrefund.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"fmt"

errorsmod "cosmossdk.io/errors"
"cosmossdk.io/log"
"cosmossdk.io/math"
storetypes "cosmossdk.io/store/types"

Expand All @@ -17,12 +18,14 @@
var _ sdk.PostDecorator = &GasRefundDecorator{}

type GasRefundDecorator struct {
ek EVMKeeper
logger log.Logger
ek EVMKeeper
}

func NewGasRefundDecorator(ek EVMKeeper) sdk.PostDecorator {
func NewGasRefundDecorator(logger log.Logger, ek EVMKeeper) sdk.PostDecorator {
logger = logger.With("module", "gas_refund")
return &GasRefundDecorator{
ek,
logger, ek,
}
}

Expand Down Expand Up @@ -70,20 +73,40 @@
sdk.NewAttribute(AttributeKeyCoins, coinsRefund.String()),
))

// TODO - should we charge gas for refund?
//
// for now, we use infinite gas meter to prevent out of gas error or inconsistency between
// used gas and refunded gas.
feeCollectorAddr := authtypes.NewModuleAddress(authtypes.FeeCollectorName)
err = erd.ek.ERC20Keeper().SendCoins(ctx.WithGasMeter(storetypes.NewInfiniteGasMeter()), feeCollectorAddr, feePayer, coinsRefund)
if err != nil {
return ctx, err
}
// conduct gas refund
erd.safeRefund(ctx, feePayer, coinsRefund)

Check warning on line 77 in app/posthandler/gasrefund.go

View check run for this annotation

Codecov / codecov/patch

app/posthandler/gasrefund.go#L76-L77

Added lines #L76 - L77 were not covered by tests
}

return next(ctx, tx, simulate, success)
}

func (erd *GasRefundDecorator) safeRefund(ctx sdk.Context, feePayer sdk.AccAddress, coinsRefund sdk.Coins) {
defer func() {
if r := recover(); r != nil {
switch r := r.(type) {
case storetypes.ErrorOutOfGas:
erd.logger.Error("failed to refund gas", "err", r)
default:
beer-1 marked this conversation as resolved.
Show resolved Hide resolved
panic(r)

Check warning on line 90 in app/posthandler/gasrefund.go

View check run for this annotation

Codecov / codecov/patch

app/posthandler/gasrefund.go#L83-L90

Added lines #L83 - L90 were not covered by tests
}
}
}()

// prepare context for refund operation
const gasLimitForRefund = 1_000_000
cacheCtx, commit := ctx.CacheContext()
cacheCtx = cacheCtx.WithGasMeter(storetypes.NewGasMeter(gasLimitForRefund))

feeCollectorAddr := authtypes.NewModuleAddress(authtypes.FeeCollectorName)
err := erd.ek.ERC20Keeper().SendCoins(cacheCtx, feeCollectorAddr, feePayer, coinsRefund)
if err != nil {
erd.logger.Error("failed to refund gas", "err", err)
return
}

Check warning on line 105 in app/posthandler/gasrefund.go

View check run for this annotation

Codecov / codecov/patch

app/posthandler/gasrefund.go#L96-L105

Added lines #L96 - L105 were not covered by tests

commit()

Check warning on line 107 in app/posthandler/gasrefund.go

View check run for this annotation

Codecov / codecov/patch

app/posthandler/gasrefund.go#L107

Added line #L107 was not covered by tests
}

beer-1 marked this conversation as resolved.
Show resolved Hide resolved
const (
EventTypeGasRefund = "gas_refund"
AttributeKeyGas = "gas"
Expand Down
6 changes: 3 additions & 3 deletions app/posthandler/posthandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ package posthandler
import (
"context"

"cosmossdk.io/log"
"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"

evmtypes "github.com/initia-labs/minievm/x/evm/types"
)

// NewPostHandler returns a new sdk.PostHandler that is composed of the sdk.ChainPostDecorators
func NewPostHandler(
ak authante.AccountKeeper,
logger log.Logger,
ek EVMKeeper,
) sdk.PostHandler {
return sdk.ChainPostDecorators(
NewGasRefundDecorator(ek),
NewGasRefundDecorator(logger, ek),
)
}

Expand Down
4 changes: 2 additions & 2 deletions x/evm/keeper/txutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ func (u *TxUtils) ConvertCosmosTxToEthereumTx(ctx context.Context, sdkTx sdk.Tx)
return nil, nil, err
}

if len(fees) > 0 && fees[0].Denom != params.FeeDenom {
if !(len(fees) == 0 || (len(fees) == 1 && fees[0].Denom == params.FeeDenom)) {
return nil, nil, nil
}

Expand Down Expand Up @@ -403,7 +403,7 @@ func (u *TxUtils) IsEthereumTx(ctx context.Context, sdkTx sdk.Tx) (bool, error)
return false, err
}

if len(fees) > 0 && fees[0].Denom != params.FeeDenom {
if !(len(fees) == 0 || (len(fees) == 1 && fees[0].Denom == params.FeeDenom)) {
return false, nil
}

Expand Down
Loading