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: migrate OPSuccinctL2OutputOracle to OptimismPortalV2 #277

Merged
merged 36 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
20bd110
forge install: solady
leruaa Dec 13, 2024
1f28ebe
feat: impl IDisputeGame for OPSuccinctL2OutputOracle
leruaa Dec 13, 2024
ca1eaa9
feat: update deployment
leruaa Dec 13, 2024
8418b7c
add a thin, clonable wrapper on OPSuccinctL2OutputOracle
leruaa Jan 3, 2025
cbcf629
add a test for OPSuccinctDisputeGame initialize()
leruaa Jan 6, 2025
e386e4e
use a more recent tx to make `testOPSuccinctDisputeGame` test pass
leruaa Jan 7, 2025
4bd230a
revert useless OPSuccinctL2OutputOracle IDisputeGame impl
leruaa Jan 7, 2025
f1dc0e8
add OPSuccinctDisputeGame bindings
leruaa Jan 7, 2025
0cb9d04
add OPSuccinctDisputeGameFactory
leruaa Jan 7, 2025
c8e529e
allow to propose output roots using DisputeGameFactory
leruaa Jan 7, 2025
3c9601a
init deployment scripts
leruaa Jan 9, 2025
f238d22
fix server in mock mode
leruaa Jan 9, 2025
b6fd11f
use DGF if the address is provided
leruaa Jan 9, 2025
34b3ef1
Merge branch 'main' into aurelien/dispute-game
leruaa Jan 10, 2025
8f6ba7c
fmt
leruaa Jan 10, 2025
3158d35
add deploy-dispute-game-factory to justfile
leruaa Jan 10, 2025
a90dd34
fix: don't disable logs on the server (#320)
leruaa Jan 10, 2025
4a317f9
feat: rc10 bump + fix mock mode (#323)
ratankaliani Jan 13, 2025
0a8b80e
add test for OPSuccinctL2OutputOracleFactory
leruaa Jan 13, 2025
67b93ed
update book
leruaa Jan 13, 2025
a3e4484
small fixes
leruaa Jan 13, 2025
eb54fad
impl ISemver
leruaa Jan 13, 2025
a684b65
set permissionless proposing in the deployment
leruaa Jan 13, 2025
344c464
OPSuccinctDisputeGameFactory: allow to change impl
leruaa Jan 13, 2025
8827da9
proposer small fixes
leruaa Jan 13, 2025
6e89ac2
don't pin op-deployer version for Kurtosis
leruaa Jan 13, 2025
9ce9ec8
forge fmt
leruaa Jan 14, 2025
abbc052
update book
leruaa Jan 14, 2025
9725fe5
small fixes
leruaa Jan 14, 2025
b9e9072
nits
leruaa Jan 15, 2025
5652806
add a to do for GameTypes.OP_SUCCINCT
leruaa Jan 15, 2025
903d0b0
feat(utils/client): add secp256r1 cycle tracking (#324)
fakedev9999 Jan 14, 2025
bfc7438
chore(proposer): add jq and nc to docker image (#322)
emilianobonassi Jan 14, 2025
23a53d7
feat(programs): Use allocator (#319)
ratankaliani Jan 15, 2025
64de2f0
docs: add troubleshooting.md (#325)
fakedev9999 Jan 15, 2025
6693663
chore: add ci for elf check (#326)
fakedev9999 Jan 15, 2025
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
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
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"
}
33 changes: 33 additions & 0 deletions contracts/script/OPSuccinctDGFDeployer.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import {Script} from "forge-std/Script.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();

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

// This initializes the dipute game
leruaa marked this conversation as resolved.
Show resolved Hide resolved
OPSuccinctDisputeGame gameImpl = new OPSuccinctDisputeGame(l2OutputOracleProxy);
Proxy gameProxy = new Proxy(msg.sender);

gameProxy.upgradeTo(address(gameImpl));

// This initializes the dispute game factory
OPSuccinctDisputeGameFactory factoryImpl = new OPSuccinctDisputeGameFactory(address(gameProxy));
Proxy factoryProxy = new Proxy(msg.sender);

factoryProxy.upgradeTo(address(factoryImpl));

vm.stopBroadcast();

return address(factoryProxy);
}
}
112 changes: 112 additions & 0 deletions contracts/src/OPSuccinctDisputeGame.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// 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 {IDisputeGame} from "@optimism/src/dispute/interfaces/IDisputeGame.sol";
import {Claim, GameStatus, GameType, Hash, Timestamp} from "@optimism/src/dispute/lib/Types.sol";
import {GameNotInProgress, OutOfOrderResolution} from "@optimism/src/dispute/lib/Errors.sol";

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

/// @notice The address of the L2 output oracle contract.
leruaa marked this conversation as resolved.
Show resolved Hide resolved
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;

constructor(address _l2OutpoutOracle) {
l2OutpoutOracle = _l2OutpoutOracle;
}

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

function initialize() external payable {
// TODO: Set createdAt and status
leruaa marked this conversation as resolved.
Show resolved Hide resolved

(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 view returns (GameType) {
// TODO: Retrive and return the game type.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this still need to be added?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I set it to GameTypes.CANNON for now, and I discussed with OP Labs, they aren't opposed to adding a new GameTypes.OP_SUCCINCT

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the use of GAME_TYPE? To see the security model? When would GameTypes.OP_SUCCINCT be upstreamed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GameTypes are used in DisputeGameFactory to store multiple games in a mapping, with the game type as key.

We don't have a clear view on when GameTypes.OP_SUCCINCT will be upstreamed, but if for instance we add:

GameType internal constant OP_SUCCINCT = GameType.wrap(3);

here, we could temporary do:

return GameType.wrap(3);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, just add a comment that GameType.wrap(3) will eventually be upstreamed to be OP Succinct. Add a TODO that this GameType needs to be changed in the future, and link to the corresponding Optimism contracts PR.

}

/// @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 view returns (GameType gameType_, Claim rootClaim_, bytes memory extraData_) {
gameType_ = gameType();
rootClaim_ = rootClaim();
extraData_ = extraData();
}
}
31 changes: 31 additions & 0 deletions contracts/src/OPSuccinctDisputeGameFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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";

contract OPSuccinctDisputeGameFactory {
using LibCWIA for address;

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

/// @notice Constructs the OPSuccinctDisputeGameFactory contract.
constructor(address _gameImpl) {
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}();
}
}
57 changes: 57 additions & 0 deletions contracts/test/OPSuccinctDisputeGame.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import {Test, console} from "forge-std/Test.sol";
import {Utils} from "./helpers/Utils.sol";
import {OPSuccinctL2OutputOracle} from "../src/OPSuccinctL2OutputOracle.sol";
import {OPSuccinctDisputeGame} from "../src/OPSuccinctDisputeGame.sol";
import {IDisputeGame} from "@optimism/src/dispute/interfaces/IDisputeGame.sol";
import {LibCWIA} from "@solady/utils/legacy/LibCWIA.sol";

contract OPSuccinctL2OutputOracleTest is Test, Utils {
using LibCWIA for address;

// Example proof data for the BoB testnet. Tx: https://sepolia.etherscan.io/tx/0x35df99dce5db3d7644a005bd582af2d66533b56fdb01970f248d96e8053fc0ba
uint256 checkpointedL1BlockNum = 7438547;
bytes32 claimedOutputRoot = 0x974323e1f533bf40923f6a5f9d8752d42743bb5b784d9a6d1ce223a5cc368ae6;
uint256 claimedL2BlockNum = 6940641;
bytes proof =
hex"09069090289d338bbce470b324757ae21b8846ba36d88feb8fc9e32aa477d193153db2bc1ffead4fb681196de556343a1cd61954d5e6863327d35e0f2e0b9781278b58231af27bb83226d60c1573639e400130ed49318f28dddb9768c8a71f20de8bc07d0355ef76ec0661b0d720d36943e7d8660b6e603733afb549ffba8773cec52097011525d1239e39b8da29bec5fb18d6f4bdfd84890fedd6c0cf67342a6843bb2a28e9ceae9069e52312b7b79d4a39b7d5527bbcfefd66de3887cea63f76b672081dd49279796f07bfdb04e9c5284dd0565ac923bc2c5c01be28a22c314402280001a7aa13b9a8a1c92850ae89fcede9142542fbc13298ecab89ad8fbfbdabbee3";

function setUp() public {
// Note: L1_RPC should be a valid Sepolia RPC.
vm.createSelectFork(vm.envString("L1_RPC"), checkpointedL1BlockNum + 1);
}

// Test the DisputeGame contract.
function testOPSuccinctDisputeGame() public {
vm.startBroadcast();

Config memory cfg = readJson("opsuccinctl2ooconfig-test.json");

cfg.owner = msg.sender;

address l2ooProxy = deployWithConfig(cfg);

OPSuccinctL2OutputOracle l2oo = OPSuccinctL2OutputOracle(l2ooProxy);
OPSuccinctDisputeGame game = new OPSuccinctDisputeGame(l2ooProxy);

l2oo.addProposer(address(0));
l2oo.checkpointBlockHash(checkpointedL1BlockNum);

IDisputeGame proxy = IDisputeGame(
address(game).clone(
abi.encodePacked(
msg.sender,
claimedOutputRoot,
bytes32(0), // TODO: This should be parentHash
leruaa marked this conversation as resolved.
Show resolved Hide resolved
abi.encode(claimedL2BlockNum, checkpointedL1BlockNum, proof)
)
)
);

vm.stopBroadcast();

proxy.initialize{value: 10}();
}
}
48 changes: 39 additions & 9 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,18 @@ deploy-mock-verifier env_file=".env":
fi

cd contracts

VERIFY=""
if [ $ETHERSCAN_API_KEY != "" ]; then
VERIFY="--verify --verifier etherscan --etherscan-api-key $ETHERSCAN_API_KEY"
fi

forge script script/DeployMockVerifier.s.sol:DeployMockVerifier \
--rpc-url $L1_RPC \
--private-key $PRIVATE_KEY \
--broadcast \
--verify \
--verifier etherscan \
--etherscan-api-key $ETHERSCAN_API_KEY

$VERIFY

# Deploy the OPSuccinct L2 Output Oracle
deploy-oracle env_file=".env":
#!/usr/bin/env bash
Expand All @@ -99,16 +102,18 @@ deploy-oracle env_file=".env":

# forge install
forge install

VERIFY=""
if [ $ETHERSCAN_API_KEY != "" ]; then
VERIFY="--verify --verifier etherscan --etherscan-api-key $ETHERSCAN_API_KEY"
fi

# Run the forge deployment script
forge script script/OPSuccinctDeployer.s.sol:OPSuccinctDeployer \
--rpc-url $L1_RPC \
--private-key $PRIVATE_KEY \
--broadcast \
--verify \
--verifier etherscan \
--etherscan-api-key $ETHERSCAN_API_KEY

$VERIFY

# Upgrade the OPSuccinct L2 Output Oracle
upgrade-oracle env_file=".env":
Expand Down Expand Up @@ -170,4 +175,29 @@ update-parameters env_file=".env":
--rpc-url $L1_RPC \
--private-key $PRIVATE_KEY \
--broadcast
fi
fi

deploy-dispute-game-factory env_file=".env":
#!/usr/bin/env bash
set -euo pipefail

# Load environment variables
source {{env_file}}

# cd into contracts directory
cd contracts

# forge install
forge install

VERIFY=""
if [ $ETHERSCAN_API_KEY != "" ]; then
VERIFY="--verify --verifier etherscan --etherscan-api-key $ETHERSCAN_API_KEY"
fi

# Run the forge deployment script
L2OO_ADDRESS=$L2OO_ADDRESS forge script script/OPSuccinctDGFDeployer.s.sol:OPSuccinctDFGDeployer \
--rpc-url $L1_RPC \
--private-key $PRIVATE_KEY \
--broadcast \
$VERIFY
3 changes: 3 additions & 0 deletions proposer/op/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@ bindings:
@cd ../../contracts/ && forge inspect src/OPSuccinctL2OutputOracle.sol:OPSuccinctL2OutputOracle abi > ../proposer/op/generated_bindings/OPSuccinctL2OutputOracle.abi
@cd ../../contracts/ && forge inspect src/OPSuccinctL2OutputOracle.sol:OPSuccinctL2OutputOracle bytecode > ../proposer/op/generated_bindings/OPSuccinctL2OutputOracle.bin
@abigen --abi generated_bindings/OPSuccinctL2OutputOracle.abi --bin generated_bindings/OPSuccinctL2OutputOracle.bin --pkg bindings --type OPSuccinctL2OutputOracle --out ./bindings/opsuccinctl2outputoracle.go
@cd ../../contracts/ && forge inspect src/OPSuccinctDisputeGameFactory.sol:OPSuccinctDisputeGameFactory abi > ../proposer/op/generated_bindings/OPSuccinctDisputeGameFactory.abi
@cd ../../contracts/ && forge inspect src/OPSuccinctDisputeGameFactory.sol:OPSuccinctDisputeGameFactory bytecode > ../proposer/op/generated_bindings/OPSuccinctDisputeGameFactory.bin
@abigen --abi generated_bindings/OPSuccinctDisputeGameFactory.abi --bin generated_bindings/OPSuccinctDisputeGameFactory.bin --pkg bindings --type OPSuccinctDisputeGameFactory --out ./bindings/opsuccinctdisputegamefactory.go
@rm -rf generated_bindings
@echo "Bindings generated successfully."
Loading
Loading