Gov property #417: state that a voter's last vote is applied to the GA#1218
Gov property #417: state that a voter's last vote is applied to the GA#1218williamdemeo wants to merge 11 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new governance-spec property statement (no proof yet) capturing that when a voter casts multiple votes on the same governance action within a block, the last such vote is the one recorded in the resulting GovState.
Changes:
- Introduces
Ledger.Conway.Specification.Gov.Properties.LastVoteApplied, including helper definitions to read recorded votes and compute the last vote over aGOVSsignal list, plus statement stubs for the single-step and block-lifted claims. - Wires the new module into the
Gov.Propertiesaggregator and lists the claim in the global properties index. - Documents the addition in
CHANGELOG.md.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/Ledger/Conway/Specification/Properties.lagda.md | Adds the new governance claim to the properties index. |
| src/Ledger/Conway/Specification/Gov/Properties/LastVoteApplied.lagda.md | New module stating (and scaffolding) the “last vote is applied” governance property. |
| src/Ledger/Conway/Specification/Gov/Properties.lagda.md | Exports the new property module via the Gov properties aggregator. |
| CHANGELOG.md | Notes the newly stated (unproven) governance claim. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Machine-checked this branch with Agda 2.8.0 (via the project's Nix flake): Small follow-up to the Statement only; proof to follow. Part of #417. Generated by Claude Code |
d641c55 to
7c6304b
Compare
96ac313 to
0f70d5c
Compare
| recordedVote : GovState → GovActionID → GovVoter → Maybe Vote | ||
| recordedVote s aid voter = case lookupGAState s aid of λ where | ||
| nothing → nothing | ||
| (just gaSt) → votedOn gaSt voter |
There was a problem hiding this comment.
| recordedVote : GovState → GovActionID → GovVoter → Maybe Vote | |
| recordedVote s aid voter = case lookupGAState s aid of λ where | |
| nothing → nothing | |
| (just gaSt) → votedOn gaSt voter | |
| recordedVote : GovState → GovActionID → GovVoter → Maybe Vote | |
| recordedVote s aid voter = lookupGAState s aid >>= \gaSt -> votedOn gaSt voter |
There was a problem hiding this comment.
I (and Claude) tried this... resulting in some unresolved metas (specifically, the Monad instance for Maybe blocks reduction during rewrite) I think it's fine the way it is.
Add the statement stub for issue #417 in a new module Ledger.Conway.Specification.Gov.Properties.LastVoteApplied: - votedOn / lookupGAState / recordedVote: read back the vote a voter has recorded on a governance action in a GovState - lastVoteOn: the last vote a voter casts on an action in a block - vote-applied-to-GA: a single GOV vote step records the cast vote (base case) - last-vote-applied-to-GA: over a block (GOVS), the last vote a voter casts on an action is the one recorded in the resulting state The proof is left as future work ("coming soon"). Wire the module into the Gov.Properties aggregator, list the claim in the properties index, and note it in the changelog.
Follow-up to "update link": the index entry rendered as malformed markdown (`[Gov-LastVoteApplied]][]`` `) and the link target pointed at the module page rather than the claim. Make it a clean reference (`[Gov-LastVoteApplied][]`) and point the link at the `#clm:LastVoteApplied` anchor, matching the sibling `[Gov-ChangePPHasGroup]`. Also add the `[Gov.Properties.LastVoteApplied]` module link for completeness. Part of #417 (statement only; proof to follow). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RP1jARAhD3898yZhVnPHZ4
Add `applyVP` and `runGOVS` (the GovState update that a block of GOV signals performs) together with `GOVS→run≡`, which shows that a GOVS derivation computes exactly `runGOVS`. Mirrors `STS→updateGovSt≡` in Ledger.Conway.Specification.Ledger.Properties.Base. Stepping stone toward proving vote-applied-to-GA and last-vote-applied-to-GA.
…tion
`GovEnv.txid Γ ≢ proj₁ aid` (the voted action was not created by the current
transaction). Without it the claim is false: a same-block GOVPropose can mint a
fresh action id that collides with a pre-existing `aid` and, being inserted by
priority, shadows the voted entry. The condition holds for every reachable
ledger state, so it only excludes voting on an action proposed in the same tx.
What's proved:
- GOVS→run≡ a GOVS derivation computes the pure fold runGOVS
- vote-applied-to-GA base case (single GOV-Vote step); unconditional
- recordedVote-addVote voting on a present action records exactly that vote
- lookupᵐ?-insert lookup of a freshly inserted key
- ∈-insert-≢ / ∈-insert-≢⁻ insert at a different key preserves membership
- lookupGAState-addVote-≢ / recordedVote-addVote-≢gid
a vote on a different action leaves the lookup unchanged
- foldl-stepVote-nothing the stepVote fold is independent of its start accumulator
Remaining for the lifted proof:
- lookupᵐ?-insert-≢ (different-voter case)
- the proposal per-step fact (lookup invariance through insertGovAction),
- the derivation induction
- the final assembly.
Gov/Properties/LastVoteApplied:
- last-vote-applied-to-GA: across a block (the GOVS closure of GOV), the last
vote a voter casts on an action is the one recorded in the resulting
GovState, provided the action was not created by the current transaction
(Γ.txid ≢ proj₁ aid).
- vote-applied-to-GA: the single-GOV-step companion.
The proof is an induction on the GOVS derivation, chaining the per-step facts
(recordedVote-addVote / -≢gid / -≢voter / -addAction-≢) into the foldl that
lastVoteOn computes. Inducting on the derivation rather than a pure fold over
the signals is essential: the GOV-Vote rule supplies the proof that the voted
action is present. Without the freshness precondition the statement is false
(a same-block re-proposal can shadow the voted entry); discharging it for all
ledger-reachable states is left as a TODO noted in the source.
Axiom.Set.Map.Extras: adds four general, non-Gov-specific finite-map lemmas the
proof relies on — lookupᵐ?-insert, ∈-insert-≢, ∈-insert-≢⁻, and lookupᵐ?-insert-≢
(lookup commutes with insert at a distinct key). Candidates for upstreaming to
agda-sets.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rz41G9j463RqCCpUpFTGc4
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
- Fix title: "A voter's vote" → "A voter's last vote" (Carlos's suggestion) - Properties index: "Claim" → "Theorem" (proved, not just stated) - Add Ledger.Properties.LastVoteApplied: lift the GOVS-level theorem to the LEDGER rule, taking TxId freshness as a premise
9edccbd to
fe57eac
Compare
Exploit the txgov structure (proposals before votes) to prove LEDGER-last-vote-applied without the Γ.txid ≢ proj₁ aid premise. Three helper lemmas: + stepVote-proposals: proposals are no-ops for the vote fold + split-GOVS: decompose a GOVS derivation at a ++ boundary + lift-GOVS-votes: votes-only lift needs no freshness (GOV-Propose is ruled out by pattern matching on inj₁)
cf9884a to
d86cb92
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
build-tools/static/mkdocs/includes/links.md:61
- A new LEDGER properties page was added (
...Ledger.Properties.LastVoteApplied), but there are no corresponding link definitions here like the otherLedger.Properties.*pages/anchors. Adding these entries will make it easy to reference the theorem consistently from other docs.
[Gov-LastVoteApplied]: Ledger.Conway.Specification.Gov.Properties.LastVoteApplied.md#clm:LastVoteApplied
[issues]: https://github.com/IntersectMBO/formal-ledger-specifications/issues
[Introduction]: Ledger.Introduction.md
[LEDGER]: Ledger.Conway.Specification.Ledger.md#ledger-transition-system
[Ledger.Properties]: Ledger.Conway.Specification.Ledger.Properties.md
| module Ledger.Conway.Specification.Ledger.Properties.LastVoteApplied | ||
| (txs : _) (open TransactionStructure txs) | ||
| (abs : AbstractFunctions txs) (open AbstractFunctions abs) | ||
| where |
Description
A voter's last vote is applied to the governance action
Closes #417
#417 asks only for the statement of this property; this PR also proves it.
GOVS-level result (
Gov/Properties/LastVoteApplied)last-vote-applied-to-GA: across a block (theGOVSclosure ofGOV), the last vote a voter casts on an action is the one recorded in the resultingGovState, provided that action was not created by the current transaction (Γ.txid ≢ proj₁ aid).vote-applied-to-GA: the single-step companion.The precondition is needed at this level because the signal list is an arbitrary
List (GovVote ⊎ GovProposal)— proposals and votes can be interleaved, and a proposal after a vote can shadow it via priority insertion.The proof is by induction on the
GOVSderivation, chaining four per-steprecordedVote-*facts into the fold thatlastVoteOncomputes.LEDGER-level result (
Ledger/Properties/LastVoteApplied)LEDGER-last-vote-applied— the same property lifted to theLEDGERrule, without the freshness premiseΓ.txid ≢ proj₁ aid.The key insight is that
txgovplaces all proposals before all votes (map inj₂ txGovProposals ++ map inj₁ txGovVotes). During the proposals phase,lastVoteOnis unaffected (proposals are no-ops forstepVote). During the votes phase, no proposals occur, sorecordedVote-addAction-≢— the only lemma that consumes the freshness assumption — is never invoked.Three helper lemmas support this:
stepVote-proposals: proposals don't change the vote-fold accumulatorsplit-GOVS: decompose aGOVSderivation at a++boundarylift-GOVS-votes: votes-only lift needs no freshness (GOV-Proposeis ruled out by pattern matching oninj₁)Also. Four general lookup-after-insert lemmas were added to
Axiom.Set.Map.Extras(candidates for upstreaming to agda-sets).Checklist
CHANGELOG.md