feat: authority durable store — roles{} snapshot and read-only reconcile#9
Open
aelmanaa wants to merge 1 commit into
Open
feat: authority durable store — roles{} snapshot and read-only reconcile#9aelmanaa wants to merge 1 commit into
aelmanaa wants to merge 1 commit into
Conversation
Add the roles{} authority subtree and its read-only reconcile engine: probe
the live privileged-role surface (token/pool/TokenAdminRegistry/lockbox/hooks),
declare it in config, and reconcile the declaration against the chain.
- src/roles: RolesProbes (tolerant capability probes + 4-template dispatch:
crosschain/burnmint/factory/byo, incl. BURN_MINT_ADMIN_ROLE), RolesSnapshot
(backfill declared state from chain, honest complete markers + optional
RoleGranted/RoleRevoked event scan), RolesAuditor (declared-vs-live reconcile,
opt-in SCAN_FROM_BLOCK additive detection).
- make snapshot-chain (sole writer), make roles-check (0 clean / 1 drift /
2 rpc-down), a roles rung on make doctor, and a non-blocking CI roles-check.
- script/governance/VerifyRoles.s.sol: read-only holder dump for audits.
- docs/roles.md + config-schema.md roles{} subtree: the reconcile model,
drift-response runbook, and honest-coverage boundary.
|
👋 aelmanaa, thanks for creating this pull request! To help reviewers, please consider creating future PRs as drafts first. This allows you to self-review and make any final changes before notifying the team. Once you're ready, you can mark it as "Ready for review" to request feedback. Thanks! |
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.
What improves for the user
Before: after wiring a token, pool, TokenAdminRegistry, lockbox, and hooks, there is no single place that records who holds every privileged role, and no way to check that the intended authority still matches the live chain. Authority drift — a rotated owner, an unrevoked deployer key, an in-flight admin transfer, a rogue mint grant — is invisible until it bites.
After: a
roles{}subtree inconfig/chains/<name>.jsondeclares the full privileged-authority surface, and a read-only engine reconciles that declaration against the chain.make snapshot-chainbackfills the declaration from the chain (the only writer);make roles-checkreconciles it (never writes); a rung onmake doctorand a non-blocking scheduled CI job keep drift visible. The declaration is the durable, git-versioned record of who controls the deployment.What's in this PR
src/roles/— the engine, all reads via tolerantstaticcallso it dispatches on what a contract actually exposes:RolesProbes— capability probes + a four-template dispatch (crosschain=AccessControlDefaultAdminRules,burnmint= plainAccessControl,factory=Ownable,byo= unknown), including the separateBURN_MINT_ADMIN_ROLEthat admins mint/burn onCrossChainToken(the slot a naive sweep forgets).RolesSnapshot— backfills the declared state from chain with honestcompletemarkers; an optionalSCAN_FROM_BLOCKenables aRoleGranted/RoleRevokedevent scan to prove non-enumerable minter/burner lists, recordingscannedFromBlockas provenance.RolesAuditor— reconciles declared vs live: point-checks for single-holder slots (reliable on any RPC, noeth_getLogs), two-sided set compares for enumerable sets, and an opt-inSCAN_FROM_BLOCKadditive check that turns an undeclared (rogue) holder into a hard FAIL. Non-enumerable lists always WARN that additive grants are unverified without a scan — never a silent CLEAN.make snapshot-chain(sole writer, subtree-isolated),make roles-check(exit0clean /1drift, naming the field /2RPC-unavailable, contract owned byscript/config/roles-check.sh), a roles rung onmake doctor(wrapped so a malformed declaration degrades to a WARN, never aborts the doctor), and a non-blocking scheduledroles-checkCI job.script/governance/VerifyRoles.s.sol— a read-only holder dump for at-a-glance audits.docs/roles.md(the reconcile model, the drift-response runbook with a containment-first triage branch, and the honest-coverage boundary) and theroles{}subtree indocs/config-schema.md.Honest-coverage boundary
A clean
roles-checkproves what the engine can read. CCIP-side authority (TAR administrator, pool owner/rateLimitAdmin/feeAdmin, lockbox/hooks owner, the token admin-registration point) is fully covered for any token. A declared holder being revoked is always detected; an undeclared holder being added is detected only for enumerable tokens, or forcrosschain/burnmintwhen run withSCAN_FROM_BLOCK. Abyotoken's mint/burn internals are declaration-backed only — a clean check does not prove them safe. The docs state this in full.Testing
Unit + fork tests (
test/roles/RolesAuthority.t.sol, 14 cases: four-template detection, snapshot fidelity, clean reconcile, induced drift naming the field, type-mismatch FAIL, the honesty WARNs, a burnmint snapshot+reconcile, absent-governance SKIP) plus a live Sepolia run of the full flow: snapshot fidelity vs direct reads, clean reconcile (exit 0), an induced additive-grant FAIL underSCAN_FROM_BLOCK(exit 1) that a no-scan run correctly misses, and doctor-rung resilience against a malformed declaration.