foundry icon indicating copy to clipboard operation
foundry copied to clipboard

`vm.resumeGasMetering` can cause tests with expected reverts to fail with an `OutOfGas` error

Open emo-eth opened this issue 2 years ago • 1 comments

Component

Forge

Have you ensured that all of these are up to date?

  • [X] Foundry
  • [X] Foundryup

What version of Foundry are you on?

forge 0.2.0 (6672134 2023-08-08T00:21:59.824374000Z)

What command(s) is the bug in?

forge test

Operating System

macOS (Apple Silicon)

Describe the bug

After calling vm.pauseGasMetering, calling vm.resumeGasMetering within a reverting external call causes tests to fail with an OutOfGas error.

There seem to broadly be inconsistencies and unexpected behavior with the GasMetering functions; see #5491 as possibly related.

A reproduction, which eliminates self-external-calls as the issue.


import {Test, Vm} from "forge-std/Test.sol";

contract RevertingExternalContract {
    error MyError();

    Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));

    function externalReverts() external {
        vm.resumeGasMetering();
        revert MyError();
    }
}

contract MeteringTest is Test {
    error MyError();

    RevertingExternalContract externalContract;

    function setUp() public {
        externalContract = new RevertingExternalContract();
    }

    function testSelfNormalRevert() public {
        vm.expectRevert(MeteringTest.MyError.selector);
        this.selfReverts();
    }

    function testSelfMeteringRevert() public {
        vm.pauseGasMetering();
        vm.expectRevert(MeteringTest.MyError.selector);
        this.selfReverts();
    }

    function testExternalNormalRevert() public {
        vm.expectRevert(RevertingExternalContract.MyError.selector);
        externalContract.externalReverts();
    }

    function testExternalMeteringRevert() public {
        vm.pauseGasMetering();
        vm.expectRevert(RevertingExternalContract.MyError.selector);
        externalContract.externalReverts();
    }

    function testExternalMeteringManualCheck() public {
        vm.pauseGasMetering();
        (bool success,) = address(this).call(abi.encodeWithSelector(this.selfReverts.selector));
        assertEq(success, false);
    }

    function selfReverts() external {
        vm.resumeGasMetering();
        revert MyError();
    }
}

Output of running these tests:

[PASS] testSelfNormalRevert() (gas: 3953)
Test result: FAILED. 2 passed; 3 failed; 0 skipped; finished in 402.42µs
Ran 1 test suites: 2 tests passed, 3 failed, 0 skipped (5 total tests)

Failing tests:
Encountered 3 failing tests in test/Metering.t.sol:MeteringTest
[FAIL. Reason: EvmError: OutOfGas] testExternalMeteringManualCheck() (gas: 9223372036854754743)
[FAIL. Reason: EvmError: OutOfGas] testExternalMeteringRevert() (gas: 9223372036854754743)
[FAIL. Reason: EvmError: OutOfGas] testSelfMeteringRevert() (gas: 9223372036854754743)

emo-eth avatar Aug 08 '23 16:08 emo-eth

Unable to reproduce the OutOfGas error but am able to reproduce the huge gas consumption described in https://github.com/foundry-rs/foundry/issues/5491

Ran 5 tests for test/Metering.t.sol:MeteringTest
[PASS] testExternalMeteringManualCheck() (gas: 18302628885633699948)
[PASS] testExternalMeteringRevert() (gas: 18302628885633699399)
[PASS] testExternalNormalRevert() (gas: 8627)
[PASS] testSelfMeteringRevert() (gas: 18302628885633699354)
[PASS] testSelfNormalRevert() (gas: 4005)
Suite result: ok. 5 passed; 0 failed; 0 skipped; finished in 3.72ms (611.34µs CPU time)

With the --isolate flag

Ran 5 tests for test/Metering.t.sol:MeteringTest
[PASS] testExternalMeteringManualCheck() (gas: 6248)
[PASS] testExternalMeteringRevert() (gas: 3200)
[PASS] testExternalNormalRevert() (gas: 32191)
[PASS] testSelfMeteringRevert() (gas: 3156)
[PASS] testSelfNormalRevert() (gas: 27569)

Leaving the open as it is clear this kind of gas consumption could lead to and OutOfGas error

zerosnacks avatar Jun 28 '24 10:06 zerosnacks