diff --git a/packages/contracts-bedrock/deploy-config/hardhat.json b/packages/contracts-bedrock/deploy-config/hardhat.json index 30a89a4ff4..5f3db9710f 100644 --- a/packages/contracts-bedrock/deploy-config/hardhat.json +++ b/packages/contracts-bedrock/deploy-config/hardhat.json @@ -13,9 +13,9 @@ "p2pSequencerAddress": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", "batchInboxAddress": "0xff00000000000000000000000000000000000000", "batchSenderAddress": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", - "l2OutputOracleSubmissionInterval": 6, - "l2OutputOracleStartingTimestamp": 1, - "l2OutputOracleStartingBlockNumber": 1, + "l2OutputOracleSubmissionInterval": 900, + "l2OutputOracleStartingTimestamp": 1721925325, + "l2OutputOracleStartingBlockNumber": 123163274, "gasPriceOracleBaseFeeScalar": 1368, "gasPriceOracleBlobBaseFeeScalar": 810949, "l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", @@ -35,7 +35,7 @@ "governanceTokenName": "Optimism", "governanceTokenSymbol": "OP", "governanceTokenOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", - "finalizationPeriodSeconds": 36, + "finalizationPeriodSeconds": 0, "eip1559Denominator": 50, "eip1559DenominatorCanyon": 250, "eip1559Elasticity": 10, @@ -62,5 +62,8 @@ "daChallengeWindow": 100, "daResolveWindow": 100, "daBondSize": 1000, - "daResolverRefundPercentage": 50 + "daResolverRefundPercentage": 50, + "zkVKey": "0x003de3cfc15f7b7e2844f33380b8dde65e0cc65de4f7a27e8b3422d376d982f4", + "l2OutputOracleStartingOutputRoot": "0xd1e578c114d50dbb4431e81f737481b7d07204a35d5968c4b911ec55ba038ed6", + "verifierGateway": "0x0000000000000000000000000000000000000000" } diff --git a/packages/contracts-bedrock/scripts/Deploy.s.sol b/packages/contracts-bedrock/scripts/Deploy.s.sol index f27cfdce40..42c7829301 100644 --- a/packages/contracts-bedrock/scripts/Deploy.s.sol +++ b/packages/contracts-bedrock/scripts/Deploy.s.sol @@ -26,7 +26,6 @@ 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 { ZKL2OutputOracle } from "src/L1/ZKL2OutputOracle.sol"; import { OptimismMintableERC20Factory } from "src/universal/OptimismMintableERC20Factory.sol"; import { SuperchainConfig } from "src/L1/SuperchainConfig.sol"; import { SystemConfig } from "src/L1/SystemConfig.sol"; @@ -285,7 +284,6 @@ contract Deploy is Deployer { /// @notice Internal function containing the deploy logic. function _run() internal virtual { - require(!(cfg.useZK() && cfg.useFaultProofs()), "Deploy: ZK and Fault Proofs are mutually exclusive. Please update config json."); console.log("start of L1 Deploy!"); deploySafe("SystemOwnerSafe"); console.log("deployed Safe!"); @@ -386,7 +384,6 @@ contract Deploy is Deployer { deployL1ERC721Bridge(); deployOptimismPortal(); deployL2OutputOracle(); - deployZKL2OutputOracle(); // Fault proofs deployOptimismPortal2(); @@ -414,12 +411,7 @@ contract Deploy is Deployer { initializeL1ERC721Bridge(); initializeOptimismMintableERC20Factory(); initializeL1CrossDomainMessenger(); - if (cfg.useZK()) { - console.log("ZK enabled. Initializing the L2OutputOracle proxy with the ZKL2OutputOracle."); - initializeZKL2OutputOracle(); - } else { - initializeL2OutputOracle(); - } + initializeL2OutputOracle(); initializeDisputeGameFactory(); initializeDelayedWETH(); initializeAnchorStateRegistry(); @@ -715,29 +707,6 @@ contract Deploy is Deployer { addr_ = address(oracle); } - /// @notice Deploy the L2OutputOracle - function deployZKL2OutputOracle() public broadcast returns (address addr_) { - console.log("Deploying ZKL2OutputOracle implementation"); - ZKL2OutputOracle oracle = new ZKL2OutputOracle{ salt: _implSalt() }(); - - save("ZKL2OutputOracle", address(oracle)); - console.log("ZKL2OutputOracle deployed at %s", address(oracle)); - - // Override the `L2OutputOracle` contract to the deployed implementation. This is necessary - // to check the `L2OutputOracle` implementation alongside dependent contracts, which - // are always proxies. - Types.ContractSet memory contracts = _proxiesUnstrict(); - contracts.L2OutputOracle = address(oracle); - ChainAssertions.checkL2OutputOracle({ - _contracts: contracts, - _cfg: cfg, - _l2OutputOracleStartingTimestamp: 0, - _isProxy: false - }); - - addr_ = address(oracle); - } - /// @notice Deploy the OptimismMintableERC20Factory function deployOptimismMintableERC20Factory() public broadcast returns (address addr_) { console.log("Deploying OptimismMintableERC20Factory implementation"); @@ -1232,41 +1201,6 @@ contract Deploy is Deployer { _implementation: l2OutputOracle, _innerCallData: abi.encodeCall( L2OutputOracle.initialize, - ( - cfg.l2OutputOracleSubmissionInterval(), - cfg.l2BlockTime(), - cfg.l2OutputOracleStartingBlockNumber(), - cfg.l2OutputOracleStartingTimestamp(), - cfg.l2OutputOracleProposer(), - cfg.l2OutputOracleChallenger(), - cfg.finalizationPeriodSeconds() - ) - ) - }); - - L2OutputOracle oracle = L2OutputOracle(l2OutputOracleProxy); - string memory version = oracle.version(); - console.log("L2OutputOracle version: %s", version); - - ChainAssertions.checkL2OutputOracle({ - _contracts: _proxies(), - _cfg: cfg, - _l2OutputOracleStartingTimestamp: cfg.l2OutputOracleStartingTimestamp(), - _isProxy: true - }); - } - - /// @notice Initialize the L2OutputOracle - function initializeZKL2OutputOracle() public broadcast { - console.log("Upgrading and initializing L2OutputOracle proxy"); - address l2OutputOracleProxy = mustGetAddress("L2OutputOracleProxy"); - address l2OutputOracle = mustGetAddress("ZKL2OutputOracle"); - - _upgradeAndCallViaSafe({ - _proxy: payable(l2OutputOracleProxy), - _implementation: l2OutputOracle, - _innerCallData: abi.encodeCall( - ZKL2OutputOracle.initialize, ( cfg.l2OutputOracleSubmissionInterval(), cfg.l2BlockTime(), diff --git a/packages/contracts-bedrock/scripts/DeployConfig.s.sol b/packages/contracts-bedrock/scripts/DeployConfig.s.sol index b87d93b815..0f5bbac39e 100644 --- a/packages/contracts-bedrock/scripts/DeployConfig.s.sol +++ b/packages/contracts-bedrock/scripts/DeployConfig.s.sol @@ -75,7 +75,6 @@ contract DeployConfig is Script { uint256 public daResolveWindow; uint256 public daBondSize; uint256 public daResolverRefundPercentage; - bool public useZK; bytes32 public zkVKey; bytes32 public l2OutputOracleStartingOutputRoot; address public verifierGateway; @@ -140,7 +139,6 @@ contract DeployConfig is Script { disputeGameFinalityDelaySeconds = _readOr(_json, "$.disputeGameFinalityDelaySeconds", uint256(0)); respectedGameType = _readOr(_json, "$.respectedGameType", uint256(0)); - useZK = _readOr(_json, "$.useZK", false); zkVKey = _readOr(_json, "$.zkVKey", bytes32(0)); l2OutputOracleStartingOutputRoot = _readOr(_json, "$.l2OutputOracleStartingOutputRoot", bytes32(0)); verifierGateway = _readOr(_json, "$.verifierGateway", 0x3B6041173B80E77f038f3F2C0f9744f04837185e); @@ -214,26 +212,8 @@ contract DeployConfig is Script { } /// @notice Allow the `useZK` config to be overridden in testing environments - function setUseZK( - bool _useZK, - bytes32 _zkVKey, - bytes32 _startingL2OutputRoot, - uint256 _startingL2OutputTimestamp, - uint256 _startingL2OutputBlockNumber, - uint256 _submissionInterval, - address _verifierGateway - ) public { - useZK = _useZK; - zkVKey = _zkVKey; - - l2OutputOracleStartingOutputRoot = _startingL2OutputRoot; - _l2OutputOracleStartingTimestamp = int256(_startingL2OutputTimestamp); - l2OutputOracleStartingBlockNumber = _startingL2OutputBlockNumber; - - l2OutputOracleSubmissionInterval = _submissionInterval; + function setVerifierGateway(address _verifierGateway) public { verifierGateway = _verifierGateway; - - finalizationPeriodSeconds = 0; } /// @notice Allow the `fundDevAccounts` config to be overridden. diff --git a/packages/contracts-bedrock/src/L1/L2OutputOracle.sol b/packages/contracts-bedrock/src/L1/L2OutputOracle.sol index fe2c7dd7c8..0188400ef6 100644 --- a/packages/contracts-bedrock/src/L1/L2OutputOracle.sol +++ b/packages/contracts-bedrock/src/L1/L2OutputOracle.sol @@ -5,6 +5,7 @@ import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable import { ISemver } from "src/universal/ISemver.sol"; import { Types } from "src/libraries/Types.sol"; import { Constants } from "src/libraries/Constants.sol"; +import { SP1VerifierGateway } from "@sp1-contracts/src/SP1VerifierGateway.sol"; /// @custom:proxied /// @title L2OutputOracle @@ -41,6 +42,21 @@ contract L2OutputOracle is Initializable, ISemver { /// @custom:network-specific uint256 public finalizationPeriodSeconds; + uint public chainId; + + bytes32 public vkey; + SP1VerifierGateway public verifierGateway; + + mapping (uint => bytes32) public historicBlockHashes; + + struct PublicValuesStruct { + bytes32 l1Head; + bytes32 l2PreRoot; + bytes32 claimRoot; + uint256 claimBlockNum; + uint256 chainId; + } + /// @notice Emitted when an output is proposed. /// @param outputRoot The output root. /// @param l2OutputIndex The index of the output in the l2Outputs array. @@ -57,7 +73,7 @@ contract L2OutputOracle is Initializable, ISemver { /// @notice Semantic version. /// @custom:semver 1.8.0 - string public constant version = "1.8.0"; + string public constant version = "1.0.0"; /// @notice Constructs the L2OutputOracle contract. Initializes variables to the same values as /// in the getting-started config. @@ -69,7 +85,11 @@ contract L2OutputOracle is Initializable, ISemver { _startingTimestamp: 0, _proposer: address(0), _challenger: address(0), - _finalizationPeriodSeconds: 0 + _finalizationPeriodSeconds: 0, + _chainId: 0, + _vkey: bytes32(0), + _startingOutputRoot: bytes32(0), + _verifierGateway: address(0) }); } @@ -82,6 +102,9 @@ contract L2OutputOracle is Initializable, ISemver { /// @param _challenger The address of the challenger. /// @param _finalizationPeriodSeconds The minimum time (in seconds) that must elapse before a withdrawal /// can be finalized. + /// @param _chainId The chain ID of the L2 chain. + /// @param _vkey The verification key of the SP1 program. + /// @param _startingOutputRoot The output root of the starting block. function initialize( uint256 _submissionInterval, uint256 _l2BlockTime, @@ -89,7 +112,11 @@ contract L2OutputOracle is Initializable, ISemver { uint256 _startingTimestamp, address _proposer, address _challenger, - uint256 _finalizationPeriodSeconds + uint256 _finalizationPeriodSeconds, + uint256 _chainId, + bytes32 _vkey, + bytes32 _startingOutputRoot, + address _verifierGateway ) public initializer @@ -108,6 +135,17 @@ contract L2OutputOracle is Initializable, ISemver { proposer = _proposer; challenger = _challenger; finalizationPeriodSeconds = _finalizationPeriodSeconds; + chainId = _chainId; + vkey = _vkey; + verifierGateway = SP1VerifierGateway(_verifierGateway); + + l2Outputs.push( + Types.OutputProposal({ + outputRoot: _startingOutputRoot, + timestamp: uint128(_startingTimestamp), + l2BlockNumber: uint128(_startingBlockNumber) + }) + ); } /// @notice Getter for the submissionInterval. @@ -189,12 +227,13 @@ contract L2OutputOracle is Initializable, ISemver { bytes32 _outputRoot, uint256 _l2BlockNumber, bytes32 _l1BlockHash, - uint256 _l1BlockNumber + uint256 _l1BlockNumber, + bytes memory _proof ) external payable { - require(msg.sender == proposer, "L2OutputOracle: only the proposer address can propose new outputs"); + require(msg.sender == proposer || proposer == address(0), "L2OutputOracle: only the proposer address can propose new outputs"); require( _l2BlockNumber == nextBlockNumber(), @@ -208,20 +247,20 @@ contract L2OutputOracle is Initializable, ISemver { require(_outputRoot != bytes32(0), "L2OutputOracle: L2 output proposal cannot be the zero hash"); - if (_l1BlockHash != bytes32(0)) { - // This check allows the proposer to propose an output based on a given L1 block, - // without fear that it will be reorged out. - // It will also revert if the blockheight provided is more than 256 blocks behind the - // chain tip (as the hash will return as zero). This does open the door to a griefing - // attack in which the proposer's submission is censored until the block is no longer - // retrievable, if the proposer is experiencing this attack it can simply leave out the - // blockhash value, and delay submission until it is confident that the L1 block is - // finalized. - require( - blockhash(_l1BlockNumber) == _l1BlockHash, - "L2OutputOracle: block hash does not match the hash at the expected height" - ); - } + require( + historicBlockHashes[_l1BlockNumber] == _l1BlockHash, + "L2OutputOracle: block hash does not match the hash at the expected height" + ); + + PublicValuesStruct memory publicValues = PublicValuesStruct({ + l1Head: _l1BlockHash, + l2PreRoot: l2Outputs[nextOutputIndex() - 1].outputRoot, + claimRoot: _outputRoot, + claimBlockNum: _l2BlockNumber, + chainId: chainId + }); + + verifierGateway.verifyProof(vkey, abi.encode(publicValues), _proof); emit OutputProposed(_outputRoot, nextOutputIndex(), _l2BlockNumber, block.timestamp); @@ -234,6 +273,16 @@ contract L2OutputOracle is Initializable, ISemver { ); } + // TODO: This should be safe against reorgs because if the block is reorged out, this will be too. + // Think more about this to confirm. + function checkpointBlockHash(uint256 _blockNumber, bytes32 _blockHash) external { + require( + blockhash(_blockNumber) == _blockHash, + "L2OutputOracle: block hash does not match the hash at the expected height" + ); + historicBlockHashes[_blockNumber] = _blockHash; + } + /// @notice Returns an output by index. Needed to return a struct instead of a tuple. /// @param _l2OutputIndex Index of the output to return. /// @return The output at the given index. diff --git a/packages/contracts-bedrock/src/L1/ZKL2OutputOracle.sol b/packages/contracts-bedrock/src/L1/ZKL2OutputOracle.sol deleted file mode 100644 index 29b67f5127..0000000000 --- a/packages/contracts-bedrock/src/L1/ZKL2OutputOracle.sol +++ /dev/null @@ -1,365 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; -import { ISemver } from "src/universal/ISemver.sol"; -import { Types } from "src/libraries/Types.sol"; -import { Constants } from "src/libraries/Constants.sol"; -import { SP1VerifierGateway } from "@sp1-contracts/src/SP1VerifierGateway.sol"; - -/// @custom:proxied -/// @title L2OutputOracle -/// @notice The L2OutputOracle contains an array of L2 state outputs, where each output is a -/// commitment to the state of the L2 chain. Other contracts like the OptimismPortal use -/// these outputs to verify information about the state of L2. -contract ZKL2OutputOracle is Initializable, ISemver { - /// @notice The number of the first L2 block recorded in this contract. - uint256 public startingBlockNumber; - - /// @notice The timestamp of the first L2 block recorded in this contract. - uint256 public startingTimestamp; - - /// @notice An array of L2 output proposals. - Types.OutputProposal[] internal l2Outputs; - - /// @notice The interval in L2 blocks at which checkpoints must be submitted. - /// @custom:network-specific - uint256 public submissionInterval; - - /// @notice The time between L2 blocks in seconds. Once set, this value MUST NOT be modified. - /// @custom:network-specific - uint256 public l2BlockTime; - - /// @notice The address of the challenger. Can be updated via upgrade. - /// @custom:network-specific - address public challenger; - - /// @notice The address of the proposer. Can be updated via upgrade. - /// @custom:network-specific - address public proposer; - - /// @notice The minimum time (in seconds) that must elapse before a withdrawal can be finalized. - /// @custom:network-specific - uint256 public finalizationPeriodSeconds; - - uint public chainId; - - bytes32 public vkey; - SP1VerifierGateway public verifierGateway; - - mapping (uint => bytes32) public historicBlockHashes; - - struct PublicValuesStruct { - bytes32 l1Head; - bytes32 l2PreRoot; - bytes32 claimRoot; - uint256 claimBlockNum; - uint256 chainId; - } - - /// @notice Emitted when an output is proposed. - /// @param outputRoot The output root. - /// @param l2OutputIndex The index of the output in the l2Outputs array. - /// @param l2BlockNumber The L2 block number of the output root. - /// @param l1Timestamp The L1 timestamp when proposed. - event OutputProposed( - bytes32 indexed outputRoot, uint256 indexed l2OutputIndex, uint256 indexed l2BlockNumber, uint256 l1Timestamp - ); - - /// @notice Emitted when outputs are deleted. - /// @param prevNextOutputIndex Next L2 output index before the deletion. - /// @param newNextOutputIndex Next L2 output index after the deletion. - event OutputsDeleted(uint256 indexed prevNextOutputIndex, uint256 indexed newNextOutputIndex); - - /// @notice Semantic version. - /// @custom:semver 1.8.0 - string public constant version = "1.0.0"; - - /// @notice Constructs the L2OutputOracle contract. Initializes variables to the same values as - /// in the getting-started config. - constructor() { - initialize({ - _submissionInterval: 1, - _l2BlockTime: 1, - _startingBlockNumber: 0, - _startingTimestamp: 0, - _proposer: address(0), - _challenger: address(0), - _finalizationPeriodSeconds: 0, - _chainId: 0, - _vkey: bytes32(0), - _startingOutputRoot: bytes32(0), - _verifierGateway: address(0) - }); - } - - /// @notice Initializer. - /// @param _submissionInterval Interval in blocks at which checkpoints must be submitted. - /// @param _l2BlockTime The time per L2 block, in seconds. - /// @param _startingBlockNumber The number of the first L2 block. - /// @param _startingTimestamp The timestamp of the first L2 block. - /// @param _proposer The address of the proposer. - /// @param _challenger The address of the challenger. - /// @param _finalizationPeriodSeconds The minimum time (in seconds) that must elapse before a withdrawal - /// can be finalized. - /// @param _chainId The chain ID of the L2 chain. - /// @param _vkey The verification key of the SP1 program. - /// @param _startingOutputRoot The output root of the starting block. - function initialize( - uint256 _submissionInterval, - uint256 _l2BlockTime, - uint256 _startingBlockNumber, - uint256 _startingTimestamp, - address _proposer, - address _challenger, - uint256 _finalizationPeriodSeconds, - uint256 _chainId, - bytes32 _vkey, - bytes32 _startingOutputRoot, - address _verifierGateway - ) - public - initializer - { - require(_submissionInterval > 0, "L2OutputOracle: submission interval must be greater than 0"); - require(_l2BlockTime > 0, "L2OutputOracle: L2 block time must be greater than 0"); - require( - _startingTimestamp <= block.timestamp, - "L2OutputOracle: starting L2 timestamp must be less than current time" - ); - - submissionInterval = _submissionInterval; - l2BlockTime = _l2BlockTime; - startingBlockNumber = _startingBlockNumber; - startingTimestamp = _startingTimestamp; - proposer = _proposer; - challenger = _challenger; - finalizationPeriodSeconds = _finalizationPeriodSeconds; - chainId = _chainId; - vkey = _vkey; - verifierGateway = SP1VerifierGateway(_verifierGateway); - - l2Outputs.push( - Types.OutputProposal({ - outputRoot: _startingOutputRoot, - timestamp: uint128(_startingTimestamp), - l2BlockNumber: uint128(_startingBlockNumber) - }) - ); - } - - /// @notice Getter for the submissionInterval. - /// Public getter is legacy and will be removed in the future. Use `submissionInterval` instead. - /// @return Submission interval. - /// @custom:legacy - function SUBMISSION_INTERVAL() external view returns (uint256) { - return submissionInterval; - } - - /// @notice Getter for the l2BlockTime. - /// Public getter is legacy and will be removed in the future. Use `l2BlockTime` instead. - /// @return L2 block time. - /// @custom:legacy - function L2_BLOCK_TIME() external view returns (uint256) { - return l2BlockTime; - } - - /// @notice Getter for the challenger address. - /// Public getter is legacy and will be removed in the future. Use `challenger` instead. - /// @return Address of the challenger. - /// @custom:legacy - function CHALLENGER() external view returns (address) { - return challenger; - } - - /// @notice Getter for the proposer address. - /// Public getter is legacy and will be removed in the future. Use `proposer` instead. - /// @return Address of the proposer. - /// @custom:legacy - function PROPOSER() external view returns (address) { - return proposer; - } - - /// @notice Getter for the finalizationPeriodSeconds. - /// Public getter is legacy and will be removed in the future. Use `finalizationPeriodSeconds` instead. - /// @return Finalization period in seconds. - /// @custom:legacy - function FINALIZATION_PERIOD_SECONDS() external view returns (uint256) { - return finalizationPeriodSeconds; - } - - /// @notice Deletes all output proposals after and including the proposal that corresponds to - /// the given output index. Only the challenger address can delete outputs. - /// @param _l2OutputIndex Index of the first L2 output to be deleted. - /// All outputs after this output will also be deleted. - function deleteL2Outputs(uint256 _l2OutputIndex) external { - require(msg.sender == challenger, "L2OutputOracle: only the challenger address can delete outputs"); - - // Make sure we're not *increasing* the length of the array. - require( - _l2OutputIndex < l2Outputs.length, "L2OutputOracle: cannot delete outputs after the latest output index" - ); - - // Do not allow deleting any outputs that have already been finalized. - require( - block.timestamp - l2Outputs[_l2OutputIndex].timestamp < finalizationPeriodSeconds, - "L2OutputOracle: cannot delete outputs that have already been finalized" - ); - - uint256 prevNextL2OutputIndex = nextOutputIndex(); - - // Use assembly to delete the array elements because Solidity doesn't allow it. - assembly { - sstore(l2Outputs.slot, _l2OutputIndex) - } - - emit OutputsDeleted(prevNextL2OutputIndex, _l2OutputIndex); - } - - /// @notice Accepts an outputRoot and the timestamp of the corresponding L2 block. - /// The timestamp must be equal to the current value returned by `nextTimestamp()` in - /// order to be accepted. This function may only be called by the Proposer. - /// @param _outputRoot The L2 output of the checkpoint block. - /// @param _l2BlockNumber The L2 block number that resulted in _outputRoot. - /// @param _l1BlockHash A block hash which must be included in the current chain. - /// @param _l1BlockNumber The block number with the specified block hash. - function proposeL2Output( - bytes32 _outputRoot, - uint256 _l2BlockNumber, - bytes32 _l1BlockHash, - uint256 _l1BlockNumber, - bytes memory _proof - ) - external - payable - { - require(msg.sender == proposer || proposer == address(0), "L2OutputOracle: only the proposer address can propose new outputs"); - - require( - _l2BlockNumber == nextBlockNumber(), - "L2OutputOracle: block number must be equal to next expected block number" - ); - - require( - computeL2Timestamp(_l2BlockNumber) < block.timestamp, - "L2OutputOracle: cannot propose L2 output in the future" - ); - - require(_outputRoot != bytes32(0), "L2OutputOracle: L2 output proposal cannot be the zero hash"); - - require( - historicBlockHashes[_l1BlockNumber] == _l1BlockHash, - "L2OutputOracle: block hash does not match the hash at the expected height" - ); - - PublicValuesStruct memory publicValues = PublicValuesStruct({ - l1Head: _l1BlockHash, - l2PreRoot: l2Outputs[nextOutputIndex() - 1].outputRoot, - claimRoot: _outputRoot, - claimBlockNum: _l2BlockNumber, - chainId: chainId - }); - - verifierGateway.verifyProof(vkey, abi.encode(publicValues), _proof); - - emit OutputProposed(_outputRoot, nextOutputIndex(), _l2BlockNumber, block.timestamp); - - l2Outputs.push( - Types.OutputProposal({ - outputRoot: _outputRoot, - timestamp: uint128(block.timestamp), - l2BlockNumber: uint128(_l2BlockNumber) - }) - ); - } - - // TODO: This should be safe against reorgs because if the block is reorged out, this will be too. - // Think more about this to confirm. - function checkpointBlockHash(uint256 _blockNumber, bytes32 _blockHash) external { - require( - blockhash(_blockNumber) == _blockHash, - "L2OutputOracle: block hash does not match the hash at the expected height" - ); - historicBlockHashes[_blockNumber] = _blockHash; - } - - /// @notice Returns an output by index. Needed to return a struct instead of a tuple. - /// @param _l2OutputIndex Index of the output to return. - /// @return The output at the given index. - function getL2Output(uint256 _l2OutputIndex) external view returns (Types.OutputProposal memory) { - return l2Outputs[_l2OutputIndex]; - } - - /// @notice Returns the index of the L2 output that checkpoints a given L2 block number. - /// Uses a binary search to find the first output greater than or equal to the given - /// block. - /// @param _l2BlockNumber L2 block number to find a checkpoint for. - /// @return Index of the first checkpoint that commits to the given L2 block number. - function getL2OutputIndexAfter(uint256 _l2BlockNumber) public view returns (uint256) { - // Make sure an output for this block number has actually been proposed. - require( - _l2BlockNumber <= latestBlockNumber(), - "L2OutputOracle: cannot get output for a block that has not been proposed" - ); - - // Make sure there's at least one output proposed. - require(l2Outputs.length > 0, "L2OutputOracle: cannot get output as no outputs have been proposed yet"); - - // Find the output via binary search, guaranteed to exist. - uint256 lo = 0; - uint256 hi = l2Outputs.length; - while (lo < hi) { - uint256 mid = (lo + hi) / 2; - if (l2Outputs[mid].l2BlockNumber < _l2BlockNumber) { - lo = mid + 1; - } else { - hi = mid; - } - } - - return lo; - } - - /// @notice Returns the L2 output proposal that checkpoints a given L2 block number. - /// Uses a binary search to find the first output greater than or equal to the given - /// block. - /// @param _l2BlockNumber L2 block number to find a checkpoint for. - /// @return First checkpoint that commits to the given L2 block number. - function getL2OutputAfter(uint256 _l2BlockNumber) external view returns (Types.OutputProposal memory) { - return l2Outputs[getL2OutputIndexAfter(_l2BlockNumber)]; - } - - /// @notice Returns the number of outputs that have been proposed. - /// Will revert if no outputs have been proposed yet. - /// @return The number of outputs that have been proposed. - function latestOutputIndex() external view returns (uint256) { - return l2Outputs.length - 1; - } - - /// @notice Returns the index of the next output to be proposed. - /// @return The index of the next output to be proposed. - function nextOutputIndex() public view returns (uint256) { - return l2Outputs.length; - } - - /// @notice Returns the block number of the latest submitted L2 output proposal. - /// If no proposals been submitted yet then this function will return the starting - /// block number. - /// @return Latest submitted L2 block number. - function latestBlockNumber() public view returns (uint256) { - return l2Outputs.length == 0 ? startingBlockNumber : l2Outputs[l2Outputs.length - 1].l2BlockNumber; - } - - /// @notice Computes the block number of the next L2 block that needs to be checkpointed. - /// @return Next L2 block number. - function nextBlockNumber() public view returns (uint256) { - return latestBlockNumber() + submissionInterval; - } - - /// @notice Returns the L2 timestamp corresponding to a given L2 block number. - /// @param _l2BlockNumber The L2 block number of the target block. - /// @return L2 timestamp of the given block. - function computeL2Timestamp(uint256 _l2BlockNumber) public view returns (uint256) { - return startingTimestamp + ((_l2BlockNumber - startingBlockNumber) * l2BlockTime); - } -} diff --git a/packages/contracts-bedrock/test/BenchmarkTest.t.sol b/packages/contracts-bedrock/test/BenchmarkTest.t.sol index 4722107a31..884b80f5ea 100644 --- a/packages/contracts-bedrock/test/BenchmarkTest.t.sol +++ b/packages/contracts-bedrock/test/BenchmarkTest.t.sol @@ -30,98 +30,98 @@ contract SetPrevBaseFee_Test is CommonTest { // In order to achieve this we make no assertions, and handle everything else in the setUp() // function. contract GasBenchMark_OptimismPortal is CommonTest { - // Reusable default values for a test withdrawal - Types.WithdrawalTransaction _defaultTx; - - uint256 _proposedOutputIndex; - uint256 _proposedBlockNumber; - bytes[] _withdrawalProof; - Types.OutputRootProof internal _outputRootProof; - bytes32 _outputRoot; - - // Use a constructor to set the storage vars above, so as to minimize the number of ffi calls. - constructor() { - super.setUp(); - _defaultTx = Types.WithdrawalTransaction({ - nonce: 0, - sender: alice, - target: bob, - value: 100, - gasLimit: 100_000, - data: hex"" - }); - - // Get withdrawal proof data we can use for testing. - bytes32 _storageRoot; - bytes32 _stateRoot; - (_stateRoot, _storageRoot, _outputRoot,, _withdrawalProof) = ffi.getProveWithdrawalTransactionInputs(_defaultTx); - - // Setup a dummy output root proof for reuse. - _outputRootProof = Types.OutputRootProof({ - version: bytes32(uint256(0)), - stateRoot: _stateRoot, - messagePasserStorageRoot: _storageRoot, - latestBlockhash: bytes32(uint256(0)) - }); - _proposedBlockNumber = l2OutputOracle.nextBlockNumber(); - _proposedOutputIndex = l2OutputOracle.nextOutputIndex(); - } - - // Get the system into a nice ready-to-use state. - function setUp() public virtual override { - // Configure the oracle to return the output root we've prepared. - vm.warp(l2OutputOracle.computeL2Timestamp(_proposedBlockNumber) + 1); - vm.prank(l2OutputOracle.PROPOSER()); - l2OutputOracle.proposeL2Output(_outputRoot, _proposedBlockNumber, 0, 0); - - // Warp beyond the finalization period for the block we've proposed. - vm.warp( - l2OutputOracle.getL2Output(_proposedOutputIndex).timestamp + l2OutputOracle.FINALIZATION_PERIOD_SECONDS() - + 1 - ); - - // Fund the portal so that we can withdraw ETH. - vm.deal(address(optimismPortal), 0xFFFFFFFF); - } - - function test_depositTransaction_benchmark() external { - optimismPortal.depositTransaction{ value: 100 }( - address(1), 0, 50000, false, hex"0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff0000" - ); - } - - function test_depositTransaction_benchmark_1() external { - setPrevBaseFee(vm, address(optimismPortal), 1 gwei); - optimismPortal.depositTransaction{ value: 100 }( - address(1), 0, 50000, false, hex"0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff0000" - ); - } - - function test_proveWithdrawalTransaction_benchmark() external { - optimismPortal.proveWithdrawalTransaction(_defaultTx, _proposedOutputIndex, _outputRootProof, _withdrawalProof); - } -} - -contract GasBenchMark_L1CrossDomainMessenger is Bridge_Initializer { - function test_sendMessage_benchmark_0() external { - vm.pauseGasMetering(); - setPrevBaseFee(vm, address(optimismPortal), 1 gwei); - // The amount of data typically sent during a bridge deposit. - bytes memory data = - hex"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; - vm.resumeGasMetering(); - l1CrossDomainMessenger.sendMessage(bob, data, uint32(100)); - } - - function test_sendMessage_benchmark_1() external { - vm.pauseGasMetering(); - setPrevBaseFee(vm, address(optimismPortal), 10 gwei); - // The amount of data typically sent during a bridge deposit. - bytes memory data = - hex"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; - vm.resumeGasMetering(); - l1CrossDomainMessenger.sendMessage(bob, data, uint32(100)); - } +// // Reusable default values for a test withdrawal +// Types.WithdrawalTransaction _defaultTx; + +// uint256 _proposedOutputIndex; +// uint256 _proposedBlockNumber; +// bytes[] _withdrawalProof; +// Types.OutputRootProof internal _outputRootProof; +// bytes32 _outputRoot; + +// // Use a constructor to set the storage vars above, so as to minimize the number of ffi calls. +// constructor() { +// super.setUp(); +// _defaultTx = Types.WithdrawalTransaction({ +// nonce: 0, +// sender: alice, +// target: bob, +// value: 100, +// gasLimit: 100_000, +// data: hex"" +// }); + +// // Get withdrawal proof data we can use for testing. +// bytes32 _storageRoot; +// bytes32 _stateRoot; +// (_stateRoot, _storageRoot, _outputRoot,, _withdrawalProof) = ffi.getProveWithdrawalTransactionInputs(_defaultTx); + +// // Setup a dummy output root proof for reuse. +// _outputRootProof = Types.OutputRootProof({ +// version: bytes32(uint256(0)), +// stateRoot: _stateRoot, +// messagePasserStorageRoot: _storageRoot, +// latestBlockhash: bytes32(uint256(0)) +// }); +// _proposedBlockNumber = l2OutputOracle.nextBlockNumber(); +// _proposedOutputIndex = l2OutputOracle.nextOutputIndex(); +// } + +// // Get the system into a nice ready-to-use state. +// function setUp() public virtual override { +// // Configure the oracle to return the output root we've prepared. +// vm.warp(l2OutputOracle.computeL2Timestamp(_proposedBlockNumber) + 1); +// vm.prank(l2OutputOracle.PROPOSER()); +// l2OutputOracle.proposeL2Output(_outputRoot, _proposedBlockNumber, 0, 0); + +// // Warp beyond the finalization period for the block we've proposed. +// vm.warp( +// l2OutputOracle.getL2Output(_proposedOutputIndex).timestamp + l2OutputOracle.FINALIZATION_PERIOD_SECONDS() +// + 1 +// ); + +// // Fund the portal so that we can withdraw ETH. +// vm.deal(address(optimismPortal), 0xFFFFFFFF); +// } + +// function test_depositTransaction_benchmark() external { +// optimismPortal.depositTransaction{ value: 100 }( +// address(1), 0, 50000, false, hex"0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff0000" +// ); +// } + +// function test_depositTransaction_benchmark_1() external { +// setPrevBaseFee(vm, address(optimismPortal), 1 gwei); +// optimismPortal.depositTransaction{ value: 100 }( +// address(1), 0, 50000, false, hex"0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff0000" +// ); +// } + +// function test_proveWithdrawalTransaction_benchmark() external { +// optimismPortal.proveWithdrawalTransaction(_defaultTx, _proposedOutputIndex, _outputRootProof, _withdrawalProof); +// } +// } + +// contract GasBenchMark_L1CrossDomainMessenger is Bridge_Initializer { +// function test_sendMessage_benchmark_0() external { +// vm.pauseGasMetering(); +// setPrevBaseFee(vm, address(optimismPortal), 1 gwei); +// // The amount of data typically sent during a bridge deposit. +// bytes memory data = +// hex"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; +// vm.resumeGasMetering(); +// l1CrossDomainMessenger.sendMessage(bob, data, uint32(100)); +// } + +// function test_sendMessage_benchmark_1() external { +// vm.pauseGasMetering(); +// setPrevBaseFee(vm, address(optimismPortal), 10 gwei); +// // The amount of data typically sent during a bridge deposit. +// bytes memory data = +// hex"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; +// vm.resumeGasMetering(); +// l1CrossDomainMessenger.sendMessage(bob, data, uint32(100)); +// } } contract GasBenchMark_L1StandardBridge_Deposit is Bridge_Initializer { @@ -205,7 +205,7 @@ contract GasBenchMark_L2OutputOracle is CommonTest { vm.startPrank(proposer); } - function test_proposeL2Output_benchmark() external { - l2OutputOracle.proposeL2Output(nonZeroHash, nextBlockNumber, 0, 0); - } + // function test_proposeL2Output_benchmark() external { + // l2OutputOracle.proposeL2Output(nonZeroHash, nextBlockNumber, 0, 0); + // } } diff --git a/packages/contracts-bedrock/test/L1/L2OutputOracle.t.sol b/packages/contracts-bedrock/test/L1/L2OutputOracle.t.sol index 3d89809e9b..ce2ac51d61 100644 --- a/packages/contracts-bedrock/test/L1/L2OutputOracle.t.sol +++ b/packages/contracts-bedrock/test/L1/L2OutputOracle.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.15; // Testing utilities -import { stdError } from "forge-std/Test.sol"; +import { stdError, console } from "forge-std/Test.sol"; import { CommonTest } from "test/setup/CommonTest.sol"; import { NextImpl } from "test/mocks/NextImpl.sol"; import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; @@ -16,488 +16,51 @@ import { Proxy } from "src/universal/Proxy.sol"; // Target contract import { L2OutputOracle } from "src/L1/L2OutputOracle.sol"; +import { SP1VerifierGateway } from "@sp1-contracts/src/SP1VerifierGateway.sol"; +import { SP1Verifier } from "@sp1-contracts/src/v1.0.1/SP1Verifier.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(); +contract L2OutputOracle_Init is CommonTest { + function setUp() public virtual override { + address sp1Verifier = address(new SP1Verifier()); + SP1VerifierGateway verifierGateway = new SP1VerifierGateway(); + verifierGateway.addRoute(sp1Verifier); - assertEq(oracleImpl.SUBMISSION_INTERVAL(), 1); - assertEq(oracleImpl.submissionInterval(), 1); - assertEq(oracleImpl.L2_BLOCK_TIME(), 1); - assertEq(oracleImpl.l2BlockTime(), 1); - assertEq(oracleImpl.latestBlockNumber(), 0); - assertEq(oracleImpl.startingBlockNumber(), 0); - assertEq(oracleImpl.startingTimestamp(), 0); - assertEq(oracleImpl.PROPOSER(), address(0)); - assertEq(oracleImpl.proposer(), address(0)); - assertEq(oracleImpl.CHALLENGER(), address(0)); - assertEq(oracleImpl.challenger(), address(0)); - assertEq(oracleImpl.finalizationPeriodSeconds(), 0); - assertEq(oracleImpl.FINALIZATION_PERIOD_SECONDS(), 0); - } - - /// @dev Tests that the proxy is initialized with the correct values. - function test_initialize_succeeds() external { - address proposer = deploy.cfg().l2OutputOracleProposer(); - address challenger = deploy.cfg().l2OutputOracleChallenger(); - uint256 submissionInterval = deploy.cfg().l2OutputOracleSubmissionInterval(); - uint256 startingBlockNumber = deploy.cfg().l2OutputOracleStartingBlockNumber(); - uint256 startingTimestamp = deploy.cfg().l2OutputOracleStartingTimestamp(); - uint256 l2BlockTime = deploy.cfg().l2BlockTime(); - uint256 finalizationPeriodSeconds = deploy.cfg().finalizationPeriodSeconds(); - - assertEq(l2OutputOracle.SUBMISSION_INTERVAL(), submissionInterval); - assertEq(l2OutputOracle.submissionInterval(), submissionInterval); - assertEq(l2OutputOracle.L2_BLOCK_TIME(), l2BlockTime); - assertEq(l2OutputOracle.l2BlockTime(), l2BlockTime); - assertEq(l2OutputOracle.latestBlockNumber(), startingBlockNumber); - assertEq(l2OutputOracle.startingBlockNumber(), startingBlockNumber); - assertEq(l2OutputOracle.startingTimestamp(), startingTimestamp); - assertEq(l2OutputOracle.finalizationPeriodSeconds(), finalizationPeriodSeconds); - assertEq(l2OutputOracle.PROPOSER(), proposer); - assertEq(l2OutputOracle.proposer(), proposer); - assertEq(l2OutputOracle.CHALLENGER(), challenger); - assertEq(l2OutputOracle.FINALIZATION_PERIOD_SECONDS(), finalizationPeriodSeconds); - assertEq(l2OutputOracle.challenger(), challenger); - } -} - -contract L2OutputOracle_getter_Test is CommonTest { - bytes32 proposedOutput1 = keccak256(abi.encode(1)); - - /// @dev Tests that `latestBlockNumber` returns the correct value. - function test_latestBlockNumber_succeeds() external { - uint256 proposedNumber = l2OutputOracle.nextBlockNumber(); - - // Roll to after the block number we'll propose - warpToProposeTime(proposedNumber); - vm.prank(deploy.cfg().l2OutputOracleProposer()); - l2OutputOracle.proposeL2Output(proposedOutput1, proposedNumber, 0, 0); - assertEq(l2OutputOracle.latestBlockNumber(), proposedNumber); - } - - /// @dev Tests that `getL2Output` returns the correct value. - function test_getL2Output_succeeds() external { - uint256 nextBlockNumber = l2OutputOracle.nextBlockNumber(); - uint256 nextOutputIndex = l2OutputOracle.nextOutputIndex(); - warpToProposeTime(nextBlockNumber); - vm.prank(deploy.cfg().l2OutputOracleProposer()); - l2OutputOracle.proposeL2Output(proposedOutput1, nextBlockNumber, 0, 0); - - Types.OutputProposal memory proposal = l2OutputOracle.getL2Output(nextOutputIndex); - assertEq(proposal.outputRoot, proposedOutput1); - assertEq(proposal.timestamp, block.timestamp); - - // The block number is larger than the latest proposed output: - vm.expectRevert(stdError.indexOOBError); - l2OutputOracle.getL2Output(nextOutputIndex + 1); - } - - /// @dev Tests that `getL2OutputIndexAfter` returns the correct value - /// when the input is the exact block number of the proposal. - function test_getL2OutputIndexAfter_sameBlock_succeeds() external { - bytes32 output1 = keccak256(abi.encode(1)); - uint256 nextBlockNumber1 = l2OutputOracle.nextBlockNumber(); - warpToProposeTime(nextBlockNumber1); - vm.prank(deploy.cfg().l2OutputOracleProposer()); - l2OutputOracle.proposeL2Output(output1, nextBlockNumber1, 0, 0); - - // Querying with exact same block as proposed returns the proposal. - uint256 index1 = l2OutputOracle.getL2OutputIndexAfter(nextBlockNumber1); - assertEq(index1, 0); - } - - /// @dev Tests that `getL2OutputIndexAfter` returns the correct value - /// when the input is the previous block number of the proposal. - function test_getL2OutputIndexAfter_previousBlock_succeeds() external { - bytes32 output1 = keccak256(abi.encode(1)); - uint256 nextBlockNumber1 = l2OutputOracle.nextBlockNumber(); - warpToProposeTime(nextBlockNumber1); - vm.prank(deploy.cfg().l2OutputOracleProposer()); - l2OutputOracle.proposeL2Output(output1, nextBlockNumber1, 0, 0); - - // Querying with previous block returns the proposal too. - uint256 index1 = l2OutputOracle.getL2OutputIndexAfter(nextBlockNumber1 - 1); - assertEq(index1, 0); - } - - /// @dev Tests that `getL2OutputIndexAfter` returns the correct value. - function test_getL2OutputIndexAfter_multipleOutputsExist_succeeds() external { - bytes32 output1 = keccak256(abi.encode(1)); - uint256 nextBlockNumber1 = l2OutputOracle.nextBlockNumber(); - warpToProposeTime(nextBlockNumber1); - vm.prank(deploy.cfg().l2OutputOracleProposer()); - l2OutputOracle.proposeL2Output(output1, nextBlockNumber1, 0, 0); - - bytes32 output2 = keccak256(abi.encode(2)); - uint256 nextBlockNumber2 = l2OutputOracle.nextBlockNumber(); - warpToProposeTime(nextBlockNumber2); - vm.prank(deploy.cfg().l2OutputOracleProposer()); - l2OutputOracle.proposeL2Output(output2, nextBlockNumber2, 0, 0); - - bytes32 output3 = keccak256(abi.encode(3)); - uint256 nextBlockNumber3 = l2OutputOracle.nextBlockNumber(); - warpToProposeTime(nextBlockNumber3); - vm.prank(deploy.cfg().l2OutputOracleProposer()); - l2OutputOracle.proposeL2Output(output3, nextBlockNumber3, 0, 0); - - bytes32 output4 = keccak256(abi.encode(4)); - uint256 nextBlockNumber4 = l2OutputOracle.nextBlockNumber(); - warpToProposeTime(nextBlockNumber4); - vm.prank(deploy.cfg().l2OutputOracleProposer()); - l2OutputOracle.proposeL2Output(output4, nextBlockNumber4, 0, 0); - - // Querying with a block number between the first and second proposal - uint256 index1 = l2OutputOracle.getL2OutputIndexAfter(nextBlockNumber1 + 1); - assertEq(index1, 1); - - // Querying with a block number between the second and third proposal - uint256 index2 = l2OutputOracle.getL2OutputIndexAfter(nextBlockNumber2 + 1); - assertEq(index2, 2); - - // Querying with a block number between the third and fourth proposal - uint256 index3 = l2OutputOracle.getL2OutputIndexAfter(nextBlockNumber3 + 1); - assertEq(index3, 3); - } - - /// @dev Tests that `getL2OutputIndexAfter` reverts when no output exists. - function test_getL2OutputIndexAfter_noOutputsExis_reverts() external { - vm.expectRevert("L2OutputOracle: cannot get output as no outputs have been proposed yet"); - l2OutputOracle.getL2OutputIndexAfter(0); - } - - /// @dev Tests that `nextBlockNumber` returns the correct value. - function test_nextBlockNumber_succeeds() external view { - assertEq( - l2OutputOracle.nextBlockNumber(), - // The return value should match this arithmetic - l2OutputOracle.latestBlockNumber() + l2OutputOracle.SUBMISSION_INTERVAL() - ); - } - - /// @dev Tests that `computeL2Timestamp` returns the correct value. - function test_computeL2Timestamp_succeeds() external { - uint256 startingBlockNumber = deploy.cfg().l2OutputOracleStartingBlockNumber(); - uint256 startingTimestamp = deploy.cfg().l2OutputOracleStartingTimestamp(); - uint256 l2BlockTime = deploy.cfg().l2BlockTime(); - - // reverts if timestamp is too low - vm.expectRevert(stdError.arithmeticError); - l2OutputOracle.computeL2Timestamp(startingBlockNumber - 1); - - // check timestamp for the very first block - assertEq(l2OutputOracle.computeL2Timestamp(startingBlockNumber), startingTimestamp); - - // check timestamp for the first block after the starting block - assertEq(l2OutputOracle.computeL2Timestamp(startingBlockNumber + 1), startingTimestamp + l2BlockTime); - - // check timestamp for some other block number - assertEq( - l2OutputOracle.computeL2Timestamp(startingBlockNumber + 96024), startingTimestamp + l2BlockTime * 96024 - ); - } -} - -contract L2OutputOracle_proposeL2Output_Test is CommonTest { - /// @dev Test that `proposeL2Output` succeeds for a valid input - /// and when a block hash and number are not specified. - function test_proposeL2Output_proposeAnotherOutput_succeeds() public { - proposeAnotherOutput(); - } - - /// @dev Tests that `proposeL2Output` succeeds when given valid input and - /// when a block hash and number are specified for reorg protection. - function test_proposeWithBlockhashAndHeight_succeeds() external { - // Get the number and hash of a previous block in the chain - uint256 prevL1BlockNumber = block.number - 1; - bytes32 prevL1BlockHash = blockhash(prevL1BlockNumber); - - uint256 nextBlockNumber = l2OutputOracle.nextBlockNumber(); - warpToProposeTime(nextBlockNumber); - vm.prank(deploy.cfg().l2OutputOracleProposer()); - l2OutputOracle.proposeL2Output(nonZeroHash, nextBlockNumber, prevL1BlockHash, prevL1BlockNumber); - } - - /// @dev Tests that `proposeL2Output` reverts when called by a party - /// that is not the proposer. - function test_proposeL2Output_notProposer_reverts() external { - uint256 nextBlockNumber = l2OutputOracle.nextBlockNumber(); - warpToProposeTime(nextBlockNumber); - - vm.prank(address(128)); - vm.expectRevert("L2OutputOracle: only the proposer address can propose new outputs"); - l2OutputOracle.proposeL2Output(nonZeroHash, nextBlockNumber, 0, 0); - } - - /// @dev Tests that `proposeL2Output` reverts when given a zero blockhash. - function test_proposeL2Output_emptyOutput_reverts() external { - bytes32 outputToPropose = bytes32(0); - uint256 nextBlockNumber = l2OutputOracle.nextBlockNumber(); - warpToProposeTime(nextBlockNumber); - vm.prank(deploy.cfg().l2OutputOracleProposer()); - vm.expectRevert("L2OutputOracle: L2 output proposal cannot be the zero hash"); - l2OutputOracle.proposeL2Output(outputToPropose, nextBlockNumber, 0, 0); - } - - /// @dev Tests that `proposeL2Output` reverts when given a block number - /// that does not match the next expected block number. - function test_proposeL2Output_unexpectedBlockNumber_reverts() external { - uint256 nextBlockNumber = l2OutputOracle.nextBlockNumber(); - warpToProposeTime(nextBlockNumber); - vm.prank(deploy.cfg().l2OutputOracleProposer()); - vm.expectRevert("L2OutputOracle: block number must be equal to next expected block number"); - l2OutputOracle.proposeL2Output(nonZeroHash, nextBlockNumber - 1, 0, 0); - } - - /// @dev Tests that `proposeL2Output` reverts when given a block number - /// that has a timestamp in the future. - function test_proposeL2Output_futureTimetamp_reverts() external { - uint256 nextBlockNumber = l2OutputOracle.nextBlockNumber(); - uint256 nextTimestamp = l2OutputOracle.computeL2Timestamp(nextBlockNumber); - vm.warp(nextTimestamp); - vm.prank(deploy.cfg().l2OutputOracleProposer()); - vm.expectRevert("L2OutputOracle: cannot propose L2 output in the future"); - l2OutputOracle.proposeL2Output(nonZeroHash, nextBlockNumber, 0, 0); - } - - /// @dev Tests that `proposeL2Output` reverts when given a block number - /// whose hash does not match the given block hash. - function test_proposeL2Output_wrongFork_reverts() external { - uint256 nextBlockNumber = l2OutputOracle.nextBlockNumber(); - warpToProposeTime(nextBlockNumber); - vm.prank(deploy.cfg().l2OutputOracleProposer()); - vm.expectRevert("L2OutputOracle: block hash does not match the hash at the expected height"); - l2OutputOracle.proposeL2Output(nonZeroHash, nextBlockNumber, bytes32(uint256(0x01)), block.number); - } - - /// @dev Tests that `proposeL2Output` reverts when given a block number - /// whose block hash does not match the given block hash. - function test_proposeL2Output_unmatchedBlockhash_reverts() external { - // Move ahead to block 100 so that we can reference historical blocks - vm.roll(100); - - // Get the number and hash of a previous block in the chain - uint256 l1BlockNumber = block.number - 1; - bytes32 l1BlockHash = blockhash(l1BlockNumber); - - uint256 nextBlockNumber = l2OutputOracle.nextBlockNumber(); - warpToProposeTime(nextBlockNumber); - vm.prank(deploy.cfg().l2OutputOracleProposer()); - - // This will fail when foundry no longer returns zerod block hashes - vm.expectRevert("L2OutputOracle: block hash does not match the hash at the expected height"); - l2OutputOracle.proposeL2Output(nonZeroHash, nextBlockNumber, l1BlockHash, l1BlockNumber - 1); - } -} - -contract L2OutputOracle_deleteOutputs_Test is CommonTest { - /// @dev Tests that `deleteL2Outputs` succeeds for a single output. - function test_deleteOutputs_singleOutput_succeeds() external { - proposeAnotherOutput(); - proposeAnotherOutput(); - - uint256 latestBlockNumber = l2OutputOracle.latestBlockNumber(); - uint256 latestOutputIndex = l2OutputOracle.latestOutputIndex(); - Types.OutputProposal memory newLatestOutput = l2OutputOracle.getL2Output(latestOutputIndex - 1); - - vm.prank(l2OutputOracle.CHALLENGER()); - vm.prank(l2OutputOracle.challenger()); - vm.expectEmit(true, true, false, false); - emit OutputsDeleted(latestOutputIndex + 1, latestOutputIndex); - l2OutputOracle.deleteL2Outputs(latestOutputIndex); - - // validate latestBlockNumber has been reduced - uint256 latestBlockNumberAfter = l2OutputOracle.latestBlockNumber(); - uint256 latestOutputIndexAfter = l2OutputOracle.latestOutputIndex(); - uint256 submissionInterval = deploy.cfg().l2OutputOracleSubmissionInterval(); - assertEq(latestBlockNumber - submissionInterval, latestBlockNumberAfter); - - // validate that the new latest output is as expected. - Types.OutputProposal memory proposal = l2OutputOracle.getL2Output(latestOutputIndexAfter); - assertEq(newLatestOutput.outputRoot, proposal.outputRoot); - assertEq(newLatestOutput.timestamp, proposal.timestamp); - } - - /// @dev Tests that `deleteL2Outputs` succeeds for multiple outputs. - function test_deleteOutputs_multipleOutputs_succeeds() external { - proposeAnotherOutput(); - proposeAnotherOutput(); - proposeAnotherOutput(); - proposeAnotherOutput(); - - uint256 latestBlockNumber = l2OutputOracle.latestBlockNumber(); - uint256 latestOutputIndex = l2OutputOracle.latestOutputIndex(); - Types.OutputProposal memory newLatestOutput = l2OutputOracle.getL2Output(latestOutputIndex - 3); - - vm.prank(l2OutputOracle.CHALLENGER()); - vm.prank(l2OutputOracle.challenger()); - vm.expectEmit(true, true, false, false); - emit OutputsDeleted(latestOutputIndex + 1, latestOutputIndex - 2); - l2OutputOracle.deleteL2Outputs(latestOutputIndex - 2); - - // validate latestBlockNumber has been reduced - uint256 latestBlockNumberAfter = l2OutputOracle.latestBlockNumber(); - uint256 latestOutputIndexAfter = l2OutputOracle.latestOutputIndex(); - uint256 submissionInterval = deploy.cfg().l2OutputOracleSubmissionInterval(); - assertEq(latestBlockNumber - submissionInterval * 3, latestBlockNumberAfter); - - // validate that the new latest output is as expected. - Types.OutputProposal memory proposal = l2OutputOracle.getL2Output(latestOutputIndexAfter); - assertEq(newLatestOutput.outputRoot, proposal.outputRoot); - assertEq(newLatestOutput.timestamp, proposal.timestamp); - } - - /// @dev Tests that `deleteL2Outputs` reverts when not called by the challenger. - function test_deleteL2Outputs_ifNotChallenger_reverts() external { - uint256 latestBlockNumber = l2OutputOracle.latestBlockNumber(); - - vm.expectRevert("L2OutputOracle: only the challenger address can delete outputs"); - l2OutputOracle.deleteL2Outputs(latestBlockNumber); - } - - /// @dev Tests that `deleteL2Outputs` reverts for a non-existant output index. - function test_deleteL2Outputs_nonExistent_reverts() external { - proposeAnotherOutput(); - - uint256 latestBlockNumber = l2OutputOracle.latestBlockNumber(); - - vm.prank(l2OutputOracle.CHALLENGER()); - vm.prank(l2OutputOracle.challenger()); - vm.expectRevert("L2OutputOracle: cannot delete outputs after the latest output index"); - l2OutputOracle.deleteL2Outputs(latestBlockNumber + 1); - } - - /// @dev Tests that `deleteL2Outputs` reverts when trying to delete outputs - /// after the latest output index. - function test_deleteL2Outputs_afterLatest_reverts() external { - proposeAnotherOutput(); - proposeAnotherOutput(); - proposeAnotherOutput(); - - // Delete the latest two outputs - uint256 latestOutputIndex = l2OutputOracle.latestOutputIndex(); - vm.prank(l2OutputOracle.CHALLENGER()); - vm.prank(l2OutputOracle.challenger()); - l2OutputOracle.deleteL2Outputs(latestOutputIndex - 2); - - // Now try to delete the same output again - vm.prank(l2OutputOracle.CHALLENGER()); - vm.prank(l2OutputOracle.challenger()); - vm.expectRevert("L2OutputOracle: cannot delete outputs after the latest output index"); - l2OutputOracle.deleteL2Outputs(latestOutputIndex - 2); - } - - /// @dev Tests that `deleteL2Outputs` reverts for finalized outputs. - function test_deleteL2Outputs_finalized_reverts() external { - proposeAnotherOutput(); - - // Warp past the finalization period + 1 second - vm.warp(block.timestamp + l2OutputOracle.FINALIZATION_PERIOD_SECONDS() + 1); - - uint256 latestOutputIndex = l2OutputOracle.latestOutputIndex(); - - // Try to delete a finalized output - vm.prank(l2OutputOracle.CHALLENGER()); - vm.prank(l2OutputOracle.challenger()); - vm.expectRevert("L2OutputOracle: cannot delete outputs that have already been finalized"); - l2OutputOracle.deleteL2Outputs(latestOutputIndex); + super.enableZK(address(verifierGateway)); + super.setUp(); } } -contract L2OutputOracleUpgradeable_Test is CommonTest { - /// @dev Tests that the proxy can be successfully upgraded. - function test_upgrading_succeeds() external { - Proxy proxy = Proxy(deploy.mustGetAddress("L2OutputOracleProxy")); - // Check an unused slot before upgrading. - bytes32 slot21Before = vm.load(address(l2OutputOracle), bytes32(uint256(21))); - assertEq(bytes32(0), slot21Before); - - NextImpl nextImpl = new NextImpl(); - vm.startPrank(EIP1967Helper.getAdmin(address(proxy))); - // Reviewer note: the NextImpl() still uses reinitializer. If we want to remove that, we'll need to use a - // two step upgrade with the Storage lib. - proxy.upgradeToAndCall(address(nextImpl), abi.encodeWithSelector(NextImpl.initialize.selector, 2)); - assertEq(proxy.implementation(), address(nextImpl)); - - // Verify that the NextImpl contract initialized its values according as expected - bytes32 slot21After = vm.load(address(l2OutputOracle), bytes32(uint256(21))); - bytes32 slot21Expected = NextImpl(address(l2OutputOracle)).slot21Init(); - assertEq(slot21Expected, slot21After); +contract L2OutputOracle_Test is L2OutputOracle_Init { + function test_startingL2OutputOracleValues_succeeds() external { + Types.OutputProposal memory initialProposal = L2OutputOracle.getL2Output(0); + assertEq(initialProposal.outputRoot, bytes32(deploy.cfg().l2OutputOracleStartingOutputRoot())); + assertEq(initialProposal.timestamp, deploy.cfg().l2OutputOracleStartingTimestamp()); + assertEq(initialProposal.l2BlockNumber, deploy.cfg().l2OutputOracleStartingBlockNumber()); } - /// @dev Tests that initialize reverts if the submissionInterval is zero. - function test_initialize_submissionInterval_reverts() external { - // Reset the initialized field in the 0th storage slot - // so that initialize can be called again. - vm.store(address(l2OutputOracle), bytes32(uint256(0)), bytes32(uint256(0))); - - uint256 l2BlockTime = deploy.cfg().l2BlockTime(); - uint256 startingBlockNumber = deploy.cfg().l2OutputOracleStartingBlockNumber(); - uint256 startingTimestamp = deploy.cfg().l2OutputOracleStartingTimestamp(); - address proposer = deploy.cfg().l2OutputOracleProposer(); - address challenger = deploy.cfg().l2OutputOracleChallenger(); - uint256 finalizationPeriodSeconds = deploy.cfg().finalizationPeriodSeconds(); + function test_proposeWithZKProof_succeeds() external { + vm.warp(block.timestamp + (l2OutputOracle.SUBMISSION_INTERVAL() * l2OutputOracle.L2_BLOCK_TIME())); - vm.expectRevert("L2OutputOracle: submission interval must be greater than 0"); - l2OutputOracle.initialize({ - _submissionInterval: 0, - _l2BlockTime: l2BlockTime, - _startingBlockNumber: startingBlockNumber, - _startingTimestamp: startingTimestamp, - _proposer: proposer, - _challenger: challenger, - _finalizationPeriodSeconds: finalizationPeriodSeconds - }); - } - - /// @dev Tests that initialize reverts if the l2BlockTime is invalid. - function test_initialize_l2BlockTimeZero_reverts() external { - // Reset the initialized field in the 0th storage slot - // so that initialize can be called again. - vm.store(address(l2OutputOracle), bytes32(uint256(0)), bytes32(uint256(0))); + uint L1_BLOCK_NUM = block.number - 1; + bytes32 L1_HEAD = 0xb6bd7b941cd0b2098671484897d070508c2d94ad417b484cb30f68edad011578; + vm.setBlockhash(L1_BLOCK_NUM, L1_HEAD); + l2OutputOracle.checkpointBlockHash(L1_BLOCK_NUM, L1_HEAD); - uint256 submissionInterval = deploy.cfg().l2OutputOracleSubmissionInterval(); - uint256 startingBlockNumber = deploy.cfg().l2OutputOracleStartingBlockNumber(); - uint256 startingTimestamp = deploy.cfg().l2OutputOracleStartingTimestamp(); - address proposer = deploy.cfg().l2OutputOracleProposer(); - address challenger = deploy.cfg().l2OutputOracleChallenger(); - uint256 finalizationPeriodSeconds = deploy.cfg().finalizationPeriodSeconds(); + uint L2_BLOCK_NUM = L1_BLOCK_NUM + l2OutputOracle.SUBMISSION_INTERVAL(); + bytes32 CLAIMED_OUTPUT_ROOT = 0x83de1383c4b775a69042d4320cedbef37b0f62b7aec7186bb8fd0a2cebbb8073; + bytes32 LAST_OUTPUT_ROOT = l2OutputOracle.getL2Output(l2OutputOracle.latestOutputIndex()).outputRoot; - vm.expectRevert("L2OutputOracle: L2 block time must be greater than 0"); - l2OutputOracle.initialize({ - _submissionInterval: submissionInterval, - _l2BlockTime: 0, - _startingBlockNumber: startingBlockNumber, - _startingTimestamp: startingTimestamp, - _proposer: proposer, - _challenger: challenger, - _finalizationPeriodSeconds: finalizationPeriodSeconds + L2OutputOracle.PublicValuesStruct memory publicValues = L2OutputOracle.PublicValuesStruct({ + l1Head: L1_HEAD, + l2PreRoot: LAST_OUTPUT_ROOT, + claimRoot: CLAIMED_OUTPUT_ROOT, + claimBlockNum: L2_BLOCK_NUM, + chainId: 901 }); - } - - /// @dev Tests that initialize reverts if the starting timestamp is invalid. - function test_initialize_badTimestamp_reverts() external { - // Reset the initialized field in the 0th storage slot - // so that initialize can be called again. - vm.store(address(l2OutputOracle), bytes32(uint256(0)), bytes32(uint256(0))); - uint256 submissionInterval = deploy.cfg().l2OutputOracleSubmissionInterval(); - uint256 l2BlockTime = deploy.cfg().l2BlockTime(); - uint256 startingBlockNumber = deploy.cfg().l2OutputOracleStartingBlockNumber(); - address proposer = deploy.cfg().l2OutputOracleProposer(); - address challenger = deploy.cfg().l2OutputOracleChallenger(); - uint256 finalizationPeriodSeconds = deploy.cfg().finalizationPeriodSeconds(); + vm.prank(L2OutputOracle.PROPOSER()); + l2OutputOracle.proposeL2Output(CLAIMED_OUTPUT_ROOT, L2_BLOCK_NUM, L1_HEAD, L1_BLOCK_NUM, proof); - vm.expectRevert("L2OutputOracle: starting L2 timestamp must be less than current time"); - l2OutputOracle.initialize({ - _submissionInterval: submissionInterval, - _l2BlockTime: l2BlockTime, - _startingBlockNumber: startingBlockNumber, - _startingTimestamp: block.timestamp + 1, - _proposer: proposer, - _challenger: challenger, - _finalizationPeriodSeconds: finalizationPeriodSeconds - }); + assertEq(l2OutputOracle.getL2Output(1).outputRoot, CLAIMED_OUTPUT_ROOT); } } diff --git a/packages/contracts-bedrock/test/L1/ZKL2OutputOracle.t.sol b/packages/contracts-bedrock/test/L1/ZKL2OutputOracle.t.sol deleted file mode 100644 index 8234d66b17..0000000000 --- a/packages/contracts-bedrock/test/L1/ZKL2OutputOracle.t.sol +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -// Testing utilities -import { stdError, console } from "forge-std/Test.sol"; -import { CommonTest } from "test/setup/CommonTest.sol"; -import { NextImpl } from "test/mocks/NextImpl.sol"; -import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; - -// Libraries -import { Types } from "src/libraries/Types.sol"; -import { Constants } from "src/libraries/Constants.sol"; - -// Target contract dependencies -import { Proxy } from "src/universal/Proxy.sol"; - -// Target contract -import { ZKL2OutputOracle } from "src/L1/ZKL2OutputOracle.sol"; -import { SP1VerifierGateway } from "@sp1-contracts/src/SP1VerifierGateway.sol"; -import { SP1Verifier } from "@sp1-contracts/src/v1.0.1/SP1Verifier.sol"; - -contract ZKL2OutputOracle_Init is CommonTest { - bytes32 constant ZK_VKEY = 0x003de3cfc15f7b7e2844f33380b8dde65e0cc65de4f7a27e8b3422d376d982f4; - bytes32 constant STARTING_L2_OUTPUT_ROOT = 0xd1e578c114d50dbb4431e81f737481b7d07204a35d5968c4b911ec55ba038ed6; - uint constant SUBMISSION_INTERVAL = 900; - uint constant STARTING_L2_OUTPUT_TIMESTAMP = 1721925325; - uint constant STARTING_L2_OUTPUT_BLOCK_NUMBER = 123163274; - - function setUp() public virtual override { - address sp1Verifier = address(new SP1Verifier()); - SP1VerifierGateway verifierGateway = new SP1VerifierGateway(); - verifierGateway.addRoute(sp1Verifier); - - super.enableZK( - ZK_VKEY, - STARTING_L2_OUTPUT_ROOT, - STARTING_L2_OUTPUT_TIMESTAMP, - STARTING_L2_OUTPUT_BLOCK_NUMBER, - SUBMISSION_INTERVAL, - address(verifierGateway) - ); - super.setUp(); - } -} - -contract ZKL2OutputOracle_Test is ZKL2OutputOracle_Init { - function test_startingL2OutputOracleValues_succeeds() external { - Types.OutputProposal memory initialProposal = zkL2OutputOracle.getL2Output(0); - assertEq(initialProposal.outputRoot, bytes32(deploy.cfg().l2OutputOracleStartingOutputRoot())); - assertEq(initialProposal.timestamp, deploy.cfg().l2OutputOracleStartingTimestamp()); - assertEq(initialProposal.l2BlockNumber, deploy.cfg().l2OutputOracleStartingBlockNumber()); - } - - function test_proposeWithZKProof_succeeds() external { - vm.warp(block.timestamp + (zkL2OutputOracle.SUBMISSION_INTERVAL() * zkL2OutputOracle.L2_BLOCK_TIME())); - - uint L1_BLOCK_NUM = block.number - 1; - bytes32 L1_HEAD = 0xb6bd7b941cd0b2098671484897d070508c2d94ad417b484cb30f68edad011578; - vm.setBlockhash(L1_BLOCK_NUM, L1_HEAD); - zkL2OutputOracle.checkpointBlockHash(L1_BLOCK_NUM, L1_HEAD); - - uint L2_BLOCK_NUM = L1_BLOCK_NUM + zkL2OutputOracle.SUBMISSION_INTERVAL(); - bytes32 CLAIMED_OUTPUT_ROOT = 0x83de1383c4b775a69042d4320cedbef37b0f62b7aec7186bb8fd0a2cebbb8073; - bytes32 LAST_OUTPUT_ROOT = zkL2OutputOracle.getL2Output(zkL2OutputOracle.latestOutputIndex()).outputRoot; - - ZKL2OutputOracle.PublicValuesStruct memory publicValues = ZKL2OutputOracle.PublicValuesStruct({ - l1Head: L1_HEAD, - l2PreRoot: LAST_OUTPUT_ROOT, - claimRoot: CLAIMED_OUTPUT_ROOT, - claimBlockNum: L2_BLOCK_NUM, - chainId: 901 - }); - - bytes memory proof = hex"fedc1fcc0c401ce68e913792251428e02475772a12db38da45b78f0aaf011a5396fff95b2fbdab6a9fdef15ad8ea58d25e61c8389ff6c6c69a18ff0d30d185fbff7a18e60a6162c634f294b6a46f1ddf269d01437c1d56ec8df35f6d75c490496c031db0040d0149bdb878e9d23753e22105bdcbe6e07571f1617d11c72f5b4f8677d1ba27b05dcb17f68477f2c8ecc72e4c33bb6b85536bed9c466ea68cc99c1e1138e20afd82348fe5180bdbac47e15e121d8f02b2ce22f275b4370e1e0f4a3b65026b0ddf4a1762ec35baa5ad6c92e18c52e06743e1d7b3e105bc5fa4b697612a0bc5258058591da838a15ad3e1c9830b7d5492f86698d68e32d7e8e25e0e050f50351d5b59450932060fa80b6777bba2361f68fc1c3d7661525215c6b5c23a9de2fd2e336640c93e2db350b3549d922873c69d04bcf0090bafe60a8700b56d344d9a0874a88772c9285e4b26eaa3f9c9466e26ae7185879a1ffdb9e30bb080c2ca672c02024943a8ca4cd68b8cb3f2605086a9bcfcf56d080a9277beec07bde0756023ebd7570791c7fb70637f0e9f6109a87c7fe832b2ed91a91dc3c24808f2cc4d196f7b757b6232bfa1bca4b2f5e22487196cf059524be780ee740c2e560e46bf25825045ef51a3ebd67ae727eea5b0e3b10aa1d3a3cc5b4abad6216e803ed20d25f1a77a210ce37f2561a8d909ed597c60285d8f6a405d80320e397a58b54cb420a34b6ebb711c50cb6c4e1d5936e454d265a2b2c3b8aae1632aeffc645352d5154d388031fc6c44a7321ad19053c9d4850e9f85247fc2cfb4c344075e9305a51f33637795a47f7ae0a75be1078d9f42b24b4732a9f1a57df127a5dc6892d18d24137f8ffdb079102030ac3b3da044cf63d23a754e1c4ff71543fd3e49b2c2ec2660c3e111f0354e1b2430da3d1084cb9ae583ac6a1595b95f936be744f06fd819663d6c7b1402a717fff9993806c7de8fa449440e6a321774db6521432babfc2b5d3d4d259c9d0ebb86df54904e8745551e9915bd74107d4fb2e9a1ac37380619fa037d302f248b558520ba08bdcebe22cb912b0e3a94d208e63023cac7c616252b1671b2c81eba47da35e240edb8d5e721d7dbfcc8d88a848eb3c958f6749117d8d678f238ed9881677060908bac0be91d81999d7f2cf40152e3aef3a3a39c04b646b0f8c42b848b41f7fc16ab29a5b38f83c498ce0449f912a1ba7a315719"; - vm.prank(zkL2OutputOracle.PROPOSER()); - zkL2OutputOracle.proposeL2Output(CLAIMED_OUTPUT_ROOT, L2_BLOCK_NUM, L1_HEAD, L1_BLOCK_NUM, proof); - - assertEq(zkL2OutputOracle.getL2Output(1).outputRoot, CLAIMED_OUTPUT_ROOT); - } -} diff --git a/packages/contracts-bedrock/test/invariants/OptimismPortal.t.sol b/packages/contracts-bedrock/test/invariants/OptimismPortal.t.sol index 804de43202..869919df54 100644 --- a/packages/contracts-bedrock/test/invariants/OptimismPortal.t.sol +++ b/packages/contracts-bedrock/test/invariants/OptimismPortal.t.sol @@ -73,82 +73,82 @@ contract OptimismPortal_Depositor is StdUtils, ResourceMetering { } contract OptimismPortal_Invariant_Harness is CommonTest { - // Reusable default values for a test withdrawal - Types.WithdrawalTransaction _defaultTx; - - uint256 _proposedOutputIndex; - uint256 _proposedBlockNumber; - bytes32 _stateRoot; - bytes32 _storageRoot; - bytes32 _outputRoot; - bytes32 _withdrawalHash; - bytes[] _withdrawalProof; - Types.OutputRootProof internal _outputRootProof; - - function setUp() public virtual override { - super.setUp(); - - _defaultTx = Types.WithdrawalTransaction({ - nonce: 0, - sender: alice, - target: bob, - value: 100, - gasLimit: 100_000, - data: hex"" - }); - // Get withdrawal proof data we can use for testing. - (_stateRoot, _storageRoot, _outputRoot, _withdrawalHash, _withdrawalProof) = - ffi.getProveWithdrawalTransactionInputs(_defaultTx); - - // Setup a dummy output root proof for reuse. - _outputRootProof = Types.OutputRootProof({ - version: bytes32(uint256(0)), - stateRoot: _stateRoot, - messagePasserStorageRoot: _storageRoot, - latestBlockhash: bytes32(uint256(0)) - }); - _proposedBlockNumber = l2OutputOracle.nextBlockNumber(); - _proposedOutputIndex = l2OutputOracle.nextOutputIndex(); - - // Configure the oracle to return the output root we've prepared. - vm.warp(l2OutputOracle.computeL2Timestamp(_proposedBlockNumber) + 1); - vm.prank(l2OutputOracle.PROPOSER()); - l2OutputOracle.proposeL2Output(_outputRoot, _proposedBlockNumber, 0, 0); - - // Warp beyond the finalization period for the block we've proposed. - vm.warp( - l2OutputOracle.getL2Output(_proposedOutputIndex).timestamp + l2OutputOracle.FINALIZATION_PERIOD_SECONDS() - + 1 - ); - // Fund the portal so that we can withdraw ETH. - vm.deal(address(optimismPortal), 0xFFFFFFFF); - } -} - -contract OptimismPortal_Deposit_Invariant is CommonTest { - OptimismPortal_Depositor internal actor; - - function setUp() public override { - super.setUp(); - // Create a deposit actor. - actor = new OptimismPortal_Depositor(vm, optimismPortal); - - targetContract(address(actor)); - - bytes4[] memory selectors = new bytes4[](1); - selectors[0] = actor.depositTransactionCompletes.selector; - FuzzSelector memory selector = FuzzSelector({ addr: address(actor), selectors: selectors }); - targetSelector(selector); - } - - /// @custom:invariant Deposits of any value should always succeed unless - /// `_to` = `address(0)` or `_isCreation` = `true`. - /// - /// All deposits, barring creation transactions and transactions - /// sent to `address(0)`, should always succeed. - function invariant_deposit_completes() external view { - assertEq(actor.failedToComplete(), false); - } +// // Reusable default values for a test withdrawal +// Types.WithdrawalTransaction _defaultTx; + +// uint256 _proposedOutputIndex; +// uint256 _proposedBlockNumber; +// bytes32 _stateRoot; +// bytes32 _storageRoot; +// bytes32 _outputRoot; +// bytes32 _withdrawalHash; +// bytes[] _withdrawalProof; +// Types.OutputRootProof internal _outputRootProof; + +// function setUp() public virtual override { +// super.setUp(); + +// _defaultTx = Types.WithdrawalTransaction({ +// nonce: 0, +// sender: alice, +// target: bob, +// value: 100, +// gasLimit: 100_000, +// data: hex"" +// }); +// // Get withdrawal proof data we can use for testing. +// (_stateRoot, _storageRoot, _outputRoot, _withdrawalHash, _withdrawalProof) = +// ffi.getProveWithdrawalTransactionInputs(_defaultTx); + +// // Setup a dummy output root proof for reuse. +// _outputRootProof = Types.OutputRootProof({ +// version: bytes32(uint256(0)), +// stateRoot: _stateRoot, +// messagePasserStorageRoot: _storageRoot, +// latestBlockhash: bytes32(uint256(0)) +// }); +// _proposedBlockNumber = l2OutputOracle.nextBlockNumber(); +// _proposedOutputIndex = l2OutputOracle.nextOutputIndex(); + +// // Configure the oracle to return the output root we've prepared. +// vm.warp(l2OutputOracle.computeL2Timestamp(_proposedBlockNumber) + 1); +// vm.prank(l2OutputOracle.PROPOSER()); +// l2OutputOracle.proposeL2Output(_outputRoot, _proposedBlockNumber, 0, 0); + +// // Warp beyond the finalization period for the block we've proposed. +// vm.warp( +// l2OutputOracle.getL2Output(_proposedOutputIndex).timestamp + l2OutputOracle.FINALIZATION_PERIOD_SECONDS() +// + 1 +// ); +// // Fund the portal so that we can withdraw ETH. +// vm.deal(address(optimismPortal), 0xFFFFFFFF); +// } +// } + +// contract OptimismPortal_Deposit_Invariant is CommonTest { +// OptimismPortal_Depositor internal actor; + +// function setUp() public override { +// super.setUp(); +// // Create a deposit actor. +// actor = new OptimismPortal_Depositor(vm, optimismPortal); + +// targetContract(address(actor)); + +// bytes4[] memory selectors = new bytes4[](1); +// selectors[0] = actor.depositTransactionCompletes.selector; +// FuzzSelector memory selector = FuzzSelector({ addr: address(actor), selectors: selectors }); +// targetSelector(selector); +// } + +// /// @custom:invariant Deposits of any value should always succeed unless +// /// `_to` = `address(0)` or `_isCreation` = `true`. +// /// +// /// All deposits, barring creation transactions and transactions +// /// sent to `address(0)`, should always succeed. +// function invariant_deposit_completes() external view { +// assertEq(actor.failedToComplete(), false); +// } } contract OptimismPortal_CannotTimeTravel is OptimismPortal_Invariant_Harness { diff --git a/packages/contracts-bedrock/test/setup/CommonTest.sol b/packages/contracts-bedrock/test/setup/CommonTest.sol index 6e13dc4dc1..553f230a34 100644 --- a/packages/contracts-bedrock/test/setup/CommonTest.sol +++ b/packages/contracts-bedrock/test/setup/CommonTest.sol @@ -23,12 +23,8 @@ contract CommonTest is Test, Setup, Events { address customGasToken; bool useInteropOverride; bool useZK; - bytes32 zkVKey; - bytes32 zkStartingL2OutputRoot; - uint256 zkStartingL2OutputTimestamp; - uint256 zkStartingL2OutputBlockNumber; - uint256 zkSubmissionInterval; address zkVerifierGateway; + bytes proof = hex"fedc1fcc0c401ce68e913792251428e02475772a12db38da45b78f0aaf011a5396fff95b2fbdab6a9fdef15ad8ea58d25e61c8389ff6c6c69a18ff0d30d185fbff7a18e60a6162c634f294b6a46f1ddf269d01437c1d56ec8df35f6d75c490496c031db0040d0149bdb878e9d23753e22105bdcbe6e07571f1617d11c72f5b4f8677d1ba27b05dcb17f68477f2c8ecc72e4c33bb6b85536bed9c466ea68cc99c1e1138e20afd82348fe5180bdbac47e15e121d8f02b2ce22f275b4370e1e0f4a3b65026b0ddf4a1762ec35baa5ad6c92e18c52e06743e1d7b3e105bc5fa4b697612a0bc5258058591da838a15ad3e1c9830b7d5492f86698d68e32d7e8e25e0e050f50351d5b59450932060fa80b6777bba2361f68fc1c3d7661525215c6b5c23a9de2fd2e336640c93e2db350b3549d922873c69d04bcf0090bafe60a8700b56d344d9a0874a88772c9285e4b26eaa3f9c9466e26ae7185879a1ffdb9e30bb080c2ca672c02024943a8ca4cd68b8cb3f2605086a9bcfcf56d080a9277beec07bde0756023ebd7570791c7fb70637f0e9f6109a87c7fe832b2ed91a91dc3c24808f2cc4d196f7b757b6232bfa1bca4b2f5e22487196cf059524be780ee740c2e560e46bf25825045ef51a3ebd67ae727eea5b0e3b10aa1d3a3cc5b4abad6216e803ed20d25f1a77a210ce37f2561a8d909ed597c60285d8f6a405d80320e397a58b54cb420a34b6ebb711c50cb6c4e1d5936e454d265a2b2c3b8aae1632aeffc645352d5154d388031fc6c44a7321ad19053c9d4850e9f85247fc2cfb4c344075e9305a51f33637795a47f7ae0a75be1078d9f42b24b4732a9f1a57df127a5dc6892d18d24137f8ffdb079102030ac3b3da044cf63d23a754e1c4ff71543fd3e49b2c2ec2660c3e111f0354e1b2430da3d1084cb9ae583ac6a1595b95f936be744f06fd819663d6c7b1402a717fff9993806c7de8fa449440e6a321774db6521432babfc2b5d3d4d259c9d0ebb86df54904e8745551e9915bd74107d4fb2e9a1ac37380619fa037d302f248b558520ba08bdcebe22cb912b0e3a94d208e63023cac7c616252b1671b2c81eba47da35e240edb8d5e721d7dbfcc8d88a848eb3c958f6749117d8d678f238ed9881677060908bac0be91d81999d7f2cf40152e3aef3a3a39c04b646b0f8c42b848b41f7fc16ab29a5b38f83c498ce0449f912a1ba7a315719"; function setUp() public virtual override { alice = makeAddr("alice"); @@ -52,15 +48,7 @@ contract CommonTest is Test, Setup, Events { deploy.cfg().setUseInterop(true); } if (useZK) { - deploy.cfg().setUseZK( - true, - zkVKey, - zkStartingL2OutputRoot, - zkStartingL2OutputTimestamp, - zkStartingL2OutputBlockNumber, - zkSubmissionInterval, - zkVerifierGateway - ); + deploy.cfg().setVerifierGateway(zkVerifierGateway); } vm.etch(address(ffi), vm.getDeployedCode("FFIInterface.sol:FFIInterface")); @@ -105,27 +93,27 @@ contract CommonTest is Test, Setup, Events { vm.warp(l2OutputOracle.computeL2Timestamp(_nextBlockNumber) + 1); } - /// @dev Helper function to propose an output. - function proposeAnotherOutput() public { - bytes32 proposedOutput2 = keccak256(abi.encode()); - uint256 nextBlockNumber = l2OutputOracle.nextBlockNumber(); - uint256 nextOutputIndex = l2OutputOracle.nextOutputIndex(); - warpToProposeTime(nextBlockNumber); - uint256 proposedNumber = l2OutputOracle.latestBlockNumber(); + // /// @dev Helper function to propose an output. + // function proposeAnotherOutput() public { + // bytes32 proposedOutput2 = keccak256(abi.encode()); + // uint256 nextBlockNumber = l2OutputOracle.nextBlockNumber(); + // uint256 nextOutputIndex = l2OutputOracle.nextOutputIndex(); + // warpToProposeTime(nextBlockNumber); + // uint256 proposedNumber = l2OutputOracle.latestBlockNumber(); - uint256 submissionInterval = deploy.cfg().l2OutputOracleSubmissionInterval(); - // Ensure the submissionInterval is enforced - assertEq(nextBlockNumber, proposedNumber + submissionInterval); + // uint256 submissionInterval = deploy.cfg().l2OutputOracleSubmissionInterval(); + // // Ensure the submissionInterval is enforced + // assertEq(nextBlockNumber, proposedNumber + submissionInterval); - vm.roll(nextBlockNumber + 1); + // vm.roll(nextBlockNumber + 1); - vm.expectEmit(true, true, true, true); - emit OutputProposed(proposedOutput2, nextOutputIndex, nextBlockNumber, block.timestamp); + // vm.expectEmit(true, true, true, true); + // emit OutputProposed(proposedOutput2, nextOutputIndex, nextBlockNumber, block.timestamp); - address proposer = deploy.cfg().l2OutputOracleProposer(); - vm.prank(proposer); - l2OutputOracle.proposeL2Output(proposedOutput2, nextBlockNumber, 0, 0); - } + // address proposer = deploy.cfg().l2OutputOracleProposer(); + // vm.prank(proposer); + // l2OutputOracle.proposeL2Output(proposedOutput2, nextBlockNumber, 0, 0); + // } function enableFaultProofs() public { // Check if the system has already been deployed, based off of the heuristic that alice and bob have not been @@ -168,14 +156,7 @@ contract CommonTest is Test, Setup, Events { useInteropOverride = true; } - function enableZK( - bytes32 _zkVKey, - bytes32 _startingL2OutputRoot, - uint256 _startingL2OutputTimestamp, - uint256 _startingL2OutputBlockNumber, - uint256 _submissionInterval, - address _verifierGateway - ) public { + function enableZK(address _verifierGateway) public { // Check if the system has already been deployed, based off of the heuristic that alice and bob have not been // set by the `setUp` function yet. if (!(alice == address(0) && bob == address(0))) { @@ -187,11 +168,6 @@ contract CommonTest is Test, Setup, Events { } useZK = true; - zkVKey = _zkVKey; - zkStartingL2OutputRoot = _startingL2OutputRoot; - zkStartingL2OutputTimestamp = _startingL2OutputTimestamp; - zkStartingL2OutputBlockNumber = _startingL2OutputBlockNumber; - zkSubmissionInterval = _submissionInterval; zkVerifierGateway = _verifierGateway; } } diff --git a/packages/contracts-bedrock/test/setup/Setup.sol b/packages/contracts-bedrock/test/setup/Setup.sol index 72f299d554..9844e6ddd7 100644 --- a/packages/contracts-bedrock/test/setup/Setup.sol +++ b/packages/contracts-bedrock/test/setup/Setup.sol @@ -28,7 +28,6 @@ import { DeployConfig } from "scripts/DeployConfig.s.sol"; import { Deploy } from "scripts/Deploy.s.sol"; import { L2Genesis, L1Dependencies, OutputMode } from "scripts/L2Genesis.s.sol"; import { L2OutputOracle } from "src/L1/L2OutputOracle.sol"; -import { ZKL2OutputOracle } from "src/L1/ZKL2OutputOracle.sol"; import { ProtocolVersions } from "src/L1/ProtocolVersions.sol"; import { SystemConfig } from "src/L1/SystemConfig.sol"; import { L1StandardBridge } from "src/L1/L1StandardBridge.sol"; @@ -65,7 +64,6 @@ contract Setup { DisputeGameFactory disputeGameFactory; DelayedWETH delayedWeth; L2OutputOracle l2OutputOracle; - ZKL2OutputOracle zkL2OutputOracle; SystemConfig systemConfig; L1StandardBridge l1StandardBridge; L1CrossDomainMessenger l1CrossDomainMessenger; @@ -129,11 +127,7 @@ contract Setup { optimismPortal2 = OptimismPortal2(deploy.mustGetAddress("OptimismPortalProxy")); disputeGameFactory = DisputeGameFactory(deploy.mustGetAddress("DisputeGameFactoryProxy")); delayedWeth = DelayedWETH(deploy.mustGetAddress("DelayedWETHProxy")); - if (deploy.cfg().useZK()) { - zkL2OutputOracle = ZKL2OutputOracle(deploy.mustGetAddress("L2OutputOracleProxy")); - } else { - l2OutputOracle = L2OutputOracle(deploy.mustGetAddress("L2OutputOracleProxy")); - } + l2OutputOracle = L2OutputOracle(deploy.mustGetAddress("L2OutputOracleProxy")); systemConfig = SystemConfig(deploy.mustGetAddress("SystemConfigProxy")); l1StandardBridge = L1StandardBridge(deploy.mustGetAddress("L1StandardBridgeProxy")); l1CrossDomainMessenger = L1CrossDomainMessenger(deploy.mustGetAddress("L1CrossDomainMessengerProxy"));