WIP -- [Conway] Property : voteDelegs range lies in VDelegs of registered DReps (#1233) - #1290
WIP -- [Conway] Property : voteDelegs range lies in VDelegs of registered DReps (#1233)#1290williamdemeo wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR upgrades the “vote delegation targets are valid” statement from an informal claim to a formally proved theorem, ensuring post-CERTS states only contain vote delegations to registered DReps (or the two special constants).
Changes:
- Replaces an informal claim with a theorem statement in the Conway spec properties overview.
- Adds a full formalization and proof that
CERTSsteps leave only “active”VDelegvalues invoteDelegs. - Introduces a supporting set-theory lemma about corestriction range containment and records the change in the changelog.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/Ledger/Conway/Specification/Properties.lagda.md | Updates the high-level spec statement from “Claim” to “Theorem” with a refined description. |
| src/Ledger/Conway/Specification/Certs/Properties/VoteDelegsVDeleg.lagda.md | Defines activeVDelegs, restates the property over CertState, and adds a proof for the CERTS step. |
| src-lib-exts/abstract-set-theory/Axiom/Set/Map/Extra.agda | Adds lemma cores-range-⊆ used by the new proof. |
| CHANGELOG.md | Documents the newly stated/proved voteDelegs property. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Let `s`{.AgdaBound}, `s'`{.AgdaBound} be `CertStates`{.AgdaRecord} and | ||
| `certs`{.AgdaBound} a list of `DCerts`{.AgdaDatatype} such that `s`{.AgdaBound} | ||
| `⇀⦇`{.AgdaDatatype} `certs`{.AgdaBound} `,CERTS⦈`{.AgdaDatatype} `s'`{.AgdaBound}. |
There was a problem hiding this comment.
Keeping these, because the pluralization is a repo-wide prose convention rather than something local to this entry. It occurs 19 times under src/, including the two neighbouring bullets in this same file and the modules this bullet links to (Certs/Properties/PoV.lagda.md L61-62, Certs/Properties/PoVLemmas.lagda.md L50 and L215-218). Aligning only this entry would make it the odd one out.
You are right on the underlying point, though: CertStates and DCerts are not identifiers, so tagging them {.AgdaRecord} / {.AgdaDatatype} applies Agda-identifier styling to non-identifiers. That is pre-existing and repo-wide, so it belongs in its own pass over the prose rather than here.
| --- | ||
|
|
||
| ## Claim: <span class="AgdaField">voteDelegs</span> field values are <span class="AgdaDatatype">VDelegs</span> constructed from their keys {#clm:VDelegsInRegDReps} | ||
| ## Theorem: <span class="AgdaField">voteDelegs</span> values point at registered <span class="AgdaFunction">DReps</span> {#clm:VDelegsInRegDReps} |
There was a problem hiding this comment.
Deliberately keeping the anchor:
- It is referenced from
build-tools/static/mkdocs/includes/links.mdand is a published-docs URL fragment, so renaming breaks existing external links to#clm:VDelegsInRegDReps. - There is already in-repo precedent for a proved theorem retaining a
clm:anchor:src/Ledger/Conway/Specification/Gov/Properties/LastVoteApplied.lagda.mdL6 is## Theorem: ... {#clm:LastVoteApplied}. - Preserving this anchor was an explicit requirement for this change.
Your point stands that the clm: / thm: prefixes are no longer a reliable classification signal. With two files now in this state, the fix is one pass over every anchor plus links.md (and ideally redirects for the published fragments), not a one-off rename here.
| cores-range-⊆ : ∀ {A B : Type} ⦃ _ : DecEq B ⦄ (m : A ⇀ B) {X : ℙ B} → range (m ∣^ X) ⊆ X | ||
| cores-range-⊆ _ b∈range with Equivalence.from ∈-map b∈range | ||
| ... | _ , refl , ab∈cores = proj₁ (Equivalence.from ∈-filter ab∈cores) |
There was a problem hiding this comment.
Two separate points here; the first rests on a misreading.
cores-range-⊆ is not inside the module _ {A B : Type} ... block that opens at L64. It starts at column 0, which closes that block, so the ∀ {A B : Type} is required rather than redundant. It also must stay: that module's parameters include ⦃ CommutativeMonoid _ _ B ⦄ and ⦃ IsCommutativeSemigroup _◇_ ⦄, and a corestriction lemma has no business carrying a commutative-monoid constraint. The form used matches the adjacent top-level lemmas (lookupᵐ?-insert, ∈-insert-≢, lookupᵐ?-insert-≢), which are all ∀ {A B : Type} ⦃ _ : DecEq _ ⦄.
The naming nit is fair, and is fixed in 9dd24fb: cores-range-⊆ _ b∈range is now cores-range-⊆ m b∈range. m stays unused in the body (the proof only inspects the membership proof), but naming it saves the reader a trip to the signature to recover the argument order.
…DReps
The property in Certs.Properties.VoteDelegsVDeleg was false as stated. It
claimed the range of voteDelegs is contained in the VDelegs built from the
*domain* of voteDelegs, but the domain holds delegator stake credentials while
the range holds VDelegs wrapping DRep credentials (or vDelegAbstain /
vDelegNoConfidence). The module anchor {#clm:VDelegsInRegDReps} records the
intended claim: vote delegations point at registered DReps.
Restate it as a property of the CERTS rule and prove it:
Gamma |- s ->(certs ,CERTS) s' -> range (VoteDelegsOf s') subset-of activeVDelegs s'
where
activeVDelegs s = mapS vDelegCredential (dom (DRepsOf s))
union fromList (vDelegNoConfidence :: vDelegAbstain :: [])
is the set POST-CERT corestricts to. Nothing is assumed about the initial
state: POST-CERT ends every CERTS batch with voteDelegs |^ activeVDelegs, so
the property holds unconditionally of the output state.
The proof inverts the CERTS step, inducts over the CERT trace, and closes the
run-[] case with a new lemma cores-range-subset (range (m |^ X) subset-of X)
added to Axiom.Set.Map.Extra; agda-sets carries nothing about _|^_ beyond
cores-subset. Its map argument is explicit because _|^_ goes through
subset-map, which mentions the map only under proj1, so it is not recoverable
by unification.
Also promote the prose entry in Conway/Specification/Properties.lagda.md from
Claim to Theorem and restate it, and add a CHANGELOG entry.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Address a review nit: the clause head now mirrors the type signature. `m` is unused in the body (the proof only inspects the membership proof), but naming it saves the reader a trip to the signature to recover the argument order. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
9dd24fb to
fcc8747
Compare
For every
CERTSstepΓ ⊢ s ⇀⦇ certs ,CERTS⦈ s', the range ofvoteDelegsins'is contained in theVDelegsof theDRepsregistered ins', together withvDelegNoConfidenceandvDelegAbstain.The original statement was corrected. It said
which is well typed (
mapˢ vDelegCredentiallifts the domain intoℙ VDeleg) but false, for two independent reasons:vDelegAbstainandvDelegNoConfidenceare legalvoteDelegsvalues.DELEG-delegateadmits them andPOST-CERTdeliberately keeps them, and neither isvDelegCredential cfor anyc.regdrepnever adds a key tovoteDelegs, so a delegatee need not lie indom voteDelegs.A single
delegatecertificate already carries⟦ ∅ , ∅ , ∅ ⟧ᵈ, which satisfies the original statement, to a state that refutes it (checked in Agda while preparing this PR).The corrected statement uses the domain of the registered
DRepsand adds the two constants, so it matches the setPOST-CERTcorestricts to and the intent recorded in the anchor{#clm:VDelegsInRegDReps}. Nothing is assumed about the initial state, sincePOST-CERTenforces the property on the output of every batch.The proof inverts the
CERTSstep, inducts over theCERTtrace, and closes therun-[]case with a new lemmacores-range-⊆ : range (m ∣^ X) ⊆ XinAxiom.Set.Map.Extra.Closes #1233