You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: SPEC.md
+39-8Lines changed: 39 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -215,11 +215,19 @@ derived from the coins being spent, and this specification does not claim otherw
215
215
mismatch. **The caller MUST construct the authorizer that matches the coins it is spending.**
216
216
-**Vault protection is a property of the AUTHORIZER, not of the funds.** An implementation MUST NOT
217
217
present these rules to a user as protection that attaches to money held in a vault.
218
-
-**Only HINTED value is visible to the amount bounds** (§6.4). An un-hinted output is change and is
219
-
excluded from `SpendSummary::recipients`, so no limit here weighs it; §5.2's change-ownership check
220
-
bounds where such value may go (a puzzle hash under the same seed) but does not subject it to any
221
-
policy. A future tier-to-coin linkage is tracked separately; until it exists, the guarantees above are
222
-
the whole of what this layer provides.
218
+
-**Value is counted by DESTINATION, never by hint status** (§6.4). An output is weighed unless it pays
219
+
the exact puzzle hash of a coin the spend is itself spending — the one case where value demonstrably
220
+
has not moved. Hint status is never consulted, so an author cannot move value out of the charged total
221
+
or out of the vault destination rule by omitting a memo.
222
+
-**This deliberately OVERCOUNTS change sent to a fresh derivation.** This layer holds no key, so it
223
+
cannot distinguish a fresh derivation of the user's own wallet from a stranger's address. The only
224
+
rule that could is "any owned derivation is change", and that is precisely an attacker's exfiltration
225
+
target — an address the signer's `0..address_gap` window accepts. So a legitimate send whose change
226
+
goes to a fresh address is counted in full and escalates to the human instead of auto-sending. This is
227
+
intended, specified behaviour and MUST NOT be "fixed" by widening what counts as change: overcounting
228
+
asks a person, undercounting signs.
229
+
- A future tier-to-coin linkage is tracked separately; until it exists, the guarantees above are the
230
+
whole of what this layer provides.
223
231
224
232
Two limitations previously recorded here — that enforcement was opt-in, and that the authorization was
225
233
not bound to the signed bytes — are no longer true. §6.2 states what replaced them.
@@ -259,6 +267,15 @@ it of a host.** The enforcement is structural:
259
267
| Not loggable | Neither type implements `Debug`. |
260
268
| Not expiring | Deliberate. The rolling cap is charged when the approval is minted, so an aged approval cannot re-spend an allowance. A user re-locking DURING an async ceremony is a lock question, answered by building the signer after the ceremony from the live residency — not by dating the approval. |
261
269
270
+
**Safety comes from the approval OWNING its spends. A same-bytes comparison is NOT a guard, and this
271
+
crate MUST NOT claim one as a custody property.** The approval carries a `TransactionSummary` because
272
+
the dependency's signer takes it as a required parameter, and that signer compares it against its own
273
+
re-derivation. Both sides descend from the same `coin_spends` the approval owns, so the comparison can
274
+
only ever agree: it is structurally incapable of detecting anything, and its value here is zero. It is
275
+
passed to satisfy a signature and is explicitly NON-LOAD-BEARING. A genuine second opinion would require
276
+
an INDEPENDENT derivation — which is the two-answers-can-disagree shape §6.2 exists to remove, so no such
277
+
comparison SHOULD be reintroduced as a substitute for ownership.
278
+
262
279
`SpendSummary::new` and `WalletOps::summarize` remain public and confer NO authority: since no API
263
280
accepts a `&SpendSummary` for a custody decision, a hand-built summary is a display value with nowhere
264
281
to go.
@@ -380,9 +397,23 @@ small allowance while the spend moves an enormous amount) and MUST NOT panic (`f
380
397
`SpendSummary::native_total_mojos()` SATURATES at `u64::MAX` so no caller can observe a wrapped figure;
381
398
`checked_native_total_mojos()` is the form every custody decision MUST use.
382
399
383
-
`SpendSummary` accounts for HINTED outputs plus the fee, so no bound here can see an un-hinted output.
384
-
The complementary invariant — un-hinted value MUST NOT leave the wallet — is enforced by the money
385
-
signer's change-ownership check (§5.2). Both layers are required.
400
+
**The charged total MUST be computed by destination, not by hint status.**`SpendSummary` accounts for
401
+
every created output — hinted or not — EXCEPT one paying the exact puzzle hash of a coin the spend is
402
+
itself spending, plus the fee. Equivalently, and this is why the rule is stated this way: it charges
403
+
`xch_in` minus change returning to a spent input's own puzzle hash. It never enumerates a subset of
404
+
outputs chosen by the author, so it cannot be made incomplete by omitting a memo. §6.1.1 records the
405
+
deliberate overcount this implies. The vault destination rule (§6.1) reads the same list, so an
406
+
un-hinted vault outflow is subject to the hot-wallet-only rule exactly as a hinted one is. The money
407
+
signer's change-ownership check (§5.2) remains a required second layer.
408
+
409
+
**The dependency's OUTPUT-amount accumulation is unchecked.**`dig-wallet-backend` 0.16 sums output
410
+
amounts from CLVM conditions with an unchecked `+=` (`client/verify.rs:131`, `:165`); the checked input
411
+
sum above bounds inputs only and does not bound these. `checked_native_total_mojos()` is therefore
412
+
load-bearing, not defensive: it is the only place an unsummable output total becomes
413
+
`PolicyIndeterminate` rather than a clamped figure judged against a limit. A test reaching it cannot yet
414
+
be written — the same input panics inside the dependency at `verify.rs:165` before this guard evaluates
415
+
(tracked as dig-wallet-backend #1708) — and a mock bypassing the dependency would prove nothing about
0 commit comments