Commit 10c76a2f authored by Inphi's avatar Inphi Committed by GitHub

ctb: fix more invariant flakes (#9352)

* ctb: fix more invariant flakes

* add invariant docs

* exclude deploy contracts

* remove deployconfig from invariant exclusion list
parent 1882309d
# `AddressAliasHelper` Invariants
## Address aliases are always able to be undone.
**Test:** [`AddressAliasHelper.t.sol#L46`](../test/invariants/AddressAliasHelper.t.sol#L46)
**Test:** [`AddressAliasHelper.t.sol#L48`](../test/invariants/AddressAliasHelper.t.sol#L48)
Asserts that an address that has been aliased with `applyL1ToL2Alias` can always be unaliased with `undoL1ToL2Alias`.
\ No newline at end of file
# `Burn.Eth` Invariants
## `eth(uint256)` always burns the exact amount of eth passed.
**Test:** [`Burn.Eth.t.sol#L64`](../test/invariants/Burn.Eth.t.sol#L64)
**Test:** [`Burn.Eth.t.sol#L66`](../test/invariants/Burn.Eth.t.sol#L66)
Asserts that when `Burn.eth(uint256)` is called, it always burns the exact amount of ETH passed to the function.
\ No newline at end of file
# `Burn.Gas` Invariants
## `gas(uint256)` always burns at least the amount of gas passed.
**Test:** [`Burn.Gas.t.sol#L64`](../test/invariants/Burn.Gas.t.sol#L64)
**Test:** [`Burn.Gas.t.sol#L66`](../test/invariants/Burn.Gas.t.sol#L66)
Asserts that when `Burn.gas(uint256)` is called, it always burns at least the amount of gas passed to the function.
\ No newline at end of file
# `Encoding` Invariants
## `convertRoundTripAToB` never fails.
**Test:** [`Encoding.t.sol#L71`](../test/invariants/Encoding.t.sol#L71)
**Test:** [`Encoding.t.sol#L73`](../test/invariants/Encoding.t.sol#L73)
Asserts that a raw versioned nonce can be encoded / decoded to reach the same raw value.
## `convertRoundTripBToA` never fails.
**Test:** [`Encoding.t.sol#L80`](../test/invariants/Encoding.t.sol#L80)
**Test:** [`Encoding.t.sol#L82`](../test/invariants/Encoding.t.sol#L82)
Asserts that an encoded versioned nonce can always be decoded / re-encoded to reach the same encoded value.
\ No newline at end of file
# `Hashing` Invariants
## `hashCrossDomainMessage` reverts if `version` is > `1`.
**Test:** [`Hashing.t.sol#L117`](../test/invariants/Hashing.t.sol#L117)
**Test:** [`Hashing.t.sol#L119`](../test/invariants/Hashing.t.sol#L119)
The `hashCrossDomainMessage` function should always revert if the `version` passed is > `1`.
## `version` = `0`: `hashCrossDomainMessage` and `hashCrossDomainMessageV0` are equivalent.
**Test:** [`Hashing.t.sol#L127`](../test/invariants/Hashing.t.sol#L127)
**Test:** [`Hashing.t.sol#L129`](../test/invariants/Hashing.t.sol#L129)
If the version passed is 0, `hashCrossDomainMessage` and `hashCrossDomainMessageV0` should be equivalent.
## `version` = `1`: `hashCrossDomainMessage` and `hashCrossDomainMessageV1` are equivalent.
**Test:** [`Hashing.t.sol#L138`](../test/invariants/Hashing.t.sol#L138)
**Test:** [`Hashing.t.sol#L140`](../test/invariants/Hashing.t.sol#L140)
If the version passed is 1, `hashCrossDomainMessage` and `hashCrossDomainMessageV1` should be equivalent.
\ No newline at end of file
......@@ -13,6 +13,7 @@ This directory contains documentation for all defined invariant tests within `co
- [Encoding](./Encoding.md)
- [FaultDisputeGame](./FaultDisputeGame.md)
- [Hashing](./Hashing.md)
- [InvariantTest.sol](./InvariantTest.sol.md)
- [L2OutputOracle](./L2OutputOracle.md)
- [OptimismPortal](./OptimismPortal.md)
- [ResourceMetering](./ResourceMetering.md)
......
# `ResourceMetering` Invariants
## The base fee should increase if the last block used more than the target amount of gas.
**Test:** [`ResourceMetering.t.sol#L162`](../test/invariants/ResourceMetering.t.sol#L162)
**Test:** [`ResourceMetering.t.sol#L164`](../test/invariants/ResourceMetering.t.sol#L164)
If the last block used more than the target amount of gas (and there were no empty blocks in between), ensure this block's baseFee increased, but not by more than the max amount per block.
## The base fee should decrease if the last block used less than the target amount of gas.
**Test:** [`ResourceMetering.t.sol#L171`](../test/invariants/ResourceMetering.t.sol#L171)
**Test:** [`ResourceMetering.t.sol#L173`](../test/invariants/ResourceMetering.t.sol#L173)
If the previous block used less than the target amount of gas, the base fee should decrease, but not more than the max amount.
## A block's base fee should never be below `MINIMUM_BASE_FEE`.
**Test:** [`ResourceMetering.t.sol#L179`](../test/invariants/ResourceMetering.t.sol#L179)
**Test:** [`ResourceMetering.t.sol#L181`](../test/invariants/ResourceMetering.t.sol#L181)
This test asserts that a block's base fee can never drop below the `MINIMUM_BASE_FEE` threshold.
## A block can never consume more than `MAX_RESOURCE_LIMIT` gas.
**Test:** [`ResourceMetering.t.sol#L187`](../test/invariants/ResourceMetering.t.sol#L187)
**Test:** [`ResourceMetering.t.sol#L189`](../test/invariants/ResourceMetering.t.sol#L189)
This test asserts that a block can never consume more than the `MAX_RESOURCE_LIMIT` gas threshold.
## The base fee can never be raised more than the max base fee change.
**Test:** [`ResourceMetering.t.sol#L197`](../test/invariants/ResourceMetering.t.sol#L197)
**Test:** [`ResourceMetering.t.sol#L199`](../test/invariants/ResourceMetering.t.sol#L199)
After a block consumes more gas than the target gas, the base fee cannot be raised more than the maximum amount allowed. The max base fee change (per-block) is derived as follows: `prevBaseFee / BASE_FEE_MAX_CHANGE_DENOMINATOR`
## The base fee can never be lowered more than the max base fee change.
**Test:** [`ResourceMetering.t.sol#L207`](../test/invariants/ResourceMetering.t.sol#L207)
**Test:** [`ResourceMetering.t.sol#L209`](../test/invariants/ResourceMetering.t.sol#L209)
After a block consumes less than the target gas, the base fee cannot be lowered more than the maximum amount allowed. The max base fee change (per-block) is derived as follows: `prevBaseFee / BASE_FEE_MAX_CHANGE_DENOMINATOR`
## The `maxBaseFeeChange` calculation over multiple blocks can never underflow.
**Test:** [`ResourceMetering.t.sol#L216`](../test/invariants/ResourceMetering.t.sol#L216)
**Test:** [`ResourceMetering.t.sol#L218`](../test/invariants/ResourceMetering.t.sol#L218)
When calculating the `maxBaseFeeChange` after multiple empty blocks, the calculation should never be allowed to underflow.
\ No newline at end of file
# `SafeCall` Invariants
## If `callWithMinGas` performs a call, then it must always provide at least the specified minimum gas limit to the subcontext.
**Test:** [`SafeCall.t.sol#L31`](../test/invariants/SafeCall.t.sol#L31)
**Test:** [`SafeCall.t.sol#L33`](../test/invariants/SafeCall.t.sol#L33)
If the check for remaining gas in `SafeCall.callWithMinGas` passes, the subcontext of the call below it must be provided at least `minGas` gas.
## `callWithMinGas` reverts if there is not enough gas to pass to the subcontext.
**Test:** [`SafeCall.t.sol#L63`](../test/invariants/SafeCall.t.sol#L63)
**Test:** [`SafeCall.t.sol#L66`](../test/invariants/SafeCall.t.sol#L66)
If there is not enough gas in the callframe to ensure that `callWithMinGas` can provide the specified minimum gas limit to the subcontext of the call, then `callWithMinGas` must revert.
\ No newline at end of file
......@@ -4,6 +4,7 @@ pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol";
import { StdInvariant } from "forge-std/StdInvariant.sol";
import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol";
import { InvariantTest } from "test/invariants/InvariantTest.sol";
contract AddressAliasHelper_Converter {
bool public failedRoundtrip;
......@@ -23,10 +24,11 @@ contract AddressAliasHelper_Converter {
}
}
contract AddressAliasHelper_AddressAliasing_Invariant is StdInvariant, Test {
contract AddressAliasHelper_AddressAliasing_Invariant is StdInvariant, InvariantTest {
AddressAliasHelper_Converter internal actor;
function setUp() public {
function setUp() public override {
super.setUp();
// Create a converter actor.
actor = new AddressAliasHelper_Converter();
......
......@@ -7,6 +7,7 @@ import { Vm } from "forge-std/Vm.sol";
import { StdInvariant } from "forge-std/StdInvariant.sol";
import { Burn } from "src/libraries/Burn.sol";
import { InvariantTest } from "test/invariants/InvariantTest.sol";
contract Burn_EthBurner is StdUtils {
Vm internal vm;
......@@ -41,10 +42,11 @@ contract Burn_EthBurner is StdUtils {
}
}
contract Burn_BurnEth_Invariant is StdInvariant, Test {
contract Burn_BurnEth_Invariant is StdInvariant, InvariantTest {
Burn_EthBurner internal actor;
function setUp() public {
function setUp() public override {
super.setUp();
// Create a Eth burner actor.
actor = new Burn_EthBurner(vm);
......
......@@ -7,6 +7,7 @@ import { Vm } from "forge-std/Vm.sol";
import { StdInvariant } from "forge-std/StdInvariant.sol";
import { Burn } from "src/libraries/Burn.sol";
import { InvariantTest } from "test/invariants/InvariantTest.sol";
contract Burn_GasBurner is StdUtils {
Vm internal vm;
......@@ -42,10 +43,11 @@ contract Burn_GasBurner is StdUtils {
}
}
contract Burn_BurnGas_Invariant is StdInvariant, Test {
contract Burn_BurnGas_Invariant is StdInvariant, InvariantTest {
Burn_GasBurner internal actor;
function setUp() public {
function setUp() public override {
super.setUp();
// Create a gas burner actor.
actor = new Burn_GasBurner(vm);
......
......@@ -4,6 +4,7 @@ pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol";
import { StdInvariant } from "forge-std/StdInvariant.sol";
import { Encoding } from "src/libraries/Encoding.sol";
import { InvariantTest } from "test/invariants/InvariantTest.sol";
contract Encoding_Converter {
bool public failedRoundtripAToB;
......@@ -48,10 +49,11 @@ contract Encoding_Converter {
}
}
contract Encoding_Invariant is StdInvariant, Test {
contract Encoding_Invariant is StdInvariant, InvariantTest {
Encoding_Converter internal actor;
function setUp() public {
function setUp() public override {
super.setUp();
// Create a converter actor.
actor = new Encoding_Converter();
......
......@@ -5,6 +5,7 @@ import { Test } from "forge-std/Test.sol";
import { StdInvariant } from "forge-std/StdInvariant.sol";
import { Encoding } from "src/libraries/Encoding.sol";
import { Hashing } from "src/libraries/Hashing.sol";
import { InvariantTest } from "test/invariants/InvariantTest.sol";
contract Hash_CrossDomainHasher {
bool public failedCrossDomainHashHighVersion;
......@@ -93,10 +94,11 @@ contract Hash_CrossDomainHasher {
}
}
contract Hashing_Invariant is StdInvariant, Test {
contract Hashing_Invariant is StdInvariant, InvariantTest {
Hash_CrossDomainHasher internal actor;
function setUp() public {
function setUp() public override {
super.setUp();
// Create a hasher actor.
actor = new Hash_CrossDomainHasher();
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { FFIInterface } from "test/setup/FFIInterface.sol";
import { Deploy } from "scripts/Deploy.s.sol";
import { Test } from "forge-std/Test.sol";
/// @title InvariantTest
/// @dev An extension to `Test` that sets up excluded contracts for invariant testing.
contract InvariantTest is Test {
FFIInterface constant ffi = FFIInterface(address(uint160(uint256(keccak256(abi.encode("optimism.ffi"))))));
Deploy internal constant deploy = Deploy(address(uint160(uint256(keccak256(abi.encode("optimism.deploy"))))));
function setUp() public virtual {
excludeContract(address(ffi));
excludeContract(address(deploy));
}
}
......@@ -10,6 +10,7 @@ import { Arithmetic } from "src/libraries/Arithmetic.sol";
import { ResourceMetering } from "src/L1/ResourceMetering.sol";
import { Proxy } from "src/universal/Proxy.sol";
import { Constants } from "src/libraries/Constants.sol";
import { InvariantTest } from "test/invariants/InvariantTest.sol";
contract ResourceMetering_User is StdUtils, ResourceMetering {
bool public failedMaxGasPerBlock;
......@@ -137,10 +138,11 @@ contract ResourceMetering_User is StdUtils, ResourceMetering {
function _burnInternal(uint64 _gasToBurn) private metered(_gasToBurn) { }
}
contract ResourceMetering_Invariant is StdInvariant, Test {
contract ResourceMetering_Invariant is StdInvariant, InvariantTest {
ResourceMetering_User internal actor;
function setUp() public {
function setUp() public override {
super.setUp();
// Create a actor.
actor = new ResourceMetering_User();
......
......@@ -5,11 +5,13 @@ import { Test } from "forge-std/Test.sol";
import { StdUtils } from "forge-std/StdUtils.sol";
import { Vm } from "forge-std/Vm.sol";
import { SafeCall } from "src/libraries/SafeCall.sol";
import { InvariantTest } from "test/invariants/InvariantTest.sol";
contract SafeCall_Succeeds_Invariants is Test {
contract SafeCall_Succeeds_Invariants is InvariantTest {
SafeCaller_Actor actor;
function setUp() public {
function setUp() public override {
super.setUp();
// Create a new safe caller actor.
actor = new SafeCaller_Actor(vm, false);
......@@ -37,10 +39,11 @@ contract SafeCall_Succeeds_Invariants is Test {
}
}
contract SafeCall_Fails_Invariants is Test {
contract SafeCall_Fails_Invariants is InvariantTest {
SafeCaller_Actor actor;
function setUp() public {
function setUp() public override {
super.setUp();
// Create a new safe caller actor.
actor = new SafeCaller_Actor(vm, true);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment