Skip to content

Commit 222f36c

Browse files
williamdemeoclaude
andcommitted
Restate #1233 as a CERTS invariant after the removal of POST-CERT
Master removed the POST-CERT rule; delegated voting stake is now dropped by GOVCERT at the moment a DRep is deregistered. POST-CERT used to corestrict voteDelegs to the active VDelegs at the end of every batch, which is what made the property an unconditional postcondition of CERTS and its proof a one-liner. That sweep is gone, so an arbitrary input state may already violate the containment and nothing in a CERTS step would repair it. Restate the property as a genuine CERTS invariant and prove it: CERTS-voteDelegsVDeleg : LedgerInvariant _|-_->(_,CERTS)_ voteDelegsVDeleg The containment is now maintained incrementally by the two rules that could break it. DELEG-delegate may install only a VDeleg that is already active for the current delegatees, and GOVCERT-deregdrep, the one rule that shrinks the registered DReps, deletes every delegation to the credential it deregisters in the same step. The remaining rules either leave both fields alone or only grow the DRep domain, for which activeVDelegs is monotone. The proof establishes invariance for DELEG, GOVCERT, CERT and PRE-CERT, then lifts the CERT lemma along RTC-preserves-inv. In Axiom.Set.Map.Extra, drop cores-range-|_ (nothing corestricts any more) and add what the new proof needs: coex-|-in, for reading a pair back out of a complement corestriction, and dom-cup-l-supset-r with its corollaries dom-insert-supset and dom-mapValueRestricted-supset, for the two left-biased unions that refresh and extend the DReps. Each takes the map whose keys are preserved explicitly, since it sits under proj1 and unification cannot recover it. Also restate the prose entry in Conway/Specification/Properties.lagda.md and restore the changelog line dropped in an earlier rebase. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent e4b9434 commit 222f36c

4 files changed

Lines changed: 191 additions & 52 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
## Conway spec
3535

