Skip to content

Commit 90bf7d3

Browse files
PelleKrabCodex
andcommitted
feat(L1): bind aggregate proofs to ProtocolVersions schedule
Co-authored-by: Codex <codex-noreply@coinbase.com>
1 parent 2e348fa commit 90bf7d3

11 files changed

Lines changed: 120 additions & 12 deletions

File tree

interfaces/L1/proofs/IAggregateVerifier.sol

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { IDisputeGame } from "./IDisputeGame.sol";
55
import { IDisputeGameFactory } from "./IDisputeGameFactory.sol";
66
import { IDelayedWETH } from "./IDelayedWETH.sol";
77
import { IVerifier } from "./IVerifier.sol";
8+
import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol";
89
import { Proposal, Hash } from "src/libraries/bridge/Types.sol";
910
import { Timestamp } from "src/libraries/bridge/LibUDT.sol";
1011

@@ -23,6 +24,7 @@ interface IAggregateVerifier is IDisputeGame {
2324
function ZK_RANGE_HASH() external view returns (bytes32);
2425
function ZK_AGGREGATE_HASH() external view returns (bytes32);
2526
function CONFIG_HASH() external view returns (bytes32);
27+
function PROTOCOL_VERSIONS() external view returns (IProtocolVersions);
2628
function L2_CHAIN_ID() external view returns (uint256);
2729
function BLOCK_INTERVAL() external view returns (uint256);
2830
function INTERMEDIATE_BLOCK_INTERVAL() external view returns (uint256);
@@ -35,6 +37,7 @@ interface IAggregateVerifier is IDisputeGame {
3537
function counteredByIntermediateRootIndexPlusOne() external view returns (uint256);
3638
function expectedResolution() external view returns (Timestamp);
3739
function proofCount() external view returns (uint8);
40+
function scheduleId() external view returns (bytes32);
3841

3942
function initializeWithInitData(bytes calldata proof) external payable;
4043
function verifyProposalProof(bytes calldata proofBytes) external;

scripts/deploy/SystemDeploy.s.sol

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ contract SystemDeploy is Script {
129129
uint256 l2ChainId;
130130
uint256 multiproofBlockInterval;
131131
uint256 multiproofIntermediateBlockInterval;
132+
IProtocolVersions protocolVersions;
132133
}
133134

134135
struct MultiproofOutput {
@@ -1048,7 +1049,8 @@ contract SystemDeploy is Script {
10481049
multiproofConfigHash: _input.multiproofConfigHash,
10491050
l2ChainId: _opChainInput.l2ChainId,
10501051
multiproofBlockInterval: _input.multiproofBlockInterval,
1051-
multiproofIntermediateBlockInterval: _input.multiproofIntermediateBlockInterval
1052+
multiproofIntermediateBlockInterval: _input.multiproofIntermediateBlockInterval,
1053+
protocolVersions: _output.protocolVersionsProxy
10521054
})
10531055
);
10541056

@@ -1077,7 +1079,8 @@ contract SystemDeploy is Script {
10771079
_input.multiproofConfigHash,
10781080
_input.l2ChainId,
10791081
_input.multiproofBlockInterval,
1080-
_input.multiproofIntermediateBlockInterval
1082+
_input.multiproofIntermediateBlockInterval,
1083+
_input.protocolVersions
10811084
)
10821085
)
10831086
);

scripts/multiproof/DeployDevBase.s.sol

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import { DeployUtils } from "scripts/libraries/DeployUtils.sol";
1616

