Skip to content

Commit cf8d588

Browse files
committed
LEDGER-V: forbid direct deposits to credentials deregistered in same batch
`applyDirectDeposits` uses `∪⁺` (union-with-addition) on the `rewards` map, so a credential not present in `RewardsOf certState₂` is added as a new entry rather than rejected. The pre-existing SUBUTXO/UTXO premise `dom (DirectDepositsOf tx) ⊆ dom (AccountBalancesOf Γ)` only checks against the pre-batch balances `RewardsOf certState₀`, so it does not catch the case where a credential is registered pre-batch, deregistered during `CERTS`, and direct-deposited to in the same batch. Without the new premise, the resulting `certStateFinal` is internally inconsistent: the credential has a positive `rewards` entry but no corresponding `voteDelegs`, `stakeDelegs`, or `deposits` entry — effectively re- registering the credential without a key deposit. Add the phase-1 premise dom (allDirectDeposits tx) ⊆ dom (RewardsOf certState₂) to `LEDGER-V` to rule this out. Filtering `allDirectDeposits tx` by `dom (RewardsOf certState₂)` would be an alternative, but it would silently destroy user funds when a deposit and a deregistration race in the same batch. The premise is symmetric to (and complements) the existing pre-batch SUBUTXO/UTXO check. Update `Ledger/Properties/Computational.lagda.md` for the new premise, and pass `UTxOStateOf s₁` explicitly to `computeUtxow` in the LEDGER valid branch (the previous `_` resolved by chained unification through the LEDGER-V constructor, which was fragile and obscured intent). TODO: fix `Ledger/Properties/Computational.lagda.md`; it's choking on the new premise.
1 parent 8216e6c commit cf8d588

2 files changed

Lines changed: 22 additions & 6 deletions

File tree

