feat(healthcheck): whitelist invariant reads the timelock queue — queued syncs report as expected-pending (EXSC-918) - #2309
Conversation
…ued syncs report as expected-pending (EXSC-918) `config/whitelist.json` is merged before the Safe proposal syncing it executes, so between those events the config-vs-chain comparison reported a hard error on every pair the operation was about to add. The registration invariants resolved that window in EXSC-847; the whitelist check was the one left reporting a rollout in flight as drift. Both whitelist steps now grade missing pairs against the timelock execution queue and downgrade a pair to expected-pending only when a queued operation whitelists exactly that contract and selector on that network's own diamond. Decoded records carry an explicit `kind`, so each consumer matches its own shape rather than the absence of a field.
…d whitelist shapes (EXSC-918)
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (5)
Included review availability: Your plan provides up to 2 included reviews per hour; 0 remain after this review. WalkthroughThe change adds typed pending whitelist registrations, decodes single and batch whitelist operations, and updates whitelist health checks to classify queued additions separately from uncovered pairs. Tests cover selector matching, queue failures, and registration-kind filtering. ChangesWhitelist health-check coverage
Estimated code review effort: 4 (Complex) | ~45 minutes Merge Risk: 🔵 Low · up to Missing whitelist pairs can now be reported as expected-pending when an exact queued sync exists, reducing false alerts during rollouts. The change is mergeable with owner awareness because stale, cancelled, or incorrectly maintained queue records could temporarily delay detection of real whitelist drift. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 4 files. (1 skipped: 1 unsupported.) Full details: Title checkExplanation The title clearly identifies the main change: the healthcheck whitelist invariant now reads the timelock queue and reports queued whitelist syncs as expected-pending. It is specific and concise enough for repository history. Full details: Description checkExplanation The description follows the required template. It identifies EXSC-918, explains the implementation and design, records testing and validation, and completes the contributor checklist. The reviewer checklist remains appropriately unchecked for reviewer action. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
… read path (EXSC-918) Both `kind` guards were inert: the fixtures standing in for "a record of another kind" never carried the field the surviving predicate compares, so the comparison did the rejecting and dropping the guard kept every test green. Each fixture now carries that field, so only the discriminator separates them. Adds the multicall path — the one production networks take — the repeated-contract batch shape the live queue actually carries, and the two preconditions `splitByPendingWhitelist` does not check itself.
|
Review-gate pass (local trial) — nothing escalated, no critical findings. Recording what was not fixed, so a reviewer does not have to re-derive it:
Also worth knowing, since it changes when this code touches MongoDB: the queue read now fires on any missing whitelist pair, which is more common fleet-wide than a missing facet. During a Mongo outage the "queue unreachable" warning will therefore appear on materially more networks than #2270's did. Errors are preserved in that state. |
|
@coderabbitai review |
|
|
@coderabbitai full review |
|
Which Linear task belongs to this PR?
Fixes EXSC-918
Follow-up to EXSC-847 (#2270), which made
facets-registeredandperiphery-registeredintent-aware. The whitelist check was the one invariant of that family still reporting a rollout in flight as drift.Why did I implement it this way?
config/whitelist.jsonis merged before the Safe proposal syncing it executes, so in between,checkWhitelistIntegrityerrored on every pair the queued operation was about to add — on both of its steps (Source of Truth FAILEDfromisContractSelectorWhitelisted, andPair Array is missing N pairs from configfromgetAllContractSelectorPairs). Live today:robinhoodred on0xCC89feed…8599 / 0xf8989325while its sync operation satqueued; same shape onarc(09-01) andinjective(08-27).Two independent reasons #2270's machinery didn't already cover it, both addressed here:
extractRegistrationshandled onlydiamondCutandregisterPeripheryContract— a whitelist batch was explicitly documented as contributing nothing. It now also decodesbatchSetContractSelectorWhitelist— the calldiamondSyncWhitelist.shandproposePeripheryWithWhitelist.tsactually emit — plus the singularsetContractSelectorWhitelistdefensively, both only with_whitelistedtrue;falseun-whitelists and is ignored, the counterpart of aRemovecut. A length-mismatched batch reverts on-chain (InvalidConfig), so it decodes to nothing rather than to the pairs it could have paired up.splitByPendingWhitelistsplits missing pairs by whether a queued operation whitelists exactly that contract and selector on that network's diamond. Matching the address alone would report a sync that is never coming — the same reasoning that makes the periphery caller match the registry name.Design points worth a reviewer's attention:
kind(facet-cut/periphery/whitelist).facets-registeredpreviously inferred "this is a cut" fromperipheryName === undefined, which a whitelist record would have satisfied — a latent false-green on an error gate. Each consumer now matches its own shape, and there's a regression test that a queued whitelist entry does not downgrade a missing facet.facets-registered: the pair comparison stands on an independent on-chain signal, and a Mongo blip turning a genuinely unsynced whitelist green is far worse than a false alert during a rollout.[CONV:HEALTHCHECK-INTENT].Verified against the live queue (904 rows), not just fixtures. The single
queuedrow is robinhood's sync — carrying exactly the red pair and targeting robinhood's own diamond (0xB477751B…14Af3) — so this turns today's alert into expected-pending. Every inner-call selector present across all 904 rows was resolved:batchSetContractSelectorWhitelistappears 266 times (408 whitelist records, of which 91 are_whitelisted=falseand correctly decode to nothing), and none of the undecoded selectors writes whitelist storage. The legacy writers that would have been a genuine coverage gap (batchAddDex,batchSetFunctionApprovalBySignature,setContractWhitelist) appear zero times and have no emitter left in the repo. 74 distinct networks have carried a whitelist row historically, so the behaviour change is fleet-wide, not robinhood-specific.Mutation-tested each new guard rather than trusting the suite: dropping
kind === 'whitelist', droppingkind === 'periphery', widening the selector comparison, or removing the "skip the queue when nothing is missing" short-circuit each fail exactly one dedicated test. The first two initially passed — the fixtures standing in for "a record of another kind" did not carry the field the surviving predicate compares, so the comparison did the rejecting and the discriminator was never exercised. Fixed in the second commit.Checklist before requesting a review
Checklist for reviewer (DO NOT DEPLOY and contracts BEFORE CHECKING THIS!!!)