[Dijkstra] CIP-159-11c: Prove LEDGER preservation of value (#1187) - #1203
Conversation
a723193 to
e5190e8
Compare
5d054ab to
e24b1b0
Compare
c167cba to
3cc8b70
Compare
a1f3738 to
54853be
Compare
Split out of the LEDGER-pov work (#1203): this Conway-era properties cleanup is independent of the Dijkstra PoV proof and belongs in its own PR. - Add reusable coin/map lemmas to `Ledger.Prelude`: `getCoin-singleton`, `getCoin-cong`, `indexedSumᵛ'-∪`, `res-decomp`, `∪ˡsingleton∈dom`, `∪ˡsingleton∉dom`, `∪ˡsingleton0≡`, `sumConstZero`, `indexedSumL-proj₂-zero`, `setToList-∈`; relocate `≡ᵉ-getCoin` into the `⦃ DecEq A ⦄` module. - Drop the now-provable assumption parameters from the Conway PoV proofs, using the Prelude lemmas instead: - `Certs/Properties/PoV`: keep only `≡ᵉ-getCoinˢ'`. - `Certs/Properties/PoVLemmas`: remove the local `∪ˡsingleton*`/`getCoin-singleton` helpers and the `indexedSumᵛ'-∪` assumption module; qualify `IsEquivalence` as `Eq`. - `Ledger/Properties/PoV`: keep only `≡ᵉ-getCoinˢ`. - `Conformance/Properties`: drop the `indexedSum-∪⁺-hom` parameter. - `Utxo/Properties/GenMinSpend`: use the hoisted `getCoin-singleton`. No spec-semantic change (proof refactor only), so no CHANGELOG entry. `Ledger.Prelude` typechecks under Agda 2.8.0 via the project flake; the Conway files are their 1187 versions (which typecheck) and depend only on the hoisted lemmas. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2YdXX2ozJAdAbCrRwi6r
There was a problem hiding this comment.
Pull request overview
This PR adds Dijkstra-era preservation-of-value (PoV) proofs for the top-level LEDGER rule (parameterising lower-layer PoV facts for follow-up PRs), and fixes specification accounting so governance-action deposits and cert/governance deposit flows are counted on the correct sides of the batch-balance equations.
Changes:
- Introduces new Dijkstra PoV proof modules for
LEDGERandENTITIES, plus supportingapplyToRewardsPoV lemmas. - Fixes Dijkstra spec accounting: moves deposit terms to the correct produced/consumed sides in
Utxo, and extendsgetCoin (LedgerState)to includecoinFromGovDeposit. - Centralises reusable
getCoin/map-sum lemmas inLedger.Preludeand updates Conway call sites accordingly; adds.claude/tooling docs/hooks for Agda typechecking via Nix.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
src/Ledger/Prelude.lagda.md |
Adds reusable getCoin/map-sum and union/singleton coin lemmas used by PoV proofs. |
src/Ledger/Dijkstra/Specification/Utxo.lagda.md |
Fixes deposit term placement in consumed/produced accounting (new deposits on produced, refunds on consumed). |
src/Ledger/Dijkstra/Specification/Transaction.lagda.md |
Relocates HasDirectDeposits interface out of Transaction (record removed here). |
src/Ledger/Dijkstra/Specification/Ledger/Properties/PoV.lagda.md |
New: top-level Dijkstra LEDGER-pov proof (parameterised over lower-layer lemmas). |
src/Ledger/Dijkstra/Specification/Ledger.lagda.md |
Extends getCoin (LedgerState) with coinFromGovDeposit and documents the 4-summand accounting. |
src/Ledger/Dijkstra/Specification/Entities/Properties/PoV.lagda.md |
New: PoV for ENTITIES bridging withdrawals/direct-deposits around CERTS. |
src/Ledger/Dijkstra/Specification/Entities/Properties/ApplyToRewardsPoV.lagda.md |
New: PoV lemmas for applyWithdrawals and applyDirectDeposits fold structure. |
src/Ledger/Dijkstra/Specification/Certs.lagda.md |
Adds HasDirectDeposits instance for CertEnv. |
src/Ledger/Dijkstra/Specification/Account.lagda.md |
Adds/hosts the HasDirectDeposits interface next to DirectDeposits. |
src/Ledger/Conway/Specification/Utxo/Properties/GenMinSpend.lagda.md |
Adapts to getCoin-singleton being provided by Ledger.Prelude. |
src/Ledger/Conway/Specification/Ledger/Properties/PoV.lagda.md |
Simplifies assumptions and updates to the new Prelude lemma surface. |
src/Ledger/Conway/Specification/Certs/Properties/PoVLemmas.lagda.md |
Refactors imports/assumptions to rely on Prelude-provided coin lemmas. |
src/Ledger/Conway/Specification/Certs/Properties/PoV.lagda.md |
Updates Certs-PoV module assumptions accordingly. |
src/Ledger/Conway/Conformance/Properties.lagda.md |
Removes now-unneeded indexed-sum homomorphism assumption. |
CHANGELOG.md |
Documents the semantic spec fixes (deposit-side swap; governance deposits counted in ledger total). |
.claude/skills/agda-typecheck/SKILL.md |
Adds developer guidance for typechecking Agda via the project Nix flake. |
.claude/settings.json |
Registers a SessionStart hook for Claude Code sessions. |
.claude/hooks/session-start.sh |
Adds a hook to provision Nix + warm the dev shell for Agda typechecking. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…mas (#1186) First piece of the utxo/utxow-pov work that discharges #1187/#1203's UTxO-side module parameters. Ported verbatim from the stale #1189 branch (1186-dijkstra-utxo-and-utxow-pov); typechecks unchanged against the current spec (Agda 2.8.0 via the Nix flake), since these lemmas are era-independent: - ∙-homo-Coin : coin (x + y) ≡ coin x + coin y (coin monoid homomorphism) - coin-∑ˡ : coin distributes over a list-indexed ∑ˡ - newTxid⇒disj : TxId freshness ⇒ disjointness of dom utxo and dom (outs tx) - outs-disjoint : the (utxo ∣ SpendInputs ᶜ) / outs specialisation used by PoV balance-∪ / split-balance remain module parameters of Utxo.Properties.PoV (the set-theoretic balance-arithmetic port is tracked separately). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2YdXX2ozJAdAbCrRwi6r
Split out of the LEDGER-pov work (#1203): this Conway-era properties cleanup is independent of the Dijkstra PoV proof and belongs in its own PR. - Add reusable coin/map lemmas to `Ledger.Prelude`: `getCoin-singleton`, `getCoin-cong`, `indexedSumᵛ'-∪`, `res-decomp`, `∪ˡsingleton∈dom`, `∪ˡsingleton∉dom`, `∪ˡsingleton0≡`, `sumConstZero`, `indexedSumL-proj₂-zero`, `setToList-∈`; relocate `≡ᵉ-getCoin` into the `⦃ DecEq A ⦄` module. - Drop the now-provable assumption parameters from the Conway PoV proofs, using the Prelude lemmas instead: - `Certs/Properties/PoV`: keep only `≡ᵉ-getCoinˢ'`. - `Certs/Properties/PoVLemmas`: remove the local `∪ˡsingleton*`/`getCoin-singleton` helpers and the `indexedSumᵛ'-∪` assumption module; qualify `IsEquivalence` as `Eq`. - `Ledger/Properties/PoV`: keep only `≡ᵉ-getCoinˢ`. - `Conformance/Properties`: drop the `indexedSum-∪⁺-hom` parameter. - `Utxo/Properties/GenMinSpend`: use the hoisted `getCoin-singleton`. No spec-semantic change (proof refactor only), so no CHANGELOG entry. `Ledger.Prelude` typechecks under Agda 2.8.0 via the project flake; the Conway files are their 1187 versions (which typecheck) and depend only on the hoisted lemmas. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We2YdXX2ozJAdAbCrRwi6r
Add Utxow.Properties.PoV (#1186). Both UTXOW constructors (and SUBUTXOW) embed their state-changing UTXO (SUBUTXO) derivation as the final premise, so a pair of extractors (UTXOW⇒UTXO, SUBUTXOW⇒SUBUTXO) reduces every statement to its Utxo.Properties.PoV counterpart: * module UTXOW-PoV (tx) (noMintSubTx): utxow-pov-invalid, UTXOW-V-mechanical, and UTXOW-batch-balance-coin, with statements matching the corresponding module parameters of Ledger.Properties.PoV (#1203) verbatim. * module SUBUTXOW-PoV: subutxow-step-coin, given the two batch-threading invariants (per-sub-tx TxId freshness and running-vs-snapshot spend-input agreement) as module parameters — the SUBUTXOW premises do not provide them; they belong to the same follow-up family as the utxo₁-tx-spend-eq and fresh-top-tx-id parameters of LEDGER-pov. Instantiating LEDGER-PoV with these lemmas (balance-∪/split-balance/ outs-disjoint from Utxo.Properties.Base with the η-wrappers from the PR #1203 design notes, subutxow-step-coin from SUBUTXOW-PoV, and the three UTXOW facts from UTXOW-PoV) has been verified to elaborate LEDGER-pov end-to-end. Register the module in the Utxow.Properties aggregator. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
04cf4c7 to
a651f09
Compare
…s.Base Port the Conway balance lemmas to Dijkstra: balance-cong, balance-cong-coin, balance-∪ (additivity on disjoint unions) and split-balance (partition along a key-set restriction), plus the freshness lemmas newTxid⇒disj / outs-disjoint, the coin homomorphism law ∙-homo-Coin, and coin-∑ˡ (coin distributes over list-indexed Value sums). The Conway indexedSumᵐ proofs now port directly because the Dijkstra balance is again an indexed sum over a finite map of transaction outputs (∑[ x ← mapValues txOutToValue utxo ] x); the earlier ∑ˢ-over-ℙ-Value definition that blocked this port is gone. balance-∪, split-balance and outs-disjoint match the corresponding module parameters of Ledger.Properties.PoV (#1203), which they discharge (#1186). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add Utxow.Properties.PoV (#1186). Both UTXOW constructors (and SUBUTXOW) embed their state-changing UTXO (SUBUTXO) derivation as the final premise, so a pair of extractors (UTXOW⇒UTXO, SUBUTXOW⇒SUBUTXO) reduces every statement to its Utxo.Properties.PoV counterpart: * module UTXOW-PoV (tx) (noMintSubTx): utxow-pov-invalid, UTXOW-V-mechanical, and UTXOW-batch-balance-coin, with statements matching the corresponding module parameters of Ledger.Properties.PoV (#1203) verbatim. * module SUBUTXOW-PoV: subutxow-step-coin, given the two batch-threading invariants (per-sub-tx TxId freshness and running-vs-snapshot spend-input agreement) as module parameters — the SUBUTXOW premises do not provide them; they belong to the same follow-up family as the utxo₁-tx-spend-eq and fresh-top-tx-id parameters of LEDGER-pov. Instantiating LEDGER-PoV with these lemmas (balance-∪/split-balance/ outs-disjoint from Utxo.Properties.Base with the η-wrappers from the PR #1203 design notes, subutxow-step-coin from SUBUTXOW-PoV, and the three UTXOW facts from UTXOW-PoV) has been verified to elaborate LEDGER-pov end-to-end. Register the module in the Utxow.Properties aggregator. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
b277131 to
6bea3e8
Compare
…s.Base Port the Conway balance lemmas to Dijkstra: balance-cong, balance-cong-coin, balance-∪ (additivity on disjoint unions) and split-balance (partition along a key-set restriction), plus the freshness lemmas newTxid⇒disj / outs-disjoint, the coin homomorphism law ∙-homo-Coin, and coin-∑ˡ (coin distributes over list-indexed Value sums). The Conway indexedSumᵐ proofs now port directly because the Dijkstra balance is again an indexed sum over a finite map of transaction outputs (∑[ x ← mapValues txOutToValue utxo ] x); the earlier ∑ˢ-over-ℙ-Value definition that blocked this port is gone. balance-∪, split-balance and outs-disjoint match the corresponding module parameters of Ledger.Properties.PoV (#1203), which they discharge (#1186). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add Utxow.Properties.PoV (#1186). Both UTXOW constructors (and SUBUTXOW) embed their state-changing UTXO (SUBUTXO) derivation as the final premise, so a pair of extractors (UTXOW⇒UTXO, SUBUTXOW⇒SUBUTXO) reduces every statement to its Utxo.Properties.PoV counterpart: * module UTXOW-PoV (tx) (noMintSubTx): utxow-pov-invalid, UTXOW-V-mechanical, and UTXOW-batch-balance-coin, with statements matching the corresponding module parameters of Ledger.Properties.PoV (#1203) verbatim. * module SUBUTXOW-PoV: subutxow-step-coin, given the two batch-threading invariants (per-sub-tx TxId freshness and running-vs-snapshot spend-input agreement) as module parameters — the SUBUTXOW premises do not provide them; they belong to the same follow-up family as the utxo₁-tx-spend-eq and fresh-top-tx-id parameters of LEDGER-pov. Instantiating LEDGER-PoV with these lemmas (balance-∪/split-balance/ outs-disjoint from Utxo.Properties.Base with the η-wrappers from the PR #1203 design notes, subutxow-step-coin from SUBUTXOW-PoV, and the three UTXOW facts from UTXOW-PoV) has been verified to elaborate LEDGER-pov end-to-end. Register the module in the Utxow.Properties aggregator. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s.Base Port the Conway balance lemmas to Dijkstra: balance-cong, balance-cong-coin, balance-∪ (additivity on disjoint unions) and split-balance (partition along a key-set restriction), plus the freshness lemmas newTxid⇒disj / outs-disjoint, the coin homomorphism law ∙-homo-Coin, and coin-∑ˡ (coin distributes over list-indexed Value sums). The Conway indexedSumᵐ proofs now port directly because the Dijkstra balance is again an indexed sum over a finite map of transaction outputs (∑[ x ← mapValues txOutToValue utxo ] x); the earlier ∑ˢ-over-ℙ-Value definition that blocked this port is gone. balance-∪, split-balance and outs-disjoint match the corresponding module parameters of Ledger.Properties.PoV (#1203), which they discharge (#1186). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add Utxow.Properties.PoV (#1186). Both UTXOW constructors (and SUBUTXOW) embed their state-changing UTXO (SUBUTXO) derivation as the final premise, so a pair of extractors (UTXOW⇒UTXO, SUBUTXOW⇒SUBUTXO) reduces every statement to its Utxo.Properties.PoV counterpart: * module UTXOW-PoV (tx) (noMintSubTx): utxow-pov-invalid, UTXOW-V-mechanical, and UTXOW-batch-balance-coin, with statements matching the corresponding module parameters of Ledger.Properties.PoV (#1203) verbatim. * module SUBUTXOW-PoV: subutxow-step-coin, given the two batch-threading invariants (per-sub-tx TxId freshness and running-vs-snapshot spend-input agreement) as module parameters — the SUBUTXOW premises do not provide them; they belong to the same follow-up family as the utxo₁-tx-spend-eq and fresh-top-tx-id parameters of LEDGER-pov. Instantiating LEDGER-PoV with these lemmas (balance-∪/split-balance/ outs-disjoint from Utxo.Properties.Base with the η-wrappers from the PR #1203 design notes, subutxow-step-coin from SUBUTXOW-PoV, and the three UTXOW facts from UTXOW-PoV) has been verified to elaborate LEDGER-pov end-to-end. Register the module in the Utxow.Properties aggregator. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6718d6a to
164f116
Compare
…s.Base Port the Conway balance lemmas to Dijkstra: balance-cong, balance-cong-coin, balance-∪ (additivity on disjoint unions) and split-balance (partition along a key-set restriction), plus the freshness lemmas newTxid⇒disj / outs-disjoint, the coin homomorphism law ∙-homo-Coin, and coin-∑ˡ (coin distributes over list-indexed Value sums). The Conway indexedSumᵐ proofs now port directly because the Dijkstra balance is again an indexed sum over a finite map of transaction outputs (∑[ x ← mapValues txOutToValue utxo ] x); the earlier ∑ˢ-over-ℙ-Value definition that blocked this port is gone. balance-∪, split-balance and outs-disjoint match the corresponding module parameters of Ledger.Properties.PoV (#1203), which they discharge (#1186). AI-assisted: Claude Fable 5 (Anthropic)
Add Utxow.Properties.PoV (#1186). Both UTXOW constructors (and SUBUTXOW) embed their state-changing UTXO (SUBUTXO) derivation as the final premise, so a pair of extractors (UTXOW⇒UTXO, SUBUTXOW⇒SUBUTXO) reduces every statement to its Utxo.Properties.PoV counterpart: * module UTXOW-PoV (tx) (noMintSubTx): utxow-pov-invalid, UTXOW-V-mechanical, and UTXOW-batch-balance-coin, with statements matching the corresponding module parameters of Ledger.Properties.PoV (#1203) verbatim. * module SUBUTXOW-PoV: subutxow-step-coin, given the two batch-threading invariants (per-sub-tx TxId freshness and running-vs-snapshot spend-input agreement) as module parameters — the SUBUTXOW premises do not provide them; they belong to the same follow-up family as the utxo₁-tx-spend-eq and fresh-top-tx-id parameters of LEDGER-pov. Instantiating LEDGER-PoV with these lemmas (balance-∪/split-balance/ outs-disjoint from Utxo.Properties.Base with the η-wrappers from the PR #1203 design notes, subutxow-step-coin from SUBUTXOW-PoV, and the three UTXOW facts from UTXOW-PoV) has been verified to elaborate LEDGER-pov end-to-end. Register the module in the Utxow.Properties aggregator. AI-assisted: Claude Fable 5 (Anthropic)
…s.Base Port the Conway balance lemmas to Dijkstra: balance-cong, balance-cong-coin, balance-∪ (additivity on disjoint unions) and split-balance (partition along a key-set restriction), plus the freshness lemmas newTxid⇒disj / outs-disjoint, the coin homomorphism law ∙-homo-Coin, and coin-∑ˡ (coin distributes over list-indexed Value sums). The Conway indexedSumᵐ proofs now port directly because the Dijkstra balance is again an indexed sum over a finite map of transaction outputs (∑[ x ← mapValues txOutToValue utxo ] x); the earlier ∑ˢ-over-ℙ-Value definition that blocked this port is gone. balance-∪, split-balance and outs-disjoint match the corresponding module parameters of Ledger.Properties.PoV (#1203), which they discharge (#1186). AI-assisted: Claude Fable 5 (Anthropic)
Add Utxow.Properties.PoV (#1186). Both UTXOW constructors (and SUBUTXOW) embed their state-changing UTXO (SUBUTXO) derivation as the final premise, so a pair of extractors (UTXOW⇒UTXO, SUBUTXOW⇒SUBUTXO) reduces every statement to its Utxo.Properties.PoV counterpart: * module UTXOW-PoV (tx) (noMintSubTx): utxow-pov-invalid, UTXOW-V-mechanical, and UTXOW-batch-balance-coin, with statements matching the corresponding module parameters of Ledger.Properties.PoV (#1203) verbatim. * module SUBUTXOW-PoV: subutxow-step-coin, given the two batch-threading invariants (per-sub-tx TxId freshness and running-vs-snapshot spend-input agreement) as module parameters — the SUBUTXOW premises do not provide them; they belong to the same follow-up family as the utxo₁-tx-spend-eq and fresh-top-tx-id parameters of LEDGER-pov. Instantiating LEDGER-PoV with these lemmas (balance-∪/split-balance/ outs-disjoint from Utxo.Properties.Base with the η-wrappers from the PR #1203 design notes, subutxow-step-coin from SUBUTXOW-PoV, and the three UTXOW facts from UTXOW-PoV) has been verified to elaborate LEDGER-pov end-to-end. Register the module in the Utxow.Properties aggregator. AI-assisted: Claude Fable 5 (Anthropic)
| - Add `PoolDepositsRegistered` (every pool-deposit entry belongs to a registered | ||
| pool) to `Certs`; the deposit accounting genuinely fails without it, since | ||
| `POOL-reg` adds its deposit with a left-biased union. | ||
| - Count governance-action deposits in `getCoin LedgerState` via a new | ||
| `coinFromGovDeposit : GovState → Coin` (sum of `GovActionState.deposit`). These | ||
| deposits live in `GovActionState.deposit`, not `GState.deposits`, so the three | ||
| `CertState` deposit pots alone do not account for them. | ||
| - Make `getCoin` on `CertState` total (rewards balance plus the three deposit pots) and | ||
| add the `coinFromRewards`/`coinFromDeposits` projections in `Certs`. |
There was a problem hiding this comment.
We should really think through the policy about a CHANGELOG on something that is WIP
There was a problem hiding this comment.
I'll remove the changelog entries from this PR.
e3b7d4a to
3d69937
Compare
|
@carlostome thanks for the review. I cleaned up the prose and removed the paragraph you suggested looked out-of-place. I think this PR is finally ready! |
…s.Base Port the Conway balance lemmas to Dijkstra: balance-cong, balance-cong-coin, balance-∪ (additivity on disjoint unions) and split-balance (partition along a key-set restriction), plus the freshness lemmas newTxid⇒disj / outs-disjoint, the coin homomorphism law ∙-homo-Coin, and coin-∑ˡ (coin distributes over list-indexed Value sums). The Conway indexedSumᵐ proofs now port directly because the Dijkstra balance is again an indexed sum over a finite map of transaction outputs (∑[ x ← mapValues txOutToValue utxo ] x); the earlier ∑ˢ-over-ℙ-Value definition that blocked this port is gone. balance-∪, split-balance and outs-disjoint match the corresponding module parameters of Ledger.Properties.PoV (#1203), which they discharge (#1186). AI-assisted: Claude Fable 5 (Anthropic)
carlostome
left a comment
There was a problem hiding this comment.
LGTM! I left a couple of small nitpicks but it looks great
| - Add `PoolDepositsRegistered` (every pool-deposit entry belongs to a registered | ||
| pool) to `Certs`; the deposit accounting genuinely fails without it, since | ||
| `POOL-reg` adds its deposit with a left-biased union. | ||
| - Count governance-action deposits in `getCoin LedgerState` via a new | ||
| `coinFromGovDeposit : GovState → Coin` (sum of `GovActionState.deposit`). These | ||
| deposits live in `GovActionState.deposit`, not `GState.deposits`, so the three | ||
| `CertState` deposit pots alone do not account for them. | ||
| - Make `getCoin` on `CertState` total (rewards balance plus the three deposit pots) and | ||
| add the `coinFromRewards`/`coinFromDeposits` projections in `Certs`. |
Prove `LEDGER-pov` for the Dijkstra LEDGER rule: getCoin s ≡ getCoin s', where getCoin (LedgerState) sums UTxO coin, the rewards balance, the three cert-deposit pots, and the gov-action deposits. Typechecks under --safe with no postulates or holes. Top-down plan: the supporting Certs / UTxO / Gov facts are module parameters of `Ledger.Properties.PoV`, forming the frozen contract for the follow-up PRs (#1186 utxo/utxow, #1210 certs, and a future Gov PoV). New modules: - Ledger/Properties/PoV: LEDGER-pov. The LEDGER-V case is a single LedgerState-level equational chain; direct-deposit value cancels between the UTxO and CertState sides, gov-deposit growth is matched against the produced-side govProposalsDeposits, and `bat'` converts the closed-form batch balance (#1186 shape) to the chain's two-level posPart/negPart deposit form via the batch-cert-deposits-bridge (#1210 shape) and net-arith. - Entities/Properties/PoV: ENTITIES-pov and SUBENTITIES-pov, the value-flow equations for the post-#1256 tx-signal rules. NetworkId and domain facts come from the rules' own premises; the no-truncation bound (withdrawal amount ≤ input-state balance) is an explicit hypothesis — the phantom-withdrawal gap flagged in the #1256 review, provided at the LEDGER level by the ENTITIES/SUBENTITIES-wdrls-bounded parameters. - Entities/Properties/ApplyToRewardsPoV: applyWithdrawals-pov and applyDirectDeposits-pov fold lemmas. Spec changes: - Ledger: getCoin (LedgerState) gains a coinFromGovDeposit summand (gov-action deposits live in GovActionState.deposit, not GState.deposits, yet the UTXO batch balance charges govProposalsDeposits on the produced side). - Certs: getCoin (CertState) is now total (rewards + deposit pots), with coinFromRewards / coinFromDeposits projections. - Prelude: era-independent sum lemmas (+-interleave, sum-map-+). Squash of the pre-rebase branch (13 WIP commits, head f677e1b), rebased onto master and adapted to #1250 (explicit cert deposits, closed-form newCertDeposits/refundCertDeposits), #1253/#1256 (EntitiesEnv, SUBENTITIES, tx-signal ENTITIES), #1262 (legacyMode threading), and deposit sign-swap fix in #1250, so it is no longer part of the diff. AI-assisted: Claude Fable 5 (Anthropic)
… solver Replace the hand-written equational chains of the pure +-rearrangement helpers (swap-right, abcd-to-acdb, mid-extract, rearr3, outer-rearr, arithmetic-1..5, net-arith's rearr-X/Y/Z, reshuffle-to-DD's core) with the reflective ring solver over the ℕ commutative semiring (Data.Nat.Tactic.RingSolver): solve-∀ for closed all-visible ∀-goals, in-context solve where trailing variables are implicit. The solver matches the ring's operations syntactically, so the solver-facing statements use the raw Data.Nat._+_ (imported as _+ᴺ_) rather than the Ledger.Prelude HasAdd overload, which only *reduces* to it; the two are definitionally equal, so all call sites are unchanged. reshuffle-to-DD generalises its where-bound abbreviations to a closed ∀-goal first, since syntactic atom matching cannot see through them. Also drop unused integer imports (0ℤ, _-_, +ℤ-comm). Net: 133 lines of tedious associativity/commutativity shuffling deleted; statements and chain structure unchanged. Typechecks under --safe (full src/Ledger root green). AI-assisted: Claude Fable 5 (Anthropic)
…batch bridge Per review: the Certs-level parameters now state the complete value accounting of a CERTS run, and the ENTITIES PoV module surfaces it per transaction: - CERTS-rewards-pov (rewards preserved), CERTS-deposits-pov (closed-form deposit accounting D + new ≡ D' + refunds), CERTS-deposits-registered (invariant preservation), CERTS-new-thread (newCertDeposits split at a run boundary), refundCertDeposits-++ (fold algebra). - Entities.Properties.PoV adds SUBENTITIES/ENTITIES-deposits-pov, -deposits-registered, SUBENTITIES-new-thread (all definitional transports of the CERTS facts), and the combined full-CertState-coin identities SUBENTITIES/ENTITIES-pov-total: coin in + direct deposits + new deposits ≡ coin out + withdrawals + refunds. - Ledger.Properties.PoV now *derives* the batch-wide deposit accounting (SUBLEDGERS-deposits, telescoping per-step closed forms across the batch, with SUBLEDGERS-registered threading the invariant and refund-concatMap recombining refunds); the batch-cert-deposits-bridge parameter is gone. Its premises mentioned SUBLEDGERS, so it was not statable at the Certs level anyway. Restating the per-run accounting exposed a genuine soundness condition: POOL-reg adds its deposit with a left-biased union, so at a state with a stale pot entry for an unregistered pool, registering that pool destroys the deposit the batch balance charges, and value preservation is false. New PoolDepositsRegistered invariant in Certs (every pool-deposit entry belongs to a registered pool; preserved by CERTS, holds on-chain by construction); LEDGER-pov now takes it as a hypothesis on the initial state. The old bridge parameter silently baked in the same assumption and would have been undischargeable as stated. Also apply the review's prose policy: status/roadmap/issue-number prose moved from module documentation to the PR description; explanations deduplicated across modules (stated once, referenced elsewhere). Final touches: + general improvements + remove unnecessary CHANGELOG entry + add mkdocs nav entries AI-assisted: Claude Fable 5 (Anthropic)
Co-authored-by: Carlos Tomé Cortiñas <carlos.tome-cortinas@iohk.io>
Description
Property proved
The Dijkstra
LEDGERrule preserves value.where
getCoin (LedgerState)sums UTxO coin, the rewards balance, the cert-deposit pots, and the gov-action deposits.New hypothesis
POOL-regadds the pool deposit with a left-biased union.At a state holding a stale pot entry for an unregistered pool, registering that pool destroys the deposit of the batch balance; preservation is genuinely false there, not merely unprovable.
PoolDepositsRegisteredasserts that every pool-deposit entry belongs to a registered pool, defined inCerts.This precludes a state holding a stale pot entry for an unregistered pool. Moreover, this condition holds on-chain by construction, so a chain-level reachability argument discharges it.
Shape of the proof
There are no per-rule PoV theorems to compose here: Dijkstra's only balance premise is batch-wide.
So
LEDGER-Vis a singleLedgerState-level equational chain, resting on three cancellations:producedBatch) and the CertState side (applyDirectDepositsinENTITIES);SUBLEDGERS-deposits, telescoping the per-step closed forms) reconciles the deposit pots against the batch balance;govProposalsDeposits.Everything below
LEDGERis left as a module parameter, so this PR proves exactly whatLEDGER-povneeds and the parameter list is a frozen contract for the follow-ups.What lands
Ledger/Properties/PoV.lagda.md:LEDGER-pov, the fourSUBLEDGERSinductions,posNeg-deposits;Entities/Properties/PoV.lagda.md: per-transaction accounting for the post-[Dijkstra] Refactor ENTITIES and add SUBENTITIES #1256 tx-signal rules, split bygetCoin (CertState)component, combining to coin in + direct deposits + new deposits ≡ coin out + withdrawals + refunds;Entities/Properties/ApplyToRewardsPoV.lagda.md: the withdrawal / direct-deposit fold lemmas.Spec changes, each forced by the accounting.
Ledger.lagda.md:getCoin (LedgerState)gainscoinFromGovDeposit; Gov-action deposits live inGovActionState.deposit, notGState.deposits, yet theUTXObatch balance chargesgovProposalsDepositson the produced side; without this summand the total is not preserved.Certs.lagda.md:getCoin (CertState)is now total (rewards balance plus the three deposit pots), withcoinFromRewards/coinFromDepositsprojections, and the newPoolDepositsRegisteredinvariant.Prelude.lagda.md: era-independent sum lemmas (+-interleave,sum-map-+).Assumptions: module parameters left open
CERTS-rewards-pov,CERTS-deposits-pov,CERTS-deposits-registered,CERTS-new-thread,refundCertDeposits-++UTXOW-batch-balance-coin,UTXOW-V-mechanical,utxow-pov-invalid,subutxow-step-coin,balance-∪,split-balance,outs-disjoint,noMintTx,noMintSubTx,utxo₁-tx-spend-eq,fresh-top-tx-idGOVS-coinFromGovDeposit,rmOrphanDRepVotes-coinFromGovDeposit*ENTITIES-wdrls-bounded,SUBENTITIES-wdrls-bounded**PoolDepositsRegisteredfor on-chain initial states**∪ˡ-lookup-preserve,sum-map-proj₂≡getCoin,setToList-Unique(agda-setscandidates)*= TODO (nothing filed yet)Three of these deserve a word on why they are shaped the way they are.
CERTS-deposits-povstates the deposit accounting in closed form,D + newCertDeposits (dom pools) certs ≡ D' + refundCertDeposits certs, under aPoolDepositsRegisteredpremise, without whichPOOL-reg's left-biased update falsifies it.CERTS-new-threadsplitsnewCertDepositsoverxs ++ ysat a run boundary with theyshalf against the run's final pool set; that is precisely what lets per-run accounting compose across a batch.SUBLEDGERS-depositsderives it.UTXOW-batch-balance-coinkeeps cert deposits in the spec's closed form, so it stays a pure UTxO obligation with no post-batch cert states in it.rewards₀(except top-level legacy mode), which does not bound them against the input state of a later step in the batch; the phantom-withdrawal gap flagged in the [Dijkstra] Refactor ENTITIES and add SUBENTITIES #1256 review. Needs a spec-side premise or a batch-threading invariant.Checklist
CHANGELOG.md🤖 Generated with Claude Code
🧑 Curated and revised by @williamdemeo