fix: don't revert gas refund logic when transaction reverted (#751)

* fix gas consumption when reverted

Apply suggestions from code review

changelog

* comments

* Update x/evm/keeper/state_transition.go

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
yihuang 2021-11-16 22:49:59 +08:00 committed by GitHub
parent e96a4b9f9d
commit b7e8dd8216
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 91 additions and 6 deletions

View File

@ -42,6 +42,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (evm, ante) [tharsis#620](https://github.com/tharsis/ethermint/pull/620) Add fee market field to EVM `Keeper` and `AnteHandler`.
* (all) [tharsis#231](https://github.com/tharsis/ethermint/pull/231) Bump go-ethereum version to [`v1.10.9`](https://github.com/ethereum/go-ethereum/releases/tag/v1.10.9)
* (ante) [tharsis#703](https://github.com/tharsis/ethermint/pull/703) Fix some fields in transaction are not authenticated by signature.
* (evm) [tharsis#751](https://github.com/tharsis/ethermint/pull/751) don't revert gas refund logic when transaction reverted
### Features

View File

@ -525,3 +525,85 @@ func (suite *EvmTestSuite) TestErrorWhenDeployContract() {
// TODO: snapshot checking
}
func (suite *EvmTestSuite) deployERC20Contract() common.Address {
k := suite.app.EvmKeeper
nonce := k.GetNonce(suite.from)
ctorArgs, err := types.ERC20Contract.ABI.Pack("", suite.from, big.NewInt(0))
suite.Require().NoError(err)
msg := ethtypes.NewMessage(
suite.from,
nil,
nonce,
big.NewInt(0),
2000000,
big.NewInt(1),
nil,
nil,
append(types.ERC20Contract.Bin, ctorArgs...),
nil,
true,
)
rsp, err := k.ApplyMessage(msg, nil, true)
suite.Require().NoError(err)
suite.Require().False(rsp.Failed())
return crypto.CreateAddress(suite.from, nonce)
}
// TestGasRefundWhenReverted check that when transaction reverted, gas refund should still work.
func (suite *EvmTestSuite) TestGasRefundWhenReverted() {
suite.SetupTest()
k := suite.app.EvmKeeper
// the bug only reproduce when there are hooks
k.SetHooks(&DummyHook{})
// add some fund to pay gas fee
k.AddBalance(suite.from, big.NewInt(10000000000))
contract := suite.deployERC20Contract()
// the call will fail because no balance
data, err := types.ERC20Contract.ABI.Pack("transfer", common.BigToAddress(big.NewInt(1)), big.NewInt(10))
suite.Require().NoError(err)
tx := types.NewTx(
suite.chainID,
k.GetNonce(suite.from),
&contract,
big.NewInt(0),
41000,
big.NewInt(1),
nil,
nil,
data,
nil,
)
tx.From = suite.from.String()
err = tx.Sign(suite.ethSigner, suite.signer)
suite.Require().NoError(err)
before := k.GetBalance(suite.from)
txData, err := types.UnpackTxData(tx.Data)
suite.Require().NoError(err)
_, err = k.DeductTxCostsFromUserBalance(suite.ctx, *tx, txData, "aphoton", true, true, true)
suite.Require().NoError(err)
res, err := k.EthereumTx(sdk.WrapSDKContext(suite.ctx), tx)
suite.Require().NoError(err)
suite.Require().True(res.Failed())
after := k.GetBalance(suite.from)
suite.Require().Equal(uint64(21861), res.GasUsed)
// check gas refund works
suite.Require().Equal(big.NewInt(21861), new(big.Int).Sub(before, after))
}
// DummyHook implements EvmHooks interface
type DummyHook struct{}
func (dh *DummyHook) PostTxProcessing(ctx sdk.Context, txHash common.Hash, logs []*ethtypes.Log) error {
return nil
}

View File

@ -218,12 +218,6 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT
return nil, stacktrace.Propagate(err, "failed to apply ethereum core message")
}
// refund gas prior to handling the vm error in order to match the Ethereum gas consumption instead of the default SDK one.
err = k.RefundGas(msg, msg.Gas()-res.GasUsed, cfg.Params.EvmDenom)
if err != nil {
return nil, stacktrace.Propagate(err, "failed to refund gas leftover gas to sender %s", msg.From())
}
res.Hash = txHash.Hex()
logs := k.GetTxLogsTransient(txHash)
@ -241,6 +235,14 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT
}
}
// change to original context
k.WithContext(ctx)
// refund gas according to Ethereum gas accounting rules.
if err := k.RefundGas(msg, msg.Gas()-res.GasUsed, cfg.Params.EvmDenom); err != nil {
return nil, stacktrace.Propagate(err, "failed to refund gas leftover gas to sender %s", msg.From())
}
if len(logs) > 0 {
res.Logs = types.NewLogsFromEth(logs)
// Update transient block bloom filter