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

feat(contracts): Add modifiers, owner #230

Merged
merged 9 commits into from
Nov 26, 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
5 changes: 3 additions & 2 deletions book/advanced/l2-output-oracle.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ The original `L2OutputOracle` contract can be found [here](https://github.com/et
The changes introduced in the `OPSuccinctL2OutputOracle` contract are:

1. The `submissionInterval` parameter is now the minimum interval in L2 blocks at which checkpoints must be submitted. An aggregation proof can be posted after this interval has passed.
2. The addition of the `aggregationVkey`, `rangeVkeyCommitment`, `verifierGateway`, `startingOutputRoot`, and `rollupConfigHash` parameters. `startingOutputRoot` is used for initalizing the contract from an empty state, because `op-succinct` requires a starting output root from which to prove the next state root. The other parameters are used for verifying the proofs posted to the contract.
2. The addition of the `aggregationVkey`, `rangeVkeyCommitment`, `verifier`, `startingOutputRoot`, and `rollupConfigHash` parameters. `startingOutputRoot` is used for initalizing the contract from an empty state, because `op-succinct` requires a starting output root from which to prove the next state root. The other parameters are used for verifying the proofs posted to the contract.
3. The addition of `historicBlockHashes` to store the L1 block hashes which the `op-succinct` proofs are anchored to. Whenever a proof is posted, the merkle proof verification will use these L1 block hashes to verify the state of the L2 which is posted as blobs or calldata to the L1.
4. The new `checkpointBlockHash` function which checkpoints the L1 block hash at a given L1 block number using the `blockhash` function.
5. The `proposeL2Output` function now takes an additional `_proof` parameter, which is the proof that is posted to the contract, and removes the unnecessary `_l1BlockHash` parameter (which is redundant given the `historicBlockHashes` mapping). This function also verifies the proof using the `ISP1VerifierGateway` contract.
Expand Down Expand Up @@ -39,12 +39,13 @@ You can configure additional parameters when deploying or upgrading the `OPSucci

| Parameter | Description |
|-----------|-------------|
| `VERIFIER_ADDRESS` | Default: Succinct's official Groth16 VerifierGateway. Address of the `ISP1VerifierGateway` contract used to verify proofs. For mock proofs, this is the address of the `SP1MockVerifier` contract. |
| `VERIFIER_ADDRESS` | Default: Succinct's official Groth16 VerifierGateway. Address of the `ISP1Verifier` contract used to verify proofs. For mock proofs, this is the address of the `SP1MockVerifier` contract. |
| `STARTING_BLOCK_NUMBER` | Default: The finalized block number on L2. The block number to initialize the contract from. OP Succinct will start proving state roots from this block number. |
| `SUBMISSION_INTERVAL` | Default: `1000`. The minimum interval in L2 blocks at which checkpoints must be submitted. An aggregation proof can be posted for any range larger than this interval. |
| `FINALIZATION_PERIOD` | Default: `0`. The time period (in seconds) after which a proposed output becomes finalized and withdrawals can be processed. |
| `PROPOSER` | Default: The address of the account associated with `PRIVATE_KEY`. Ethereum address authorized to submit proofs. Set to `address(0)` to allow permissionless submissions. |
| `CHALLENGER` | Default: `address(0)`, no one can dispute proofs. Ethereum address authorized to dispute proofs. |
| `OWNER` | Default: The address of the account associated with `PRIVATE_KEY`. Ethereum address authorized to update the `aggregationVkey`, `rangeVkeyCommitment`, `verifier`, and `rollupConfigHash` parameters and transfer ownership of the contract. In a production setting, set to the governance smart contract or multi-sig of the chain. |

## Upgrading `OPSuccinctL2OutputOracle`

Expand Down
13 changes: 7 additions & 6 deletions contracts/opsuccinctl2ooconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
"challenger": "0x0000000000000000000000000000000000000000",
"finalizationPeriod": 0,
"l2BlockTime": 2,
"owner": "0xDEd0000E32f8F40414d3ab3a830f735a3553E18e",
"proposer": "0xDEd0000E32f8F40414d3ab3a830f735a3553E18e",
"rollupConfigHash": "0x5e32d5f9f902c6a46cb75cbdb083ea67b9475d7026542a009dc9d99072f4bdf1",
"startingBlockNumber": 20256273,
"startingOutputRoot": "0x9ad6f24abc0df5e4cab37c1efae21643938ed5393389ce9e747524a59546a878",
"startingTimestamp": 1732315086,
"startingBlockNumber": 20387725,
"startingOutputRoot": "0x31d6b69ae155c7b840f7b8e7b050522f5a29b8d40911976847ffc32257e92ff4",
"startingTimestamp": 1732577990,
"submissionInterval": 1200,
"verifierGateway": "0x4cb20fa9e6FdFE8FDb6CE0942c5f40d49c898646",
"aggregationVkey": "0x0001346d7ea10cc78c48e3da6f1890bb16cf27e962202f0f9b2c5c57f3cfb0c5",
"rangeVkeyCommitment": "0x59ec1dca031e9fc246ec47246109ebb324a357302110de5d447af13a07f52762"
"verifier": "0x397A5f7f3dBd538f23DE225B51f532c34448dA9B",
"aggregationVkey": "0x009a6acd07328fc24a911fdb29fc3d6a57eee48a6db6694f966d64019753b541",
"rangeVkeyCommitment": "0x2413b6da2c76aa356ced7e5c0fe05eff4397cc68744c01ad25a9477d1df2d5be"
}
149 changes: 109 additions & 40 deletions contracts/src/OPSuccinctL2OutputOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.s
import {ISemver} from "@optimism/src/universal/ISemver.sol";
import {Types} from "@optimism/src/libraries/Types.sol";
import {Constants} from "@optimism/src/libraries/Constants.sol";
import {ISP1VerifierGateway} from "@sp1-contracts/src/ISP1VerifierGateway.sol";
import {ISP1Verifier} from "@sp1-contracts/src/ISP1Verifier.sol";

/// @custom:proxied
/// @title OPSuccinctL2OutputOracle
Expand All @@ -14,13 +14,21 @@ import {ISP1VerifierGateway} from "@sp1-contracts/src/ISP1VerifierGateway.sol";
/// these outputs to verify information about the state of L2. The outputs posted to this contract
/// are proved to be valid with `op-succinct`.
contract OPSuccinctL2OutputOracle is Initializable, ISemver {
/// @notice Parameters to initialize the contract.
/// @notice Parameters to initialize the OPSuccinctL2OutputOracle contract.
struct InitParams {
address challenger;
address proposer;
address owner;
uint256 finalizationPeriodSeconds;
uint256 l2BlockTime;
bytes32 aggregationVkey;
bytes32 rangeVkeyCommitment;
address verifierGateway;
bytes32 startingOutputRoot;
bytes32 rollupConfigHash;
bytes32 startingOutputRoot;
uint256 startingBlockNumber;
uint256 startingTimestamp;
uint256 submissionInterval;
address verifier;
}

/// @notice The public values committed to for an OP Succinct aggregation program.
Expand Down Expand Up @@ -69,12 +77,15 @@ contract OPSuccinctL2OutputOracle is Initializable, ISemver {
/// this verification is the output of converting the [u32; 8] range BabyBear verification key to a [u8; 32] array.
bytes32 public rangeVkeyCommitment;

/// @notice The deployed SP1VerifierGateway contract to request proofs from.
address public verifierGateway;
/// @notice The deployed SP1Verifier contract to verify proofs.
address public verifier;

/// @notice The hash of the chain's rollup config, which ensures the proofs submitted are for the correct chain.
bytes32 public rollupConfigHash;

/// @notice The owner of the contract, who has admin permissions.
address public owner;

/// @notice A trusted mapping of block numbers to block hashes.
mapping(uint256 => bytes32) public historicBlockHashes;

Expand All @@ -96,6 +107,31 @@ contract OPSuccinctL2OutputOracle is Initializable, ISemver {
/// @param newNextOutputIndex Next L2 output index after the deletion.
event OutputsDeleted(uint256 indexed prevNextOutputIndex, uint256 indexed newNextOutputIndex);

/// @notice Emitted when the aggregation verification key is updated.
/// @param oldAggregationVkey The old aggregation verification key.
/// @param newAggregationVkey The new aggregation verification key.
event AggregationVkeyUpdated(bytes32 indexed oldAggregationVkey, bytes32 indexed newAggregationVkey);

/// @notice Emitted when the range verification key commitment is updated.
/// @param oldRangeVkeyCommitment The old range verification key commitment.
/// @param newRangeVkeyCommitment The new range verification key commitment.
event RangeVkeyCommitmentUpdated(bytes32 indexed oldRangeVkeyCommitment, bytes32 indexed newRangeVkeyCommitment);

/// @notice Emitted when the verifier address is updated.
/// @param oldVerifier The old verifier address.
/// @param newVerifier The new verifier address.
event VerifierUpdated(address indexed oldVerifier, address indexed newVerifier);

/// @notice Emitted when the rollup config hash is updated.
/// @param oldRollupConfigHash The old rollup config hash.
/// @param newRollupConfigHash The new rollup config hash.
event RollupConfigHashUpdated(bytes32 indexed oldRollupConfigHash, bytes32 indexed newRollupConfigHash);

/// @notice Emitted when the owner address is updated.
/// @param previousOwner The previous owner address.
/// @param newOwner The new owner address.
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

////////////////////////////////////////////////////////////
// Errors //
////////////////////////////////////////////////////////////
Expand All @@ -111,6 +147,15 @@ contract OPSuccinctL2OutputOracle is Initializable, ISemver {
/// @custom:semver 0.1.0
string public constant version = "0.1.0";

////////////////////////////////////////////////////////////
// Modifiers //
////////////////////////////////////////////////////////////

modifier onlyOwner() {
require(msg.sender == owner, "L2OutputOracle: caller is not the owner");
_;
}

////////////////////////////////////////////////////////////
// Functions //
////////////////////////////////////////////////////////////
Expand All @@ -121,60 +166,43 @@ contract OPSuccinctL2OutputOracle is Initializable, ISemver {
}

/// @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 _initParams The aggregation vkey, range vkey commitment, verifier gateway, rollup config hash, and starting output root for the contract.
/// @dev Starting block number, timestamp and output root are ignored for upgrades, because these values already exist.
function initialize(
uint256 _submissionInterval,
uint256 _l2BlockTime,
uint256 _startingBlockNumber,
uint256 _startingTimestamp,
address _proposer,
address _challenger,
uint256 _finalizationPeriodSeconds,
InitParams memory _initParams
) 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");
/// @param _initParams The initialization parameters for the contract.
function initialize(InitParams memory _initParams) public initializer {
require(_initParams.submissionInterval > 0, "L2OutputOracle: submission interval must be greater than 0");
require(_initParams.l2BlockTime > 0, "L2OutputOracle: L2 block time must be greater than 0");
require(
_startingTimestamp <= block.timestamp,
_initParams.startingTimestamp <= block.timestamp,
"L2OutputOracle: starting L2 timestamp must be less than current time"
);

submissionInterval = _submissionInterval;
l2BlockTime = _l2BlockTime;
submissionInterval = _initParams.submissionInterval;
l2BlockTime = _initParams.l2BlockTime;

// For proof verification to work, there must be an initial output.
// Disregard the _startingBlockNumber and _startingTimestamp parameters during upgrades, as they're already set.
if (l2Outputs.length == 0) {
l2Outputs.push(
Types.OutputProposal({
outputRoot: _initParams.startingOutputRoot,
timestamp: uint128(_startingTimestamp),
l2BlockNumber: uint128(_startingBlockNumber)
timestamp: uint128(_initParams.startingTimestamp),
l2BlockNumber: uint128(_initParams.startingBlockNumber)
})
);

startingBlockNumber = _startingBlockNumber;
startingTimestamp = _startingTimestamp;
startingBlockNumber = _initParams.startingBlockNumber;
startingTimestamp = _initParams.startingTimestamp;
}

proposer = _proposer;
challenger = _challenger;
finalizationPeriodSeconds = _finalizationPeriodSeconds;
proposer = _initParams.proposer;
challenger = _initParams.challenger;
finalizationPeriodSeconds = _initParams.finalizationPeriodSeconds;

// OP Succinct initialization parameters.
aggregationVkey = _initParams.aggregationVkey;
rangeVkeyCommitment = _initParams.rangeVkeyCommitment;
verifierGateway = _initParams.verifierGateway;
verifier = _initParams.verifier;
rollupConfigHash = _initParams.rollupConfigHash;
owner = _initParams.owner;
}

/// @notice Getter for the submissionInterval.
Expand Down Expand Up @@ -288,7 +316,7 @@ contract OPSuccinctL2OutputOracle is Initializable, ISemver {
rangeVkeyCommitment: rangeVkeyCommitment
});

ISP1VerifierGateway(verifierGateway).verifyProof(aggregationVkey, abi.encode(publicValues), _proof);
ISP1Verifier(verifier).verifyProof(aggregationVkey, abi.encode(publicValues), _proof);

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

Expand Down Expand Up @@ -391,4 +419,45 @@ contract OPSuccinctL2OutputOracle is Initializable, ISemver {
function computeL2Timestamp(uint256 _l2BlockNumber) public view returns (uint256) {
return startingTimestamp + ((_l2BlockNumber - startingBlockNumber) * l2BlockTime);
}

/// @notice Update the submission interval.
/// @param _submissionInterval The new submission interval.
function updateSubmissionInterval(uint256 _submissionInterval) external onlyOwner {
submissionInterval = _submissionInterval;
}

/// @notice Updates the aggregation verification key.
/// @param _aggregationVkey The new aggregation verification key.
function updateAggregationVkey(bytes32 _aggregationVkey) external onlyOwner {
emit AggregationVkeyUpdated(aggregationVkey, _aggregationVkey);
aggregationVkey = _aggregationVkey;
}

/// @notice Updates the range verification key commitment.
/// @param _rangeVkeyCommitment The new range verification key commitment.
function updateRangeVkeyCommitment(bytes32 _rangeVkeyCommitment) external onlyOwner {
emit RangeVkeyCommitmentUpdated(rangeVkeyCommitment, _rangeVkeyCommitment);
rangeVkeyCommitment = _rangeVkeyCommitment;
}

/// @notice Updates the verifier address.
/// @param _verifier The new verifier address.
function updateVerifier(address _verifier) external onlyOwner {
emit VerifierUpdated(verifier, _verifier);
verifier = _verifier;
}

/// @notice Updates the rollup config hash.
/// @param _rollupConfigHash The new rollup config hash.
function updateRollupConfigHash(bytes32 _rollupConfigHash) external onlyOwner {
emit RollupConfigHashUpdated(rollupConfigHash, _rollupConfigHash);
rollupConfigHash = _rollupConfigHash;
}

/// Updates the owner address.
/// @param _owner The new owner address.
function transferOwnership(address _owner) external onlyOwner {
emit OwnershipTransferred(owner, _owner);
owner = _owner;
}
}
2 changes: 1 addition & 1 deletion contracts/test/Upgrade.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ contract UpgradeTest is Test, Utils {

Config memory config = readJson("opsuccinctl2ooconfig.json");
// This is never called, so we just need to add some code to the address so the check passes.
config.verifierGateway = address(new Proxy(address(this)));
config.verifier = address(new Proxy(address(this)));
config.startingOutputRoot = exampleOutputRoot;
config.startingTimestamp = exampleTimestamp;
OPSuccinctL2OutputOracle l2oo = OPSuccinctL2OutputOracle(deployWithConfig(config));
Expand Down
3 changes: 2 additions & 1 deletion contracts/test/helpers/JSONDecoder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ contract JSONDecoder {
address challenger;
uint256 finalizationPeriod;
uint256 l2BlockTime;
address owner;
address proposer;
bytes32 rangeVkeyCommitment;
bytes32 rollupConfigHash;
uint256 startingBlockNumber;
bytes32 startingOutputRoot;
uint256 startingTimestamp;
uint256 submissionInterval;
address verifierGateway;
address verifier;
}

struct OutputAtBlock {
Expand Down
28 changes: 13 additions & 15 deletions contracts/test/helpers/Utils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,27 @@ contract Utils is Test, JSONDecoder {
{
// Require that the verifier gateway is deployed
require(
address(cfg.verifierGateway).code.length > 0,
"OPSuccinctL2OutputOracleUpgrader: verifier gateway not deployed"
address(cfg.verifier).code.length > 0, "OPSuccinctL2OutputOracleUpgrader: verifier gateway not deployed"
);

OPSuccinctL2OutputOracle.InitParams memory initParams = OPSuccinctL2OutputOracle.InitParams({
verifierGateway: cfg.verifierGateway,
verifier: cfg.verifier,
aggregationVkey: cfg.aggregationVkey,
rangeVkeyCommitment: cfg.rangeVkeyCommitment,
startingOutputRoot: cfg.startingOutputRoot,
rollupConfigHash: cfg.rollupConfigHash
rollupConfigHash: cfg.rollupConfigHash,
proposer: cfg.proposer,
challenger: cfg.challenger,
owner: cfg.owner,
finalizationPeriodSeconds: cfg.finalizationPeriod,
l2BlockTime: cfg.l2BlockTime,
startingBlockNumber: cfg.startingBlockNumber,
startingTimestamp: cfg.startingTimestamp,
submissionInterval: cfg.submissionInterval
});

bytes memory initializationParams = abi.encodeWithSelector(
OPSuccinctL2OutputOracle.initialize.selector,
cfg.submissionInterval,
cfg.l2BlockTime,
cfg.startingBlockNumber,
cfg.startingTimestamp,
cfg.proposer,
cfg.challenger,
cfg.finalizationPeriod,
initParams
);
bytes memory initializationParams =
abi.encodeWithSelector(OPSuccinctL2OutputOracle.initialize.selector, initParams);

if (executeUpgradeCall) {
Proxy existingProxy = Proxy(payable(l2OutputOracleProxy));
Expand Down
Loading
Loading