fix(L1): retarget the AggregateVerifier cadence switch to Denim - #434
Merged
Conversation
Collaborator
✅ Heimdall Review Status
|
0xth4nh
added a commit
to base/base
that referenced
this pull request
Sep 9, 2026
The 200ms block-time hardfork moved back to Denim, so the prose and the one remaining fixture name follow base/contracts#434. No behaviour change: the proposer reads the interval pair from the verifier per game and never names a fork itself. Co-Authored-By: Claude <noreply@anthropic.com>
0xth4nh
added a commit
to base/base
that referenced
this pull request
Sep 9, 2026
The 200ms block-time hardfork moved back to Denim, so the prose and the anchor test fixture names follow base/contracts#434. No behaviour change: the anchor updater and scanner resolve the interval pair from the verifier per game and never name a fork themselves. Co-Authored-By: Claude <noreply@anthropic.com>
This was referenced Sep 9, 2026
0x00101010
reviewed
Sep 9, 2026
The 200ms block-time hardfork moved back to Denim, so `FAST_BLOCK_UPGRADE_INDEX` goes from 12 (Cobalt) to 13 (Denim). Index 13 is one past the end of the current Base mainnet schedule, so `getSchedule()` returns a shorter array until Denim is registered. `_firstFastBlock()` already treats a short array as unscheduled, which is the same state the zero timestamp at index 12 produced, so behaviour is unchanged until the entry is written. Semver stays at 0.2.0 — no deployment has consumed it yet. Co-Authored-By: Claude <noreply@anthropic.com>
0xth4nh
force-pushed
the
feat/verifier-retarget-denim
branch
from
September 9, 2026 17:27
26bb55f to
3d3e683
Compare
jackchuma
approved these changes
Sep 9, 2026
0xth4nh
added a commit
to base/base
that referenced
this pull request
Sep 10, 2026
The 200ms block-time hardfork moved back to Denim, so the prose and the one remaining fixture name follow base/contracts#434. No behaviour change: the proposer reads the interval pair from the verifier per game and never names a fork itself. Co-Authored-By: Claude <noreply@anthropic.com>
0xth4nh
added a commit
to base/base
that referenced
this pull request
Sep 10, 2026
The 200ms block-time hardfork moved back to Denim, so the prose and the anchor test fixture names follow base/contracts#434. No behaviour change: the anchor updater and scanner resolve the interval pair from the verifier per game and never name a fork themselves. Co-Authored-By: Claude <noreply@anthropic.com>
mablr
pushed a commit
to foundry-rs/base
that referenced
this pull request
Sep 10, 2026
) * feat(proposer): resolve proposal intervals per game for Denim The proposer read BLOCK_INTERVAL and INTERMEDIATE_BLOCK_INTERVAL from the AggregateVerifier implementation once at startup and copied them into four config structs that were never refreshed. Denim switches the verifier to a shorter cadence at a fixed L2 block, so those startup values are wrong for every game on the far side of the boundary: the recovery forward walk builds the next game's UUID with the stale interval, gets Address::ZERO back, and re-proposes over games that already exist. Add intervalsForStartingBlock(uint256) to the AggregateVerifier bindings and an IntervalResolver that reads it — along with the factory's current gameImpls entry — per lookup, keyed on the starting block of the game being created, reconstructed, or looked up. The dispatcher, collector, submitter and recovery walk all resolve through it instead of carrying cached copies. ProofSubmitter::submit now takes the starting block from the caller rather than deriving it as target_block - block_interval, and rejects a target that contradicts the interval resolved for that starting block. Co-Authored-By: Claude <noreply@anthropic.com> * fix(proposer): target Cobalt for the cadence switch Retargets the switchover from Denim (`ProtocolVersions` index 13, which the Base mainnet schedule does not reach) to Cobalt (index 12), matching base/contracts#431. The mock verifier's fields follow the contract's slow/fast vocabulary so a later cadence change needs no rename. Also drops the stray whitespace `cargo fmt` left in the `INTERMEDIATE_BLOCK_INTERVAL` divisibility error, flagged in review. Co-Authored-By: Claude <noreply@anthropic.com> * fix(proposer): fail loudly on target block overflow `saturating_add` clamps to `u64::MAX`, which would let a saturated sum match a `target_block` of `u64::MAX` and pass the guard. `checked_add` cannot. Co-Authored-By: Claude <noreply@anthropic.com> * refactor(proposer): trim interval plumbing Co-authored-by: Codex <codex-noreply@coinbase.com> * fix(proposer): validate interval resolver at startup Co-authored-by: Codex <codex-noreply@coinbase.com> * fix(basectl): resolve game intervals by starting block Co-authored-by: Codex <codex-noreply@coinbase.com> * docs(proposer): retarget the cadence switch back to Denim The 200ms block-time hardfork moved back to Denim, so the prose and the one remaining fixture name follow base/contracts#434. No behaviour change: the proposer reads the interval pair from the verifier per game and never names a fork itself. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Codex <codex-noreply@coinbase.com>
huklaa
pushed a commit
to huklaa/base
that referenced
this pull request
Sep 11, 2026
…#4931) * feat(challenger): resolve proposal intervals per game for Denim `AnchorUpdater` built the next game's UUID from the `BLOCK_INTERVAL` / `INTERMEDIATE_BLOCK_INTERVAL` pair read once at startup, and `GameScanner` cached the intermediate interval per implementation address. Denim switches the `AggregateVerifier` to a shorter cadence at a fixed L2 block, so a single implementation serves both sides of the boundary and neither cache can be right for every game. Both now resolve the pair from the starting block of the game they are handling, via the shared `resolve_intervals` helper in `base-proof-contracts`. The proposer's `IntervalResolver` delegates to the same helper instead of duplicating the `gameImpls` lookup. Co-Authored-By: Claude <noreply@anthropic.com> * fix(challenger): target Cobalt for the cadence switch Follows base/contracts#431 and the proposer branch: the switchover is Cobalt (`ProtocolVersions` index 12), not Denim (index 13). Test fixtures and mock builders use the contract's slow/fast vocabulary. Co-Authored-By: Claude <noreply@anthropic.com> * fix(challenger): read existing game intervals from proxy Co-authored-by: Codex <codex-noreply@coinbase.com> * docs(challenger): retarget the cadence switch back to Denim The 200ms block-time hardfork moved back to Denim, so the prose and the anchor test fixture names follow base/contracts#434. No behaviour change: the anchor updater and scanner resolve the interval pair from the verifier per game and never name a fork themselves. Co-Authored-By: Claude <noreply@anthropic.com> * docs(challenger): address review on per-game interval resolution - resolve_intervals: drop the stale "legacy games have no intervalsForStartingBlock" justification; existing games are read through their own proxy and fall back to the fixed getters there. - read_intervals_for_starting_block: rename the parameter to verifier_address (it takes an implementation or a game proxy) and spell out that a game proxy is a CWIA clone, not an upgradeable proxy. - scanner: note the dependency on the missing-method fallback for pre-upgrade games. - anchor: record the setImplementation limitation and the factory-scan fix. - service: log what happened, not the intent. - scanner test: sort the recorded reads; scan runs evaluate_game through buffer_unordered, so the order is incidental. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Codex <codex-noreply@coinbase.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reverts the fork target introduced in #431 (merged). The 200ms block-time hardfork moved back to Denim.
What changed?
FAST_BLOCK_UPGRADE_INDEXinAggregateVerifier:12(Cobalt) →13(Denim).FAST_BLOCK_UPGRADE_INDEXinAggregateVerifier.t.solfollows it. Every test references the constant symbolically, so no fixture arithmetic changed.snapshots/semver-lock.jsonregenerated — only theAggregateVerifierentry moves.0.2.0. Nothing has consumed 0.2.0 yet — it was introduced by feat(L1): fork-gate AggregateVerifier proposal intervals for Cobalt #431 and no deployment reads it — so there is no version anyone could be pinned to and nothing to bump away from.No machinery changed.
_firstFastBlock(),_l2Timestamp(), and_intervalsAt()are unchanged; only the index they read is different.Why?
The switch to Cobalt in #431 was made on the reasoning that the Base mainnet schedule is 13 entries long (indices 0–12) and could therefore never reach index 13. That is true today but it isn't a constraint —
_timestampsis a dynamic array andregisterUpgradeextends it, which is exactly how Denim gets its entry.Both indices fail open in the same way and for the same duration:
_firstFastBlock()hitsif (fastActivationTimestamp == 0) return type(uint256).max._firstFastBlock()hitsif (schedule.length <= FAST_BLOCK_UPGRADE_INDEX) return type(uint256).max.Either way the verifier reports "unscheduled" and every game gets the slow-block intervals until the entry is written. So this change is behaviour-neutral on every chain as of today, and the operational requirement is identical to what it was before: the speedup activation has to be registered on
ProtocolVersionsbefore the fork, or the verifier stays on the slow cadence silently.How to test?
32 passed, 0 failed on this branch.
The two cases that pin the index behaviour directly:
_importSpeedupSchedulebuilds aFAST_BLOCK_UPGRADE_INDEX + 2-long schedule and writes the activation at the constant, so the scheduled-speedup cases (test_intervalsForStartingBlock_selectsOnFirstFastBlock_succeeds,test_initialize_scheduleChangesAtL2ActivationBoundary_succeeds,test_initialize_straddlingGame_usesSlowInterval_succeeds) now exercise a 15-entry registry with Denim at 13 rather than a 14-entry one with Cobalt at 12.Lock check — should be a no-op after the commit:
Note
test/L1/ProtocolVersions.t.solstill names Cobalt at index 12 (schedule[12] = 0; // Cobalt is unscheduled on Base mainnet.). That is a factual statement about contract registration order, not a targeting choice, and is unrelated to this change — left alone.