Skip to content

Commit

Permalink
feat: migrate OPSuccinctL2OutputOracle to OptimismPortalV2 (#277)
Browse files Browse the repository at this point in the history
* forge install: solady

v0.0.281

* feat: impl IDisputeGame for OPSuccinctL2OutputOracle

* feat: update deployment

* add a thin, clonable wrapper on OPSuccinctL2OutputOracle

* add a test for OPSuccinctDisputeGame initialize()

* use a more recent tx to make `testOPSuccinctDisputeGame` test pass

* revert useless OPSuccinctL2OutputOracle IDisputeGame impl

* add OPSuccinctDisputeGame bindings

* add OPSuccinctDisputeGameFactory

* allow to propose output roots using DisputeGameFactory

* init deployment scripts

* fix server in mock mode

* use DGF if the address is provided

* fmt

* add deploy-dispute-game-factory to justfile

* fix: don't disable logs on the server (#320)

* feat: rc10 bump + fix mock mode (#323)

* add test for OPSuccinctL2OutputOracleFactory

* update book

* small fixes

* impl ISemver

* set permissionless proposing in the deployment

* OPSuccinctDisputeGameFactory: allow to change impl

* proposer small fixes

* don't pin op-deployer version for Kurtosis

* forge fmt

* update book

* small fixes

* nits

* add a to do for GameTypes.OP_SUCCINCT
  • Loading branch information
leruaa authored Jan 15, 2025
1 parent aa9a4a1 commit d2f0d4f
Show file tree
Hide file tree
Showing 24 changed files with 784 additions and 24 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@
[submodule "contracts/lib/sp1-contracts"]
path = contracts/lib/sp1-contracts
url = https://github.com/succinctlabs/sp1-contracts
[submodule "contracts/lib/solady"]
path = contracts/lib/solady
url = https://github.com/vectorized/solady
2 changes: 2 additions & 0 deletions book/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,7 @@
- [Upgrade Contract](./contracts/upgrade.md)
- [Update Contract Parameters](./contracts/update-parameters.md)
- [Modifications to Original `L2OutputOracle`](./contracts/modifications.md)
- [Experimental](./experimental/intro.md)
- [OptimismPortalV2](./experimental/optimism-portal-v2.md)
- [FAQ](./faq.md)
- [Troubleshooting](./troubleshooting.md)
3 changes: 3 additions & 0 deletions book/experimental/intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Experimental

This section contains experimental topics for OP Succinct.
46 changes: 46 additions & 0 deletions book/experimental/optimism-portal-v2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# OptimismPortalV2

If you want to use `OptimismPortalV2` or conform to Optimism's `IDisputeGame`, you can follow this section that describe how to deploy the 2 contracts:

* `OPSuccinctDisputeGame` a thin wrapper around `OPSuccinctL2OutputOracle` that implements `IDisputeGame`.
* `OPSuccinctDisputeGameFactory` the proposer entry point when creating new dispute game.

And instructions about how to configure the proposer to use them.

After having done the step 2) either in mock or full mode, with `L2OO_ADDRESS` set with the address of the `OPSuccinctL2OutputOracle` contract in your `.env` file,
run the following to deploy the contracts:

```shell
just deploy-dispute-game-factory
```

If successful, you should see the following output:

```
[⠊] Compiling...
[⠊] Compiling 1 files with Solc 0.8.15
[⠒] Solc 0.8.15 finished in 1.93s
Compiler run successful!
Script ran successfully.
== Return ==
0: address 0x6B3342821680031732Bc7d4E88A6528478aF9E38
## Setting up 1 EVM.
==========================
Chain 3151908
Estimated gas price: 1.000000014 gwei
Estimated total gas used for script: 1614671
Estimated amount required: 0.001614671022605394 ETH
==========================
```

In these deployment logs, `0x6B3342821680031732Bc7d4E88A6528478aF9E38` is the address of the proxy for the `OPSuccinctDisputeGameFactory` contract.

In order to have the poposer to use it, you have to add a new variable `DGF_ADDRESS` to your `.env` file with the value above.
2 changes: 1 addition & 1 deletion book/quick-start/full.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ In the root directory, create a file called `.env` and set the following environ

There are additional optional parameters that you can set in the `.env` file. See the [Advanced Parameters](../contracts/configuration.md#optional-advanced-parameters) section for more information.

### 2) Deploy the `OPSuccinctL2OutputOracle` contract.
### 2) Deploy the `OPSuccinctL2OutputOracle` contract

This contract is a modification of the `L2OutputOracle` contract which verifies a proof along with the proposed state root.

Expand Down
2 changes: 1 addition & 1 deletion book/quick-start/mock.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ If you have multiple environments, you can pass the environment file to the `dep
just deploy-mock-verifier <env_file>
```

### 3) Deploy the `OPSuccinctL2OutputOracle` contract.
### 3) Deploy the `OPSuccinctL2OutputOracle` contract

This contract is a modification of the `L2OutputOracle` contract which verifies a proof along with the proposed state root.

Expand Down
7 changes: 6 additions & 1 deletion contracts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ remappings = [
"@openzeppelin/=lib/openzeppelin-contracts/",
"@optimism/=lib/optimism/packages/contracts-bedrock/",
"@forge-std/=lib/forge-std/src/",
"@solady/=lib/solady/src",
# Note: Use zobront/sp1-contracts as the current version for SP1 contracts is not compatible with the hard
# version for 0.8.15 on some Optimism contracts.
"@sp1-contracts/=lib/sp1-contracts/contracts/",
Expand All @@ -15,9 +16,13 @@ remappings = [
"src/libraries/=lib/optimism/packages/contracts-bedrock/src/libraries/",
"src/L1/=lib/optimism/packages/contracts-bedrock/src/L1/",
"src/L2/=lib/optimism/packages/contracts-bedrock/src/L2/",
"src/dispute/=lib/optimism/packages/contracts-bedrock/src/dispute/"
]

# Enable read-write access to opsuccinctl2ooconfig.json
fs_permissions = [{ access = "read-write", path = "./opsuccinctl2ooconfig.json" }]
fs_permissions = [
{ access = "read-write", path = "./opsuccinctl2ooconfig.json" },
{ access = "read-write", path = "./opsuccinctl2ooconfig-test.json" }
]

# See more config options https://github.com/foundry-rs/foundry/tree/master/config
1 change: 1 addition & 0 deletions contracts/lib/solady
Submodule solady added at 513f58
15 changes: 15 additions & 0 deletions contracts/opsuccinctl2ooconfig-test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"challenger": "0x0000000000000000000000000000000000000000",
"finalizationPeriod": 3600,
"l2BlockTime": 2,
"owner": "0xDEd0000E32f8F40414d3ab3a830f735a3553E18e",
"proposer": "0x0000000000000000000000000000000000000000",
"rollupConfigHash": "0x0d7101e2acc7eae1fb42cfce5c604d14da561726e4e01b509315e5a9f97a9816",
"startingBlockNumber": 5726082,
"startingOutputRoot": "0xafcc854e9d3af302a5c749703bb4593fff9471f2ea1b55ec0ade1e1d3c4a0d6e",
"startingTimestamp": 1733804652,
"submissionInterval": 1200,
"verifier": "0x397A5f7f3dBd538f23DE225B51f532c34448dA9B",
"aggregationVkey": "0x00d4e72bc998d0528b0722a53bedd9c6f0143c9157af194ad4bb2502e37a496f",
"rangeVkeyCommitment": "0x33e3678015df481724af3aac49d000923caeec277027610b1490f857769f9459"
}
28 changes: 28 additions & 0 deletions contracts/script/OPSuccinctDGFDeployer.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import {Script} from "forge-std/Script.sol";
import {OPSuccinctL2OutputOracle} from "../src/OPSuccinctL2OutputOracle.sol";
import {OPSuccinctDisputeGame} from "../src/OPSuccinctDisputeGame.sol";
import {OPSuccinctDisputeGameFactory} from "../src/OPSuccinctDisputeGameFactory.sol";
import {Utils} from "../test/helpers/Utils.sol";
import {Proxy} from "@optimism/src/universal/Proxy.sol";
import {console} from "forge-std/console.sol";

contract OPSuccinctDFGDeployer is Script, Utils {
function run() public returns (address) {
vm.startBroadcast();

OPSuccinctL2OutputOracle l2OutputOracleProxy = OPSuccinctL2OutputOracle(vm.envAddress("L2OO_ADDRESS"));

l2OutputOracleProxy.addProposer(address(0));

// Initialize the dispute game based on the existing L2OO_ADDRESS.
OPSuccinctDisputeGame game = new OPSuccinctDisputeGame(address(l2OutputOracleProxy));
OPSuccinctDisputeGameFactory gameFactory = new OPSuccinctDisputeGameFactory(msg.sender, address(game));

vm.stopBroadcast();

return address(gameFactory);
}
}
120 changes: 120 additions & 0 deletions contracts/src/OPSuccinctDisputeGame.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import {OPSuccinctL2OutputOracle} from "./OPSuccinctL2OutputOracle.sol";
import {CWIA} from "@solady/utils/legacy/CWIA.sol";
import {LibBytes} from "@solady/utils/LibBytes.sol";
import {ISemver} from "@optimism/src/universal/ISemver.sol";
import {IDisputeGame} from "@optimism/src/dispute/interfaces/IDisputeGame.sol";
import {Claim, GameStatus, GameType, GameTypes, Hash, Timestamp} from "@optimism/src/dispute/lib/Types.sol";
import {GameNotInProgress, OutOfOrderResolution} from "@optimism/src/dispute/lib/Errors.sol";

contract OPSuccinctDisputeGame is ISemver, CWIA, IDisputeGame {
using LibBytes for bytes;

/// @notice The address of the L2 output oracle proxy contract.
address internal immutable l2OutpoutOracle;

/// @notice The timestamp of the game's global creation.
Timestamp public createdAt;

/// @notice The timestamp of the game's global resolution.
Timestamp public resolvedAt;

/// @notice Returns the current status of the game.
GameStatus public status;

/// @notice Semantic version.
/// @custom:semver v1.0.0-rc2
string public constant version = "v1.0.0-rc2";

constructor(address _l2OutpoutOracle) {
l2OutpoutOracle = _l2OutpoutOracle;
}

////////////////////////////////////////////////////////////
// IDisputeGame impl //
////////////////////////////////////////////////////////////

function initialize() external payable {
createdAt = Timestamp.wrap(uint64(block.timestamp));
status = GameStatus.IN_PROGRESS;

(uint256 l2BlockNumber, uint256 l1BlockNumber, bytes memory proof) =
abi.decode(extraData(), (uint256, uint256, bytes));

OPSuccinctL2OutputOracle(l2OutpoutOracle).proposeL2Output(
rootClaim().raw(), l2BlockNumber, l1BlockNumber, proof
);

this.resolve();
}

/// @notice Getter for the game type.
/// @dev The reference impl should be entirely different depending on the type (fault, validity)
/// i.e. The game type should indicate the security model.
/// @return gameType_ The type of proof system being used.
function gameType() public pure returns (GameType) {
// TODO: Once the following PR https://github.com/ethereum-optimism/optimism/pull/13780 is merged,
// update this to return the correct game type: GameTypes.OP_SUCCINCT
return GameType.wrap(3);
}

/// @notice Getter for the creator of the dispute game.
/// @dev `clones-with-immutable-args` argument #1
/// @return The creator of the dispute game.
function gameCreator() public pure returns (address) {
return _getArgAddress(0x00);
}

/// @notice Getter for the root claim.
/// @dev `clones-with-immutable-args` argument #2
/// @return The root claim of the DisputeGame.
function rootClaim() public pure returns (Claim) {
return Claim.wrap(_getArgBytes32(0x14));
}

/// @notice Getter for the parent hash of the L1 block when the dispute game was created.
/// @dev `clones-with-immutable-args` argument #3
/// @return The parent hash of the L1 block when the dispute game was created.
function l1Head() public pure returns (Hash) {
return Hash.wrap(_getArgBytes32(0x34));
}

/// @notice Getter for the extra data.
/// @dev `clones-with-immutable-args` argument #4
/// @return Any extra data supplied to the dispute game contract by the creator.
function extraData() public pure returns (bytes memory) {
// The extra data starts at the second word within the cwia calldata
return _getArgBytes().slice(0x54);
}

/// @notice If all necessary information has been gathered, this function should mark the game
/// status as either `CHALLENGER_WINS` or `DEFENDER_WINS` and return the status of
/// the resolved game. It is at this stage that the bonds should be awarded to the
/// necessary parties.
/// @dev May only be called if the `status` is `IN_PROGRESS`.
/// @return status_ The status of the game after resolution.
function resolve() external returns (GameStatus status_) {
// INVARIANT: Resolution cannot occur unless the game is currently in progress.
if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress();

resolvedAt = Timestamp.wrap(uint64(block.timestamp));
status_ = GameStatus.DEFENDER_WINS;

emit Resolved(status = status_);
}

/// @notice A compliant implementation of this interface should return the components of the
/// game UUID's preimage provided in the cwia payload. The preimage of the UUID is
/// constructed as `keccak256(gameType . rootClaim . extraData)` where `.` denotes
/// concatenation.
/// @return gameType_ The type of proof system being used.
/// @return rootClaim_ The root claim of the DisputeGame.
/// @return extraData_ Any extra data supplied to the dispute game contract by the creator.
function gameData() external pure returns (GameType gameType_, Claim rootClaim_, bytes memory extraData_) {
gameType_ = gameType();
rootClaim_ = rootClaim();
extraData_ = extraData();
}
}
65 changes: 65 additions & 0 deletions contracts/src/OPSuccinctDisputeGameFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import {IDisputeGame} from "@optimism/src/dispute/interfaces/IDisputeGame.sol";
import {LibCWIA} from "@solady/utils/legacy/LibCWIA.sol";
import {ISemver} from "@optimism/src/universal/ISemver.sol";

contract OPSuccinctDisputeGameFactory is ISemver {
using LibCWIA for address;

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

/// @notice The address of the OP Succinct DisputeGame implementation contract.
address public gameImpl;

/// @notice Semantic version.
/// @custom:semver v1.0.0-rc2
string public constant version = "v1.0.0-rc2";

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

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

////////////////////////////////////////////////////////////
// Functions //
////////////////////////////////////////////////////////////

/// @notice Constructs the OPSuccinctDisputeGameFactory contract.
constructor(address _owner, address _gameImpl) {
owner = _owner;
gameImpl = _gameImpl;
}

/// @notice Creates a new DisputeGame proxy contract.
function create(bytes32 _rootClaim, uint256 _l2BlockNumber, uint256 _l1BlockNumber, bytes memory _proof)
external
payable
{
IDisputeGame game = IDisputeGame(
gameImpl.clone(
abi.encodePacked(msg.sender, _rootClaim, bytes32(0), abi.encode(_l2BlockNumber, _l1BlockNumber, _proof))
)
);

game.initialize{value: msg.value}();
}

/// Updates the owner address.
/// @param _owner The new owner address.
function transferOwnership(address _owner) external onlyOwner {
owner = _owner;
}

/// @notice Sets the implementation address.
/// @param _implementation New implementation address.
function setImplementation(address _implementation) external onlyOwner {
gameImpl = _implementation;
}
}
Loading

0 comments on commit d2f0d4f

Please sign in to comment.