Skip to content

Commit

Permalink
maint: add more L1 interfaces
Browse files Browse the repository at this point in the history
Another PR adding contract interfaces. L1 contract interfaces are
way more involved than everything else so we're required to start
replacing the contracts with interfaces all over the place.
  • Loading branch information
smartcontracts committed Sep 10, 2024
1 parent 2116126 commit b00e16f
Show file tree
Hide file tree
Showing 90 changed files with 1,237 additions and 559 deletions.
8 changes: 4 additions & 4 deletions packages/contracts-bedrock/invariant-docs/OptimismPortal.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
# `OptimismPortal` Invariants

## Deposits of any value should always succeed unless `_to` = `address(0)` or `_isCreation` = `true`.
**Test:** [`OptimismPortal.t.sol#L148`](../test/invariants/OptimismPortal.t.sol#L148)
**Test:** [`OptimismPortal.t.sol#L156`](../test/invariants/OptimismPortal.t.sol#L156)

All deposits, barring creation transactions and transactions sent to `address(0)`, should always succeed.

## `finalizeWithdrawalTransaction` should revert if the finalization period has not elapsed.
**Test:** [`OptimismPortal.t.sol#L171`](../test/invariants/OptimismPortal.t.sol#L171)
**Test:** [`OptimismPortal.t.sol#L179`](../test/invariants/OptimismPortal.t.sol#L179)

A withdrawal that has been proven should not be able to be finalized until after the finalization period has elapsed.

## `finalizeWithdrawalTransaction` should revert if the withdrawal has already been finalized.
**Test:** [`OptimismPortal.t.sol#L201`](../test/invariants/OptimismPortal.t.sol#L201)
**Test:** [`OptimismPortal.t.sol#L209`](../test/invariants/OptimismPortal.t.sol#L209)

Ensures that there is no chain of calls that can be made that allows a withdrawal to be finalized twice.

## A withdrawal should **always** be able to be finalized `FINALIZATION_PERIOD_SECONDS` after it was successfully proven.
**Test:** [`OptimismPortal.t.sol#L230`](../test/invariants/OptimismPortal.t.sol#L230)
**Test:** [`OptimismPortal.t.sol#L238`](../test/invariants/OptimismPortal.t.sol#L238)

This invariant asserts that there is no chain of calls that can be made that will prevent a withdrawal from being finalized exactly `FINALIZATION_PERIOD_SECONDS` after it was successfully proven.
8 changes: 4 additions & 4 deletions packages/contracts-bedrock/invariant-docs/OptimismPortal2.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
# `OptimismPortal2` Invariants

## Deposits of any value should always succeed unless `_to` = `address(0)` or `_isCreation` = `true`.
**Test:** [`OptimismPortal2.t.sol#L161`](../test/invariants/OptimismPortal2.t.sol#L161)
**Test:** [`OptimismPortal2.t.sol#L168`](../test/invariants/OptimismPortal2.t.sol#L168)

All deposits, barring creation transactions and transactions sent to `address(0)`, should always succeed.

## `finalizeWithdrawalTransaction` should revert if the proof maturity period has not elapsed.
**Test:** [`OptimismPortal2.t.sol#L183`](../test/invariants/OptimismPortal2.t.sol#L183)
**Test:** [`OptimismPortal2.t.sol#L190`](../test/invariants/OptimismPortal2.t.sol#L190)

A withdrawal that has been proven should not be able to be finalized until after the proof maturity period has elapsed.

## `finalizeWithdrawalTransaction` should revert if the withdrawal has already been finalized.
**Test:** [`OptimismPortal2.t.sol#L212`](../test/invariants/OptimismPortal2.t.sol#L212)
**Test:** [`OptimismPortal2.t.sol#L219`](../test/invariants/OptimismPortal2.t.sol#L219)

Ensures that there is no chain of calls that can be made that allows a withdrawal to be finalized twice.

## A withdrawal should **always** be able to be finalized `PROOF_MATURITY_DELAY_SECONDS` after it was successfully proven, if the game has resolved and passed the air-gap.
**Test:** [`OptimismPortal2.t.sol#L240`](../test/invariants/OptimismPortal2.t.sol#L240)
**Test:** [`OptimismPortal2.t.sol#L247`](../test/invariants/OptimismPortal2.t.sol#L247)

This invariant asserts that there is no chain of calls that can be made that will prevent a withdrawal from being finalized exactly `PROOF_MATURITY_DELAY_SECONDS` after it was successfully proven and the game has resolved and passed the air-gap.
14 changes: 7 additions & 7 deletions packages/contracts-bedrock/invariant-docs/ResourceMetering.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
# `ResourceMetering` Invariants

## The base fee should increase if the last block used more than the target amount of gas.
**Test:** [`ResourceMetering.t.sol#L163`](../test/invariants/ResourceMetering.t.sol#L163)
**Test:** [`ResourceMetering.t.sol#L171`](../test/invariants/ResourceMetering.t.sol#L171)

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#L172`](../test/invariants/ResourceMetering.t.sol#L172)
**Test:** [`ResourceMetering.t.sol#L180`](../test/invariants/ResourceMetering.t.sol#L180)

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#L180`](../test/invariants/ResourceMetering.t.sol#L180)
**Test:** [`ResourceMetering.t.sol#L188`](../test/invariants/ResourceMetering.t.sol#L188)

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#L188`](../test/invariants/ResourceMetering.t.sol#L188)
**Test:** [`ResourceMetering.t.sol#L196`](../test/invariants/ResourceMetering.t.sol#L196)

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#L198`](../test/invariants/ResourceMetering.t.sol#L198)
**Test:** [`ResourceMetering.t.sol#L206`](../test/invariants/ResourceMetering.t.sol#L206)

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#L208`](../test/invariants/ResourceMetering.t.sol#L208)
**Test:** [`ResourceMetering.t.sol#L216`](../test/invariants/ResourceMetering.t.sol#L216)

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#L217`](../test/invariants/ResourceMetering.t.sol#L217)
**Test:** [`ResourceMetering.t.sol#L225`](../test/invariants/ResourceMetering.t.sol#L225)

When calculating the `maxBaseFeeChange` after multiple empty blocks, the calculation should never be allowed to underflow.
2 changes: 1 addition & 1 deletion packages/contracts-bedrock/invariant-docs/SystemConfig.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# `SystemConfig` Invariants

## Gas limit boundaries
**Test:** [`SystemConfig.t.sol#L70`](../test/invariants/SystemConfig.t.sol#L70)
**Test:** [`SystemConfig.t.sol#L71`](../test/invariants/SystemConfig.t.sol#L71)

The gas limit of the `SystemConfig` contract can never be lower than the hard-coded lower bound or higher than the hard-coded upper bound. The lower bound must never be higher than the upper bound.
6 changes: 3 additions & 3 deletions packages/contracts-bedrock/scripts/L2Genesis.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { OptimismMintableERC721Factory } from "src/universal/OptimismMintableERC
import { BaseFeeVault } from "src/L2/BaseFeeVault.sol";
import { L1FeeVault } from "src/L2/L1FeeVault.sol";
import { GovernanceToken } from "src/governance/GovernanceToken.sol";
import { L1CrossDomainMessenger } from "src/L1/L1CrossDomainMessenger.sol";
import { CrossDomainMessenger } from "src/universal/CrossDomainMessenger.sol";
import { L1StandardBridge } from "src/L1/L1StandardBridge.sol";
import { FeeVault } from "src/universal/FeeVault.sol";
import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol";
Expand Down Expand Up @@ -277,10 +277,10 @@ contract L2Genesis is Deployer {
function setL2CrossDomainMessenger(address payable _l1CrossDomainMessengerProxy) public {
address impl = _setImplementationCode(Predeploys.L2_CROSS_DOMAIN_MESSENGER);

L2CrossDomainMessenger(impl).initialize({ _l1CrossDomainMessenger: L1CrossDomainMessenger(address(0)) });
L2CrossDomainMessenger(impl).initialize({ _l1CrossDomainMessenger: CrossDomainMessenger(address(0)) });

L2CrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER).initialize({
_l1CrossDomainMessenger: L1CrossDomainMessenger(_l1CrossDomainMessengerProxy)
_l1CrossDomainMessenger: CrossDomainMessenger(_l1CrossDomainMessengerProxy)
});
}

Expand Down
17 changes: 16 additions & 1 deletion packages/contracts-bedrock/scripts/checks/check-interfaces.sh
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,23 @@ for interface_file in $JSON_FILES; do
interface_abi=$(jq '[.abi[] | select(.type != "constructor")]' < "$interface_file")
contract_abi=$(jq '[.abi[] | select(.type != "constructor")]' < "$corresponding_contract_file")

# Function to normalize ABI by replacing interface name with contract name
# Base contracts aren't allowed to use interfaces (guarantees a 1:1 match)
# This means that the interface will redefine types in the base contract
# We normalize the ABI as if the interface and contract are the same name
normalize_abi() {
local abi="$1"
local interface_name="$2"
local contract_name="$3"
echo "${abi//$interface_name/$contract_name}"
}

# Normalize the ABIs
normalized_interface_abi=$(normalize_abi "$interface_abi" "$contract_name" "$contract_basename")
normalized_contract_abi="$contract_abi"

# Use jq to compare the ABIs
if ! diff_result=$(diff -u <(echo "$interface_abi" | jq -S .) <(echo "$contract_abi" | jq -S .)); then
if ! diff_result=$(diff -u <(echo "$normalized_interface_abi" | jq -S .) <(echo "$normalized_contract_abi" | jq -S .)); then
if ! grep -q "^$contract_name$" "$REPORTED_INTERFACES_FILE"; then
echo "$contract_name" >> "$REPORTED_INTERFACES_FILE"
if ! is_excluded "$contract_name"; then
Expand Down
57 changes: 33 additions & 24 deletions packages/contracts-bedrock/scripts/deploy/ChainAssertions.sol
Original file line number Diff line number Diff line change
@@ -1,28 +1,37 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { ProxyAdmin } from "src/universal/ProxyAdmin.sol";
import { ResourceMetering } from "src/L1/ResourceMetering.sol";
// Testing
import { Vm } from "forge-std/Vm.sol";
import { console2 as console } from "forge-std/console2.sol";

// Scripts
import { DeployConfig } from "scripts/deploy/DeployConfig.s.sol";
import { Deployer } from "scripts/deploy/Deployer.sol";
import { SystemConfig } from "src/L1/SystemConfig.sol";
import { Constants } from "src/libraries/Constants.sol";

// Contracts
import { ProxyAdmin } from "src/universal/ProxyAdmin.sol";
import { L1StandardBridge } from "src/L1/L1StandardBridge.sol";
import { IL2OutputOracle } from "src/L1/interfaces/IL2OutputOracle.sol";
import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol";
import { DelayedWETH } from "src/dispute/weth/DelayedWETH.sol";
import { ProtocolVersion, ProtocolVersions } from "src/L1/ProtocolVersions.sol";
import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
import { OptimismPortal } from "src/L1/OptimismPortal.sol";
import { OptimismPortal2 } from "src/L1/OptimismPortal2.sol";
import { L1CrossDomainMessenger } from "src/L1/L1CrossDomainMessenger.sol";
import { OptimismMintableERC20Factory } from "src/universal/OptimismMintableERC20Factory.sol";
import { L1ERC721Bridge } from "src/L1/L1ERC721Bridge.sol";

// Libraries
import { Constants } from "src/libraries/Constants.sol";
import { Predeploys } from "src/libraries/Predeploys.sol";
import { Types } from "scripts/libraries/Types.sol";
import { Vm } from "forge-std/Vm.sol";

// Interfaces
import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol";
import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol";
import { IL2OutputOracle } from "src/L1/interfaces/IL2OutputOracle.sol";
import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol";
import { IL1CrossDomainMessenger } from "src/L1/interfaces/IL1CrossDomainMessenger.sol";
import { IOptimismPortal } from "src/L1/interfaces/IOptimismPortal.sol";
import { IOptimismPortal2 } from "src/L1/interfaces/IOptimismPortal2.sol";
import { ISystemConfigV0 } from "scripts/interfaces/ISystemConfigV0.sol";
import { console2 as console } from "forge-std/console2.sol";

library ChainAssertions {
Vm internal constant vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
Expand All @@ -39,8 +48,8 @@ library ChainAssertions {
view
{
console.log("Running post-deploy assertions");
ResourceMetering.ResourceConfig memory rcfg = SystemConfig(_prox.SystemConfig).resourceConfig();
ResourceMetering.ResourceConfig memory dflt = Constants.DEFAULT_RESOURCE_CONFIG();
IResourceMetering.ResourceConfig memory rcfg = ISystemConfig(_prox.SystemConfig).resourceConfig();
IResourceMetering.ResourceConfig memory dflt = Constants.DEFAULT_RESOURCE_CONFIG();
require(keccak256(abi.encode(rcfg)) == keccak256(abi.encode(dflt)));

checkSystemConfig({ _contracts: _prox, _cfg: _cfg, _isProxy: true });
Expand All @@ -62,12 +71,12 @@ library ChainAssertions {
/// @notice Asserts that the SystemConfig is setup correctly
function checkSystemConfig(Types.ContractSet memory _contracts, DeployConfig _cfg, bool _isProxy) internal view {
console.log("Running chain assertions on the SystemConfig");
SystemConfig config = SystemConfig(_contracts.SystemConfig);
ISystemConfig config = ISystemConfig(_contracts.SystemConfig);

// Check that the contract is initialized
assertSlotValueIsOne({ _contractAddress: address(config), _slot: 0, _offset: 0 });

ResourceMetering.ResourceConfig memory resourceConfig = config.resourceConfig();
IResourceMetering.ResourceConfig memory resourceConfig = config.resourceConfig();

if (_isProxy) {
require(config.owner() == _cfg.finalSystemOwner());
Expand All @@ -78,7 +87,7 @@ library ChainAssertions {
require(config.unsafeBlockSigner() == _cfg.p2pSequencerAddress());
require(config.scalar() >> 248 == 1);
// Check _config
ResourceMetering.ResourceConfig memory rconfig = Constants.DEFAULT_RESOURCE_CONFIG();
IResourceMetering.ResourceConfig memory rconfig = Constants.DEFAULT_RESOURCE_CONFIG();
require(resourceConfig.maxResourceLimit == rconfig.maxResourceLimit);
require(resourceConfig.elasticityMultiplier == rconfig.elasticityMultiplier);
require(resourceConfig.baseFeeMaxChangeDenominator == rconfig.baseFeeMaxChangeDenominator);
Expand Down Expand Up @@ -127,7 +136,7 @@ library ChainAssertions {
/// @notice Asserts that the L1CrossDomainMessenger is setup correctly
function checkL1CrossDomainMessenger(Types.ContractSet memory _contracts, Vm _vm, bool _isProxy) internal view {
console.log("Running chain assertions on the L1CrossDomainMessenger");
L1CrossDomainMessenger messenger = L1CrossDomainMessenger(_contracts.L1CrossDomainMessenger);
IL1CrossDomainMessenger messenger = IL1CrossDomainMessenger(_contracts.L1CrossDomainMessenger);

// Check that the contract is initialized
assertSlotValueIsOne({ _contractAddress: address(messenger), _slot: 0, _offset: 20 });
Expand Down Expand Up @@ -201,7 +210,7 @@ library ChainAssertions {
if (_isProxy) {
require(weth.owner() == _expectedOwner);
require(weth.delay() == _cfg.faultGameWithdrawalDelay());
require(weth.config() == SuperchainConfig(_contracts.SuperchainConfig));
require(weth.config() == ISuperchainConfig(_contracts.SuperchainConfig));
} else {
require(weth.owner() == _expectedOwner);
require(weth.delay() == _cfg.faultGameWithdrawalDelay());
Expand All @@ -227,7 +236,7 @@ library ChainAssertions {
if (_isProxy) {
require(weth.owner() == _expectedOwner);
require(weth.delay() == _cfg.faultGameWithdrawalDelay());
require(weth.config() == SuperchainConfig(_contracts.SuperchainConfig));
require(weth.config() == ISuperchainConfig(_contracts.SuperchainConfig));
} else {
require(weth.owner() == _expectedOwner);
require(weth.delay() == _cfg.faultGameWithdrawalDelay());
Expand Down Expand Up @@ -322,7 +331,7 @@ library ChainAssertions {
function checkOptimismPortal(Types.ContractSet memory _contracts, DeployConfig _cfg, bool _isProxy) internal view {
console.log("Running chain assertions on the OptimismPortal");

OptimismPortal portal = OptimismPortal(payable(_contracts.OptimismPortal));
IOptimismPortal portal = IOptimismPortal(payable(_contracts.OptimismPortal));

// Check that the contract is initialized
assertSlotValueIsOne({ _contractAddress: address(portal), _slot: 0, _offset: 0 });
Expand All @@ -337,7 +346,7 @@ library ChainAssertions {
require(address(portal.systemConfig()) == _contracts.SystemConfig);
require(portal.guardian() == guardian);
require(address(portal.superchainConfig()) == address(_contracts.SuperchainConfig));
require(portal.paused() == SuperchainConfig(_contracts.SuperchainConfig).paused());
require(portal.paused() == ISuperchainConfig(_contracts.SuperchainConfig).paused());
require(portal.l2Sender() == Constants.DEFAULT_L2_SENDER);
} else {
require(address(portal.l2Oracle()) == address(0));
Expand All @@ -358,7 +367,7 @@ library ChainAssertions {
{
console.log("Running chain assertions on the OptimismPortal2");

OptimismPortal2 portal = OptimismPortal2(payable(_contracts.OptimismPortal2));
IOptimismPortal2 portal = IOptimismPortal2(payable(_contracts.OptimismPortal2));

// Check that the contract is initialized
assertSlotValueIsOne({ _contractAddress: address(portal), _slot: 0, _offset: 0 });
Expand All @@ -373,7 +382,7 @@ library ChainAssertions {
require(address(portal.systemConfig()) == _contracts.SystemConfig);
require(portal.guardian() == guardian);
require(address(portal.superchainConfig()) == address(_contracts.SuperchainConfig));
require(portal.paused() == SuperchainConfig(_contracts.SuperchainConfig).paused());
require(portal.paused() == ISuperchainConfig(_contracts.SuperchainConfig).paused());
require(portal.l2Sender() == Constants.DEFAULT_L2_SENDER);
} else {
require(address(portal.disputeGameFactory()) == address(0));
Expand Down Expand Up @@ -422,7 +431,7 @@ library ChainAssertions {
view
{
console.log("Running chain assertions on the SuperchainConfig");
SuperchainConfig superchainConfig = SuperchainConfig(_contracts.SuperchainConfig);
ISuperchainConfig superchainConfig = ISuperchainConfig(_contracts.SuperchainConfig);

// Check that the contract is initialized
assertSlotValueIsOne({ _contractAddress: address(superchainConfig), _slot: 0, _offset: 0 });
Expand Down
Loading

0 comments on commit b00e16f

Please sign in to comment.