Skip to content
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
73 changes: 60 additions & 13 deletions scripts/deploy/SystemDeploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,30 @@ contract SystemDeploy is Script {
require(_isDevMultiproof(implInput), "SystemDeploy: registerAggregateVerifier is dev-multiproof only");
_assertValidMultiproofInput(implInput);

// Initialize the AnchorStateRegistry now that the real L2 genesis output root is known. The
// main deploy deferred ASR initialization (see _initializeOPChain) because the starting
// anchor (the L2 genesis output root) cannot be computed until after L2 genesis. This must
// run before deploying the AggregateVerifier, whose constructor reads
// ANCHOR_STATE_REGISTRY.disputeGameFactory(). Idempotent: skip if a prior run (e.g. a retry)
// already initialized the registry, detected via a non-zero starting anchor root.
IAnchorStateRegistry anchorStateRegistry =
IAnchorStateRegistry(artifacts.mustGetAddress("AnchorStateRegistryProxy"));
if (Hash.unwrap(anchorStateRegistry.getStartingAnchorRoot().root) == bytes32(0)) {
bytes32 genesisOutputRoot = cfg.multiproofGenesisOutputRoot();
require(
genesisOutputRoot != bytes32(0) && genesisOutputRoot != bytes32(uint256(1)),
"SystemDeploy: real multiproofGenesisOutputRoot required for deferred anchor"
);
vm.broadcast(msg.sender);
anchorStateRegistry.initialize(
ISystemConfig(artifacts.mustGetAddress("SystemConfigProxy")),
IDisputeGameFactory(artifacts.mustGetAddress("DisputeGameFactoryProxy")),
Proposal({ root: Hash.wrap(genesisOutputRoot), l2SequenceNumber: cfg.multiproofGenesisBlockNumber() }),
GameTypes.AGGREGATE_VERIFIER
);
console.log("Initialized AnchorStateRegistry with deferred genesis output root");
}

GameType gameType = GameType.wrap(uint32(cfg.multiproofGameType()));

// zkVerifier is the dev sentinel (0xdead); this entrypoint is dev-multiproof only.
Expand Down Expand Up @@ -290,10 +314,16 @@ contract SystemDeploy is Script {
function _runConfigured() internal returns (DeployOutput memory output_) {
output_ = deploy(_deployInput());

vm.startPrank(ISuperchainConfig(artifacts.mustGetAddress("SuperchainConfigProxy")).guardian());
IAnchorStateRegistry(artifacts.mustGetAddress("AnchorStateRegistryProxy"))
.setRespectedGameType(GameType.wrap(uint32(cfg.respectedGameType())));
vm.stopPrank();
// When dev-multiproof registration is deferred, the main deploy leaves the
// AnchorStateRegistry uninitialized (see _initializeOPChain); its initialize() — which also
// sets the respected game type — runs in the post-genesis registerAggregateVerifier(bytes32)
// one-shot. Skip the guardian call here to avoid reverting on the uninitialized registry.
if (!_deferAggregateVerifierRegistration(_configuredImplementationsInput())) {
vm.startPrank(ISuperchainConfig(artifacts.mustGetAddress("SuperchainConfigProxy")).guardian());
IAnchorStateRegistry(artifacts.mustGetAddress("AnchorStateRegistryProxy"))
.setRespectedGameType(GameType.wrap(uint32(cfg.respectedGameType())));
vm.stopPrank();
}

console.log("set up op chain!");
}
Expand Down Expand Up @@ -583,7 +613,7 @@ contract SystemDeploy is Script {
vm.broadcast(msg.sender);
output_.opChainProxyAdmin.setImplementationName(address(output_.l1CrossDomainMessengerProxy), messengerName);

_initializeOPChain(_input, _superchainConfig, impls_, output_);
_initializeOPChain(_input, _superchainConfig, impls_, output_, _implementationsInput);

_upgradeToAndCall(
output_.opChainProxyAdmin,
Expand Down Expand Up @@ -614,7 +644,8 @@ contract SystemDeploy is Script {
Types.DeployInput memory _input,
ISuperchainConfig _superchainConfig,
Types.Implementations memory _impls,
Types.DeployOutput memory _output
Types.DeployOutput memory _output,
ImplementationInput memory _implementationsInput
)
internal
{
Expand Down Expand Up @@ -678,12 +709,23 @@ contract SystemDeploy is Script {
abi.encodeCall(IDisputeGameFactory.initialize, (msg.sender))
);

_upgradeToAndCall(
_output.opChainProxyAdmin,
address(_output.anchorStateRegistryProxy),
_impls.anchorStateRegistryImpl,
_encodeAnchorStateRegistryInitializer(_input, _output)
);
if (_deferAggregateVerifierRegistration(_implementationsInput)) {
// Dev-multiproof: the AnchorStateRegistry's starting anchor is the L2 genesis output
// root, which is only known after L2 genesis. Set the implementation now (so the proxy
// is upgrade-complete) but defer initialize() to the post-genesis
// registerAggregateVerifier(bytes32) one-shot, which calls it exactly once with the real
// anchor. Nothing else in the deploy reads the registry's state.
_upgradeTo(
_output.opChainProxyAdmin, address(_output.anchorStateRegistryProxy), _impls.anchorStateRegistryImpl
);
} else {
_upgradeToAndCall(
_output.opChainProxyAdmin,
address(_output.anchorStateRegistryProxy),
_impls.anchorStateRegistryImpl,
_encodeAnchorStateRegistryInitializer(_input, _output)
);
}
}

function _upgradeSuperchainConfigIfNeeded(
Expand Down Expand Up @@ -1180,7 +1222,12 @@ contract SystemDeploy is Script {
/// genesis hash and is therefore unknown at L1 deploy time. Opt-in via the
/// MULTIPROOF_DEFER_REGISTRATION env var so other dev flows that precompute the hash keep
/// deploying the verifier inline.
function _deferAggregateVerifierRegistration(ImplementationInput memory _input) internal view returns (bool) {
function _deferAggregateVerifierRegistration(ImplementationInput memory _input)
internal
view
virtual
returns (bool)
{
return _isDevMultiproof(_input) && vm.envOr("MULTIPROOF_DEFER_REGISTRATION", false);
}

Expand Down
74 changes: 72 additions & 2 deletions test/deploy/SystemDeploy.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,20 @@ contract MockSP1Verifier {
function verifyProof(bytes32, bytes calldata, bytes calldata) external pure { }
}

/// @notice Test harness that forces dev-multiproof registration deferral on, mirroring
/// MULTIPROOF_DEFER_REGISTRATION=true without mutating the process-global env (which would
/// race other parallel test contracts).
contract SystemDeployDeferredHarness is SystemDeploy {
function _deferAggregateVerifierRegistration(ImplementationInput memory _input)
internal
pure
override
returns (bool)
{
return _isDevMultiproof(_input);
}
}

contract SystemDeploy_Test is Test, SystemDeployAssertions {
Artifacts internal constant artifacts =
Artifacts(address(uint160(uint256(keccak256(abi.encode("optimism.artifacts"))))));
Expand Down Expand Up @@ -219,6 +233,62 @@ contract SystemDeploy_Test is Test, SystemDeployAssertions {
assertNotEq(address(factory.gameImpls(gameType)), address(0), "game type registered on factory");
}

/// @notice Dev-multiproof with deferred registration: the main deploy must leave the
/// AnchorStateRegistry uninitialized (zero starting anchor) and the game type
/// unregistered, because the real anchor (L2 genesis output root) is only known after
/// L2 genesis. The post-genesis one-shot then initializes the registry with the real
/// anchor — exactly once — which is what registerAggregateVerifier does against
/// cfg + artifacts.
function test_deploy_devMultiproof_deferred_anchorInitializedPostGenesis() public {
address devSigner = 0x6cebCF805c5191BCf26602E43DECa89AEe092b5d;
SystemDeploy.DeployInput memory input = _defaultDeployInput();
input.implementationsInput.nitroEnclaveVerifier = address(0);
input.implementationsInput.sp1Verifier = ISP1Verifier(address(0));
input.implementationsInput.zkRangeHash = bytes32(0);
input.implementationsInput.zkAggregationHash = bytes32(0);
input.implementationsInput.devTeeSigner = devSigner;
input.implementationsInput.proofMaturityDelaySeconds = 0;
input.implementationsInput.withdrawalDelaySeconds = 0;
input.implementationsInput.disputeGameFinalityDelaySeconds = 0;
input.implementationsInput.slowFinalizationDelay = 0;
input.implementationsInput.fastFinalizationDelay = 0;

// Use a harness that forces deferral on (mirroring MULTIPROOF_DEFER_REGISTRATION=true)
// without mutating the process-global env, which would race other parallel test contracts.
SystemDeploy.DeployOutput memory output = new SystemDeployDeferredHarness().deploy(input);

IDisputeGameFactory factory = IDisputeGameFactory(address(output.opChain.disputeGameFactoryProxy));
GameType gameType = GameType.wrap(uint32(input.implementationsInput.multiproofGameType));

// Deferred: the AggregateVerifier is not registered and the registry is uninitialized.
assertEq(address(factory.gameImpls(gameType)), address(0), "game type not registered yet (deferred)");
assertEq(
Hash.unwrap(output.opChain.anchorStateRegistryProxy.getStartingAnchorRoot().root),
bytes32(0),
"anchor uninitialized after deferred deploy"
);

// Simulate the post-genesis one-shot: the ProxyAdmin owner (this test contract) initializes
// the registry with the real genesis output root.
bytes32 genesisOutputRoot = bytes32(uint256(0xC0FFEE));
output.opChain.anchorStateRegistryProxy
.initialize(
output.opChain.systemConfigProxy,
output.opChain.disputeGameFactoryProxy,
Proposal({ root: Hash.wrap(genesisOutputRoot), l2SequenceNumber: 0 }),
gameType
);

(Hash anchorRoot, uint256 anchorBlock) = output.opChain.anchorStateRegistryProxy.getAnchorRoot();
assertEq(Hash.unwrap(anchorRoot), genesisOutputRoot, "anchor root set to real genesis output root");
assertEq(anchorBlock, 0, "anchor block is genesis");
assertEq(
GameType.unwrap(output.opChain.anchorStateRegistryProxy.respectedGameType()),
GameType.unwrap(gameType),
"respected game type set on init"
);
}

function test_deploy_devMultiproof_onProductionChain_reverts() public {
SystemDeploy.DeployInput memory input = _defaultDeployInput();
input.implementationsInput.nitroEnclaveVerifier = address(0);
Expand All @@ -237,9 +307,9 @@ contract SystemDeploy_Test is Test, SystemDeployAssertions {
vm.expectRevert("SystemDeploy: dev multiproof cannot be deployed on production chains");
systemDeploy.deploy(input);

// Base Sepolia
// Base Sepolia (84532) is a valid dev-multiproof settlement target (see PRIV-2004), so it
// must NOT revert.
vm.chainId(84532);
vm.expectRevert("SystemDeploy: dev multiproof cannot be deployed on production chains");
systemDeploy.deploy(input);
}

Expand Down
Loading