3636
- Remove POST-CERT; delegated stake for voting is now removed by GOVCERT at the moment of deregistration
37+
- State and prove that `CERTS` preserves the property that every `voteDelegs` value is a `VDeleg` of a registered `DRep`, `vDelegAbstain`, or `vDelegNoConfidence` (see #1233)
3738
- Move `txIns ∩ refInputs ≡ ∅` precondition to `allowedLanguages` to allow non-disjoint tx and ref. inputs for Plutus V1-V2
3839
- Require collateral inputs to be present in the UTxO set in the UTXO rule
3940
- State and prove the claim that a voter's (last) vote in a block is applied to the governance action (see #417)

src-lib-exts/abstract-set-theory/Axiom/Set/Map/Extra.agda

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -705,14 +705,38 @@ module _ {A B : Type}
705705

706706
-- Corestriction
707707

708-
-- Corestricting a map to a set `X` of values confines its range to `X`.
709-
-- Indeed, `(m ∣^ X) ˢ` is `m ˢ` filtered by the predicate `(_∈ X) ∘ proj₂`, so every
710-
-- pair surviving the filter carries a proof that its value belongs to `X`.
708+
-- Corestricting a map away from a set `X` of values: `(m ∣^ X ᶜ) ˢ` is `m ˢ` filtered
709+
-- by `(_∉ X) ∘ proj₂`, so every surviving pair is a pair of `m` whose value avoids `X`.
711710
-- The map `m` is an explicit argument because it cannot be recovered by unification:
712-
-- `_∣^_` goes through `⊆-map`, which mentions `m` only as `m ˢ` (i.e., `proj₁ m`).
713-
cores-range-⊆ : {A B : Type} ⦃ _ : DecEq B ⦄ (m : A ⇀ B) {X : ℙ B} range (m ∣^ X) ⊆ X
714-
cores-range-⊆ m b∈range with Equivalence.from ∈-map b∈range
715-
... | _ , refl , ab∈cores = proj₁ (Equivalence.from ∈-filter ab∈cores)
711+
-- `_∣^_ᶜ` goes through `⊆-map`, which mentions `m` only as `m ˢ` (i.e., `proj₁ m`).
712+
coex-∈⁻ : {A B : Type} ⦃ _ : DecEq B ⦄ (m : A ⇀ B) {X : ℙ B} {a : A} {b : B}
713+
(a , b) ∈ (m ∣^ X ᶜ) ˢ b ∉ X × (a , b) ∈ (m ˢ)
714+
coex-∈⁻ m = Equivalence.from ∈-filter
715+
716+
717+
-- A left-biased union never drops a key of its right operand. A key that `m` also
718+
-- binds is taken from `m`; a key that `m` does not bind survives the filter that
719+
-- `_∪ˡ_` applies to `m'`. Either way the key stays in the domain.
720+
dom-∪ˡ-⊇ʳ : {A B : Type} ⦃ _ : DecEq A ⦄ (m m' : A ⇀ B) dom m' ⊆ dom (m ∪ˡ m')
721+
dom-∪ˡ-⊇ʳ m m' {a} a∈dom' with a ∈? dom m
722+
... | yes a∈dom =
723+
Equivalence.to dom∈ ( proj₁ (Equivalence.from dom∈ a∈dom)
724+
, Properties.∈-∪⁺ (inj₁ (proj₂ (Equivalence.from dom∈ a∈dom))) )
725+
... | no a∉dom =
726+
Equivalence.to dom∈ ( proj₁ (Equivalence.from dom∈ a∈dom')
727+
, Properties.∈-∪⁺ (inj₂ (Equivalence.to ∈-filter
728+
(a∉dom , proj₂ (Equivalence.from dom∈ a∈dom')))) )
729+
730+
-- Two consequences, phrased so that the map whose keys are preserved is the explicit
731+
-- argument: it is the one a caller can name, whereas the overriding map generally is
732+
-- not (it sits under `proj₁`, so unification cannot recover it from the goal).
733+
dom-insert-⊇ : {A B : Type} ⦃ _ : DecEq A ⦄ (m : A ⇀ B) {k : A} {v : B}
734+
dom m ⊆ dom (insert m k v)
735+
dom-insert-⊇ m {k} {v} = dom-∪ˡ-⊇ʳ ❴ k , v ❵ m
736+
737+
dom-mapValueRestricted-⊇ : {A B : Type} ⦃ _ : DecEq A ⦄ (m : A ⇀ B) {f : B B} {X : ℙ A}
738+
dom m ⊆ dom (mapValueRestricted f m X)
739+
dom-mapValueRestricted-⊇ m {f} {X} = dom-∪ˡ-⊇ʳ (mapValues f (m ∣ X)) m
716740

717741

718742
-- Map lemmas: lookup after insert

src/Ledger/Conway/Specification/Certs/Properties/VoteDelegsVDeleg.lagda.md

Lines changed: 147 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,17 @@ open import Ledger.Conway.Specification.Certs gs
1818
open import Ledger.Prelude
1919
open import Ledger.Conway.Specification.Gov.Actions gs
2020
21+
open import Data.List.Relation.Unary.Any using (here; there)
22+
2123
private variable
22-
Γ : CertEnv
23-
s s' : CertState
24-
certs : List DCert
24+
Γ : CertEnv
25+
s s' : CertState
26+
stᵈ stᵈ' : DState
27+
certs : List DCert
28+
dCert : DCert
29+
D D' : ℙ Credential
30+
m : VoteDelegs
31+
v : VDeleg
2532
```
2633
-->
2734

@@ -40,63 +47,164 @@ contains a field `dreps`{.AgdaField} whose domain is the set of registered
4047
`vDelegAbstain`{.AgdaInductiveConstructor} and
4148
`vDelegNoConfidence`{.AgdaInductiveConstructor}.
4249

43-
Call a `VDeleg`{.AgdaDatatype} *active* in a state if it is one of those two constants or
44-
if it wraps the `Credential`{.AgdaDatatype} of a `DRep` registered in that state. The
45-
present property asserts that a `CERTS`{.AgdaDatatype} step leaves no other kind of
46-
`VDeleg`{.AgdaDatatype} behind: every value of the `voteDelegs`{.AgdaField} map of the
47-
resulting state is active in that state. Nothing is assumed about the initial state,
48-
because the `POST-CERT`{.AgdaDatatype} rule concluding every `CERTS`{.AgdaDatatype} step
49-
corestricts `voteDelegs`{.AgdaField} to exactly the set of active
50-
`VDelegs`{.AgdaDatatype}.
50+
Call a `VDeleg`{.AgdaDatatype} *active* for a set of credentials if it is one of those
51+
two constants or if it wraps a credential from that set. The present property asserts
52+
that no `CERTS`{.AgdaDatatype} step introduces a vote delegation that is inactive for the
53+
registered `DReps`{.AgdaFunction}: if every value of `voteDelegs`{.AgdaField} is active
54+
before the batch of certificates, then so is every value after it.
55+
56+
The two rules that could break this maintain it themselves, in opposite ways.
57+
`DELEG-delegate`{.AgdaInductiveConstructor} may only install a `VDeleg`{.AgdaDatatype}
58+
that is already active, and `GOVCERT-deregdrep`{.AgdaInductiveConstructor}, which
59+
*shrinks* the set of registered `DReps`{.AgdaFunction}, simultaneously deletes every
60+
delegation to the credential it deregisters.
5161

5262
*Formally*.
5363

5464
```agda
55-
activeVDelegs : CertState → ℙ VDeleg
56-
activeVDelegs s = mapˢ vDelegCredential (dom (DRepsOf s))
65+
activeVDelegs : ℙ Credential → ℙ VDeleg
66+
activeVDelegs D = mapˢ vDelegCredential D
5767
∪ fromList (vDelegNoConfidence ∷ vDelegAbstain ∷ [])
5868
5969
voteDelegsVDeleg : CertState → Type
60-
voteDelegsVDeleg s = range (VoteDelegsOf s) ⊆ activeVDelegs s
70+
voteDelegsVDeleg s = range (VoteDelegsOf s) ⊆ activeVDelegs (dom (DRepsOf s))
6171
62-
CERTS-voteDelegsVDeleg : Γ ⊢ s ⇀⦇ certs ,CERTS⦈ s' → voteDelegsVDeleg s'
72+
CERTS-voteDelegsVDeleg : LedgerInvariant _⊢_⇀⦇_,CERTS⦈_ voteDelegsVDeleg
6373
```
6474

6575
*Proof*.
6676

67-
A `CERTS`{.AgdaDatatype} step consists of a `PRE-CERT`{.AgdaDatatype} step followed by a
68-
trace of `CERT`{.AgdaDatatype} steps that ends with a `POST-CERT`{.AgdaDatatype} step, so
69-
it suffices to establish the property at the end of such a trace. We do so in two
70-
lemmas.
77+
It is convenient to read the property off one entry at a time, so we name the pointwise
78+
form and record that the two forms agree.
79+
80+
```agda
81+
vDelegsIn : ℙ Credential → VoteDelegs → Type
82+
vDelegsIn D m = ∀ {c v} → (c , v) ∈ m → v ∈ activeVDelegs D
83+
84+
⊆⇒vDelegsIn : (m : VoteDelegs) → range m ⊆ activeVDelegs D → vDelegsIn D m
85+
⊆⇒vDelegsIn _ h cv∈ = h (∈-map′ cv∈)
86+
87+
vDelegsIn⇒⊆ : (m : VoteDelegs) → vDelegsIn D m → range m ⊆ activeVDelegs D
88+
vDelegsIn⇒⊆ _ h v∈range with Equivalence.from ∈-map v∈range
89+
... | _ , refl , cv∈ = h cv∈
90+
```
91+
92+
The set of active `VDelegs`{.AgdaDatatype} grows with the set of credentials, and the
93+
two constants are active for every set.
94+
95+
```agda
96+
activeVDelegs-mono : D ⊆ D' → activeVDelegs D ⊆ activeVDelegs D'
97+
activeVDelegs-mono D⊆D' v∈ with Equivalence.from ∈-∪ v∈
98+
... | inj₂ v∈consts = Equivalence.to ∈-∪ (inj₂ v∈consts)
99+
... | inj₁ v∈creds with Equivalence.from ∈-map v∈creds
100+
... | c , refl , c∈D =
101+
Equivalence.to ∈-∪ (inj₁ (Equivalence.to ∈-map (c , refl , D⊆D' c∈D)))
102+
103+
abstain∈active : vDelegAbstain ∈ activeVDelegs D
104+
abstain∈active = Equivalence.to ∈-∪ (inj₂ (Equivalence.to ∈-fromList (there (here refl))))
105+
106+
noConfidence∈active : vDelegNoConfidence ∈ activeVDelegs D
107+
noConfidence∈active = Equivalence.to ∈-∪ (inj₂ (Equivalence.to ∈-fromList (here refl)))
108+
```
109+
110+
**Lemma (`DELEG`{.AgdaDatatype} preserves the property).** The delegatee set is fixed
111+
throughout, so this is a statement about `voteDelegs`{.AgdaField} alone. The premise of
112+
`DELEG-delegate`{.AgdaInductiveConstructor} says precisely that the installed
113+
`VDeleg`{.AgdaDatatype} is active; `DELEG-dereg`{.AgdaInductiveConstructor} only removes
114+
entries, and `DELEG-reg`{.AgdaInductiveConstructor} leaves `voteDelegs`{.AgdaField} alone.
115+
116+
```agda
117+
delegatee∈active :
118+
just v ∈ mapˢ (just ∘ vDelegCredential) D
119+
∪ fromList (nothing ∷ just vDelegAbstain ∷ just vDelegNoConfidence ∷ [])
120+
→ v ∈ activeVDelegs D
121+
delegatee∈active mvd∈ with Equivalence.from ∈-∪ mvd∈
122+
... | inj₁ ∈creds with Equivalence.from ∈-map ∈creds
123+
... | c , refl , c∈D = Equivalence.to ∈-∪ (inj₁ (Equivalence.to ∈-map (c , refl , c∈D)))
124+
delegatee∈active mvd∈ | inj₂ ∈consts with Equivalence.from ∈-fromList ∈consts
125+
... | there (here refl) = abstain∈active
126+
... | there (there (here refl)) = noConfidence∈active
127+
```
128+
129+
```agda
130+
DELEG-vDelegsIn : ∀ {pp : PParams} {pools : Pools}
131+
→ ⟦ pp , pools , D ⟧ ⊢ stᵈ ⇀⦇ dCert ,DELEG⦈ stᵈ'
132+
→ vDelegsIn D (VoteDelegsOf stᵈ) → vDelegsIn D (VoteDelegsOf stᵈ')
133+
DELEG-vDelegsIn (DELEG-delegate {mvd = nothing} _) h = h
134+
DELEG-vDelegsIn (DELEG-delegate {mvd = just _} (_ , _ , mvd∈ , _)) h cv∈
135+
with Properties.∈-∪⁻ cv∈
136+
... | inj₂ cv∈rest = h (proj₂ (Equivalence.from ∈-filter cv∈rest))
137+
... | inj₁ cv∈new =
138+
subst (_∈ activeVDelegs _)
139+
(sym (cong proj₂ (Equivalence.from ∈-singleton cv∈new)))
140+
(delegatee∈active mvd∈)
141+
DELEG-vDelegsIn (DELEG-dereg _) h cv∈ = h (ex-⊆ cv∈)
142+
DELEG-vDelegsIn (DELEG-reg _) h = h
143+
```
71144

72-
**Lemma (`POST-CERT`{.AgdaDatatype} establishes the property).** The single constructor
73-
`CERT-post`{.AgdaInductiveConstructor} reveals the `voteDelegs`{.AgdaField} of its
74-
resulting state to be a corestriction `vd`{.AgdaBound} `∣^`{.AgdaOperator}
75-
`X`{.AgdaBound} of the incoming map `vd`{.AgdaBound}, and the range of a corestricted map
76-
is contained in the corestricting set (`cores-range-⊆`{.AgdaFunction}). The set
77-
`X`{.AgdaBound} is read off the `GState`{.AgdaRecord}, which the rule leaves untouched, so
78-
`X`{.AgdaBound} is `activeVDelegs`{.AgdaFunction} of the resulting state.
145+
**Lemma (`GOVCERT`{.AgdaDatatype} preserves the property).**
146+
`GOVCERT-regdrep`{.AgdaInductiveConstructor} only grows the domain of
147+
`dreps`{.AgdaField}, so `activeVDelegs`{.AgdaFunction} only grows;
148+
`GOVCERT-ccreghot`{.AgdaInductiveConstructor} touches neither field. In the
149+
`GOVCERT-deregdrep`{.AgdaInductiveConstructor} case a value `v`{.AgdaBound} of the
150+
resulting map comes from the incoming map and, by the corestriction, differs from
151+
`vDelegCredential`{.AgdaInductiveConstructor} `c`{.AgdaBound}. If `v`{.AgdaBound} is one
152+
of the two constants it stays active; otherwise `v`{.AgdaBound} is
153+
`vDelegCredential`{.AgdaInductiveConstructor} `c'`{.AgdaBound} for some registered
154+
`c'`{.AgdaBound}, and `c'`{.AgdaBound} ``{.AgdaFunction} `c`{.AgdaBound}, so
155+
`c'`{.AgdaBound} is still registered after the deregistration.
79156

80157
```agda
81-
POST-CERT-voteDelegsVDeleg : Γ ⊢ s ⇀⦇ _ ,POST-CERT⦈ s' → voteDelegsVDeleg s'
82-
POST-CERT-voteDelegsVDeleg (CERT-post {voteDelegs = vd}) = cores-range-⊆ vd
158+
GOVCERT-voteDelegsVDeleg : LedgerInvariant _⊢_⇀⦇_,GOVCERT⦈_ voteDelegsVDeleg
159+
GOVCERT-voteDelegsVDeleg (GOVCERT-regdrep {dReps = dReps} _) h =
160+
activeVDelegs-mono (dom-insert-⊇ dReps) ∘ h
161+
GOVCERT-voteDelegsVDeleg (GOVCERT-ccreghot _) h = h
162+
GOVCERT-voteDelegsVDeleg (GOVCERT-deregdrep {c = c} {dReps = dReps} {vDelegs = vDelegs} _) h =
163+
vDelegsIn⇒⊆ (vDelegs ∣^ ❴ vDelegCredential c ❵ ᶜ) λ cv∈ →
164+
let v∉ , cv∈vd = coex-∈⁻ vDelegs cv∈ in
165+
reinstate v∉ (⊆⇒vDelegsIn vDelegs h cv∈vd)
166+
where
167+
-- A delegation to `c'` survives the deregistration of `c` because `c' ≢ c`: were they
168+
-- equal, `v` would be the very `vDelegCredential c` the corestriction ruled out.
169+
keep : ∀ {v c'} → v ∉ ❴ vDelegCredential c ❵ → v ≡ vDelegCredential c'
170+
→ c' ∈ dom dReps → v ∈ activeVDelegs (dom (dReps ∣ ❴ c ❵ ᶜ))
171+
keep {c' = c'} v∉ v≡ c'∈dom = Equivalence.to ∈-∪ (inj₁ (Equivalence.to ∈-map
172+
( c' , v≡
173+
, ∈-resᶜ-dom⁺ ( (λ c'∈ → v∉ (Equivalence.to ∈-singleton (trans v≡
174+
(cong vDelegCredential (Equivalence.from ∈-singleton c'∈)))))
175+
, Equivalence.from dom∈ c'∈dom ) )))
176+
177+
reinstate : ∀ {v} → v ∉ ❴ vDelegCredential c ❵ → v ∈ activeVDelegs (dom dReps)
178+
→ v ∈ activeVDelegs (dom (dReps ∣ ❴ c ❵ ᶜ))
179+
reinstate v∉ v∈ with Equivalence.from ∈-∪ v∈
180+
... | inj₂ v∈consts = Equivalence.to ∈-∪ (inj₂ v∈consts)
181+
... | inj₁ v∈creds =
182+
let c' , v≡ , c'∈dom = Equivalence.from ∈-map v∈creds in keep v∉ v≡ c'∈dom
83183
```
84184

85-
**Lemma (the property holds at the end of a `CERT`{.AgdaDatatype} trace).** Induct on the
86-
trace. The intermediate `CERT`{.AgdaDatatype} steps are irrelevant: only the final state
87-
is constrained, and it is produced by the `POST-CERT`{.AgdaDatatype} step in the
88-
`run-[]`{.AgdaInductiveConstructor} case.
185+
**Lemma (`CERT`{.AgdaDatatype} and `PRE-CERT`{.AgdaDatatype} preserve the property).**
186+
`CERT-pool`{.AgdaInductiveConstructor} touches neither field, and
187+
`CERT-pre`{.AgdaInductiveConstructor} leaves `voteDelegs`{.AgdaField} alone while
188+
refreshing `dreps`{.AgdaField} with a left-biased union that keeps every key.
89189

90190
```agda
91-
CERT-trace-voteDelegsVDeleg :
92-
RunTraceAndThen _⊢_⇀⦇_,CERT⦈_ _⊢_⇀⦇_,POST-CERT⦈_ Γ s certs s' → voteDelegsVDeleg s'
93-
CERT-trace-voteDelegsVDeleg (run-[] post) = POST-CERT-voteDelegsVDeleg post
94-
CERT-trace-voteDelegsVDeleg (run-∷ _ trace) = CERT-trace-voteDelegsVDeleg trace
191+
CERT-voteDelegsVDeleg : LedgerInvariant _⊢_⇀⦇_,CERT⦈_ voteDelegsVDeleg
192+
CERT-voteDelegsVDeleg (CERT-deleg {stᵈ = stᵈ} {stᵈ' = stᵈ'} deleg) h =
193+
vDelegsIn⇒⊆ (VoteDelegsOf stᵈ')
194+
(DELEG-vDelegsIn deleg (⊆⇒vDelegsIn (VoteDelegsOf stᵈ) h))
195+
CERT-voteDelegsVDeleg (CERT-pool _) h = h
196+
CERT-voteDelegsVDeleg (CERT-vdel govcert) h = GOVCERT-voteDelegsVDeleg govcert h
197+
198+
PRE-CERT-voteDelegsVDeleg : LedgerInvariant _⊢_⇀⦇_,PRE-CERT⦈_ voteDelegsVDeleg
199+
PRE-CERT-voteDelegsVDeleg (CERT-pre {dReps = dReps} _) h =
200+
activeVDelegs-mono (dom-mapValueRestricted-⊇ dReps) ∘ h
95201
```
96202

97-
The theorem follows by inverting the `CERTS`{.AgdaDatatype} step and discarding its
98-
`PRE-CERT`{.AgdaDatatype} component.
203+
A `CERTS`{.AgdaDatatype} step is a `PRE-CERT`{.AgdaDatatype} step followed by a trace of
204+
`CERT`{.AgdaDatatype} steps, so the theorem follows by lifting the two lemmas along the
205+
reflexive-transitive closure.
99206

100207
```agda
101-
CERTS-voteDelegsVDeleg (run (_ , trace)) = CERT-trace-voteDelegsVDeleg trace
208+
CERTS-voteDelegsVDeleg (run (pre , trace)) =
209+
RTC-preserves-inv CERT-voteDelegsVDeleg trace ∘ PRE-CERT-voteDelegsVDeleg pre
102210
```

src/Ledger/Conway/Specification/Properties.lagda.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,19 @@ proposals in `tx`{.AgdaBound}.
162162
+ **Theorem** [Certs-VoteDelegsVDeleg][]. Vote delegations point at registered
163163
`DReps`{.AgdaFunction}.
164164

165-
Let `s`{.AgdaBound}, `s'`{.AgdaBound} be `CertStates`{.AgdaRecord} and
166-
`certs`{.AgdaBound} a list of `DCerts`{.AgdaDatatype} such that `s`{.AgdaBound}
167-
`⇀⦇`{.AgdaDatatype} `certs`{.AgdaBound} `,CERTS⦈`{.AgdaDatatype} `s'`{.AgdaBound}.
168-
Then every value of the `voteDelegs`{.AgdaField} map of `s'`{.AgdaBound} either wraps
169-
the `Credential`{.AgdaDatatype} of a `DRep` registered in `s'`{.AgdaBound}, or is
165+
Say a `CertState`{.AgdaRecord} `s`{.AgdaBound} *delegates actively* when every value
166+
of its `voteDelegs`{.AgdaField} map either wraps the `Credential`{.AgdaDatatype} of a
167+
`DRep` registered in `s`{.AgdaBound}, or is
170168
`vDelegAbstain`{.AgdaInductiveConstructor}, or is
171-
`vDelegNoConfidence`{.AgdaInductiveConstructor}.
169+
`vDelegNoConfidence`{.AgdaInductiveConstructor}. This is an invariant of the
170+
`CERTS`{.AgdaDatatype} rule: if `s`{.AgdaBound} delegates actively and `s`{.AgdaBound}
171+
`⇀⦇`{.AgdaDatatype} `certs`{.AgdaBound} `,CERTS⦈`{.AgdaDatatype} `s'`{.AgdaBound},
172+
then `s'`{.AgdaBound} delegates actively.
173+
174+
Two rules keep it so. `DELEG-delegate`{.AgdaInductiveConstructor} may install only a
175+
delegation that is already active, and
176+
`GOVCERT-deregdrep`{.AgdaInductiveConstructor}, the one rule that unregisters a
177+
`DRep`, deletes every delegation to it in the same step.
172178

173179
---
174180

0 commit comments

Comments
 (0)