Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

maint: add interface for L2OutputOracle #11817

Merged
merged 1 commit into from
Sep 10, 2024
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
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#L149`](../test/invariants/OptimismPortal.t.sol#L149)
**Test:** [`OptimismPortal.t.sol#L148`](../test/invariants/OptimismPortal.t.sol#L148)

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

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#L202`](../test/invariants/OptimismPortal.t.sol#L202)
**Test:** [`OptimismPortal.t.sol#L201`](../test/invariants/OptimismPortal.t.sol#L201)

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#L231`](../test/invariants/OptimismPortal.t.sol#L231)
**Test:** [`OptimismPortal.t.sol#L230`](../test/invariants/OptimismPortal.t.sol#L230)

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.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Deployer } from "scripts/deploy/Deployer.sol";
import { SystemConfig } from "src/L1/SystemConfig.sol";
import { Constants } from "src/libraries/Constants.sol";
import { L1StandardBridge } from "src/L1/L1StandardBridge.sol";
import { L2OutputOracle } from "src/L1/L2OutputOracle.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";
Expand Down Expand Up @@ -245,7 +245,7 @@ library ChainAssertions {
view
{
console.log("Running chain assertions on the L2OutputOracle");
L2OutputOracle oracle = L2OutputOracle(_contracts.L2OutputOracle);
IL2OutputOracle oracle = IL2OutputOracle(_contracts.L2OutputOracle);

// Check that the contract is initialized
assertSlotValueIsOne({ _contractAddress: address(oracle), _slot: 0, _offset: 0 });
Expand Down
31 changes: 22 additions & 9 deletions packages/contracts-bedrock/scripts/deploy/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { OptimismPortalInterop } from "src/L1/OptimismPortalInterop.sol";
import { L1ChugSplashProxy } from "src/legacy/L1ChugSplashProxy.sol";
import { ResolvedDelegateProxy } from "src/legacy/ResolvedDelegateProxy.sol";
import { L1CrossDomainMessenger } from "src/L1/L1CrossDomainMessenger.sol";
import { L2OutputOracle } from "src/L1/L2OutputOracle.sol";
import { IL2OutputOracle } from "src/L1/interfaces/IL2OutputOracle.sol";
import { OptimismMintableERC20Factory } from "src/universal/OptimismMintableERC20Factory.sol";
import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
import { SystemConfig } from "src/L1/SystemConfig.sol";
Expand Down Expand Up @@ -697,11 +697,7 @@ contract Deploy is Deployer {

/// @notice Deploy the L2OutputOracle
function deployL2OutputOracle() public broadcast returns (address addr_) {
console.log("Deploying L2OutputOracle implementation");
L2OutputOracle oracle = new L2OutputOracle{ salt: _implSalt() }();

save("L2OutputOracle", address(oracle));
console.log("L2OutputOracle deployed at %s", address(oracle));
IL2OutputOracle oracle = IL2OutputOracle(_deploy("L2OutputOracle", hex""));

// Override the `L2OutputOracle` contract to the deployed implementation. This is necessary
// to check the `L2OutputOracle` implementation alongside dependent contracts, which
Expand Down Expand Up @@ -1235,7 +1231,7 @@ contract Deploy is Deployer {
_proxy: payable(l2OutputOracleProxy),
_implementation: l2OutputOracle,
_innerCallData: abi.encodeCall(
L2OutputOracle.initialize,
IL2OutputOracle.initialize,
(
cfg.l2OutputOracleSubmissionInterval(),
cfg.l2BlockTime(),
Expand All @@ -1248,7 +1244,7 @@ contract Deploy is Deployer {
)
});

L2OutputOracle oracle = L2OutputOracle(l2OutputOracleProxy);
IL2OutputOracle oracle = IL2OutputOracle(l2OutputOracleProxy);
string memory version = oracle.version();
console.log("L2OutputOracle version: %s", version);

Expand All @@ -1275,7 +1271,7 @@ contract Deploy is Deployer {
_innerCallData: abi.encodeCall(
OptimismPortal.initialize,
(
L2OutputOracle(l2OutputOracleProxy),
IL2OutputOracle(l2OutputOracleProxy),
SystemConfig(systemConfigProxy),
SuperchainConfig(superchainConfigProxy)
)
Expand Down Expand Up @@ -1615,4 +1611,21 @@ contract Deploy is Deployer {
require(dac.bondSize() == daBondSize);
require(dac.resolverRefundPercentage() == daResolverRefundPercentage);
}

/// @notice Deploys a contract via CREATE2.
/// @param _name The name of the contract.
/// @param _constructorParams The constructor parameters.
function _deploy(string memory _name, bytes memory _constructorParams) internal returns (address addr_) {
console.log("Deploying %s", _name);
bytes32 salt = _implSalt();
bytes memory initCode = abi.encodePacked(vm.getCode(_name), _constructorParams);
address preComputedAddress = vm.computeCreate2Address(salt, keccak256(initCode));
require(preComputedAddress.code.length == 0, "Deploy: contract already deployed");
assembly {
addr_ := create2(0, add(initCode, 0x20), mload(initCode), salt)
}
require(addr_ != address(0), "deployment failed");
save(_name, addr_);
console.log("%s deployed at %s", _name, addr_);
}
}
4 changes: 2 additions & 2 deletions packages/contracts-bedrock/semver-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@
},
"src/L1/L2OutputOracle.sol": {
"initCodeHash": "0x433fac9de52d8ce8fc3471b78ef6cc9cff1019f480c9ad91b6e09ab8738a8edb",
"sourceCodeHash": "0xc11a2a514a051bdc82b21ec39c7b923145d384629adb1efc52b534f3c686f6a0"
"sourceCodeHash": "0xde4df0f9633dc0cdb1c9f634003ea5b0f7c5c1aebc407bc1b2f44c0ecf938649"
},
"src/L1/OPStackManager.sol": {
"initCodeHash": "0x67bf02405bf1ca7d78c4215c350ad9c5c7b4cece35d9fab837f279d65f995c5d",
"sourceCodeHash": "0x8e272e707e383d516b8f1cce0ea29ff46a0eb448c8386fa146e6a43f3100042a"
},
"src/L1/OptimismPortal.sol": {
"initCodeHash": "0x6bf59539298b20221de6c51db21016be8d3278bdbe0be1cdd49638dc828e003e",
"sourceCodeHash": "0xad97b31e3855ad0af82a0fc80db9773b7eacc85215611854dcc54f60f97f06bf"
"sourceCodeHash": "0x07f6f4c0cc14be4aeb9a29ec4a238ea3748d5cb6b244e0054845a18308c16ab5"
},
"src/L1/OptimismPortal2.sol": {
"initCodeHash": "0x414ad1fdb6296ac32fc67ce01288b6e67bedd2ed239d7eb8ed40c7f55f9021f2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@
{
"inputs": [
{
"internalType": "contract L2OutputOracle",
"internalType": "contract IL2OutputOracle",
"name": "_l2Oracle",
"type": "address"
},
Expand Down Expand Up @@ -223,7 +223,7 @@
"name": "l2Oracle",
"outputs": [
{
"internalType": "contract L2OutputOracle",
"internalType": "contract IL2OutputOracle",
"name": "",
"type": "address"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
"label": "l2Oracle",
"offset": 0,
"slot": "54",
"type": "contract L2OutputOracle"
"type": "contract IL2OutputOracle"
},
{
"bytes": "20",
Expand Down
7 changes: 6 additions & 1 deletion packages/contracts-bedrock/src/L1/L2OutputOracle.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

// Contracts
import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import { ISemver } from "src/universal/interfaces/ISemver.sol";

// Libraries
import { Types } from "src/libraries/Types.sol";
import { Constants } from "src/libraries/Constants.sol";

// Interfaces
import { ISemver } from "src/universal/interfaces/ISemver.sol";
smartcontracts marked this conversation as resolved.
Show resolved Hide resolved

/// @custom:proxied true
/// @title L2OutputOracle
/// @notice The L2OutputOracle contains an array of L2 state outputs, where each output is a
Expand Down
8 changes: 4 additions & 4 deletions packages/contracts-bedrock/src/L1/OptimismPortal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity 0.8.15;

import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import { SafeCall } from "src/libraries/SafeCall.sol";
import { L2OutputOracle } from "src/L1/L2OutputOracle.sol";
import { IL2OutputOracle } from "src/L1/interfaces/IL2OutputOracle.sol";
import { SystemConfig } from "src/L1/SystemConfig.sol";
import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
import { Constants } from "src/libraries/Constants.sol";
Expand Down Expand Up @@ -68,7 +68,7 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {

/// @notice Contract of the L2OutputOracle.
/// @custom:network-specific
L2OutputOracle public l2Oracle;
IL2OutputOracle public l2Oracle;

/// @notice Contract of the SystemConfig.
/// @custom:network-specific
Expand Down Expand Up @@ -136,7 +136,7 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
/// @notice Constructs the OptimismPortal contract.
constructor() {
initialize({
_l2Oracle: L2OutputOracle(address(0)),
_l2Oracle: IL2OutputOracle(address(0)),
_systemConfig: SystemConfig(address(0)),
_superchainConfig: SuperchainConfig(address(0))
});
Expand All @@ -147,7 +147,7 @@ contract OptimismPortal is Initializable, ResourceMetering, ISemver {
/// @param _systemConfig Contract of the SystemConfig.
/// @param _superchainConfig Contract of the SuperchainConfig.
function initialize(
L2OutputOracle _l2Oracle,
IL2OutputOracle _l2Oracle,
SystemConfig _systemConfig,
SuperchainConfig _superchainConfig
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { Types } from "src/libraries/Types.sol";

interface IL2OutputOracle {
event Initialized(uint8 version);
event OutputProposed(
bytes32 indexed outputRoot, uint256 indexed l2OutputIndex, uint256 indexed l2BlockNumber, uint256 l1Timestamp
);
event OutputsDeleted(uint256 indexed prevNextOutputIndex, uint256 indexed newNextOutputIndex);

function CHALLENGER() external view returns (address);
function FINALIZATION_PERIOD_SECONDS() external view returns (uint256);
function L2_BLOCK_TIME() external view returns (uint256);
function PROPOSER() external view returns (address);
function SUBMISSION_INTERVAL() external view returns (uint256);
function challenger() external view returns (address);
function computeL2Timestamp(uint256 _l2BlockNumber) external view returns (uint256);
function deleteL2Outputs(uint256 _l2OutputIndex) external;
function finalizationPeriodSeconds() external view returns (uint256);
function getL2Output(uint256 _l2OutputIndex) external view returns (Types.OutputProposal memory);
smartcontracts marked this conversation as resolved.
Show resolved Hide resolved
function getL2OutputAfter(uint256 _l2BlockNumber) external view returns (Types.OutputProposal memory);
smartcontracts marked this conversation as resolved.
Show resolved Hide resolved
function getL2OutputIndexAfter(uint256 _l2BlockNumber) external view returns (uint256);
function initialize(
uint256 _submissionInterval,
uint256 _l2BlockTime,
uint256 _startingBlockNumber,
uint256 _startingTimestamp,
address _proposer,
address _challenger,
uint256 _finalizationPeriodSeconds
)
external;
function l2BlockTime() external view returns (uint256);
function latestBlockNumber() external view returns (uint256);
function latestOutputIndex() external view returns (uint256);
function nextBlockNumber() external view returns (uint256);
function nextOutputIndex() external view returns (uint256);
function proposeL2Output(
bytes32 _outputRoot,
uint256 _l2BlockNumber,
bytes32 _l1BlockHash,
uint256 _l1BlockNumber
)
external
payable;
function proposer() external view returns (address);
function startingBlockNumber() external view returns (uint256);
function startingTimestamp() external view returns (uint256);
function submissionInterval() external view returns (uint256);
function version() external view returns (string memory);
smartcontracts marked this conversation as resolved.
Show resolved Hide resolved
}
3 changes: 2 additions & 1 deletion packages/contracts-bedrock/test/L1/L2OutputOracle.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ import { Proxy } from "src/universal/Proxy.sol";

// Target contract
import { L2OutputOracle } from "src/L1/L2OutputOracle.sol";
import { IL2OutputOracle } from "src/L1/interfaces/IL2OutputOracle.sol";

contract L2OutputOracle_constructor_Test is CommonTest {
/// @dev Tests that constructor sets the initial values correctly.
function test_constructor_succeeds() external {
L2OutputOracle oracleImpl = new L2OutputOracle();
IL2OutputOracle oracleImpl = IL2OutputOracle(address(new L2OutputOracle()));

assertEq(oracleImpl.SUBMISSION_INTERVAL(), 1);
assertEq(oracleImpl.submissionInterval(), 1);
Expand Down
16 changes: 8 additions & 8 deletions packages/contracts-bedrock/test/L1/OptimismPortal.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { Constants } from "src/libraries/Constants.sol";
import { Proxy } from "src/universal/Proxy.sol";
import { ResourceMetering } from "src/L1/ResourceMetering.sol";
import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol";
import { L2OutputOracle } from "src/L1/L2OutputOracle.sol";
import { IL2OutputOracle } from "src/L1/interfaces/IL2OutputOracle.sol";
import { SystemConfig } from "src/L1/SystemConfig.sol";
import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
import { L1Block } from "src/L2/L1Block.sol";
Expand Down Expand Up @@ -403,7 +403,7 @@ contract OptimismPortal_Test is CommonTest {
uint256 ts = block.timestamp;
vm.mockCall(
address(optimismPortal.l2Oracle()),
abi.encodeWithSelector(L2OutputOracle.getL2Output.selector),
abi.encodeWithSelector(IL2OutputOracle.getL2Output.selector),
abi.encode(Types.OutputProposal(bytes32(uint256(1)), uint128(ts), uint128(startingBlockNumber)))
);

Expand Down Expand Up @@ -831,7 +831,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest {
// this case we just use bytes32(uint256(1)).
vm.mockCall(
address(optimismPortal.l2Oracle()),
abi.encodeWithSelector(L2OutputOracle.getL2Output.selector),
abi.encodeWithSelector(IL2OutputOracle.getL2Output.selector),
abi.encode(bytes32(uint256(1)), _proposedBlockNumber)
);

Expand Down Expand Up @@ -886,7 +886,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest {
// to finalize the withdrawal.
vm.mockCall(
address(optimismPortal.l2Oracle()),
abi.encodeWithSelector(L2OutputOracle.getL2Output.selector),
abi.encodeWithSelector(IL2OutputOracle.getL2Output.selector),
abi.encode(
Types.OutputProposal(bytes32(uint256(0)), uint128(block.timestamp), uint128(_proposedBlockNumber))
)
Expand Down Expand Up @@ -917,7 +917,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest {
// finalization period.
vm.mockCall(
address(optimismPortal.l2Oracle()),
abi.encodeWithSelector(L2OutputOracle.getL2Output.selector),
abi.encodeWithSelector(IL2OutputOracle.getL2Output.selector),
abi.encode(Types.OutputProposal(_outputRoot, uint128(block.timestamp + 1), uint128(_proposedBlockNumber)))
);

Expand Down Expand Up @@ -953,7 +953,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest {
uint256 recentTimestamp = block.timestamp - 1;
vm.mockCall(
address(optimismPortal.l2Oracle()),
abi.encodeWithSelector(L2OutputOracle.getL2Output.selector),
abi.encodeWithSelector(IL2OutputOracle.getL2Output.selector),
abi.encode(Types.OutputProposal(_outputRoot, uint128(recentTimestamp), uint128(_proposedBlockNumber)))
);

Expand Down Expand Up @@ -1005,7 +1005,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest {

vm.mockCall(
address(optimismPortal.l2Oracle()),
abi.encodeWithSelector(L2OutputOracle.getL2Output.selector),
abi.encodeWithSelector(IL2OutputOracle.getL2Output.selector),
abi.encode(
Types.OutputProposal(
Hashing.hashOutputRootProof(outputRootProof),
Expand Down Expand Up @@ -1054,7 +1054,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest {
uint256 finalizedTimestamp = block.timestamp - l2OutputOracle.FINALIZATION_PERIOD_SECONDS() - 1;
vm.mockCall(
address(optimismPortal.l2Oracle()),
abi.encodeWithSelector(L2OutputOracle.getL2Output.selector),
abi.encodeWithSelector(IL2OutputOracle.getL2Output.selector),
abi.encode(Types.OutputProposal(outputRoot, uint128(finalizedTimestamp), uint128(_proposedBlockNumber)))
);

Expand Down
3 changes: 2 additions & 1 deletion packages/contracts-bedrock/test/Specs.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -907,10 +907,11 @@ contract Specification_Test is CommonTest {

/// @notice Ensures that there's an auth spec for every L1 contract function.
function testContractAuth() public {
string[] memory pathExcludes = new string[](3);
string[] memory pathExcludes = new string[](4);
pathExcludes[0] = "src/dispute/interfaces/*";
pathExcludes[1] = "src/dispute/lib/*";
pathExcludes[2] = "src/Safe/SafeSigners.sol";
pathExcludes[3] = "src/L1/interfaces/*";
Abi[] memory abis = ForgeArtifacts.getContractFunctionAbis(
"src/{L1,dispute,governance,Safe,universal/ProxyAdmin.sol}", pathExcludes
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { DisputeGameFactory_Init } from "test/dispute/DisputeGameFactory.t.sol";
import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol";
import { PermissionedDisputeGame } from "src/dispute/PermissionedDisputeGame.sol";
import { DelayedWETH } from "src/dispute/weth/DelayedWETH.sol";
import { L2OutputOracle } from "src/L1/L2OutputOracle.sol";
import { PreimageOracle } from "src/cannon/PreimageOracle.sol";
import { PreimageKeyLib } from "src/cannon/PreimageKeyLib.sol";

Expand Down
Loading