Skip to content

Commit

Permalink
feat(contracts): support fallback to optimistic mode
Browse files Browse the repository at this point in the history
  • Loading branch information
emilianobonassi committed Dec 17, 2024
1 parent f28db3c commit af72a0d
Showing 1 changed file with 92 additions and 0 deletions.
92 changes: 92 additions & 0 deletions contracts/src/OPSuccinctL2OutputOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ contract OPSuccinctL2OutputOracle is Initializable, ISemver {
/// @notice A trusted mapping of block numbers to block hashes.
mapping(uint256 => bytes32) public historicBlockHashes;

/// @notice Active optimistic mode. When true, the contract will accept outputs without verification.
bool public optimisticMode;

////////////////////////////////////////////////////////////
// Events //
////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -146,6 +149,11 @@ contract OPSuccinctL2OutputOracle is Initializable, ISemver {
/// @param newSubmissionInterval The new submission interval.
event SubmissionIntervalUpdated(uint256 oldSubmissionInterval, uint256 newSubmissionInterval);

/// @notice Emitted when the optimistic mode is toggled.
/// @param enabled Indicates whether optimistic mode is enabled or disabled.
/// @param finalizationPeriodSeconds The new finalization period in seconds.
event OptimisticModeToggled(bool indexed enabled, uint256 finalizationPeriodSeconds);

////////////////////////////////////////////////////////////
// Errors //
////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -173,6 +181,16 @@ contract OPSuccinctL2OutputOracle is Initializable, ISemver {
_;
}

modifier whenOptimistic() {
require(optimisticMode, "L2OutputOracle: optimistic mode is not enabled");
_;
}

modifier whenNotOptimistic() {
require(!optimisticMode, "L2OutputOracle: optimistic mode is enabled");
_;
}

////////////////////////////////////////////////////////////
// Functions //
////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -303,6 +321,7 @@ contract OPSuccinctL2OutputOracle is Initializable, ISemver {
function proposeL2Output(bytes32 _outputRoot, uint256 _l2BlockNumber, uint256 _l1BlockNumber, bytes memory _proof)
external
payable
whenNotOptimistic
{
// The proposer must be explicitly approved, or the zero address must be approved (permissionless proposing).
require(
Expand Down Expand Up @@ -349,6 +368,63 @@ contract OPSuccinctL2OutputOracle is Initializable, ISemver {
);
}

/// @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
)
external
payable
whenOptimistic()
{
require(msg.sender == proposer, "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");

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"
);
}

emit OutputProposed(_outputRoot, nextOutputIndex(), _l2BlockNumber, block.timestamp);

l2Outputs.push(
Types.OutputProposal({
outputRoot: _outputRoot,
timestamp: uint128(block.timestamp),
l2BlockNumber: uint128(_l2BlockNumber)
})
);
}

/// @notice Checkpoints a block hash at a given block number.
/// @param _blockNumber Block number to checkpoint the hash at.
/// @dev If the block hash is not available, this will revert.
Expand Down Expand Up @@ -507,4 +583,20 @@ contract OPSuccinctL2OutputOracle is Initializable, ISemver {
approvedProposers[_proposer] = false;
emit ProposerUpdated(_proposer, false);
}

/// @notice Enables optimistic mode.
/// @param _finalizationPeriodSeconds The new finalization window.
function enableOptimisticMode(uint256 _finalizationPeriodSeconds) external onlyOwner whenNotOptimistic {
finalizationPeriodSeconds = _finalizationPeriodSeconds;
optimisticMode = true;
emit OptimisticModeToggled(true, _finalizationPeriodSeconds);
}

/// @notice Disables optimistic mode.
/// @param _finalizationPeriodSeconds The new finalization window.
function disableOptimisticMode(uint256 _finalizationPeriodSeconds) external onlyOwner whenOptimistic {
finalizationPeriodSeconds = _finalizationPeriodSeconds;
optimisticMode = false;
emit OptimisticModeToggled(false, _finalizationPeriodSeconds);
}
}

0 comments on commit af72a0d

Please sign in to comment.