[Dijkstra] CIP-159-08 phantom asset attack prevention (#1120)#1162
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR adds a CIP-159 “phantom asset” mitigation to the Dijkstra UTXO rule by bounding batch-wide withdrawals using pre-batch account balances, and updates the computational properties to match the new premise.
Changes:
- Add
allWithdrawalsto aggregate withdrawals across a top-level tx and its sub-transactions using additive union (∪⁺). - Introduce
NoPhantomWithdrawalsand add it as a new premise to theUTXOrule (usingAccountBalancesOf Γas the frozen pre-batch snapshot). - Update
Computational-UTXOpremise packaging/unpacking to account for the additional predicate.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/Ledger/Dijkstra/Specification/Transaction.lagda.md | Adds allWithdrawals helper to compute batch-wide withdrawal totals used by the new safety check. |
| src/Ledger/Dijkstra/Specification/Utxo.lagda.md | Defines and documents NoPhantomWithdrawals, and threads it into the UTXO rule as a new premise. |
| src/Ledger/Dijkstra/Specification/Utxo/Properties/Computational.lagda.md | Updates the computational proof plumbing to include the new UTXO premise. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| NoPhantomWithdrawals : Rewards → TopLevelTx → Type | ||
| NoPhantomWithdrawals preBalances txTop = | ||
| ∀[ (addr , amt) ∈ allWithdrawals txTop ˢ ] | ||
| amt ≤ maybe id 0 (lookupᵐ? preBalances (RewardAddress.stake addr)) |
There was a problem hiding this comment.
The expression maybe id 0 (lookupᵐ? ...) encodes an important defaulting policy (missing key ⇒ 0) but is fairly opaque and likely to be repeated (it already appears elsewhere in UTXO for balance-interval checks). Consider introducing a small helper (e.g., lookupᵐ?-default0 / lookup0) to centralize the semantics and improve readability, and then use it here.
| NoPhantomWithdrawals : Rewards → TopLevelTx → Type | |
| NoPhantomWithdrawals preBalances txTop = | |
| ∀[ (addr , amt) ∈ allWithdrawals txTop ˢ ] | |
| amt ≤ maybe id 0 (lookupᵐ? preBalances (RewardAddress.stake addr)) | |
| lookup0 : Rewards → StakeCredential → Coin | |
| lookup0 balances cred = maybe id 0 (lookupᵐ? balances cred) | |
| NoPhantomWithdrawals : Rewards → TopLevelTx → Type | |
| NoPhantomWithdrawals preBalances txTop = | |
| ∀[ (addr , amt) ∈ allWithdrawals txTop ˢ ] | |
| amt ≤ lookup0 preBalances (RewardAddress.stake addr) |
| into account addresses. | ||
|
|
||
| In the preservation-of-value equation, direct deposits appear on the | ||
| *produced* side: value leaves the UTxO and enters account balances. The |
There was a problem hiding this comment.
The text uses “account addresses”, but the formalization below is phrased in terms of reward addresses / stake credentials (e.g., withdrawals keyed by reward addresses and looked up via RewardAddress.stake). To avoid ambiguity for readers, it’d help to align terminology here (e.g., explicitly say “reward accounts (stake credentials)” if that’s what is meant, or define what “account address” refers to in this spec).
| into account addresses. | |
| In the preservation-of-value equation, direct deposits appear on the | |
| *produced* side: value leaves the UTxO and enters account balances. The | |
| into reward accounts (stake credentials). | |
| In the preservation-of-value equation, direct deposits appear on the | |
| *produced* side: value leaves the UTxO and enters reward-account balances. The |
| `balanceIntervals`. Direct deposits represent value that flows from the transaction | ||
| into account addresses. |
There was a problem hiding this comment.
I don't understand what "value that flows from the transaction into account addresses" means
| single batch. If a sub-transaction deposits ADA into an account and a later | ||
| sub-transaction withdraws it, Plutus scripts may be tricked into believing native | ||
| assets were minted. |
There was a problem hiding this comment.
I don't see how this is a problem from the POV of Plutus scripts. Already in nested transactions V4 scripts cannot assume transactions to be balanced since a sub-transaction might look like it pulls ADA out of thin air. I don't understand why in that case is not an issue but with direct deposits and withdrawals it is.
There was a problem hiding this comment.
Instead of explaining this here, let's address this in a more prominent place where we cover not spending outputs created within the batch. And add examples. Make an issue for this.
37f2bf6 to
33b565b
Compare
182388c to
8796ae6
Compare
…nce intervals (#1117) CIP-159 changes the transaction balancing rules and introduces Phase-1 balance interval validation. This commit updates the UTxO transition system accordingly. `Utxo.lagda.md`: + Add accountBalances : Rewards field to UTxOEnv and SubUTxOEnv for pre-batch account balance lookups; + Add HasAccountBalances type class and instances; + Update producedTx to include direct deposit amounts on the produced side of the preservation-of-value equation; + Add direct deposit registration premise to UTXO and SUBUTXO (`dom DirectDepositsOf ⊆ dom AccountBalancesOf`); + Add balance interval validation premise to UTXO and SUBUTXO (∀ (c,interval) ∈ BalanceIntervalsOf, InBalanceInterval using pre-batch account balances). `Utxo/Properties/Computational.lagda.md`: + Update Computational-UTXO for new premise tuple arity (19+h → 21+h) `Ledger.lagda.md`: + Add accountBalances field to SubLedgerEnv; + Populate accountBalances in SUBLEDGER-V, SUBLEDGER-I, LEDGER-V, LEDGER-I using RewardsOf certState₀ (pre-batch balances).
33b565b to
0c5d520
Compare
Add batch-wide withdrawal bound check to prevent phantom asset attacks when nested transactions combine deposits and withdrawals. `Transaction.lagda.md`: + Define allWithdrawals batch aggregation helper (mirrors allDirectDeposits) `Utxo.lagda.md`: + Define NoPhantomWithdrawals predicate using allWithdrawals + Add NoPhantomWithdrawals premise to UTXO rule + Document phantom asset attack and spend-side safety analogy `Utxo/Properties/Computational.lagda.md`: + Update Computational-UTXO for new premise tuple arity (21+h → 22+h)
8796ae6 to
95e47c8
Compare
0c5d520 to
389d947
Compare
Description
This PR closes Issue #1120.
NOTE FOR REVIEWER. This PR depends on #1117 (UTxO rules) for the
accountBalancesfield inUTxOEnv. It does not depend on #1119 at the code level (no imports fromUtxow), though #1119 is a logical prerequisite (version gating ensures CIP-159 fields can't appear in legacy mode).Changes
src/Ledger/Dijkstra/Specification/Transaction.lagda.mdallWithdrawals : TopLevelTx → Withdrawals— aggregates withdrawal amounts across the top-level transaction and all sub-transactions using∪⁺, mirroring the existingallDirectDepositshelper.src/Ledger/Dijkstra/Specification/Utxo.lagda.mdNew predicate.
NoPhantomWithdrawals : Rewards → TopLevelTx → Type— for each(addr, amt)in the batch-wide aggregated withdrawals, checks thatamt ≤ lookupᵐ? preBalances (stake addr).New
UTXOpremise.NoPhantomWithdrawals (AccountBalancesOf Γ) txTopadded as a conjunct in the UTXO rule.New documentation. Explains the phantom asset attack, the mitigation (pre-batch withdrawal bounds), and the analogy with spend-side safety (
utxo₀pattern).src/Ledger/Dijkstra/Specification/Utxo/Properties/Computational.lagda.mdComputational-UTXO. Premise tuple grows from 21+h to 22+h.Design Notes
Batch-wide check in UTXO, not per-sub-tx in SUBUTXO. The phantom asset check inspects all sub-transactions at once via
allWithdrawals.SUBUTXOonly sees one sub-transaction at a time and cannot enforce the batch-wide invariant.allWithdrawalsuses∪⁺(additive union). If the same reward address appears in multiple sub-transactions, amounts are summed. This correctly captures the total withdrawal pressure on each account.Pre-batch balances via
AccountBalancesOf Γ. The check uses the frozen pre-batch snapshot, not the running balance after earlier sub-transactions. This mirrors theutxo₀pattern for spend-side safety.Acceptance criteria checklist
NoPhantomWithdrawalspredicate definedallWithdrawalsbatch aggregation helper definedUTXOruleAccountBalancesOf ΓComputational-UTXOupdated for new premise--safeChecklist
CHANGELOG.md