src/Ledger/Dijkstra/Specification/Ledger.lagda.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,15 @@ the post-deposit `DRep` state. (In practice, `applyDirectDeposits`{.AgdaFunctio
329329
only modifies rewards, so `rmOrphanDRepVotes` would produce the same result either
330330
way, but using `certStateFinal` is semantically correct.)
331331

332+
**Deposits target post-batch registered accounts**. `SUBUTXO`{.AgdaDatatype}
333+
and `UTXO`{.AgdaDatatype} check that direct-deposit targets are registered in the
334+
*pre-batch* balances, but they cannot account for deregistrations that occur during
335+
the batch. Therefore, we add a new premise, `dom (allDirectDeposits tx) ⊆ dom (RewardsOf
336+
certState₂)`, to check that `applyDirectDeposits` does not silently re-introduce a
337+
deregistered credential into the `rewards` map without re-registering it via
338+
`voteDelegs`, `stakeDelegs`, or `deposits`, as that would result in an inconsistent
339+
`DState`. The premise rules this out at phase 1.
340+
332341
### Design Rationale: Batch-wide Direct Deposit Application
333342

334343
A natural alternative to applying direct deposits batch-wide (as above) is to
@@ -425,6 +434,7 @@ data _⊢_⇀⦇_,LEDGER⦈_ : LedgerEnv → LedgerState → TopLevelTx → Ledg
425434
∙ IsValidFlagOf tx ≡ true
426435
∙ ⟦ slot , ppolicy , pp , enactState , treasury , utxo₀ , IsValidFlagOf tx , allScripts , RewardsOf certState₀ ⟧ ⊢ ⟦ utxoState₀ , govState₀ , certState₀ ⟧ ⇀⦇ SubTransactionsOf tx ,SUBLEDGERS⦈ ⟦ utxoState₁ , govState₁ , certState₁ ⟧
427436
∙ ⟦ epoch slot , pp , ListOfGovVotesOf tx , WithdrawalsOf tx , allColdCreds govState₁ enactState ⟧ ⊢ certState₁ ⇀⦇ DCertsOf tx ,CERTS⦈ certState₂
437+
∙ dom (allDirectDeposits tx) ⊆ dom (RewardsOf certState₂)
428438
∙ ⟦ TxIdOf tx , epoch slot , pp , ppolicy , enactState , certState₂ , dom (RewardsOf certState₂) ⟧ ⊢ govState₁ ⇀⦇ GovProposals+Votes tx ,GOVS⦈ govState₂
429439
∙ ⟦ slot , pp , treasury , utxo₀ , depositsChange , allScripts , RewardsOf certState₀ ⟧ ⊢ utxoState₁ ⇀⦇ tx ,UTXOW⦈ utxoState₂
430440
────────────────────────────────

src/Ledger/Dijkstra/Specification/Ledger/Properties/Computational.lagda.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -224,10 +224,12 @@ instance
224224
(certSt₂ , certStep) → computeGov (govΓOf Γ txTop certSt₂) (GovStateOf s₁) (GovProposals+Votes txTop) >>= λ where
225225
(govSt₂ , govStep) →
226226
-- UTXOW must run from the post-SUBLEDGERS UTxOState ((UTxOState s₁))
227-
computeUtxow (utxoΓ-valid Γ s txTop (CertStateOf s₁) certSt₂) _ txTop >>= λ where
228-
(utxoSt₂ , utxoStep) →
229-
success ( ⟦ utxoSt₂ , rmOrphanDRepVotes (certStateWithDDeps txTop certSt₂) govSt₂ , certStateWithDDeps txTop certSt₂ ⟧ˡ
230-
, LEDGER-V (isV , subStep , certStep , govStep , utxoStep))
227+
computeUtxow (utxoΓ-valid Γ s txTop (CertStateOf s₁) certSt₂) (UTxOStateOf s₁) txTop >>= λ where
228+
(utxoSt₂ , utxoStep) → case ¿ dom (allDirectDeposits txTop) ⊆ dom (RewardsOf certSt₂) ¿ of λ where
229+
(yes h) →
230+
success ( ⟦ utxoSt₂ , rmOrphanDRepVotes (certStateWithDDeps txTop certSt₂) govSt₂ , certStateWithDDeps txTop certSt₂ ⟧ˡ
231+
, LEDGER-V (isV , subStep , certStep , h , govStep , utxoStep) )
232+
(no _) → failure "direct deposit target was deregistered during this batch"
231233
232234
(no ¬isV) → case computeSubledgers (subΓOf Γ s txTop) s (SubTransactionsOf txTop) of λ where
233235
(failure e) → failure e
@@ -252,7 +254,7 @@ instance
252254
```agda
253255
completeness Γ s txTop s'
254256
(LEDGER-V {certState₁ = certSt₁} {certSt₂} {utxoState₁ = utxoSt₁} {govSt₁} {govSt₂} {utxoSt₂}
255-
(isV , subStep , certStep , govStep , utxoStep))
257+
(isV , subStep , certStep , h , govStep , utxoStep))
256258
with IsValidFlagOf txTop ≟ true
257259
... | no ¬isV = contradiction isV ¬isV
258260
... | yes refl
@@ -272,7 +274,11 @@ instance
272274
with computeUtxow (utxoΓ-valid Γ s txTop certSt₁ certSt₂) utxoSt₁ txTop
273275
| complete {STS = _⊢_⇀⦇_,UTXOW⦈_}
274276
(utxoΓ-valid Γ s txTop certSt₁ certSt₂) utxoSt₁ txTop utxoSt₂ utxoStep
275-
... | success (utxoSt₂ , _) | refl = refl
277+
... | success (utxoSt₂ , _) | refl
278+
with ¿ dom (allDirectDeposits txTop) ⊆ dom (RewardsOf certSt₂) ¿
279+
... | yes _ = refl
280+
... | no ¬h = ⊥-elim (¬h h)
281+
276282
277283
completeness Γ s txTop s' (LEDGER-I {utxoState₁ = utxoSt₁} (isI , subStep , utxoStep))
278284
with IsValidFlagOf txTop ≟ true

0 commit comments

Comments
 (0)