1717
import { AggregateVerifier } from "src/L1/proofs/AggregateVerifier.sol";
1818
import { IVerifier } from "interfaces/L1/proofs/IVerifier.sol";
19+
import { ProtocolVersions } from "src/L1/ProtocolVersions.sol";
20+
import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol";
1921
import { MockVerifier } from "test/mocks/MockVerifier.sol";
2022
import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol";
2123
import { TEEVerifier } from "src/L1/proofs/tee/TEEVerifier.sol";
@@ -104,6 +106,12 @@ abstract contract DeployDevBase is Script {
104106
AggregateVerifier.ZkHashes memory zkHashes =
105107
AggregateVerifier.ZkHashes({ rangeHash: cfg.zkRangeHash(), aggregateHash: cfg.zkAggregationHash() });
106108

109+
Proxy protocolVersionsProxy = new Proxy(msg.sender);
110+
protocolVersionsProxy.upgradeToAndCall(
111+
address(new ProtocolVersions()), abi.encodeCall(IProtocolVersions.initialize, (address(0)))
112+
);
113+
protocolVersionsProxy.changeAdmin(address(0xdead));
114+
107115
aggregateVerifier = address(
108116
new AggregateVerifier(
109117
gameType,
@@ -116,7 +124,8 @@ abstract contract DeployDevBase is Script {
116124
cfg.multiproofConfigHash(),
117125
cfg.l2ChainId(),
118126
_blockInterval(),
119-
_intermediateBlockInterval()
127+
_intermediateBlockInterval(),
128+
IProtocolVersions(address(protocolVersionsProxy))
120129
)
121130
);
122131

snapshots/abi/AggregateVerifier.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@
6767
"internalType": "uint256",
6868
"name": "intermediateBlockInterval",
6969
"type": "uint256"
70+
},
71+
{
72+
"internalType": "contract IProtocolVersions",
73+
"name": "protocolVersions",
74+
"type": "address"
7075
}
7176
],
7277
"stateMutability": "nonpayable",
@@ -215,6 +220,19 @@
215220
"stateMutability": "view",
216221
"type": "function"
217222
},
223+
{
224+
"inputs": [],
225+
"name": "PROTOCOL_VERSIONS",
226+
"outputs": [
227+
{
228+
"internalType": "contract IProtocolVersions",
229+
"name": "",
230+
"type": "address"
231+
}
232+
],
233+
"stateMutability": "view",
234+
"type": "function"
235+
},
218236
{
219237
"inputs": [],
220238
"name": "SLOW_FINALIZATION_DELAY",
@@ -681,6 +699,19 @@
681699
"stateMutability": "pure",
682700
"type": "function"
683701
},
702+
{
703+
"inputs": [],
704+
"name": "scheduleId",
705+
"outputs": [
706+
{
707+
"internalType": "bytes32",
708+
"name": "",
709+
"type": "bytes32"
710+
}
711+
],
712+
"stateMutability": "view",
713+
"type": "function"
714+
},
684715
{
685716
"inputs": [],
686717
"name": "startingBlockNumber",

snapshots/semver-lock.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232
"sourceCodeHash": "0xf122a50487efe9bd5a620262ba20ef4adbca14eeec2af7fd32e6e16739001596"
3333
},
3434
"src/L1/proofs/AggregateVerifier.sol:AggregateVerifier": {
35-
"initCodeHash": "0x90629c679e821a70553d899a68e818bcddb672a004a0d2e09078d17028a2f082",
36-
"sourceCodeHash": "0x4835d3de998b2951898f0e427c61b0c330ac3feb5f7a8dc9fd0deb78912a9b7a"
35+
"initCodeHash": "0x6f3c53245830b9c72c8199695e93959d5ed3b0cb8352e5b50b3dfa28183ac787",
36+
"sourceCodeHash": "0x1f2e2b07ebc4359fa31ea7400ace9bab0e4f6099d8ff3ff7b6a2b14cccfb89ca"
3737
},
3838
"src/L1/proofs/AnchorStateRegistry.sol:AnchorStateRegistry": {
3939
"initCodeHash": "0x6f3afd2d0ef97a82ca3111976322b99343a270e54cd4a405028f2f29c75f7fb1",

snapshots/storageLayout/AggregateVerifier.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,5 +96,12 @@
9696
"offset": 8,
9797
"slot": "7",
9898
"type": "uint8"
99+
},
100+
{
101+
"bytes": "32",
102+
"label": "scheduleId",
103+
"offset": 0,
104+
"slot": "8",
105+
"type": "bytes32"
99106
}
100107
]

src/L1/proofs/AggregateVerifier.sol

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { ReentrancyGuard } from "lib/solady/src/utils/ReentrancyGuard.sol";
2626

2727
import { IVerifier } from "interfaces/L1/proofs/IVerifier.sol";
2828
import { ISemver } from "interfaces/universal/ISemver.sol";
29+
import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol";
2930

