Skip to content

Byte-verify ownership transfers and subasset reissuances; guard the borrow - #217

Closed
droplister wants to merge 1 commit into
harden/byte-equality-mpmafrom
harden/issuance-transfer-and-borrow-guards
Closed

Byte-verify ownership transfers and subasset reissuances; guard the borrow#217
droplister wants to merge 1 commit into
harden/byte-equality-mpmafrom
harden/issuance-transfer-and-borrow-guards

Conversation

@droplister

Copy link
Copy Markdown
Member

Stacked on #216. Closes two of the three deliberate byte-equality gaps, and tightens a borrow shipped in #215.

Ownership transfers now byte-verify

The decline was over-conservative. A transfer's message is byte-for-byte a reissuance — core carries the new owner only in an output paying transfer_destination (issuance.py returns (source, [(destination, None)], data)). A new compose-oracle case proves it against a live node: core composes identical data with and without the transfer. So the message packs, equality covers every field, and the output policy — which already picks up transfer_destination via addressesNamedIn and requires every output to be explained — is what pins the ownership output. Same division of labor dispense relies on.

Subasset reissuances now byte-verify, under a borrowed id

Updating a subasset's description composes the standard layout under the ledger-resolved numeric id, which the request cannot determine. It's now borrowed from the composed message, like the initial issuance's random draw. The honest accounting: the field fallback is equally blind to a substituted id (it compares the longname the user typed, which the response echoes faithfully), so borrowing makes nothing worse and buys byte equality on the description, quantity, flags and MIME type.

The borrow refuses irreversible operations — on both subasset paths

A substituted id can land the operation on a different numeric asset the user owns (matching divisibility required; the range guard limits targets to numeric assets). For issue-and-describe the wrong outcome is recoverable — destroy the supply, rewrite the description. For lock, reset, and transfer it is permanent against the wrong asset, so the borrow refuses those and they stay on the field fallback: no blinder, but byte equality does not sign off on them.

This retroactively tightens #215's initial-issuance borrow, whose safety argument under-weighted the lock case. The full analysis is an addendum in the (deliberately uncommitted) audit log; the vector's real fix — an independent ledger view or local composition — is folded into the local-compose decision.

Verification

  • tsc --noEmit clean; all 681 counterparty tests pass with both oracles live against api.counterparty.io:4000, including the new transfer oracle case.
  • Fixtures regenerated with core's exact arithmetic for the unlocked subasset shape.
  • E2E locally, one file at a time: issuance/index.spec.ts (10/10), transfer-ownership.spec.ts (3/3), update-description.spec.ts (3/3).

https://claude.ai/code/session_01CcjnCrgosSeshymXLBxdGj

…orrow

An ownership transfer's message is byte-for-byte a reissuance: core
carries the new owner only in an output paying transfer_destination,
so declining to pack it verified nothing the standard layout could
not already express. The message now packs -- a live oracle case
confirms core composes identical data with and without the transfer
-- and the output policy, which requires every output to be an
address the request names, is what pins the ownership output. The
same division of labor dispense already relies on.

Subasset reissuances (update description, add supply) now byte-verify
too, in the standard layout under an asset id borrowed from the
composed message, since the longname resolves to its numeric id
through the ledger. The field fallback is equally blind to a
substituted id -- it compares the longname the user typed, which the
response echoes faithfully -- so borrowing makes nothing worse and
buys byte equality on everything the user authored.

What the borrow now refuses, on both subasset paths: lock, reset and
transfer. A substituted id can land the operation on a different
numeric asset the user owns, and while issue-and-describe is
recoverable there (destroy the supply, rewrite the description),
those three are permanent against the wrong asset. This tightens the
initial-issuance borrow shipped in the previous release, whose safety
argument under-weighted the lock case; the analysis is recorded in
the audit log, and the vector's real fix -- an independent ledger
view or local composition -- is folded into the local-compose
decision.

Fixtures regenerated for the unlocked subasset shape; the compose
oracle gains the transfer case. All 681 counterparty tests pass with
the oracles live; the issuance, transfer-ownership and
update-description E2E specs pass locally, one file at a time.

Claude-Session: https://claude.ai/code/session_01CcjnCrgosSeshymXLBxdGj
@droplister
droplister deleted the branch harden/byte-equality-mpma August 3, 2026 20:42
@droplister droplister closed this Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant