Skip to content

Commit a622ff3

Browse files
feat: have AnchorStateRegistry use a single root
Updates the AnchorStateRegistry to use a single unified anchor root by checking with the OptimismPortal for the currently respected game type. Additionally makes the AnchorStateRegistry MCP ready. Users MUST deploy this contract as a new proxy and cannot upgrade their existing proxy.
1 parent 4cd07f5 commit a622ff3

27 files changed

+1247
-410
lines changed

op-deployer/pkg/deployer/bootstrap/opcm.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,6 @@ func DeployOPCMInputForChain(release string, chainID uint64) (opcm.DeployOPCMInp
213213
ProxyAdminBlueprint: blueprints.ProxyAdmin,
214214
L1ChugSplashProxyBlueprint: blueprints.L1ChugSplashProxy,
215215
ResolvedDelegateProxyBlueprint: blueprints.ResolvedDelegateProxy,
216-
AnchorStateRegistryBlueprint: blueprints.AnchorStateRegistry,
217216
PermissionedDisputeGame1Blueprint: blueprints.PermissionedDisputeGame1,
218217
PermissionedDisputeGame2Blueprint: blueprints.PermissionedDisputeGame2,
219218

@@ -224,6 +223,7 @@ func DeployOPCMInputForChain(release string, chainID uint64) (opcm.DeployOPCMInp
224223
L1CrossDomainMessengerImpl: releases.L1CrossDomainMessenger.ImplementationAddress,
225224
L1StandardBridgeImpl: releases.L1StandardBridge.ImplementationAddress,
226225
DisputeGameFactoryImpl: releases.DisputeGameFactory.ImplementationAddress,
226+
AnchorStateRegistryImpl: releases.AnchorStateRegistry.ImplementationAddress,
227227
DelayedWETHImpl: releases.DelayedWETH.ImplementationAddress,
228228
MipsImpl: releases.MIPS.Address,
229229
}, nil

op-deployer/pkg/deployer/opcm/opcm.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ type DeployOPCMInput struct {
1717
ProxyAdminBlueprint common.Address
1818
L1ChugSplashProxyBlueprint common.Address
1919
ResolvedDelegateProxyBlueprint common.Address
20-
AnchorStateRegistryBlueprint common.Address
2120
PermissionedDisputeGame1Blueprint common.Address
2221
PermissionedDisputeGame2Blueprint common.Address
2322

@@ -28,6 +27,7 @@ type DeployOPCMInput struct {
2827
L1CrossDomainMessengerImpl common.Address
2928
L1StandardBridgeImpl common.Address
3029
DisputeGameFactoryImpl common.Address
30+
AnchorStateRegistryImpl common.Address
3131
DelayedWETHImpl common.Address
3232
MipsImpl common.Address
3333
}
Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,36 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.0;
33

4+
import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol";
45
import { IFaultDisputeGame } from "interfaces/dispute/IFaultDisputeGame.sol";
56
import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol";
67
import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol";
8+
import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol";
79
import { GameType, Hash, OutputRoot } from "src/dispute/lib/Types.sol";
810

911
interface IAnchorStateRegistry {
10-
struct StartingAnchorRoot {
11-
GameType gameType;
12-
OutputRoot outputRoot;
13-
}
14-
15-
error InvalidGameStatus();
12+
error InvalidAnchorGame(string reason);
1613
error Unauthorized();
17-
error UnregisteredGame();
1814

15+
event AnchorNotUpdated(IFaultDisputeGame indexed game, string reason);
16+
event AnchorUpdated(IFaultDisputeGame indexed game);
1917
event Initialized(uint8 version);
2018

21-
function anchors(GameType) external view returns (Hash root, uint256 l2BlockNumber); // nosemgrep
19+
function anchorGame() external view returns (IFaultDisputeGame);
20+
function anchors(GameType) external view returns (Hash, uint256);
21+
function getAnchorRoot() external view returns (Hash, uint256);
2222
function disputeGameFactory() external view returns (IDisputeGameFactory);
23-
function initialize(
24-
StartingAnchorRoot[] memory _startingAnchorRoots,
25-
ISuperchainConfig _superchainConfig
26-
)
27-
external;
23+
function initialize(ISuperchainConfig _superchainConfig, IDisputeGameFactory _disputeGameFactory, IOptimismPortal2 _portal, OutputRoot memory _startingAnchorRoot) external;
24+
function isGameRegistered(IDisputeGame _game) external view returns (bool);
25+
function isGameBlacklisted(IDisputeGame _game) external view returns (bool);
26+
function isGameRespected(IDisputeGame _game) external view returns (bool);
27+
function isGameRetired(IDisputeGame _game) external view returns (bool);
28+
function isGameProper(IDisputeGame _game) external view returns (bool, string memory);
29+
function portal() external view returns (IOptimismPortal2);
2830
function setAnchorState(IFaultDisputeGame _game) external;
2931
function superchainConfig() external view returns (ISuperchainConfig);
3032
function tryUpdateAnchorState() external;
3133
function version() external view returns (string memory);
3234

33-
function __constructor__(IDisputeGameFactory _disputeGameFactory) external;
35+
function __constructor__() external;
3436
}

packages/contracts-bedrock/justfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,13 @@ test-upgrade *ARGS: build-go-ffi
7373
#!/bin/bash
7474
echo "Running upgrade tests at block $pinnedBlockNumber"
7575
export FORK_BLOCK_NUMBER=$pinnedBlockNumber
76-
export NO_MATCH_CONTRACTS="OptimismPortal2WithMockERC20_Test|OptimismPortal2_FinalizeWithdrawal_Test|AnchorStateRegistry_Initialize_Test|AnchorStateRegistry_TryUpdateAnchorState_Test|FaultDisputeGame_Test|FaultDispute_1v1_Actors_Test"
76+
export NO_MATCH_CONTRACTS="OptimismPortal2WithMockERC20_Test|OptimismPortal2_FinalizeWithdrawal_Test|'AnchorStateRegistry_*'|FaultDisputeGame_Test|FaultDispute_1v1_Actors_Test"
77+
export NO_MATCH_PATHS="test/dispute/AnchorStateRegistry.t.sol"
7778
FORK_RPC_URL=$ETH_RPC_URL \
7879
FORK_TEST=true \
7980
forge test --match-path "test/{L1,dispute}/**" \
8081
--no-match-contract "$NO_MATCH_CONTRACTS" \
82+
--no-match-path "$NO_MATCH_PATHS" \
8183
{{ARGS}}
8284

8385
test-upgrade-rerun *ARGS: build-go-ffi

packages/contracts-bedrock/scripts/deploy/ChainAssertions.sol

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -529,10 +529,6 @@ library ChainAssertions {
529529
Blueprint.parseBlueprintPreamble(address(blueprints.resolvedDelegateProxy).code);
530530
require(keccak256(rdProxyPreamble.initcode) == keccak256(vm.getCode("ResolvedDelegateProxy")), "CHECK-OPCM-180");
531531

532-
Blueprint.Preamble memory asrPreamble =
533-
Blueprint.parseBlueprintPreamble(address(blueprints.anchorStateRegistry).code);
534-
require(keccak256(asrPreamble.initcode) == keccak256(vm.getCode("AnchorStateRegistry")), "CHECK-OPCM-190");
535-
536532
Blueprint.Preamble memory pdg1Preamble =
537533
Blueprint.parseBlueprintPreamble(address(blueprints.permissionedDisputeGame1).code);
538534
Blueprint.Preamble memory pdg2Preamble =

packages/contracts-bedrock/scripts/deploy/Deploy.s.sol

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,6 @@ contract Deploy is Deployer {
384384
artifacts.save("DisputeGameFactoryProxy", address(deployOutput.disputeGameFactoryProxy));
385385
artifacts.save("PermissionedDelayedWETHProxy", address(deployOutput.delayedWETHPermissionedGameProxy));
386386
artifacts.save("AnchorStateRegistryProxy", address(deployOutput.anchorStateRegistryProxy));
387-
artifacts.save("AnchorStateRegistryImpl", address(deployOutput.anchorStateRegistryImpl));
388387
artifacts.save("PermissionedDisputeGame", address(deployOutput.permissionedDisputeGame));
389388
artifacts.save("OptimismPortalProxy", address(deployOutput.optimismPortalProxy));
390389
artifacts.save("OptimismPortal2Proxy", address(deployOutput.optimismPortalProxy));
@@ -859,24 +858,6 @@ contract Deploy is Deployer {
859858

860859
/// @notice Get the DeployInput struct to use for testing
861860
function getDeployInput() public view returns (OPContractsManager.DeployInput memory) {
862-
OutputRoot memory testOutputRoot = OutputRoot({
863-
root: Hash.wrap(cfg.faultGameGenesisOutputRoot()),
864-
l2BlockNumber: cfg.faultGameGenesisBlock()
865-
});
866-
IAnchorStateRegistry.StartingAnchorRoot[] memory startingAnchorRoots =
867-
new IAnchorStateRegistry.StartingAnchorRoot[](5);
868-
startingAnchorRoots[0] =
869-
IAnchorStateRegistry.StartingAnchorRoot({ gameType: GameTypes.CANNON, outputRoot: testOutputRoot });
870-
startingAnchorRoots[1] = IAnchorStateRegistry.StartingAnchorRoot({
871-
gameType: GameTypes.PERMISSIONED_CANNON,
872-
outputRoot: testOutputRoot
873-
});
874-
startingAnchorRoots[2] =
875-
IAnchorStateRegistry.StartingAnchorRoot({ gameType: GameTypes.ASTERISC, outputRoot: testOutputRoot });
876-
startingAnchorRoots[3] =
877-
IAnchorStateRegistry.StartingAnchorRoot({ gameType: GameTypes.FAST, outputRoot: testOutputRoot });
878-
startingAnchorRoots[4] =
879-
IAnchorStateRegistry.StartingAnchorRoot({ gameType: GameTypes.ALPHABET, outputRoot: testOutputRoot });
880861
string memory saltMixer = "salt mixer";
881862
return OPContractsManager.DeployInput({
882863
roles: OPContractsManager.Roles({
@@ -890,7 +871,9 @@ contract Deploy is Deployer {
890871
basefeeScalar: cfg.basefeeScalar(),
891872
blobBasefeeScalar: cfg.blobbasefeeScalar(),
892873
l2ChainId: cfg.l2ChainID(),
893-
startingAnchorRoots: abi.encode(startingAnchorRoots),
874+
startingAnchorRoot: abi.encode(
875+
OutputRoot({ root: Hash.wrap(cfg.faultGameGenesisOutputRoot()), l2BlockNumber: cfg.faultGameGenesisBlock() })
876+
),
894877
saltMixer: saltMixer,
895878
gasLimit: uint64(cfg.l2GenesisBlockGasLimit()),
896879
disputeGameType: GameTypes.PERMISSIONED_CANNON,

packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { IDelayedWETH } from "interfaces/dispute/IDelayedWETH.sol";
1515
import { IPreimageOracle } from "interfaces/cannon/IPreimageOracle.sol";
1616
import { IMIPS } from "interfaces/cannon/IMIPS.sol";
1717
import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol";
18-
18+
import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol";
1919
import { OPContractsManager } from "src/L1/OPContractsManager.sol";
2020
import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol";
2121
import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol";
@@ -165,6 +165,7 @@ contract DeployImplementationsOutput is BaseDeployIO {
165165
IL1StandardBridge internal _l1StandardBridgeImpl;
166166
IOptimismMintableERC20Factory internal _optimismMintableERC20FactoryImpl;
167167
IDisputeGameFactory internal _disputeGameFactoryImpl;
168+
IAnchorStateRegistry internal _anchorStateRegistryImpl;
168169

169170
function set(bytes4 _sel, address _addr) public {
170171
require(_addr != address(0), "DeployImplementationsOutput: cannot set zero address");
@@ -181,6 +182,7 @@ contract DeployImplementationsOutput is BaseDeployIO {
181182
else if (_sel == this.l1StandardBridgeImpl.selector) _l1StandardBridgeImpl = IL1StandardBridge(payable(_addr));
182183
else if (_sel == this.optimismMintableERC20FactoryImpl.selector) _optimismMintableERC20FactoryImpl = IOptimismMintableERC20Factory(_addr);
183184
else if (_sel == this.disputeGameFactoryImpl.selector) _disputeGameFactoryImpl = IDisputeGameFactory(_addr);
185+
else if (_sel == this.anchorStateRegistryImpl.selector) _anchorStateRegistryImpl = IAnchorStateRegistry(_addr);
184186
else revert("DeployImplementationsOutput: unknown selector");
185187
// forgefmt: disable-end
186188
}
@@ -202,7 +204,8 @@ contract DeployImplementationsOutput is BaseDeployIO {
202204
address(this.l1ERC721BridgeImpl()),
203205
address(this.l1StandardBridgeImpl()),
204206
address(this.optimismMintableERC20FactoryImpl()),
205-
address(this.disputeGameFactoryImpl())
207+
address(this.disputeGameFactoryImpl()),
208+
address(this.anchorStateRegistryImpl())
206209
);
207210

208211
DeployUtils.assertValidContractAddresses(Solarray.extend(addrs1, addrs2));
@@ -265,10 +268,16 @@ contract DeployImplementationsOutput is BaseDeployIO {
265268
return _disputeGameFactoryImpl;
266269
}
267270

271+
function anchorStateRegistryImpl() public view returns (IAnchorStateRegistry) {
272+
DeployUtils.assertValidContractAddress(address(_anchorStateRegistryImpl));
273+
return _anchorStateRegistryImpl;
274+
}
275+
268276
// -------- Deployment Assertions --------
269277
function assertValidDeploy(DeployImplementationsInput _dii) public view {
270278
assertValidDelayedWETHImpl(_dii);
271279
assertValidDisputeGameFactoryImpl(_dii);
280+
assertValidAnchorStateRegistryImpl(_dii);
272281
assertValidL1CrossDomainMessengerImpl(_dii);
273282
assertValidL1ERC721BridgeImpl(_dii);
274283
assertValidL1StandardBridgeImpl(_dii);
@@ -410,6 +419,12 @@ contract DeployImplementationsOutput is BaseDeployIO {
410419

411420
require(address(factory.owner()) == address(0), "DG-10");
412421
}
422+
423+
function assertValidAnchorStateRegistryImpl(DeployImplementationsInput) internal view {
424+
IAnchorStateRegistry registry = anchorStateRegistryImpl();
425+
426+
DeployUtils.assertInitialized({ _contractAddress: address(registry), _isProxy: false, _slot: 0, _offset: 0 });
427+
}
413428
}
414429

415430
contract DeployImplementations is Script {
@@ -427,7 +442,7 @@ contract DeployImplementations is Script {
427442
deployPreimageOracleSingleton(_dii, _dio);
428443
deployMipsSingleton(_dii, _dio);
429444
deployDisputeGameFactoryImpl(_dii, _dio);
430-
445+
deployAnchorStateRegistryImpl(_dii, _dio);
431446
// Deploy the OP Contracts Manager with the new implementations set.
432447
deployOPContractsManager(_dii, _dio);
433448

@@ -459,6 +474,7 @@ contract DeployImplementations is Script {
459474
l1CrossDomainMessengerImpl: address(_dio.l1CrossDomainMessengerImpl()),
460475
l1StandardBridgeImpl: address(_dio.l1StandardBridgeImpl()),
461476
disputeGameFactoryImpl: address(_dio.disputeGameFactoryImpl()),
477+
anchorStateRegistryImpl: address(_dio.anchorStateRegistryImpl()),
462478
delayedWETHImpl: address(_dio.delayedWETHImpl()),
463479
mipsImpl: address(_dio.mipsSingleton())
464480
});
@@ -499,7 +515,6 @@ contract DeployImplementations is Script {
499515
blueprints.proxyAdmin = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("ProxyAdmin")), salt);
500516
blueprints.l1ChugSplashProxy = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("L1ChugSplashProxy")), salt);
501517
blueprints.resolvedDelegateProxy = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("ResolvedDelegateProxy")), salt);
502-
blueprints.anchorStateRegistry = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("AnchorStateRegistry")), salt);
503518
(blueprints.permissionedDisputeGame1, blueprints.permissionedDisputeGame2) = deployBigBytecode(vm.getCode("PermissionedDisputeGame"), salt);
504519
vm.stopBroadcast();
505520
// forgefmt: disable-end
@@ -660,7 +675,7 @@ contract DeployImplementations is Script {
660675
// | Contract | Proxied | Deployment | MCP Ready |
661676
// |-------------------------|---------|-----------------------------------|------------|
662677
// | DisputeGameFactory | Yes | Bespoke | Yes |
663-
// | AnchorStateRegistry | Yes | Bespoke | No |
678+
// | AnchorStateRegistry | Yes | Bespoke | Yes |
664679
// | FaultDisputeGame | No | Bespoke | No | Not yet supported by OPCM
665680
// | PermissionedDisputeGame | No | Bespoke | No |
666681
// | DelayedWETH | Yes | Two bespoke (one per DisputeGame) | Yes *️⃣ |
@@ -677,6 +692,7 @@ contract DeployImplementations is Script {
677692
// here we deploy:
678693
//
679694
// - DisputeGameFactory (implementation)
695+
// - AnchorStateRegistry (implementation)
680696
// - OptimismPortal2 (implementation)
681697
// - DelayedWETH (implementation)
682698
// - PreimageOracle (singleton)
@@ -685,7 +701,6 @@ contract DeployImplementations is Script {
685701
// For contracts which are not MCP ready neither the Proxy nor the implementation can be shared, therefore they
686702
// are deployed by `DeployOpChain.s.sol`.
687703
// These are:
688-
// - AnchorStateRegistry (proxy and implementation)
689704
// - FaultDisputeGame (not proxied)
690705
// - PermissionedDisputeGame (not proxied)
691706
// - DelayedWeth (proxies only)
@@ -839,6 +854,35 @@ contract DeployImplementations is Script {
839854
_dio.set(_dio.disputeGameFactoryImpl.selector, address(impl));
840855
}
841856

857+
function deployAnchorStateRegistryImpl(
858+
DeployImplementationsInput _dii,
859+
DeployImplementationsOutput _dio
860+
)
861+
public
862+
virtual
863+
{
864+
string memory release = _dii.l1ContractsRelease();
865+
string memory stdVerToml = _dii.standardVersionsToml();
866+
string memory contractName = "anchor_state_registry";
867+
IAnchorStateRegistry impl;
868+
869+
address existingImplementation = getReleaseAddress(release, contractName, stdVerToml);
870+
if (existingImplementation != address(0)) {
871+
impl = IAnchorStateRegistry(payable(existingImplementation));
872+
} else {
873+
vm.broadcast(msg.sender);
874+
impl = IAnchorStateRegistry(
875+
DeployUtils.create1({
876+
_name: "AnchorStateRegistry",
877+
_args: DeployUtils.encodeConstructor(abi.encodeCall(IAnchorStateRegistry.__constructor__, ()))
878+
})
879+
);
880+
}
881+
882+
vm.label(address(impl), "AnchorStateRegistryImpl");
883+
_dio.set(_dio.anchorStateRegistryImpl.selector, address(impl));
884+
}
885+
842886
// -------- Utilities --------
843887

844888
function etchIOContracts() public returns (DeployImplementationsInput dii_, DeployImplementationsOutput dio_) {
@@ -969,6 +1013,7 @@ contract DeployImplementationsInterop is DeployImplementations {
9691013
l1CrossDomainMessengerImpl: address(_dio.l1CrossDomainMessengerImpl()),
9701014
l1StandardBridgeImpl: address(_dio.l1StandardBridgeImpl()),
9711015
disputeGameFactoryImpl: address(_dio.disputeGameFactoryImpl()),
1016+
anchorStateRegistryImpl: address(_dio.anchorStateRegistryImpl()),
9721017
delayedWETHImpl: address(_dio.delayedWETHImpl()),
9731018
mipsImpl: address(_dio.mipsSingleton())
9741019
});

0 commit comments

Comments
 (0)