diff --git a/snapshots/semver-lock.json b/snapshots/semver-lock.json index 0391005f..cb531e17 100644 --- a/snapshots/semver-lock.json +++ b/snapshots/semver-lock.json @@ -16,8 +16,8 @@ "sourceCodeHash": "0x811596e7486cab9ceeeb61405b9ae510d93fcbbdfa11949201a367506c53194a" }, "src/L1/ProtocolVersions.sol:ProtocolVersions": { - "initCodeHash": "0x5ef30dbac345fadcde0bafdc22aa2d67d5d8d4c8ec9909b070f3d4d6ca765ac4", - "sourceCodeHash": "0xbfa24612855d4bb061c03d83a0380f15b936b50555908a6f52ef143c9f5ef9ba" + "initCodeHash": "0x909a923012cab53fdfc4ea8f9f54173d64d2e3c250e8d69cb10ea226e21a4adf", + "sourceCodeHash": "0x2b338e2c3cb163445841a1bfca2370e731b4cba4be35018cb07b2d22a3465f67" }, "src/L1/SuperchainConfig.sol:SuperchainConfig": { "initCodeHash": "0x9b1f3555b499709485d51d5d9665002c0eb1e5eb893be1fb978a30749e894858", diff --git a/src/L1/ProtocolVersions.sol b/src/L1/ProtocolVersions.sol index 02ab8aae..93988f28 100644 --- a/src/L1/ProtocolVersions.sol +++ b/src/L1/ProtocolVersions.sol @@ -110,7 +110,7 @@ contract ProtocolVersions is ProxyAdminOwnedBase, Initializable, Reinitializable error ProtocolVersions_TimestampNotAfterPrevious( uint256 id, uint256 previousId, uint64 previousTimestamp, uint64 timestamp ); - /// @notice Thrown when a non-zero timestamp is not less than the next scheduled upgrade. + /// @notice Thrown when a non-zero timestamp is greater than the next scheduled upgrade. error ProtocolVersions_TimestampNotBeforeNext(uint256 id, uint256 nextId, uint64 timestamp, uint64 nextTimestamp); /// @notice Thrown when scheduleId is read before initialize has been called. error ProtocolVersions_NotInitialized(); @@ -394,14 +394,14 @@ contract ProtocolVersions is ProxyAdminOwnedBase, Initializable, Reinitializable } } - /// @dev Requires `timestamp` to be less than the closest higher-id scheduled upgrade. + /// @dev Requires `timestamp` to be less than or equal to the closest higher-id scheduled upgrade. function _assertTimestampBeforeNext(uint256 id, uint64 timestamp) private view { if (timestamp == 0) return; for (uint256 i = id + 1; i < _timestamps.length; i++) { uint64 next = _timestamps[i]; if (next != 0) { - if (timestamp >= next) revert ProtocolVersions_TimestampNotBeforeNext(id, i, timestamp, next); + if (timestamp > next) revert ProtocolVersions_TimestampNotBeforeNext(id, i, timestamp, next); return; } } diff --git a/test/L1/ProtocolVersions.t.sol b/test/L1/ProtocolVersions.t.sol index 22e10ce0..290f19b6 100644 --- a/test/L1/ProtocolVersions.t.sol +++ b/test/L1/ProtocolVersions.t.sol @@ -610,11 +610,28 @@ contract ProtocolVersions_SetTimestamp_Test is ProtocolVersions_TestInit { protocolVersions.setTimestamp(ECOTONE, previous - 1); } - /// @notice Tests that `setTimestamp` reverts when the timestamp is not before the next one. - function test_setTimestamp_timestampNotBeforeNext_reverts() external { + /// @notice Tests that `setTimestamp` may share the next scheduled upgrade's timestamp. + function test_setTimestamp_timestampEqualToNext_succeeds() external { uint64 current = uint64(block.timestamp) + protocolVersions.MIN_NOTICE() + 100; uint64 next = current + 100; + vm.startPrank(_owner); + protocolVersions.registerUpgrade(current, 0); + protocolVersions.registerUpgrade(next, 0); + protocolVersions.setTimestamp(CANYON, next); + vm.stopPrank(); + + uint64[] memory schedule = protocolVersions.getSchedule(); + assertEq(schedule[CANYON], next); + assertEq(schedule[ECOTONE], next); + } + + /// @notice Tests that `setTimestamp` reverts when the timestamp is after the next one. + function test_setTimestamp_timestampAfterNext_reverts() external { + uint64 current = uint64(block.timestamp) + protocolVersions.MIN_NOTICE() + 100; + uint64 next = current + 100; + uint64 afterNext = next + 1; + vm.startPrank(_owner); protocolVersions.registerUpgrade(current, 0); protocolVersions.registerUpgrade(next, 0); @@ -622,11 +639,11 @@ contract ProtocolVersions_SetTimestamp_Test is ProtocolVersions_TestInit { vm.expectRevert( abi.encodeWithSelector( - IProtocolVersions.ProtocolVersions_TimestampNotBeforeNext.selector, CANYON, ECOTONE, next, next + IProtocolVersions.ProtocolVersions_TimestampNotBeforeNext.selector, CANYON, ECOTONE, afterNext, next ) ); vm.prank(_owner); - protocolVersions.setTimestamp(CANYON, next); + protocolVersions.setTimestamp(CANYON, afterNext); } /// @notice Tests that `setTimestamp` reverts when the upgrade has already activated. @@ -766,8 +783,8 @@ contract ProtocolVersions_DelayTimestamp_Test is ProtocolVersions_TestInit { protocolVersions.delayTimestamp(CANYON, ts); } - /// @notice Tests that `delayTimestamp` cannot move an upgrade to or beyond its next scheduled successor. - function test_delayTimestamp_timestampNotBeforeNext_reverts() external { + /// @notice Tests that `delayTimestamp` may share the next scheduled upgrade's timestamp. + function test_delayTimestamp_timestampEqualToNext_succeeds() external { uint64 current = uint64(block.timestamp) + protocolVersions.MIN_NOTICE() + 100; uint64 next = current + 100; @@ -777,13 +794,33 @@ contract ProtocolVersions_DelayTimestamp_Test is ProtocolVersions_TestInit { protocolVersions.setIncidentResponder(_incidentResponder); vm.stopPrank(); + vm.prank(_incidentResponder); + protocolVersions.delayTimestamp(CANYON, next); + + uint64[] memory schedule = protocolVersions.getSchedule(); + assertEq(schedule[CANYON], next); + assertEq(schedule[ECOTONE], next); + } + + /// @notice Tests that `delayTimestamp` cannot move an upgrade after its next scheduled successor. + function test_delayTimestamp_timestampAfterNext_reverts() external { + uint64 current = uint64(block.timestamp) + protocolVersions.MIN_NOTICE() + 100; + uint64 next = current + 100; + uint64 afterNext = next + 1; + + vm.startPrank(_owner); + protocolVersions.registerUpgrade(current, 0); + protocolVersions.registerUpgrade(next, 0); + protocolVersions.setIncidentResponder(_incidentResponder); + vm.stopPrank(); + vm.expectRevert( abi.encodeWithSelector( - IProtocolVersions.ProtocolVersions_TimestampNotBeforeNext.selector, CANYON, ECOTONE, next, next + IProtocolVersions.ProtocolVersions_TimestampNotBeforeNext.selector, CANYON, ECOTONE, afterNext, next ) ); vm.prank(_incidentResponder); - protocolVersions.delayTimestamp(CANYON, next); + protocolVersions.delayTimestamp(CANYON, afterNext); } /// @notice Tests that `delayTimestamp` reverts when the upgrade has no scheduled timestamp.