Skip to content

Commit

Permalink
fix(evm): consume gas in CallContractWithInput (#2180)
Browse files Browse the repository at this point in the history
* fix(evm): consume gas in CallContractWithInput

* Update CHANGELOG.md

* fix: gas consumption in ApplyEvmMsg

* fix: move commit after gas refund calculation so that we can return a valid evmResp

* refactor: GasToRefund

* fix: proper checking of err pointer nil

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fix: remove fullRefundLeftoverGas

* test: add gas assertions

* Update gas_fees.go

---------

Co-authored-by: Oleg Nikonychev <[email protected]>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Jan 28, 2025
1 parent bc29b88 commit 1ddbc2a
Show file tree
Hide file tree
Showing 12 changed files with 231 additions and 188 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ needed to include double quotes around the hexadecimal string.
- [#2173](https://github.com/NibiruChain/nibiru/pull/2173) - fix(evm): clear `StateDB` between calls
- [#2177](https://github.com/NibiruChain/nibiru/pull/2177) - fix(cmd): Continue from #2127 and unwire vesting flags and logic from genaccounts.go
- [#2176](https://github.com/NibiruChain/nibiru/pull/2176) - tests(evm): add dirty state tests from code4rena audit
- [#2180](https://github.com/NibiruChain/nibiru/pull/2180) - fix(evm): apply gas consumption across the entire EVM codebase at `CallContractWithInput`

#### Nibiru EVM | Before Audit 2 - 2024-12-06

Expand Down
10 changes: 6 additions & 4 deletions x/evm/keeper/call_contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (k Keeper) CallContractWithInput(
) (evmResp *evm.MsgEthereumTxResponse, err error) {
// This is a `defer` pattern to add behavior that runs in the case that the
// error is non-nil, creating a concise way to add extra information.
defer HandleOutOfGasPanic(&err, "CallContractError")
defer HandleOutOfGasPanic(&err, "CallContractError")()
nonce := k.GetAccNonce(ctx, fromAcc)

unusedBigInt := big.NewInt(0)
Expand All @@ -61,11 +61,13 @@ func (k Keeper) CallContractWithInput(
// sent by a user
txConfig := k.TxConfig(ctx, gethcommon.BigToHash(big.NewInt(0)))
evmResp, err = k.ApplyEvmMsg(
ctx, evmMsg, evmObj, evm.NewNoOpTracer(), commit, txConfig.TxHash, true,
ctx, evmMsg, evmObj, evm.NewNoOpTracer(), commit, txConfig.TxHash,
)
if evmResp != nil {
ctx.GasMeter().ConsumeGas(evmResp.GasUsed, "CallContractWithInput")
}
if err != nil {
err = errors.Wrap(err, "failed to apply ethereum core message")
return
return nil, errors.Wrap(err, "failed to apply ethereum core message")
}

if evmResp.Failed() {
Expand Down
12 changes: 6 additions & 6 deletions x/evm/keeper/erc20.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ func (e erc20Calls) loadERC20String(
if err != nil {
return out, err
}
res, err := e.Keeper.CallContractWithInput(
evmResp, err := e.Keeper.CallContractWithInput(
ctx,
evmObj,
evm.EVM_MODULE_ADDRESS,
Expand All @@ -211,13 +211,13 @@ func (e erc20Calls) loadERC20String(

erc20Val := new(ERC20String)
if err := erc20Abi.UnpackIntoInterface(
erc20Val, methodName, res.Ret,
erc20Val, methodName, evmResp.Ret,
); err == nil {
return erc20Val.Value, err
}

erc20Bytes32Val := new(ERC20Bytes32)
if err := erc20Abi.UnpackIntoInterface(erc20Bytes32Val, methodName, res.Ret); err == nil {
if err := erc20Abi.UnpackIntoInterface(erc20Bytes32Val, methodName, evmResp.Ret); err == nil {
return bytes32ToString(erc20Bytes32Val.Value), nil
}

Expand All @@ -239,7 +239,7 @@ func (e erc20Calls) loadERC20Uint8(
if err != nil {
return out, err
}
res, err := e.Keeper.CallContractWithInput(
evmResp, err := e.Keeper.CallContractWithInput(
ctx,
evmObj,
evm.EVM_MODULE_ADDRESS,
Expand All @@ -254,14 +254,14 @@ func (e erc20Calls) loadERC20Uint8(

erc20Val := new(ERC20Uint8)
if err := erc20Abi.UnpackIntoInterface(
erc20Val, methodName, res.Ret,
erc20Val, methodName, evmResp.Ret,
); err == nil {
return erc20Val.Value, err
}

erc20Uint256Val := new(ERC20BigInt)
if err := erc20Abi.UnpackIntoInterface(
erc20Uint256Val, methodName, res.Ret,
erc20Uint256Val, methodName, evmResp.Ret,
); err == nil {
// We can safely cast to uint8 because it's nonsense for decimals to be larger than 255
return uint8(erc20Uint256Val.Value.Uint64()), err
Expand Down
4 changes: 1 addition & 3 deletions x/evm/keeper/funtoken_from_coin.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func (k *Keeper) deployERC20ForBankCoin(
k.Bank.StateDB = nil
}()
evmObj := k.NewEVM(ctx, evmMsg, evmCfg, nil /*tracer*/, stateDB)
evmResp, err := k.CallContractWithInput(
_, err = k.CallContractWithInput(
ctx, evmObj, evm.EVM_MODULE_ADDRESS, nil, true /*commit*/, input, Erc20GasLimitDeploy,
)
if err != nil {
Expand All @@ -114,7 +114,5 @@ func (k *Keeper) deployERC20ForBankCoin(
return gethcommon.Address{}, errors.Wrap(err, "failed to commit stateDB")
}

ctx.GasMeter().ConsumeGas(evmResp.GasUsed, "deploy erc20 funtoken contract")

return erc20Addr, nil
}
34 changes: 31 additions & 3 deletions x/evm/keeper/funtoken_from_coin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func (s *FunTokenFromCoinSuite) TestCreateFunTokenFromCoin() {
})

s.Run("happy: CreateFunToken for the bank coin", func() {
deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
s.Require().NoError(testapp.FundAccount(
deps.App.BankKeeper,
deps.Ctx,
Expand All @@ -97,6 +98,7 @@ func (s *FunTokenFromCoinSuite) TestCreateFunTokenFromCoin() {
},
)
s.Require().NoError(err)
s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed())

s.Equal(
createFuntokenResp.FuntokenMapping,
Expand Down Expand Up @@ -167,6 +169,7 @@ func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() {
funToken := s.fundAndCreateFunToken(deps, 100)

s.T().Log("Convert bank coin to erc-20")
deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
_, err := deps.EvmKeeper.ConvertCoinToEvm(
sdk.WrapSDKContext(deps.Ctx),
&evm.MsgConvertCoinToEvm{
Expand All @@ -178,6 +181,7 @@ func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() {
},
)
s.Require().NoError(err)
s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed())

s.T().Log("Check typed event")
testutil.RequireContainsTypedEvent(
Expand Down Expand Up @@ -226,6 +230,7 @@ func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() {
deps.Sender.NibiruAddr.String(),
)
s.Require().NoError(err)
deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
evmObj, _ = deps.NewEVM()
_, err = deps.EvmKeeper.CallContractWithInput(
deps.Ctx,
Expand All @@ -237,6 +242,7 @@ func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() {
evmtest.FunTokenGasLimitSendToEvm,
)
s.Require().NoError(err)
s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed())

// Check 1: module balance
moduleBalance = deps.App.BankKeeper.GetBalance(deps.Ctx, authtypes.NewModuleAddress(evm.ModuleName), evm.EVMBankDenom)
Expand All @@ -252,6 +258,7 @@ func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() {
s.Require().Equal("0", balance.String())

s.T().Log("sad: Convert more erc-20 to back to bank coin, insufficient funds")
deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
evmObj, _ = deps.NewEVM()
_, err = deps.EvmKeeper.CallContractWithInput(
deps.Ctx,
Expand All @@ -263,6 +270,7 @@ func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() {
evmtest.FunTokenGasLimitSendToEvm,
)
s.Require().ErrorContains(err, "transfer amount exceeds balance")
s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed())
}

// TestNativeSendThenPrecompileSend tests a race condition where the state DB
Expand Down Expand Up @@ -362,6 +370,7 @@ func (s *FunTokenFromCoinSuite) TestNativeSendThenPrecompileSend() {
newSendAmtSendToBank, /*amount*/
)
s.Require().NoError(err)
deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
evmObj, _ = deps.NewEVM()
evmResp, err := deps.EvmKeeper.CallContractWithInput(
deps.Ctx,
Expand All @@ -373,6 +382,9 @@ func (s *FunTokenFromCoinSuite) TestNativeSendThenPrecompileSend() {
evmtest.FunTokenGasLimitSendToEvm,
)
s.Require().NoError(err)
s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed())
s.Require().NotZero(evmResp.GasUsed)
s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM")
s.Empty(evmResp.VmError)
gasUsedFor2Ops := evmResp.GasUsed

Expand Down Expand Up @@ -404,6 +416,7 @@ func (s *FunTokenFromCoinSuite) TestNativeSendThenPrecompileSend() {
newSendAmtSendToBank, /*amount*/
)
s.Require().NoError(err)
deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
evmObj, _ = deps.NewEVM()
evmResp, err = deps.EvmKeeper.CallContractWithInput(
deps.Ctx,
Expand All @@ -415,6 +428,9 @@ func (s *FunTokenFromCoinSuite) TestNativeSendThenPrecompileSend() {
evmtest.DefaultEthCallGasLimit,
)
s.Require().NoError(err)
s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed())
s.Require().NotZero(evmResp.GasUsed)
s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM")
s.Empty(evmResp.VmError)
gasUsedFor1Op := evmResp.GasUsed

Expand Down Expand Up @@ -517,8 +533,9 @@ func (s *FunTokenFromCoinSuite) TestERC20TransferThenPrecompileSend() {
big.NewInt(9e6), /*amount*/
)
s.Require().NoError(err)
deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
evmObj, _ = deps.NewEVM()
_, err = deps.EvmKeeper.CallContractWithInput(
evmResp, err := deps.EvmKeeper.CallContractWithInput(
deps.Ctx,
evmObj,
deps.Sender.EthAddr, // from
Expand All @@ -528,6 +545,9 @@ func (s *FunTokenFromCoinSuite) TestERC20TransferThenPrecompileSend() {
10_000_000, // gas limit
)
s.Require().NoError(err)
s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed())
s.Require().NotZero(evmResp.GasUsed)
s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM")

evmtest.FunTokenBalanceAssert{
FunToken: funToken,
Expand Down Expand Up @@ -620,6 +640,7 @@ func (s *FunTokenFromCoinSuite) TestPrecompileSelfCallRevert() {
charles := evmtest.NewEthPrivAcc()

s.T().Log("call test contract")
deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
evmObj, _ = deps.NewEVM()
contractInput, err := embeds.SmartContract_TestPrecompileSelfCallRevert.ABI.Pack(
"selfCallTransferFunds",
Expand All @@ -629,7 +650,7 @@ func (s *FunTokenFromCoinSuite) TestPrecompileSelfCallRevert() {
big.NewInt(9e6),
)
s.Require().NoError(err)
_, err = deps.EvmKeeper.CallContractWithInput(
evpResp, err := deps.EvmKeeper.CallContractWithInput(
deps.Ctx,
evmObj,
deps.Sender.EthAddr,
Expand All @@ -639,6 +660,9 @@ func (s *FunTokenFromCoinSuite) TestPrecompileSelfCallRevert() {
evmtest.FunTokenGasLimitSendToEvm,
)
s.Require().NoError(err)
s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed())
s.Require().NotZero(evpResp.GasUsed)
s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evpResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM")

evmtest.FunTokenBalanceAssert{
FunToken: funToken,
Expand Down Expand Up @@ -726,8 +750,9 @@ func (s *FunTokenFromCoinSuite) TestPrecompileSendToBankThenErc20Transfer() {
"attack",
)
s.Require().NoError(err)
deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
evmObj, _ := deps.NewEVM()
_, err = deps.EvmKeeper.CallContractWithInput(
evpResp, err := deps.EvmKeeper.CallContractWithInput(
deps.Ctx,
evmObj,
deps.Sender.EthAddr,
Expand All @@ -737,6 +762,9 @@ func (s *FunTokenFromCoinSuite) TestPrecompileSendToBankThenErc20Transfer() {
evmtest.FunTokenGasLimitSendToEvm,
)
s.Require().ErrorContains(err, "execution reverted")
s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed())
s.Require().NotZero(evpResp.GasUsed)
s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evpResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM")

evmtest.FunTokenBalanceAssert{
FunToken: funToken,
Expand Down
Loading

0 comments on commit 1ddbc2a

Please sign in to comment.