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
1 change: 1 addition & 0 deletions deploy-config/local.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"operatorFeeVaultWithdrawalNetwork": 0,
"p2pSequencerAddress": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc",
"proofMaturityDelaySeconds": 604800,
"protocolVersionsInitialMinimumVersion": "0x1000000000000000000000000",
"proxyAdminOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc",
"respectedGameType": 621,
"sequencerFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000",
Expand Down
1 change: 1 addition & 0 deletions scripts/deploy/DeployConfig.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ contract DeployConfig is Script {

function _readProtocolVersionsInitialMinimumVersion(string memory _json) internal {
uint256 minimumVersion = _json.readUintOr("$.protocolVersionsInitialMinimumVersion", 0);
require(minimumVersion != 0, "DeployConfig: initial minimum protocol version must be non-zero");
require(minimumVersion <= type(uint128).max, "DeployConfig: initial minimum protocol version exceeds uint128");
protocolVersionsInitialMinimumVersion = minimumVersion;
}
Expand Down
5 changes: 1 addition & 4 deletions scripts/libraries/DeployUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ library DeployUtils {
internal
view
{
if (_minimumProtocolVersion > type(uint128).max) {
if (_minimumProtocolVersion == 0 || _minimumProtocolVersion > type(uint128).max) {
revert IProtocolVersions.ProtocolVersions_InvalidProtocolVersion();
}

Expand All @@ -117,9 +117,6 @@ library DeployUtils {
uint64 previousTimestamp;
for (uint256 id = 0; id < _schedule.length; id++) {
uint64 timestamp = _schedule[id];
if (timestamp != 0 && _minimumProtocolVersion == 0) {
revert IProtocolVersions.ProtocolVersions_InvalidProtocolVersion();
}
if (timestamp > currentTimestamp && timestamp < minimumFutureTimestamp) {
revert IProtocolVersions.ProtocolVersions_InsufficientNotice(timestamp);
}
Expand Down
3 changes: 1 addition & 2 deletions scripts/libraries/Types.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ library Types {
/// upgrade in the node's fork order, zero for unscheduled ones. Seeds
/// `ProtocolVersions` at initialization, which is the only way to enter
/// activations that are already in the past.
/// @custom:field initialMinimumProtocolVersion Packed semver required by non-zero timestamps in the initial
/// schedule.
/// @custom:field initialMinimumProtocolVersion Non-zero packed semver required by nodes.
struct DeployInput {
Roles roles;
uint32 basefeeScalar;
Expand Down
2 changes: 1 addition & 1 deletion scripts/multiproof/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Other relevant fields:
| `multiproofGenesisOutputRoot` | Initial anchor output root |
| `multiproofGenesisBlockNumber` | Initial anchor L2 block number |
| `protocolVersionsInitialSchedule` | Hardfork activation timestamps in the node's fork order, `0` for unscheduled forks. Omit for a chain with no history; past activations cannot be added after deployment |
| `protocolVersionsInitialMinimumVersion` | Packed semver required by every non-zero initial schedule timestamp. Must be non-zero when the initial schedule contains an activation |
| `protocolVersionsInitialMinimumVersion` | Non-zero packed semver required by nodes |

### Step 2: Deploy contracts

Expand Down
4 changes: 2 additions & 2 deletions snapshots/semver-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
"sourceCodeHash": "0x811596e7486cab9ceeeb61405b9ae510d93fcbbdfa11949201a367506c53194a"
},
"src/L1/ProtocolVersions.sol:ProtocolVersions": {
"initCodeHash": "0x54c2a142728d60511fd15a7d6bb962b0f33d833ee727de16189fd18b6246b492",
"sourceCodeHash": "0x53c8ad6dc42e61b43b3ef2a91e154a02e719c595c8d1481b4b738faa7ebdf13d"
"initCodeHash": "0xbca4c0d69dcb7a196a9ebfcc8cb77162fa090377c5dda99831d61c6c68ba4352",
"sourceCodeHash": "0x411f2590b43bf6fe9d70f393c8e0349192a1aa895ec0fbe0d7695831d3b6cb94"
},
"src/L1/SuperchainConfig.sol:SuperchainConfig": {
"initCodeHash": "0x9b1f3555b499709485d51d5d9665002c0eb1e5eb893be1fb978a30749e894858",
Expand Down
21 changes: 8 additions & 13 deletions src/L1/ProtocolVersions.sol
Original file line number Diff line number Diff line change
Expand Up @@ -128,16 +128,15 @@ contract ProtocolVersions is ProxyAdminOwnedBase, Initializable, Reinitializable
/// already has a hardfork history can be represented faithfully at deployment, while it is
/// still impossible for any proof game to have pinned a commitment from this registry.
/// Future activations must provide MIN_NOTICE, matching every post-initialization write path.
/// @dev Any non-zero imported timestamp requires a non-zero packed protocol version so nodes can
/// validate the schedule immediately after deployment.
/// @dev A non-zero packed protocol version is required so nodes can validate the registry
/// immediately after deployment.
///
/// @param _incidentResponder Initial incidentResponder allowed to delay activations, or address(0) to leave unset.
/// @param _initialSchedule Activation timestamps for already-known upgrades, ordered by ascending
/// upgrade id, using 0 for an upgrade that is registered but unscheduled.
/// Future timestamps must be at least MIN_NOTICE from block.timestamp.
/// Pass an empty array for a chain with no upgrade history.
/// @param _initialMinimumProtocolVersion Packed semver required by an imported activation, or 0 when
/// the initial schedule has no non-zero timestamps.
/// @param _initialMinimumProtocolVersion Packed semver required by nodes. Must be non-zero and fit in 128 bits.
function initialize(
address _incidentResponder,
uint64[] calldata _initialSchedule,
Expand All @@ -148,7 +147,7 @@ contract ProtocolVersions is ProxyAdminOwnedBase, Initializable, Reinitializable
{
// Initialization transactions must come from the ProxyAdmin or its owner.
_assertOnlyProxyAdminOrProxyAdminOwner();
if (_initialMinimumProtocolVersion > type(uint128).max) {
if (_initialMinimumProtocolVersion == 0 || _initialMinimumProtocolVersion > type(uint128).max) {
revert ProtocolVersions_InvalidProtocolVersion();
}

Expand All @@ -159,9 +158,6 @@ contract ProtocolVersions is ProxyAdminOwnedBase, Initializable, Reinitializable

for (uint256 id = 0; id < _initialSchedule.length; id++) {
uint64 timestamp = _initialSchedule[id];
if (timestamp != 0 && _initialMinimumProtocolVersion == 0) {
revert ProtocolVersions_InvalidProtocolVersion();
}
if (timestamp > uint64(block.timestamp) && timestamp < uint64(block.timestamp) + MIN_NOTICE) {
revert ProtocolVersions_InsufficientNotice(timestamp);
}
Expand All @@ -177,9 +173,7 @@ contract ProtocolVersions is ProxyAdminOwnedBase, Initializable, Reinitializable
// pass. With an empty import this just re-emits the seed as the current commitment.
_refreshScheduleId(0);

if (_initialMinimumProtocolVersion != 0) {
_writeMinimumProtocolVersion(_initialMinimumProtocolVersion);
}
_writeMinimumProtocolVersion(_initialMinimumProtocolVersion);

incidentResponder = _incidentResponder;
emit IncidentResponderUpdated(address(0), _incidentResponder);
Expand Down Expand Up @@ -232,8 +226,9 @@ contract ProtocolVersions is ProxyAdminOwnedBase, Initializable, Reinitializable
/// @param protocolVersion Packed semver uint256 (must be non-zero and fit in 128 bits).
function setMinimumProtocolVersion(uint256 protocolVersion) external {
_assertOnlyProxyAdminOwner();
if (protocolVersion == 0) revert ProtocolVersions_InvalidProtocolVersion();
if (protocolVersion > type(uint128).max) revert ProtocolVersions_InvalidProtocolVersion();
if (protocolVersion == 0 || protocolVersion > type(uint128).max) {
revert ProtocolVersions_InvalidProtocolVersion();
}
_writeMinimumProtocolVersion(protocolVersion);
}

Expand Down
29 changes: 7 additions & 22 deletions test/L1/ProtocolVersions.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ contract ProtocolVersions_Initialize_Test is ProtocolVersions_TestInit {
vm.expectEmit(true, true, false, false, address(uninitialized));
emit IncidentResponderUpdated(address(0), _incidentResponder);
vm.prank(EIP1967Helper.getAdmin(address(uninitialized)));
uninitialized.initialize(_incidentResponder, new uint64[](0), 0);
uninitialized.initialize(_incidentResponder, new uint64[](0), 1);
assertEq(uninitialized.incidentResponder(), _incidentResponder);
}

Expand All @@ -92,7 +92,7 @@ contract ProtocolVersions_Initialize_Test is ProtocolVersions_TestInit {
IProtocolVersions uninitialized = _deployUninitializedProxy();
vm.expectRevert(IProxyAdminOwnedBase.ProxyAdminOwnedBase_NotProxyAdminOrProxyAdminOwner.selector);
vm.prank(_nonOwner);
uninitialized.initialize(_incidentResponder, new uint64[](0), 0);
uninitialized.initialize(_incidentResponder, new uint64[](0), 1);
}

/// @notice Tests that the initializer imports a preexisting schedule, building the same hash
Expand Down Expand Up @@ -170,27 +170,12 @@ contract ProtocolVersions_Initialize_Test is ProtocolVersions_TestInit {
imported.initialize(address(0), schedule, 42);
}

/// @notice Tests that an imported activation cannot omit the minimum protocol version required by nodes.
function test_initialize_importWithoutMinimumProtocolVersion_reverts() external {
uint64[] memory schedule = new uint64[](1);
schedule[0] = 1;

/// @notice Tests that the initial minimum protocol version is required even with an empty schedule.
function test_initialize_zeroMinimumProtocolVersion_reverts() external {
IProtocolVersions imported = _deployUninitializedProxy();
vm.expectRevert(IProtocolVersions.ProtocolVersions_InvalidProtocolVersion.selector);
vm.prank(EIP1967Helper.getAdmin(address(imported)));
imported.initialize(address(0), schedule, 0);
}

/// @notice Tests that zero-only imports may leave the minimum protocol version unset.
function test_initialize_zeroOnlyScheduleWithoutMinimumProtocolVersion_succeeds() external {
uint64[] memory schedule = new uint64[](1);

IProtocolVersions imported = _deployUninitializedProxy();
vm.prank(EIP1967Helper.getAdmin(address(imported)));
imported.initialize(address(0), schedule, 0);

assertEq(imported.getSchedule().length, 1);
assertEq(imported.minimumProtocolVersion(), 0);
imported.initialize(address(0), new uint64[](0), 0);
}

/// @notice Tests that the initial minimum protocol version must fit in the node's 128-bit packed semver layout.
Expand All @@ -205,14 +190,14 @@ contract ProtocolVersions_Initialize_Test is ProtocolVersions_TestInit {
function test_initialize_alreadyInitialized_reverts() external {
vm.expectRevert("Initializable: contract is already initialized");
vm.prank(EIP1967Helper.getAdmin(address(protocolVersions)));
protocolVersions.initialize(address(0), new uint64[](0), 0);
protocolVersions.initialize(address(0), new uint64[](0), 1);
}

/// @notice Tests that the implementation itself cannot be initialized (initializers disabled).
function test_initialize_implementationDisabled_reverts() external {
IProtocolVersions impl = IProtocolVersions(EIP1967Helper.getImplementation(address(protocolVersions)));
vm.expectRevert("Initializable: contract is already initialized");
impl.initialize(address(0), new uint64[](0), 0);
impl.initialize(address(0), new uint64[](0), 1);
}
}

Expand Down
2 changes: 1 addition & 1 deletion test/L1/proofs/BaseTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ contract BaseTest is Test {
);
factory.initialize(address(this));
delayedWETH.initialize(systemConfig);
protocolVersions.initialize(address(0), new uint64[](0), 0);
protocolVersions.initialize(address(0), new uint64[](0), 1);
}

/// @dev Rebuilds the schedule registry around a preset schedule and rebinds the verifier to it.
Expand Down
14 changes: 7 additions & 7 deletions test/deploy/DeployConfig.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,23 +63,23 @@ contract DeployConfig_Test is Test {
assertEq(cfg.protocolVersionsInitialMinimumVersion(), 42);
}

function test_readMinimumVersion_omitted_defaultsToZero_succeeds() public {
function test_readMinimumVersion_omitted_reverts() public {
vm.expectRevert("DeployConfig: initial minimum protocol version must be non-zero");
cfg.readMinimumVersion('{"l1ChainId":1}');

assertEq(cfg.protocolVersionsInitialMinimumVersion(), 0);
}

function test_readMinimumVersion_aboveUint128_reverts() public {
vm.expectRevert("DeployConfig: initial minimum protocol version exceeds uint128");
cfg.readMinimumVersion('{"protocolVersionsInitialMinimumVersion":340282366920938463463374607431768211456}');
}

/// @notice The shipped configs describe chains without a recorded history, so they must keep the initial registry
/// state empty.
function test_read_localConfig_leavesProtocolVersionsStateEmpty_succeeds() public {
/// @notice The shipped configs describe chains without a recorded history, so the imported schedule stays empty.
/// The minimum protocol version is required even without recorded upgrade history.
function test_read_localConfig_leavesScheduleEmptyWithMinimumVersionSet_succeeds() public {
cfg.read("deploy-config/local.json");

assertEq(cfg.protocolVersionsInitialSchedule().length, 0);
assertEq(cfg.protocolVersionsInitialMinimumVersion(), 0);
assertGt(cfg.protocolVersionsInitialMinimumVersion(), 0);
assertLe(cfg.protocolVersionsInitialMinimumVersion(), type(uint128).max);
}
}
9 changes: 3 additions & 6 deletions test/deploy/SystemDeploy.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -187,12 +187,9 @@ contract SystemDeploy_Test is Test, SystemDeployAssertions {
}

/// @notice Pins ProtocolVersions input validation before superchain deployment can broadcast.
function test_deploy_initialScheduleWithoutMinimumProtocolVersion_reverts() public {
uint64[] memory schedule = new uint64[](1);
schedule[0] = 1;

function test_deploy_zeroInitialMinimumProtocolVersion_reverts() public {
SystemDeploy.DeployInput memory input = _defaultDeployInput();
input.opChainInput.initialUpgradeSchedule = schedule;
input.opChainInput.initialMinimumProtocolVersion = 0;
input.superchainInput.superchainProxyAdminOwner = address(0);

vm.expectRevert(IProtocolVersions.ProtocolVersions_InvalidProtocolVersion.selector);
Expand Down Expand Up @@ -485,7 +482,7 @@ contract SystemDeploy_Test is Test, SystemDeployAssertions {
saltMixer: "system-deploy-test",
gasLimit: 60_000_000,
initialUpgradeSchedule: new uint64[](0),
initialMinimumProtocolVersion: 0
initialMinimumProtocolVersion: 1
});
}

Expand Down
Loading