Skip to content
This repository has been archived by the owner on Apr 4, 2024. It is now read-only.

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

Merged
merged 5 commits into from
Nov 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
82 changes: 82 additions & 0 deletions x/evm/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
14 changes: 8 additions & 6 deletions x/evm/keeper/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down