Commit 3f4d94a4 authored by Matthew Slipper's avatar Matthew Slipper Committed by GitHub

ctb: Implement addGameType method on OPCM (#13653)

parent 3d3deab1
...@@ -9,8 +9,6 @@ import { IResourceMetering } from "interfaces/L1/IResourceMetering.sol"; ...@@ -9,8 +9,6 @@ import { IResourceMetering } from "interfaces/L1/IResourceMetering.sol";
import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol";
import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol"; import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol";
import { Bytes } from "src/libraries/Bytes.sol";
import { IDelayedWETH } from "interfaces/dispute/IDelayedWETH.sol"; import { IDelayedWETH } from "interfaces/dispute/IDelayedWETH.sol";
import { IPreimageOracle } from "interfaces/cannon/IPreimageOracle.sol"; import { IPreimageOracle } from "interfaces/cannon/IPreimageOracle.sol";
import { IMIPS } from "interfaces/cannon/IMIPS.sol"; import { IMIPS } from "interfaces/cannon/IMIPS.sol";
...@@ -493,14 +491,22 @@ contract DeployImplementations is Script { ...@@ -493,14 +491,22 @@ contract DeployImplementations is Script {
bytes32 salt = _dii.salt(); bytes32 salt = _dii.salt();
OPContractsManager.Blueprints memory blueprints; OPContractsManager.Blueprints memory blueprints;
address checkAddress;
vm.startBroadcast(msg.sender); vm.startBroadcast(msg.sender);
blueprints.addressManager = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("AddressManager")), salt); (blueprints.addressManager, checkAddress) = Blueprint.create(vm.getCode("AddressManager"), salt);
blueprints.proxy = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("Proxy")), salt); require(checkAddress == address(0), "OPCM-10");
blueprints.proxyAdmin = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("ProxyAdmin")), salt); (blueprints.proxy, checkAddress) = Blueprint.create(vm.getCode("Proxy"), salt);
blueprints.l1ChugSplashProxy = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("L1ChugSplashProxy")), salt); require(checkAddress == address(0), "OPCM-20");
blueprints.resolvedDelegateProxy = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("ResolvedDelegateProxy")), salt); (blueprints.proxyAdmin, checkAddress) = Blueprint.create(vm.getCode("ProxyAdmin"), salt);
blueprints.anchorStateRegistry = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("AnchorStateRegistry")), salt); require(checkAddress == address(0), "OPCM-30");
(blueprints.permissionedDisputeGame1, blueprints.permissionedDisputeGame2) = deployBigBytecode(vm.getCode("PermissionedDisputeGame"), salt); (blueprints.l1ChugSplashProxy, checkAddress) = Blueprint.create(vm.getCode("L1ChugSplashProxy"), salt);
require(checkAddress == address(0), "OPCM-40");
(blueprints.resolvedDelegateProxy, checkAddress) = Blueprint.create(vm.getCode("ResolvedDelegateProxy"), salt);
require(checkAddress == address(0), "OPCM-50");
(blueprints.anchorStateRegistry, checkAddress) = Blueprint.create(vm.getCode("AnchorStateRegistry"), salt);
require(checkAddress == address(0), "OPCM-60");
(blueprints.permissionedDisputeGame1, blueprints.permissionedDisputeGame2) = Blueprint.create(vm.getCode("PermissionedDisputeGame"), salt);
(blueprints.permissionlessDisputeGame1, blueprints.permissionlessDisputeGame2) = Blueprint.create(vm.getCode("FaultDisputeGame"), salt);
vm.stopBroadcast(); vm.stopBroadcast();
// forgefmt: disable-end // forgefmt: disable-end
...@@ -862,33 +868,6 @@ contract DeployImplementations is Script { ...@@ -862,33 +868,6 @@ contract DeployImplementations is Script {
dio_ = DeployImplementationsOutput(DeployUtils.toIOAddress(msg.sender, "optimism.DeployImplementationsOutput")); dio_ = DeployImplementationsOutput(DeployUtils.toIOAddress(msg.sender, "optimism.DeployImplementationsOutput"));
} }
function deployBytecode(bytes memory _bytecode, bytes32 _salt) public returns (address newContract_) {
assembly ("memory-safe") {
newContract_ := create2(0, add(_bytecode, 0x20), mload(_bytecode), _salt)
}
require(newContract_ != address(0), "DeployImplementations: create2 failed");
}
function deployBigBytecode(
bytes memory _bytecode,
bytes32 _salt
)
public
returns (address newContract1_, address newContract2_)
{
// Preamble needs 3 bytes.
uint256 maxInitCodeSize = 24576 - 3;
require(_bytecode.length > maxInitCodeSize, "DeployImplementations: Use deployBytecode instead");
bytes memory part1Slice = Bytes.slice(_bytecode, 0, maxInitCodeSize);
bytes memory part1 = Blueprint.blueprintDeployerBytecode(part1Slice);
bytes memory part2Slice = Bytes.slice(_bytecode, maxInitCodeSize, _bytecode.length - maxInitCodeSize);
bytes memory part2 = Blueprint.blueprintDeployerBytecode(part2Slice);
newContract1_ = deployBytecode(part1, _salt);
newContract2_ = deployBytecode(part2, _salt);
}
// Zero address is returned if the address is not found in '_standardVersionsToml'. // Zero address is returned if the address is not found in '_standardVersionsToml'.
function getReleaseAddress( function getReleaseAddress(
string memory _version, string memory _version,
......
...@@ -198,7 +198,9 @@ contract DeployOPCM is Script { ...@@ -198,7 +198,9 @@ contract DeployOPCM is Script {
resolvedDelegateProxy: _doi.resolvedDelegateProxyBlueprint(), resolvedDelegateProxy: _doi.resolvedDelegateProxyBlueprint(),
anchorStateRegistry: _doi.anchorStateRegistryBlueprint(), anchorStateRegistry: _doi.anchorStateRegistryBlueprint(),
permissionedDisputeGame1: _doi.permissionedDisputeGame1Blueprint(), permissionedDisputeGame1: _doi.permissionedDisputeGame1Blueprint(),
permissionedDisputeGame2: _doi.permissionedDisputeGame2Blueprint() permissionedDisputeGame2: _doi.permissionedDisputeGame2Blueprint(),
permissionlessDisputeGame1: address(0),
permissionlessDisputeGame2: address(0)
}); });
OPContractsManager.Implementations memory implementations = OPContractsManager.Implementations({ OPContractsManager.Implementations memory implementations = OPContractsManager.Implementations({
l1ERC721BridgeImpl: address(_doi.l1ERC721BridgeImpl()), l1ERC721BridgeImpl: address(_doi.l1ERC721BridgeImpl()),
......
...@@ -57,6 +57,16 @@ ...@@ -57,6 +57,16 @@
"internalType": "address", "internalType": "address",
"name": "permissionedDisputeGame2", "name": "permissionedDisputeGame2",
"type": "address" "type": "address"
},
{
"internalType": "address",
"name": "permissionlessDisputeGame1",
"type": "address"
},
{
"internalType": "address",
"name": "permissionlessDisputeGame2",
"type": "address"
} }
], ],
"internalType": "struct OPContractsManager.Blueprints", "internalType": "struct OPContractsManager.Blueprints",
...@@ -132,6 +142,104 @@ ...@@ -132,6 +142,104 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"components": [
{
"internalType": "string",
"name": "saltMixer",
"type": "string"
},
{
"internalType": "contract ISystemConfig",
"name": "systemConfig",
"type": "address"
},
{
"internalType": "contract IProxyAdmin",
"name": "proxyAdmin",
"type": "address"
},
{
"internalType": "contract IDelayedWETH",
"name": "delayedWETH",
"type": "address"
},
{
"internalType": "GameType",
"name": "disputeGameType",
"type": "uint32"
},
{
"internalType": "Claim",
"name": "disputeAbsolutePrestate",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "disputeMaxGameDepth",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "disputeSplitDepth",
"type": "uint256"
},
{
"internalType": "Duration",
"name": "disputeClockExtension",
"type": "uint64"
},
{
"internalType": "Duration",
"name": "disputeMaxClockDuration",
"type": "uint64"
},
{
"internalType": "uint256",
"name": "initialBond",
"type": "uint256"
},
{
"internalType": "contract IBigStepper",
"name": "vm",
"type": "address"
},
{
"internalType": "bool",
"name": "permissioned",
"type": "bool"
}
],
"internalType": "struct OPContractsManager.AddGameInput[]",
"name": "_gameConfigs",
"type": "tuple[]"
}
],
"name": "addGameType",
"outputs": [
{
"components": [
{
"internalType": "contract IDelayedWETH",
"name": "delayedWETH",
"type": "address"
},
{
"internalType": "contract IFaultDisputeGame",
"name": "faultDisputeGame",
"type": "address"
}
],
"internalType": "struct OPContractsManager.AddGameOutput[]",
"name": "",
"type": "tuple[]"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "blueprints", "name": "blueprints",
...@@ -177,6 +285,16 @@ ...@@ -177,6 +285,16 @@
"internalType": "address", "internalType": "address",
"name": "permissionedDisputeGame2", "name": "permissionedDisputeGame2",
"type": "address" "type": "address"
},
{
"internalType": "address",
"name": "permissionlessDisputeGame1",
"type": "address"
},
{
"internalType": "address",
"name": "permissionlessDisputeGame2",
"type": "address"
} }
], ],
"internalType": "struct OPContractsManager.Blueprints", "internalType": "struct OPContractsManager.Blueprints",
...@@ -596,6 +714,11 @@ ...@@ -596,6 +714,11 @@
"name": "InvalidChainId", "name": "InvalidChainId",
"type": "error" "type": "error"
}, },
{
"inputs": [],
"name": "InvalidGameConfigs",
"type": "error"
},
{ {
"inputs": [ "inputs": [
{ {
...@@ -622,6 +745,11 @@ ...@@ -622,6 +745,11 @@
"name": "NotABlueprint", "name": "NotABlueprint",
"type": "error" "type": "error"
}, },
{
"inputs": [],
"name": "OnlyDelegatecall",
"type": "error"
},
{ {
"inputs": [], "inputs": [],
"name": "ReservedBitsSet", "name": "ReservedBitsSet",
......
...@@ -57,6 +57,16 @@ ...@@ -57,6 +57,16 @@
"internalType": "address", "internalType": "address",
"name": "permissionedDisputeGame2", "name": "permissionedDisputeGame2",
"type": "address" "type": "address"
},
{
"internalType": "address",
"name": "permissionlessDisputeGame1",
"type": "address"
},
{
"internalType": "address",
"name": "permissionlessDisputeGame2",
"type": "address"
} }
], ],
"internalType": "struct OPContractsManager.Blueprints", "internalType": "struct OPContractsManager.Blueprints",
...@@ -132,6 +142,104 @@ ...@@ -132,6 +142,104 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"components": [
{
"internalType": "string",
"name": "saltMixer",
"type": "string"
},
{
"internalType": "contract ISystemConfig",
"name": "systemConfig",
"type": "address"
},
{
"internalType": "contract IProxyAdmin",
"name": "proxyAdmin",
"type": "address"
},
{
"internalType": "contract IDelayedWETH",
"name": "delayedWETH",
"type": "address"
},
{
"internalType": "GameType",
"name": "disputeGameType",
"type": "uint32"
},
{
"internalType": "Claim",
"name": "disputeAbsolutePrestate",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "disputeMaxGameDepth",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "disputeSplitDepth",
"type": "uint256"
},
{
"internalType": "Duration",
"name": "disputeClockExtension",
"type": "uint64"
},
{
"internalType": "Duration",
"name": "disputeMaxClockDuration",
"type": "uint64"
},
{
"internalType": "uint256",
"name": "initialBond",
"type": "uint256"
},
{
"internalType": "contract IBigStepper",
"name": "vm",
"type": "address"
},
{
"internalType": "bool",
"name": "permissioned",
"type": "bool"
}
],
"internalType": "struct OPContractsManager.AddGameInput[]",
"name": "_gameConfigs",
"type": "tuple[]"
}
],
"name": "addGameType",
"outputs": [
{
"components": [
{
"internalType": "contract IDelayedWETH",
"name": "delayedWETH",
"type": "address"
},
{
"internalType": "contract IFaultDisputeGame",
"name": "faultDisputeGame",
"type": "address"
}
],
"internalType": "struct OPContractsManager.AddGameOutput[]",
"name": "",
"type": "tuple[]"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "blueprints", "name": "blueprints",
...@@ -177,6 +285,16 @@ ...@@ -177,6 +285,16 @@
"internalType": "address", "internalType": "address",
"name": "permissionedDisputeGame2", "name": "permissionedDisputeGame2",
"type": "address" "type": "address"
},
{
"internalType": "address",
"name": "permissionlessDisputeGame1",
"type": "address"
},
{
"internalType": "address",
"name": "permissionlessDisputeGame2",
"type": "address"
} }
], ],
"internalType": "struct OPContractsManager.Blueprints", "internalType": "struct OPContractsManager.Blueprints",
...@@ -596,6 +714,11 @@ ...@@ -596,6 +714,11 @@
"name": "InvalidChainId", "name": "InvalidChainId",
"type": "error" "type": "error"
}, },
{
"inputs": [],
"name": "InvalidGameConfigs",
"type": "error"
},
{ {
"inputs": [ "inputs": [
{ {
...@@ -622,6 +745,11 @@ ...@@ -622,6 +745,11 @@
"name": "NotABlueprint", "name": "NotABlueprint",
"type": "error" "type": "error"
}, },
{
"inputs": [],
"name": "OnlyDelegatecall",
"type": "error"
},
{ {
"inputs": [], "inputs": [],
"name": "ReservedBitsSet", "name": "ReservedBitsSet",
......
...@@ -16,8 +16,8 @@ ...@@ -16,8 +16,8 @@
"sourceCodeHash": "0xa91b445bdc666a02ba18e3b91ba94b6d54bbe65da714002fc734814201319d57" "sourceCodeHash": "0xa91b445bdc666a02ba18e3b91ba94b6d54bbe65da714002fc734814201319d57"
}, },
"src/L1/OPContractsManager.sol": { "src/L1/OPContractsManager.sol": {
"initCodeHash": "0x4b413cbe79bd10d41d8f3e9f0408e773dd49ced823d457b9f9aa92f446828105", "initCodeHash": "0xe0c14a8fee7ad4c4e28a3ff6ca4e726721a6c3fea0a74ab7eac7ef07fe4da0ae",
"sourceCodeHash": "0xe5179a20ae40d4e4773c52df98bac67e73e04044bec9e8750073b4e2f14fe81b" "sourceCodeHash": "0xe0f5413e0a0a335016d773f02ef6bd25551416635981e83b7a4da601b9b65bc4"
}, },
"src/L1/OptimismPortal2.sol": { "src/L1/OptimismPortal2.sol": {
"initCodeHash": "0x7e533474310583593c2d57d30fcd1ec11e1568dbaaf37a2dd28c5cc574068bac", "initCodeHash": "0x7e533474310583593c2d57d30fcd1ec11e1568dbaaf37a2dd28c5cc574068bac",
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
"type": "string" "type": "string"
}, },
{ {
"bytes": "256", "bytes": "320",
"label": "blueprint", "label": "blueprint",
"offset": 0, "offset": 0,
"slot": "1", "slot": "1",
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
"bytes": "288", "bytes": "288",
"label": "implementation", "label": "implementation",
"offset": 0, "offset": 0,
"slot": "9", "slot": "11",
"type": "struct OPContractsManager.Implementations" "type": "struct OPContractsManager.Implementations"
} }
] ]
\ No newline at end of file
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
"type": "string" "type": "string"
}, },
{ {
"bytes": "256", "bytes": "320",
"label": "blueprint", "label": "blueprint",
"offset": 0, "offset": 0,
"slot": "1", "slot": "1",
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
"bytes": "288", "bytes": "288",
"label": "implementation", "label": "implementation",
"offset": 0, "offset": 0,
"slot": "9", "slot": "11",
"type": "struct OPContractsManager.Implementations" "type": "struct OPContractsManager.Implementations"
} }
] ]
\ No newline at end of file
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import { Bytes } from "src/libraries/Bytes.sol";
/// @notice Methods for working with ERC-5202 blueprint contracts. /// @notice Methods for working with ERC-5202 blueprint contracts.
/// https://eips.ethereum.org/EIPS/eip-5202 /// https://eips.ethereum.org/EIPS/eip-5202
library Blueprint { library Blueprint {
...@@ -157,6 +159,50 @@ library Blueprint { ...@@ -157,6 +159,50 @@ library Blueprint {
if (newContract_ == address(0)) revert DeploymentFailed(); if (newContract_ == address(0)) revert DeploymentFailed();
} }
/// @notice Deploys a blueprint contract with the given `_rawBytecode` and `_salt`. If the blueprint is too large to
/// fit in a single deployment, it is split across two addresses. It is the responsibility of the caller to handle
/// large contracts by checking if the second return value is not address(0).
function create(
bytes memory _rawBytecode,
bytes32 _salt
)
internal
returns (address newContract1_, address newContract2_)
{
if (_rawBytecode.length <= maxInitCodeSize()) {
newContract1_ = deploySmallBytecode(blueprintDeployerBytecode(_rawBytecode), _salt);
return (newContract1_, address(0));
}
(newContract1_, newContract2_) = deployBigBytecode(_rawBytecode, _salt);
}
/// @notice Deploys a blueprint contract that can fit in a single address.
function deploySmallBytecode(bytes memory _bytecode, bytes32 _salt) internal returns (address newContract_) {
assembly ("memory-safe") {
newContract_ := create2(0, add(_bytecode, 0x20), mload(_bytecode), _salt)
}
require(newContract_ != address(0), "Blueprint: create2 failed");
}
/// @notice Deploys a two blueprint contracts, splitting the bytecode across both of them.
function deployBigBytecode(
bytes memory _bytecode,
bytes32 _salt
)
internal
returns (address newContract1_, address newContract2_)
{
uint32 maxSize = maxInitCodeSize();
bytes memory part1Slice = Bytes.slice(_bytecode, 0, maxSize);
bytes memory part1 = blueprintDeployerBytecode(part1Slice);
bytes memory part2Slice = Bytes.slice(_bytecode, maxSize, _bytecode.length - maxSize);
bytes memory part2 = blueprintDeployerBytecode(part2Slice);
newContract1_ = deploySmallBytecode(part1, _salt);
newContract2_ = deploySmallBytecode(part2, _salt);
}
/// @notice Convert a bytes array to a uint256. /// @notice Convert a bytes array to a uint256.
function bytesToUint(bytes memory _b) internal pure returns (uint256) { function bytesToUint(bytes memory _b) internal pure returns (uint256) {
if (_b.length > 32) revert BytesArrayTooLong(); if (_b.length > 32) revert BytesArrayTooLong();
...@@ -166,4 +212,9 @@ library Blueprint { ...@@ -166,4 +212,9 @@ library Blueprint {
} }
return number; return number;
} }
/// @notice Returns the maximum init code size for each blueprint. The preamble needs 3 bytes.
function maxInitCodeSize() internal pure returns (uint32) {
return 24576 - 3;
}
} }
...@@ -8,6 +8,8 @@ import { FaultDisputeGame_Init, _changeClaimStatus } from "test/dispute/FaultDis ...@@ -8,6 +8,8 @@ import { FaultDisputeGame_Init, _changeClaimStatus } from "test/dispute/FaultDis
import "src/dispute/lib/Types.sol"; import "src/dispute/lib/Types.sol";
import "src/dispute/lib/Errors.sol"; import "src/dispute/lib/Errors.sol";
import { Hash } from "src/dispute/lib/Types.sol";
contract AnchorStateRegistry_Init is FaultDisputeGame_Init { contract AnchorStateRegistry_Init is FaultDisputeGame_Init {
function setUp() public virtual override { function setUp() public virtual override {
// Duplicating the initialization/setup logic of FaultDisputeGame_Test. // Duplicating the initialization/setup logic of FaultDisputeGame_Test.
......
...@@ -781,6 +781,7 @@ contract Specification_Test is CommonTest { ...@@ -781,6 +781,7 @@ contract Specification_Test is CommonTest {
_addSpec({ _name: "OPContractsManager", _sel: OPContractsManager.blueprints.selector }); _addSpec({ _name: "OPContractsManager", _sel: OPContractsManager.blueprints.selector });
_addSpec({ _name: "OPContractsManager", _sel: OPContractsManager.chainIdToBatchInboxAddress.selector }); _addSpec({ _name: "OPContractsManager", _sel: OPContractsManager.chainIdToBatchInboxAddress.selector });
_addSpec({ _name: "OPContractsManager", _sel: OPContractsManager.implementations.selector }); _addSpec({ _name: "OPContractsManager", _sel: OPContractsManager.implementations.selector });
_addSpec({ _name: "OPContractsManager", _sel: OPContractsManager.addGameType.selector });
// OPContractsManagerInterop // OPContractsManagerInterop
_addSpec({ _name: "OPContractsManagerInterop", _sel: _getSel("version()") }); _addSpec({ _name: "OPContractsManagerInterop", _sel: _getSel("version()") });
...@@ -792,6 +793,7 @@ contract Specification_Test is CommonTest { ...@@ -792,6 +793,7 @@ contract Specification_Test is CommonTest {
_addSpec({ _name: "OPContractsManagerInterop", _sel: OPContractsManager.blueprints.selector }); _addSpec({ _name: "OPContractsManagerInterop", _sel: OPContractsManager.blueprints.selector });
_addSpec({ _name: "OPContractsManagerInterop", _sel: OPContractsManager.chainIdToBatchInboxAddress.selector }); _addSpec({ _name: "OPContractsManagerInterop", _sel: OPContractsManager.chainIdToBatchInboxAddress.selector });
_addSpec({ _name: "OPContractsManagerInterop", _sel: OPContractsManager.implementations.selector }); _addSpec({ _name: "OPContractsManagerInterop", _sel: OPContractsManager.implementations.selector });
_addSpec({ _name: "OPContractsManagerInterop", _sel: OPContractsManager.addGameType.selector });
// DeputyGuardianModule // DeputyGuardianModule
_addSpec({ _addSpec({
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment