diff --git a/interfaces/L1/proofs/IAggregateVerifier.sol b/interfaces/L1/proofs/IAggregateVerifier.sol index 1bb74be3a..1d5416e69 100644 --- a/interfaces/L1/proofs/IAggregateVerifier.sol +++ b/interfaces/L1/proofs/IAggregateVerifier.sol @@ -29,6 +29,7 @@ interface IAggregateVerifier is IDisputeGame { function L2_GENESIS_BLOCK_NUMBER() external view returns (uint256); function L2_GENESIS_TIMESTAMP() external view returns (uint64); function L2_BLOCK_TIME() external view returns (uint64); + function FAST_BLOCK_ACTIVATION_TIMESTAMP() external view returns (uint64); function SLOW_BLOCK_INTERVAL() external view returns (uint256); function SLOW_INTERMEDIATE_BLOCK_INTERVAL() external view returns (uint256); function FAST_BLOCK_INTERVAL() external view returns (uint256); diff --git a/scripts/deploy/DeployConfig.s.sol b/scripts/deploy/DeployConfig.s.sol index 42e1bbcc8..b6e743380 100644 --- a/scripts/deploy/DeployConfig.s.sol +++ b/scripts/deploy/DeployConfig.s.sol @@ -52,6 +52,7 @@ contract DeployConfig is Script { uint256 public l2OutputOracleStartingBlockNumber; uint256 public l2OutputOracleStartingTimestamp; uint256 public multiproofSlowBlockInterval; + uint256 public multiproofFastBlockActivationTimestamp; uint256 public multiproofFastBlockInterval; uint256 public multiproofFastIntermediateBlockInterval; uint256 public multiproofGameType; @@ -118,6 +119,7 @@ contract DeployConfig is Script { l2GenesisBlockNumber = _json.readUintOr("$.l2GenesisBlockNumber", 0); l2GenesisTimestamp = _json.readUintOr("$.l2GenesisTimestamp", 0); multiproofSlowBlockInterval = _json.readUintOr("$.multiproofSlowBlockInterval", 100); + multiproofFastBlockActivationTimestamp = _json.readUintOr("$.multiproofFastBlockActivationTimestamp", 0); multiproofFastBlockInterval = _json.readUintOr("$.multiproofFastBlockInterval", 1000); multiproofFastIntermediateBlockInterval = _json.readUintOr("$.multiproofFastIntermediateBlockInterval", 100); multiproofGameType = _json.readUintOr("$.multiproofGameType", 621); diff --git a/scripts/deploy/SystemDeploy.s.sol b/scripts/deploy/SystemDeploy.s.sol index 0923c411c..760510474 100644 --- a/scripts/deploy/SystemDeploy.s.sol +++ b/scripts/deploy/SystemDeploy.s.sol @@ -286,14 +286,19 @@ contract SystemDeploy is Script { function _configuredScheduleConfig() internal view returns (AggregateVerifier.ScheduleConfig memory config_) { uint256 genesisTimestamp = cfg.l2GenesisTimestamp(); uint256 blockTime = cfg.l2BlockTime(); + uint256 fastBlockActivationTimestamp = cfg.multiproofFastBlockActivationTimestamp(); require(genesisTimestamp <= type(uint64).max, "SystemDeploy: L2 genesis timestamp overflow"); require(blockTime <= type(uint64).max, "SystemDeploy: L2 block time overflow"); + require( + fastBlockActivationTimestamp <= type(uint64).max, "SystemDeploy: fast block activation timestamp overflow" + ); config_ = AggregateVerifier.ScheduleConfig({ protocolVersions: IProtocolVersions(address(0)), genesisBlockNumber: cfg.l2GenesisBlockNumber(), genesisTimestamp: uint64(genesisTimestamp), - blockTime: uint64(blockTime) + blockTime: uint64(blockTime), + fastBlockActivationTimestamp: uint64(fastBlockActivationTimestamp) }); } diff --git a/scripts/multiproof/DeployDevBase.s.sol b/scripts/multiproof/DeployDevBase.s.sol index 7b097f3c3..5028dc087 100644 --- a/scripts/multiproof/DeployDevBase.s.sol +++ b/scripts/multiproof/DeployDevBase.s.sol @@ -133,7 +133,8 @@ abstract contract DeployDevBase is Script { protocolVersions: IProtocolVersions(address(protocolVersionsProxy)), genesisBlockNumber: cfg.l2GenesisBlockNumber(), genesisTimestamp: uint64(cfg.l2GenesisTimestamp()), - blockTime: uint64(cfg.l2BlockTime()) + blockTime: uint64(cfg.l2BlockTime()), + fastBlockActivationTimestamp: 0 }) ) ); diff --git a/snapshots/semver-lock.json b/snapshots/semver-lock.json index bbb801acd..c707bc666 100644 --- a/snapshots/semver-lock.json +++ b/snapshots/semver-lock.json @@ -28,8 +28,8 @@ "sourceCodeHash": "0x780ff372493ba9010bc0d13100ac896f2bf75730a9b17d4bb63aaf694dc3c634" }, "src/L1/proofs/AggregateVerifier.sol:AggregateVerifier": { - "initCodeHash": "0x0642919b493a739e0280ded0dab2123c0c0d569fb613203ad71f79eca25af8e4", - "sourceCodeHash": "0xa03d89648901890b4e2ab06a72578045f75d6dfa649dbac0ae9364fd20f0657d" + "initCodeHash": "0x7b12663e3d6891ad906be1ec99d5e962873344931faae6b79b2f8cc1e85b56fa", + "sourceCodeHash": "0x48a54be27a13e1037310c927fbc869cadf714ee22131bd004b1542fd6279b2d6" }, "src/L1/proofs/AnchorStateRegistry.sol:AnchorStateRegistry": { "initCodeHash": "0x6f3afd2d0ef97a82ca3111976322b99343a270e54cd4a405028f2f29c75f7fb1", diff --git a/src/L1/proofs/AggregateVerifier.sol b/src/L1/proofs/AggregateVerifier.sol index 44b9ca218..815f59a2c 100644 --- a/src/L1/proofs/AggregateVerifier.sol +++ b/src/L1/proofs/AggregateVerifier.sol @@ -51,6 +51,7 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { uint256 genesisBlockNumber; uint64 genesisTimestamp; uint64 blockTime; + uint64 fastBlockActivationTimestamp; } /// @notice Proposal block intervals for each side of the block-speedup activation. @@ -86,13 +87,6 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { /// @notice The minimum number of proofs required to resolve the game. uint256 public constant PROOF_THRESHOLD = 1; - /// @notice The ProtocolVersions upgrade index at which L2 blocks switch to the fast cadence. - /// @dev This is the one place the contract is tied to a specific hardfork: index 12 is Cobalt, - /// which drops the L2 block time from 2s to 200ms. Everything downstream is expressed as - /// slow-vs-fast blocks, so a later cadence change is a new index and new interval pair - /// rather than new machinery. - uint256 private constant FAST_BLOCK_UPGRADE_INDEX = 12; - /// @notice The number of whole fast-cadence L2 blocks produced per second. uint256 private constant FAST_BLOCKS_PER_SECOND = 5; //////////////////////////////////////////////////////////////// @@ -137,6 +131,16 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { /// @notice The legacy number of seconds between consecutive L2 blocks. uint64 public immutable L2_BLOCK_TIME; + /// @notice The L2 timestamp at which blocks switch to the fast cadence, or 0 when unscheduled. + /// @dev This is the one place the contract is tied to a specific hardfork: it is Cobalt's + /// activation, which drops the L2 block time from 2s to 200ms. Everything downstream is + /// expressed as slow-vs-fast blocks, so a later cadence change is a new deployment with a + /// new timestamp and interval pair rather than new machinery. + /// @dev 0 means the speedup is not scheduled, so every game uses the slow-block intervals. + /// This lets the implementation be deployed before the activation time is agreed, at the + /// cost of a redeploy once it is. + uint64 public immutable FAST_BLOCK_ACTIVATION_TIMESTAMP; + /// @notice The block interval between each proposal, for games starting on slow blocks. /// @dev The parent's block number + SLOW_BLOCK_INTERVAL = this proposal's block number. uint256 public immutable SLOW_BLOCK_INTERVAL; @@ -360,6 +364,7 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { L2_GENESIS_BLOCK_NUMBER = scheduleConfig.genesisBlockNumber; L2_GENESIS_TIMESTAMP = scheduleConfig.genesisTimestamp; L2_BLOCK_TIME = scheduleConfig.blockTime; + FAST_BLOCK_ACTIVATION_TIMESTAMP = scheduleConfig.fastBlockActivationTimestamp; SLOW_BLOCK_INTERVAL = intervalConfig.slowBlockInterval; SLOW_INTERMEDIATE_BLOCK_INTERVAL = intervalConfig.slowIntermediateBlockInterval; FAST_BLOCK_INTERVAL = intervalConfig.fastBlockInterval; @@ -431,8 +436,7 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { startingOutputRoot = ANCHOR_STATE_REGISTRY.getStartingAnchorRoot(); } - // Resolved once and threaded through both fork-sensitive decisions below, which would - // otherwise re-read the schedule from `PROTOCOL_VERSIONS` a second time. + // Resolved once and threaded through both fork-sensitive decisions below. uint256 firstFastBlock = _firstFastBlock(); // The block number must be one block interval after the starting block number. The interval @@ -1159,9 +1163,9 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { } /// @notice Semantic version. - /// @custom:semver 0.2.0 + /// @custom:semver 0.3.0 function version() public pure virtual returns (string memory) { - return "0.2.0"; + return "0.3.0"; } /// @notice Derives an L2 block timestamp: the slow cadence before the speedup, whole-second groups after it. @@ -1206,37 +1210,26 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { /// @notice Returns the first L2 block number produced at the fast cadence, or `type(uint256).max` /// when the speedup is not scheduled. - /// @dev Reading the live schedule is safe despite it being mutable, in both directions: - /// - /// - A game that selected the fast-block intervals has a starting block at or past the - /// activation, so the activation is in the past. `ProtocolVersions._assertNotFrozen` - /// rejects every mutation of a passed activation, from `setTimestamp` and - /// `delayTimestamp` alike, so that game's selection can never be revoked. - /// - A game that selected the slow-block intervals cannot be pulled across the boundary - /// either, including by the owner moving the activation *earlier* rather than later. - /// `initializeWithInitData` rejects a game whose ending L2 timestamp L1 has not yet - /// reached, so every initialized game satisfies `startingTimestamp < endingTimestamp <= - /// block.timestamp`, while any new activation must clear `block.timestamp + MIN_NOTICE`. - /// The activation therefore always lands after the game's starting block. + /// @dev The activation is an immutable, so every game this implementation ever backs selects + /// its intervals against the same boundary. There is no window in which one game reads a + /// different activation than another, and no mutation that can move a game across the + /// boundary after the fact. Changing the activation means a new implementation, which is + /// a new game type in the factory and therefore never retroactive. function _firstFastBlock() private view returns (uint256) { - uint64[] memory schedule = PROTOCOL_VERSIONS.getSchedule(); - if (schedule.length <= FAST_BLOCK_UPGRADE_INDEX) return type(uint256).max; - - uint64 fastActivationTimestamp = schedule[FAST_BLOCK_UPGRADE_INDEX]; - if (fastActivationTimestamp == 0) return type(uint256).max; + if (FAST_BLOCK_ACTIVATION_TIMESTAMP == 0) return type(uint256).max; - if (fastActivationTimestamp <= L2_GENESIS_TIMESTAMP) { + if (FAST_BLOCK_ACTIVATION_TIMESTAMP <= L2_GENESIS_TIMESTAMP) { return L2_GENESIS_BLOCK_NUMBER; } return L2_GENESIS_BLOCK_NUMBER - + FixedPointMathLib.divUp(fastActivationTimestamp - L2_GENESIS_TIMESTAMP, L2_BLOCK_TIME); + + FixedPointMathLib.divUp(FAST_BLOCK_ACTIVATION_TIMESTAMP - L2_GENESIS_TIMESTAMP, L2_BLOCK_TIME); } /// @notice Selects the proposal intervals governing a game, from its starting block number and an /// already-resolved speedup activation block. /// @dev Takes `firstFastBlock` rather than resolving it so a caller making more than one - /// fork-sensitive decision pays for `PROTOCOL_VERSIONS.getSchedule()` once. + /// fork-sensitive decision derives the activation block once. function _intervalsAt(uint256 startingBlock, uint256 firstFastBlock) private view returns (uint256, uint256) { if (startingBlock < firstFastBlock) return (SLOW_BLOCK_INTERVAL, SLOW_INTERMEDIATE_BLOCK_INTERVAL); return (FAST_BLOCK_INTERVAL, FAST_INTERMEDIATE_BLOCK_INTERVAL); diff --git a/test/L1/OptimismPortal2.t.sol b/test/L1/OptimismPortal2.t.sol index ff44eb887..1fccd2fdc 100644 --- a/test/L1/OptimismPortal2.t.sol +++ b/test/L1/OptimismPortal2.t.sol @@ -96,7 +96,11 @@ abstract contract OptimismPortal2_TestInit is DisputeGameFactory_TestInit { fastIntermediateBlockInterval: 100 }), AggregateVerifier.ScheduleConfig({ - protocolVersions: protocolVersions, genesisBlockNumber: 0, genesisTimestamp: 1, blockTime: 2 + protocolVersions: protocolVersions, + genesisBlockNumber: 0, + genesisTimestamp: 1, + blockTime: 2, + fastBlockActivationTimestamp: 0 }) ); disputeGameFactory.setImplementation(respectedGameType, IDisputeGame(address(gameImpl))); diff --git a/test/L1/proofs/AggregateVerifier.t.sol b/test/L1/proofs/AggregateVerifier.t.sol index 2a7f76c76..9e8b996f6 100644 --- a/test/L1/proofs/AggregateVerifier.t.sol +++ b/test/L1/proofs/AggregateVerifier.t.sol @@ -117,6 +117,7 @@ contract AggregateVerifierTest is BaseTest { schedule[FAST_BLOCK_UPGRADE_INDEX + 1] = firstFastBlockTimestamp + 1; _importProtocolVersionsSchedule(schedule); + fastBlockActivationTimestamp = fastActivationTimestamp; _setSingleBlockAggregateVerifier(L2_GENESIS_TIMESTAMP); bytes32 speedupScheduleId = protocolVersions.scheduleId(FAST_BLOCK_UPGRADE_INDEX); @@ -167,6 +168,28 @@ contract AggregateVerifierTest is BaseTest { _assertIntervals(firstFastBlock + 1, FAST_BLOCK_INTERVAL, FAST_INTERMEDIATE_BLOCK_INTERVAL); } + /// @notice The activation is an implementation immutable, so the speedup takes effect with the + /// upgrade registry left empty at the speedup index. This is what lets the cadence switch + /// land on a chain whose `ProtocolVersions` schedule is never written for it. + function test_intervalsForStartingBlock_activationIndependentOfRegistry_succeeds() public { + // A registry that stops short of the speedup index: nothing here can select fast intervals. + uint64[] memory schedule = new uint64[](FAST_BLOCK_UPGRADE_INDEX); + for (uint256 i; i < FAST_BLOCK_UPGRADE_INDEX; i++) { + schedule[i] = L2_GENESIS_TIMESTAMP; + } + _importProtocolVersionsSchedule(schedule); + assertEq(protocolVersions.getSchedule().length, FAST_BLOCK_UPGRADE_INDEX); + + // divUp(86500 - L2_GENESIS_TIMESTAMP, L2_BLOCK_TIME) == 50. + _setFastBlockActivation(L2_GENESIS_TIMESTAMP + 100); + aggregateVerifierImpl = AggregateVerifier(address(factory.gameImpls(GameTypes.AGGREGATE_VERIFIER))); + uint256 firstFastBlock = 50; + + assertEq(aggregateVerifierImpl.FAST_BLOCK_ACTIVATION_TIMESTAMP(), L2_GENESIS_TIMESTAMP + 100); + _assertIntervals(firstFastBlock - 1, SLOW_BLOCK_INTERVAL, SLOW_INTERMEDIATE_BLOCK_INTERVAL); + _assertIntervals(firstFastBlock, FAST_BLOCK_INTERVAL, FAST_INTERMEDIATE_BLOCK_INTERVAL); + } + function test_intervalsForStartingBlock_speedupUnscheduled_succeeds() public view { _assertIntervals(0, SLOW_BLOCK_INTERVAL, SLOW_INTERMEDIATE_BLOCK_INTERVAL); _assertIntervals(type(uint64).max, SLOW_BLOCK_INTERVAL, SLOW_INTERMEDIATE_BLOCK_INTERVAL); @@ -295,7 +318,8 @@ contract AggregateVerifierTest is BaseTest { protocolVersions: IProtocolVersions(address(protocolVersions)), genesisBlockNumber: 0, genesisTimestamp: 0, - blockTime: 0 + blockTime: 0, + fastBlockActivationTimestamp: 0 }); vm.expectRevert(AggregateVerifier.InvalidL2BlockTime.selector); @@ -307,7 +331,8 @@ contract AggregateVerifierTest is BaseTest { protocolVersions: IProtocolVersions(address(protocolVersions)), genesisBlockNumber: SLOW_BLOCK_INTERVAL + 1, genesisTimestamp: 0, - blockTime: L2_BLOCK_TIME + blockTime: L2_BLOCK_TIME, + fastBlockActivationTimestamp: 0 }); AggregateVerifier implementation = _deployAggregateVerifierWithScheduleConfig(scheduleConfig); factory.setImplementation(GameTypes.AGGREGATE_VERIFIER, IDisputeGame(address(implementation))); @@ -332,7 +357,8 @@ contract AggregateVerifierTest is BaseTest { protocolVersions: IProtocolVersions(address(protocolVersions)), genesisBlockNumber: 0, genesisTimestamp: type(uint64).max, - blockTime: L2_BLOCK_TIME + blockTime: L2_BLOCK_TIME, + fastBlockActivationTimestamp: 0 }); AggregateVerifier implementation = _deployAggregateVerifierWithScheduleConfig(scheduleConfig); factory.setImplementation(GameTypes.AGGREGATE_VERIFIER, IDisputeGame(address(implementation))); @@ -356,6 +382,7 @@ contract AggregateVerifierTest is BaseTest { } schedule[FAST_BLOCK_UPGRADE_INDEX] = genesisTimestamp + 1; _importProtocolVersionsSchedule(schedule); + fastBlockActivationTimestamp = genesisTimestamp + 1; _setSingleBlockAggregateVerifier(genesisTimestamp); vm.warp(uint256(type(uint64).max) + 3); @@ -630,7 +657,8 @@ contract AggregateVerifierTest is BaseTest { protocolVersions: IProtocolVersions(address(protocolVersions)), genesisBlockNumber: L2_GENESIS_BLOCK_NUMBER, genesisTimestamp: genesisTimestamp, - blockTime: L2_BLOCK_TIME + blockTime: L2_BLOCK_TIME, + fastBlockActivationTimestamp: fastBlockActivationTimestamp }) ); factory.setImplementation(GameTypes.AGGREGATE_VERIFIER, IDisputeGame(address(implementation))); @@ -786,7 +814,8 @@ contract AggregateVerifierTest is BaseTest { protocolVersions: IProtocolVersions(address(protocolVersions)), genesisBlockNumber: L2_GENESIS_BLOCK_NUMBER, genesisTimestamp: L2_GENESIS_TIMESTAMP, - blockTime: L2_BLOCK_TIME + blockTime: L2_BLOCK_TIME, + fastBlockActivationTimestamp: fastBlockActivationTimestamp }) ); } @@ -800,12 +829,16 @@ contract AggregateVerifierTest is BaseTest { /// @dev Registers a schedule whose only meaningful entry is the speedup activation, and rebinds /// the implementation to it. + /// @dev Bakes the speedup activation into a fresh implementation. The registry entry is written + /// alongside it only so `scheduleId` pinning still reflects a chain that reached the fork; + /// interval and timestamp selection read the immutable, not the registry. function _importSpeedupSchedule(uint64 fastActivationTimestamp) private { uint64[] memory schedule = new uint64[](FAST_BLOCK_UPGRADE_INDEX + 1); for (uint256 i; i < FAST_BLOCK_UPGRADE_INDEX; i++) { schedule[i] = L2_GENESIS_TIMESTAMP; } schedule[FAST_BLOCK_UPGRADE_INDEX] = fastActivationTimestamp; + fastBlockActivationTimestamp = fastActivationTimestamp; _importProtocolVersionsSchedule(schedule); aggregateVerifierImpl = AggregateVerifier(address(factory.gameImpls(GameTypes.AGGREGATE_VERIFIER))); } @@ -892,7 +925,8 @@ contract AggregateVerifierTest is BaseTest { protocolVersions: IProtocolVersions(address(protocolVersions)), genesisBlockNumber: L2_GENESIS_BLOCK_NUMBER, genesisTimestamp: L2_GENESIS_TIMESTAMP, - blockTime: L2_BLOCK_TIME + blockTime: L2_BLOCK_TIME, + fastBlockActivationTimestamp: fastBlockActivationTimestamp }); } diff --git a/test/L1/proofs/BaseTest.t.sol b/test/L1/proofs/BaseTest.t.sol index 1f75627da..44ff0df6c 100644 --- a/test/L1/proofs/BaseTest.t.sol +++ b/test/L1/proofs/BaseTest.t.sol @@ -44,6 +44,10 @@ contract BaseTest is Test { uint256 internal currentL2BlockNumber; + /// @dev The fast-cadence activation baked into the verifier implementation under test. 0 leaves + /// the speedup unscheduled, which is the default every test starts from. + uint64 internal fastBlockActivationTimestamp; + address internal immutable TEE_PROVER = makeAddr("tee-prover"); address internal immutable ZK_PROVER = makeAddr("zk-prover"); @@ -127,6 +131,14 @@ contract BaseTest is Test { _deployAndSetAggregateVerifier(); } + /// @dev Rebinds the verifier to an implementation carrying `activationTimestamp` as its + /// immutable speedup boundary. Unlike the schedule registry, this is fixed at construction, + /// so it can only be changed by deploying again. + function _setFastBlockActivation(uint64 activationTimestamp) internal { + fastBlockActivationTimestamp = activationTimestamp; + _deployAndSetAggregateVerifier(); + } + function _deployAndSetAggregateVerifier() internal { AggregateVerifier aggregateVerifierImpl = new AggregateVerifier( GameTypes.AGGREGATE_VERIFIER, @@ -148,7 +160,8 @@ contract BaseTest is Test { protocolVersions: IProtocolVersions(address(protocolVersions)), genesisBlockNumber: L2_GENESIS_BLOCK_NUMBER, genesisTimestamp: L2_GENESIS_TIMESTAMP, - blockTime: L2_BLOCK_TIME + blockTime: L2_BLOCK_TIME, + fastBlockActivationTimestamp: fastBlockActivationTimestamp }) ); diff --git a/test/L1/proofs/DisputeGameFactory.t.sol b/test/L1/proofs/DisputeGameFactory.t.sol index a09931e65..853fff95f 100644 --- a/test/L1/proofs/DisputeGameFactory.t.sol +++ b/test/L1/proofs/DisputeGameFactory.t.sol @@ -238,7 +238,11 @@ contract DisputeGameFactory_Create_Test is DisputeGameFactory_TestInit { fastIntermediateBlockInterval: AGGREGATE_FAST_INTERMEDIATE_BLOCK_INTERVAL }), AggregateVerifier.ScheduleConfig({ - protocolVersions: protocolVersions, genesisBlockNumber: 0, genesisTimestamp: 0, blockTime: 2 + protocolVersions: protocolVersions, + genesisBlockNumber: 0, + genesisTimestamp: 0, + blockTime: 2, + fastBlockActivationTimestamp: 0 }) ); _setGame(address(gameImpl), GameTypes.AGGREGATE_VERIFIER); diff --git a/test/deploy/SystemDeploy.t.sol b/test/deploy/SystemDeploy.t.sol index 7f39e14b0..eafbb1880 100644 --- a/test/deploy/SystemDeploy.t.sol +++ b/test/deploy/SystemDeploy.t.sol @@ -269,7 +269,11 @@ contract SystemDeploy_Test is Test, SystemDeployAssertions { SystemDeploy.DeployInput memory input = _defaultDeployInput(); input.implementationsInput.multiproofConfigHash = bytes32(0); input.implementationsInput.scheduleConfig = AggregateVerifier.ScheduleConfig({ - protocolVersions: IProtocolVersions(address(0)), genesisBlockNumber: 0, genesisTimestamp: 0, blockTime: 0 + protocolVersions: IProtocolVersions(address(0)), + genesisBlockNumber: 0, + genesisTimestamp: 0, + blockTime: 0, + fastBlockActivationTimestamp: 0 }); SystemDeploy.DeployOutput memory output = systemDeploy.deploy(input); @@ -458,7 +462,8 @@ contract SystemDeploy_Test is Test, SystemDeployAssertions { protocolVersions: IProtocolVersions(address(0)), genesisBlockNumber: 0, genesisTimestamp: 1, - blockTime: 2 + blockTime: 2, + fastBlockActivationTimestamp: 0 }), multiproofSlowBlockInterval: 100, multiproofSlowIntermediateBlockInterval: 10,