@@ -5,20 +5,24 @@ source_path: src/Ledger/Dijkstra/Specification/Entities/Properties/ApplyToReward
55
66# ` applyToRewards ` Preservation of Value {#sec: apply-to-rewards-pov }
77
8- This module proves preservation of value for the two specialisations of
9- ` applyToRewards ` {.AgdaFunction} used inside the ` ENTITIES ` {.AgdaDatatype} rule:
10-
11- + ` applyWithdrawals-pov ` {.AgdaFunction}: ` applyWithdrawals ` {.AgdaFunction}
12- * decreases* the total rewards balance by exactly the sum of withdrawal
13- amounts. Truncating subtraction (` _∸_ ` ) means the per-step lemma requires
14- ` amt ≤ bal ` , and the fold induction requires a ` Unique ` {.AgdaDatatype} witness on
15- the stake-projected withdrawal list so that no already-reduced balance is
16- revisited.
17- + ` applyDirectDeposits-pov ` {.AgdaFunction}: ` applyDirectDeposits ` {.AgdaFunction}
18- * increases* the total rewards balance by exactly the sum of direct-deposit
19- amounts. Because ` _+_ ` is total and commutative, revisiting a credential is
20- harmless, so neither the ` NetworkId ` {.AgdaFunction} witness nor the
21- ` Unique ` {.AgdaDatatype} premise is needed.
8+ This module proves preservation of value for the two specializations of
9+ ` applyToRewards ` {.AgdaFunction} used inside the ` ENTITIES ` {.AgdaDatatype} rule.
10+
11+ + ` applyWithdrawals-pov ` {.AgdaFunction}.
12+
13+ ` applyWithdrawals ` {.AgdaFunction} * decreases* the total rewards balance by exactly
14+ the sum of withdrawal amounts. Truncating subtraction (` _∸_ ` ) means the per-step
15+ lemma requires ` amt ≤ bal ` , and the fold induction requires a
16+ ` Unique ` {.AgdaDatatype} witness on the stake-projected withdrawal list so that no
17+ already-reduced balance is revisited.
18+
19+ + ` applyDirectDeposits-pov ` {.AgdaFunction}.
20+
21+ ` applyDirectDeposits ` {.AgdaFunction} * increases* the total rewards balance by
22+ exactly the sum of direct-deposit amounts. Because ` _+_ ` is total and
23+ commutative, revisiting a credential is harmless, so neither the
24+ ` NetworkId ` {.AgdaFunction} witness nor the ` Unique ` {.AgdaDatatype} premise is
25+ needed.
2226
2327Both lemmas share a common backbone: a per-step result about
2428` applyOne ` {.AgdaFunction} (the lambda body of ` applyToRewards ` {.AgdaFunction})
@@ -34,30 +38,26 @@ open import Ledger.Dijkstra.Specification.Gov.Base using (GovStructure)
3438module Ledger.Dijkstra.Specification.Entities.Properties.ApplyToRewardsPoV
3539 (gs : GovStructure) (open GovStructure gs) where
3640
37- open import Ledger.Prelude
38- open import Ledger.Dijkstra.Specification.Account gs using (DirectDeposits)
39- open import Ledger.Dijkstra.Specification.Certs gs
40- open import Ledger.Dijkstra.Specification.Entities gs
41- open import Ledger.Dijkstra.Specification.Gov.Actions gs hiding (yes; no)
42-
43- open import Axiom.Set.Properties th
44-
45- open import Data.Nat.Properties
46- using (+-0-monoid; +-identityʳ; +-comm; +-assoc; m∸n+n≡m; n≤0⇒n≡0)
41+ open import Data.Nat.Properties using (+-identityʳ; +-comm; +-assoc; m∸n+n≡m; n≤0⇒n≡0)
4742open import Data.Maybe.Properties using (just-injective)
48- open import Data.List.Relation.Unary.Unique.Propositional
49- using (Unique) renaming (_∷_ to _::_)
5043open import Data.List.Membership.Propositional.Properties using (∈-map⁺)
5144open import Data.List.Relation.Unary.Any using (Any)
52- import Data.List.Relation.Unary.All as All
45+ open import Data.List.Relation.Unary.All using (lookup)
46+ open import Data.List.Relation.Unary.Unique.Propositional using (Unique) renaming (_∷_ to _::_)
5347open import Relation.Binary using (IsEquivalence)
5448
49+ open import Ledger.Prelude hiding (lookup)
50+
51+ open import Ledger.Dijkstra.Specification.Account gs using (DirectDeposits)
52+ open import Ledger.Dijkstra.Specification.Certs gs using (Rewards)
53+ open import Ledger.Dijkstra.Specification.Entities gs
54+ using (applyWithdrawals; applyDirectDeposits)
55+
56+ open import Axiom.Set.Properties th
57+
5558open RewardAddress
5659open Any
5760open ≡-Reasoning
58-
59- instance
60- _ = +-0-monoid
6161```
6262-->
6363
@@ -70,13 +70,9 @@ getCoin-∪ˡ-overwrite : (acc : Rewards) (c : Credential) (v : Coin)
7070
7171<!--
7272```agda
73- -- After a recent refactor of applyToRewards, the fold body writes to its accumulator
74- -- via ❴ k , v ❵ ∪ˡ acc; that is, a singleton-on-the-left union with no complement
75- -- restriction on acc. Left-biasedness of ∪ˡ makes this extensionally equal to the
76- -- older ❴ k , v ❵ ∪ˡ (acc ∣ ❴ k ❵ ᶜ) form (singleton wins; existing entries at k are
77- -- shadowed). The lemma below is the corresponding getCoin-level equation, which
78- -- both per-step lemmas (applyOne-pov, applyOne-pov-add) land on as a common
79- -- right-hand side.
73+ -- The new applyToRewards writes to its accumulator via ❴ k , v ❵ ∪ˡ acc;
74+ -- left-biasedness of ∪ˡ makes this extensionally equal to the older
75+ -- ❴ k , v ❵ ∪ˡ (acc ∣ ❴ k ❵ ᶜ) form; the following proves this at the getCoin level.
8076getCoin-∪ˡ-overwrite acc c v =
8177 begin
8278 getCoin (❴ c , v ❵ ∪ˡ acc)
@@ -91,14 +87,8 @@ getCoin-∪ˡ-overwrite acc c v =
9187 open Equivalence
9288 module ≡ᵉ = IsEquivalence (≡ᵉ-isEquivalence {Credential × Coin})
9389
94- -- res-decomp (from Ledger.Prelude) gives
95- -- (❴ c , v ❵ᵐ ∪ˡ acc) ˢ ≡ᵉ (❴ c , v ❵ᵐ ∪ˡ (acc ∣ dom (❴ c , v ❵ᵐ ˢ) ᶜ)) ˢ
96- -- but the bridge wants acc ∣ ❴ c ❵ ᶜ on the right (set-singleton via the listing
97- -- axiom), not acc ∣ dom (❴ c , v ❵ᵐ ˢ) ᶜ (set built via replacement/mapˢ).
98- -- These two restriction sets are extensionally equal by dom-single≡single, so we
99- -- chain res-decomp with an ∪ˡ-congruence on the right operand.
10090 restrict-cong-∪ˡ :
101- (❴ c , v ❵ᵐ ∪ˡ ( acc ∣ dom ( ❴ c , v ❵ᵐ ˢ) ᶜ) ) ˢ ≡ᵉ (❴ c , v ❵ᵐ ∪ˡ (acc ∣ ❴ c ❵ ᶜ)) ˢ
91+ (❴ c , v ❵ᵐ ∪ˡ acc ∣ dom ❴ c , v ❵ᵐ ᶜ ) ˢ ≡ᵉ (❴ c , v ❵ᵐ ∪ˡ (acc ∣ ❴ c ❵ ᶜ)) ˢ
10292 restrict-cong-∪ˡ =
10393 ∪ˡ-cong {m = ❴ c , v ❵ᵐ} {m' = acc ∣ dom (❴ c , v ❵ᵐ ˢ) ᶜ}
10494 {m'' = ❴ c , v ❵ᵐ} {m''' = acc ∣ ❴ c ❵ ᶜ}
@@ -120,18 +110,14 @@ split-by-lookup : (acc : Rewards) (c : Credential) (bal : Coin)
120110<!--
121111```agda
122112-- When lookupᵐ? acc c ≡ just bal, we can decompose getCoin acc into the contribution
123- -- of c (which is bal) plus the contribution of every other credential
124- -- (getCoin (acc ∣ ❴ c ❵ ᶜ)). This is the shared prefix used by both per-step lemmas,
125- -- factored out to avoid duplication.
113+ -- of c (which is bal) plus the contribution of every other credential, getCoin (acc ∣ ❴ c ❵ ᶜ).
126114split-by-lookup acc c bal lookup-eq =
127115 begin
128116 getCoin acc
129- ≡˘⟨ ≡ᵉ-getCoin decomp acc
130- ( ≡ᵉ.trans (disjoint-∪ˡ-∪ (disjoint-sym res-ex-disjoint))
131- (≡ᵉ.trans ∪-sym (res-ex-∪ Dec-∈-singleton)) ) ⟩
117+ ≡˘⟨ ≡ᵉ-getCoin decomp acc ( ≡ᵉ.trans (disjoint-∪ˡ-∪ (disjoint-sym res-ex-disjoint))
118+ (≡ᵉ.trans ∪-sym (res-ex-∪ Dec-∈-singleton)) ) ⟩
132119 getCoin decomp
133- ≡⟨ indexedSumᵛ'-∪ (acc ∣ ❴ c ❵ ᶜ) (acc ∣ ❴ c ❵)
134- (disjoint-sym res-ex-disjoint) ⟩
120+ ≡⟨ indexedSumᵛ'-∪ (acc ∣ ❴ c ❵ ᶜ) (acc ∣ ❴ c ❵) (disjoint-sym res-ex-disjoint) ⟩
135121 getCoin (acc ∣ ❴ c ❵ ᶜ) + getCoin (acc ∣ ❴ c ❵)
136122 ≡⟨ cong (getCoin (acc ∣ ❴ c ❵ ᶜ) +_) acc∣c≡bal ⟩
137123 getCoin (acc ∣ ❴ c ❵ ᶜ) + bal
@@ -159,20 +145,17 @@ split-by-lookup acc c bal lookup-eq =
159145
160146## The ` ApplyToRewards-PoV ` module
161147
162- The three assumed identities below are the same set/map identities used
163- by the Conway PoV proofs; they are stated as module parameters here to
164- keep this commit focused on the fold-induction structure and to be
165- discharged in a follow-up against the ` agda-sets ` library.
166-
167- + ` ∪ˡ-lookup-preserve ` : lookup in a left-biased union with a singleton
168- at ` c ` agrees with lookup in the right map for any key ` c' ≠ c ` .
169- + ` sum-map-proj₂≡getCoin ` : the ` getCoin ` {.AgdaFunction} representation
170- of a ` (RewardAddress ⇀ Coin) ` map equals the list-sum of its second
171- projections.
172- + ` setToList-Unique ` : stake-projection of a withdrawal/direct-deposit
173- list is ` Unique ` {.AgdaDatatype}, assuming the per-batch
174- ` NetworkId ` {.AgdaFunction} constraint. Used only by
175- ` applyWithdrawals-pov ` .
148+ The three assumed identities below are the same set/map identities used by the Conway
149+ PoV proofs; they are stated as module parameters here, to be discharged in a
150+ follow-up against the ` agda-sets ` library.
151+
152+ + ` ∪ˡ-lookup-preserve ` : lookup in a left-biased union with a singleton at ` c ` agrees
153+ with lookup in the right map for any key ` c' ≠ c ` .
154+ + ` sum-map-proj₂≡getCoin ` : the ` getCoin ` {.AgdaFunction} representation of a
155+ ` (RewardAddress ⇀ Coin) ` map equals the list-sum of its second projections.
156+ + ` setToList-Unique ` : stake-projection of a withdrawal/direct-deposit list is
157+ ` Unique ` {.AgdaDatatype}, assuming the per-batch ` NetworkId ` {.AgdaFunction}
158+ constraint. (Used only by ` applyWithdrawals-pov ` .)
176159
177160``` agda
178161module ApplyToRewards-PoV
@@ -191,7 +174,8 @@ module ApplyToRewards-PoV
191174 where
192175```
193176
194- A local convenience definitionally equal to the lambda body of ` applyToRewards f ` :
177+ The ` applyOne ` {.AgdaFunction} is a local convenience definitionally equal to the
178+ lambda body of ` applyToRewards f ` .
195179
196180``` agda
197181 applyOne : (Coin → Coin → Coin) → Rewards → RewardAddress × Coin → Rewards
@@ -201,10 +185,10 @@ A local convenience definitionally equal to the lambda body of `applyToRewards f
201185
202186## Withdrawal preservation of value
203187
204- ### ` applyOne-pov ` ( one withdrawal step decreases ` getCoin ` by ` amt ` )
188+ ### ` applyOne-pov ` : one withdrawal step decreases ` getCoin ` by ` amt `
205189
206- When ` lookupᵐ? acc (stake addr) ≡ just bal ` and ` amt ≤ bal ` , applying a
207- single withdrawal reduces the total by exactly ` amt ` .
190+ When ` lookupᵐ? acc (stake addr) ≡ just bal ` and ` amt ≤ bal ` , applying a single
191+ withdrawal reduces the total by exactly ` amt ` .
208192
209193``` agda
210194 applyOne-pov : (acc : Rewards) (addr : RewardAddress) (amt bal : Coin)
@@ -220,10 +204,10 @@ single withdrawal reduces the total by exactly `amt`.
220204 getCoin acc
221205 ≡⟨ split-by-lookup acc c bal lookup-eq ⟩
222206 getCoin (acc ∣ ❴ c ❵ ᶜ) + bal
223- ≡⟨ cong (getCoin (acc ∣ ❴ c ❵ ᶜ) +_) (sym ( m∸n+n≡m amt≤bal) ) ⟩
207+ ≡˘ ⟨ cong (getCoin (acc ∣ ❴ c ❵ ᶜ) +_) (m∸n+n≡m amt≤bal) ⟩
224208 getCoin (acc ∣ ❴ c ❵ ᶜ) + (bal ∸ amt + amt)
225- ≡⟨ trans (sym (+-assoc (getCoin (acc ∣ ❴ c ❵ ᶜ)) (bal ∸ amt) amt))
226- (cong (_+ amt) (+-comm (getCoin (acc ∣ ❴ c ❵ ᶜ)) (bal ∸ amt))) ⟩
209+ ≡⟨ trans (sym (+-assoc (getCoin (acc ∣ ❴ c ❵ ᶜ)) (bal ∸ amt) amt))
210+ (cong (_+ amt) (+-comm (getCoin (acc ∣ ❴ c ❵ ᶜ)) (bal ∸ amt))) ⟩
227211 (bal ∸ amt) + getCoin (acc ∣ ❴ c ❵ ᶜ) + amt
228212 ≡˘⟨ cong (_+ amt) (getCoin-∪ˡ-overwrite acc c (bal ∸ amt)) ⟩
229213 getCoin (❴ c , bal ∸ amt ❵ ∪ˡ acc) + amt
@@ -233,23 +217,28 @@ single withdrawal reduces the total by exactly `amt`.
233217
234218### ` foldl-applyOne-pov ` (fold induction)
235219
236- The fold invariant tracks three things through the induction: every remaining
237- withdrawal credential is in the current accumulator's domain; every remaining
238- withdrawal amount is bounded by the current balance at that credential; and no
239- credential is revisited (the ` Unique ` {.AgdaDatatype} witness on the stake-projected
240- list). Uniqueness is essential here precisely because ` applyOne _∸_ ` * modifies*
241- the balance at the targeted credential — without it, a re-visit could attempt to
242- subtract from an already-reduced balance for which the caller's original ` amt ≤ bal `
243- bound no longer holds.
220+ The fold invariant tracks three things through the induction:
221+
222+ 1 . every remaining withdrawal credential is in the current accumulator's domain;
223+
224+ 2 . every remaining withdrawal amount is bounded by the current balance at that
225+ credential;
226+
227+ 3 . no credential is revisited (the ` Unique ` {.AgdaDatatype} witness on the
228+ stake-projected list).
229+
230+ Uniqueness is essential here precisely because ` applyOne _∸_ ` * modifies* the balance
231+ at the targeted credential — without it, a re-visit could attempt to subtract from an
232+ already-reduced balance for which the caller's original ` amt ≤ bal ` bound no longer
233+ holds.
244234
245235``` agda
246236 foldl-applyOne-pov : (acc : Rewards) (entries : List (RewardAddress × Coin))
247237 → ( ∀ {addr amt} → (addr , amt) ∈ˡ entries
248238 → stake addr ∈ dom acc
249239 × amt ≤ maybe id 0 (lookupᵐ? acc (stake addr)) )
250240 → Unique (map (stake ∘ proj₁) entries)
251- → getCoin acc
252- ≡ getCoin (foldl (applyOne _∸_) acc entries) + sum (map proj₂ entries)
241+ → getCoin acc ≡ getCoin (foldl (applyOne _∸_) acc entries) + sum (map proj₂ entries)
253242```
254243
255244<!--
@@ -299,7 +288,7 @@ bound no longer holds.
299288 ξ : stake addr' ∈ dom (acc ˢ) × amt' ≤ (maybe id 0 (lookupᵐ? acc (stake addr')))
300289 ξ = h (there mem)
301290 c'≢c : stake addr' ≢ c
302- c'≢c = ≢-sym (All. lookup c∉xs (∈-map⁺ (stake ∘ proj₁) mem))
291+ c'≢c = ≢-sym (lookup c∉xs (∈-map⁺ (stake ∘ proj₁) mem))
303292 dom' : stake addr' ∈ dom acc'
304293 dom' = dom∪ˡʳ {m = ❴ c , bal ∸ amt ❵} {m' = acc} (proj₁ ξ)
305294 bal' : lookupᵐ? acc' (stake addr') ≡ lookupᵐ? acc (stake addr')
0 commit comments