3031
contract AggregateVerifier is Clone, ReentrancyGuard, ISemver {
3132
////////////////////////////////////////////////////////////////
@@ -113,6 +114,9 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver {
113114
/// @notice The game type ID.
114115
GameType internal immutable GAME_TYPE;
115116

117+
/// @notice The ProtocolVersions upgrade schedule contract.
118+
IProtocolVersions public immutable PROTOCOL_VERSIONS;
119+
116120
////////////////////////////////////////////////////////////////
117121
// State Vars //
118122
////////////////////////////////////////////////////////////////
@@ -162,6 +166,11 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver {
162166
/// @notice The number of proofs provided.
163167
uint8 public proofCount;
164168

169+
/// @notice The ProtocolVersions scheduleId snapshotted at game initialization.
170+
/// @dev Pinned once at init; every proof in this game commits to this value, binding it to the
171+
/// upgrade schedule in effect when the game was created.
172+
bytes32 public scheduleId;
173+
165174
////////////////////////////////////////////////////////////////
166175
// Events //
167176
////////////////////////////////////////////////////////////////
@@ -259,6 +268,7 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver {
259268
/// @param l2ChainId The chain ID of the L2 network.
260269
/// @param blockInterval The block interval.
261270
/// @param intermediateBlockInterval The intermediate block interval.
271+
/// @param protocolVersions The ProtocolVersions upgrade schedule contract.
262272
constructor(
263273
GameType gameType_,
264274
IAnchorStateRegistry anchorStateRegistry_,
@@ -270,7 +280,8 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver {
270280
bytes32 configHash,
271281
uint256 l2ChainId,
272282
uint256 blockInterval,
273-
uint256 intermediateBlockInterval
283+
uint256 intermediateBlockInterval,
284+
IProtocolVersions protocolVersions
274285
) {
275286
// Block interval and intermediate block interval must be positive and divisible.
276287
if (blockInterval == 0 || intermediateBlockInterval == 0 || blockInterval % intermediateBlockInterval != 0) {
@@ -291,6 +302,7 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver {
291302
L2_CHAIN_ID = l2ChainId;
292303
BLOCK_INTERVAL = blockInterval;
293304
INTERMEDIATE_BLOCK_INTERVAL = intermediateBlockInterval;
305+
PROTOCOL_VERSIONS = protocolVersions;
294306

295307
INITIALIZE_CALLDATA_SIZE = 0x8E + 0x20 * intermediateOutputRootsCount();
296308
}
@@ -364,6 +376,10 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver {
364376
// Set the game as initialized.
365377
initialized = true;
366378

379+
// Snapshot the scheduleId from ProtocolVersions. Every proof in this game commits to this
380+
// value, pinning them to the upgrade schedule in effect at the game's creation block.
381+
scheduleId = PROTOCOL_VERSIONS.scheduleId();
382+
367383
// Set the game's starting timestamp.
368384
createdAt = Timestamp.wrap(uint64(block.timestamp));
369385

@@ -899,7 +915,8 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver {
899915
endingL2SequenceNumber,
900916
intermediateRoots,
901917
CONFIG_HASH,
902-
TEE_IMAGE_HASH
918+
TEE_IMAGE_HASH,
919+
scheduleId
903920
)
904921
);
905922

@@ -933,7 +950,8 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver {
933950
endingL2SequenceNumber,
934951
intermediateRoots,
935952
CONFIG_HASH,
936-
ZK_RANGE_HASH
953+
ZK_RANGE_HASH,
954+
scheduleId
937955
)
938956
);
939957

test/L1/OptimismPortal2.t.sol

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ abstract contract OptimismPortal2_TestInit is DisputeGameFactory_TestInit {
9090
bytes32(uint256(4)),
9191
deploy.cfg().l2ChainId(),
9292
100,
93-
10
93+
10,
94+
protocolVersions
9495
);
9596
disputeGameFactory.setImplementation(respectedGameType, IDisputeGame(address(gameImpl)));
9697
disputeGameFactory.setInitBond(respectedGameType, 0);

test/L1/proofs/AggregateVerifier.t.sol

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { Claim, Timestamp } from "src/libraries/bridge/LibUDT.sol";
1111

1212
import { AggregateVerifier } from "src/L1/proofs/AggregateVerifier.sol";
1313
import { IVerifier } from "interfaces/L1/proofs/IVerifier.sol";
14+
import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol";
1415

1516
import { LibClone } from "lib/solady/src/utils/LibClone.sol";
1617

@@ -36,6 +37,31 @@ contract AggregateVerifierTest is BaseTest {
3637
_createAndAssertInitializedGame("zk-proof", AggregateVerifier.ProofType.ZK, ZK_PROVER, address(0), ZK_PROVER);
3738
}
3839

40+
/// @notice init snapshots the live scheduleId into storage and pins it: a later schedule change
41+
/// must not alter the game's snapshot (proves it is stored once, not read live).
42+
/// @dev Only covers the stored getter. That the snapshot flows into the proof journal is not
43+
/// asserted here — MockVerifier ignores the journal, so journal binding is untestable.
44+
function test_initialize_pinsScheduleId_succeeds() public {
45+
// Register an upgrade so the schedule commitment is non-trivial (non-zero).
46+
protocolVersions.registerUpgrade(uint64(block.timestamp) + protocolVersions.MIN_NOTICE() + 100, 1);
47+
bytes32 pinned = protocolVersions.scheduleId();
48+
assertTrue(pinned != bytes32(0));
49+
50+
Claim rootClaim = _advanceL2BlockAndClaim();
51+
bytes memory proof = _generateProof("tee-proof", AggregateVerifier.ProofType.TEE);
52+
53+
AggregateVerifier game = _createAggregateVerifierGame(
54+
TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proof
55+
);
56+
57+
assertEq(game.scheduleId(), pinned);
58+
59+
// Move the live schedule after creation; the game's snapshot must stay pinned.
60+
protocolVersions.registerUpgrade(uint64(block.timestamp) + protocolVersions.MIN_NOTICE() + 200, 2);
61+
assertNotEq(protocolVersions.scheduleId(), pinned);
62+
assertEq(game.scheduleId(), pinned);
63+
}
64+
3965
function testInitializeFailsIfInvalidCallDataSize() public {
4066
Claim rootClaim = _advanceL2BlockAndClaim();
4167

@@ -417,7 +443,8 @@ contract AggregateVerifierTest is BaseTest {
417443
CONFIG_HASH,
418444
L2_CHAIN_ID,
419445
blockInterval,
420-
intermediateBlockInterval
446+
intermediateBlockInterval,
447+
IProtocolVersions(address(protocolVersions))
421448
);
422449
}
423450
}

test/L1/proofs/BaseTest.t.sol

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import { ProxyAdmin } from "src/universal/ProxyAdmin.sol";
1919

2020
import { AggregateVerifier } from "src/L1/proofs/AggregateVerifier.sol";
2121
import { IVerifier } from "interfaces/L1/proofs/IVerifier.sol";
22+
import { ProtocolVersions } from "src/L1/ProtocolVersions.sol";
23+
import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol";
2224

2325
import { MockVerifier } from "test/mocks/MockVerifier.sol";
2426

@@ -54,6 +56,7 @@ contract BaseTest is Test {
5456

5557
MockVerifier internal teeVerifier;
5658
MockVerifier internal zkVerifier;
59+
ProtocolVersions internal protocolVersions;
5760

5861
function setUp() public virtual {
5962
_deployContractsAndProxies();
@@ -78,9 +81,12 @@ contract BaseTest is Test {
7881

7982
proxyAdmin = new ProxyAdmin(address(this));
8083

84+
ProtocolVersions _protocolVersions = new ProtocolVersions();
85+
8186
anchorStateRegistry = AnchorStateRegistry(_deployProxy(address(_anchorStateRegistry)));
8287
factory = DisputeGameFactory(_deployProxy(address(_factory)));
8388
delayedWETH = DelayedWETH(payable(_deployProxy(address(_delayedWETH))));
89+
protocolVersions = ProtocolVersions(_deployProxy(address(_protocolVersions)));
8490

8591
teeVerifier = new MockVerifier(IAnchorStateRegistry(address(anchorStateRegistry)));
8692
zkVerifier = new MockVerifier(IAnchorStateRegistry(address(anchorStateRegistry)));
@@ -103,6 +109,7 @@ contract BaseTest is Test {
103109
);
104110
factory.initialize(address(this));
105111
delayedWETH.initialize(systemConfig);
112+
protocolVersions.initialize(address(0));
106113
}
107114

108115
function _deployAndSetAggregateVerifier() internal {
@@ -117,7 +124,8 @@ contract BaseTest is Test {
117124
CONFIG_HASH,
118125
L2_CHAIN_ID,
119126
BLOCK_INTERVAL,
120-
INTERMEDIATE_BLOCK_INTERVAL
127+
INTERMEDIATE_BLOCK_INTERVAL,
128+
IProtocolVersions(address(protocolVersions))
121129
);
122130

123131
factory.setImplementation(GameTypes.AGGREGATE_VERIFIER, IDisputeGame(address(aggregateVerifierImpl)));

0 commit comments

Comments
 (0)