BSV-21 — Validity Proofs
A wallet receives a token payment and has no way to check, on its own, that the tokens are real and not just a made-up balance in some JSON. Unlike simple payments where a chain of ownership is a straight line, token transfers can split and merge across many transactions, so proving one output is valid means gathering and checking a whole web of related transactions. This defines exactly which transactions must be bundled together and how to walk them to confirm the tokens are genuine, without needing to trust a central index.
Reference for an AI
Everything an assistant needs to answer questions about BRC-176 accurately, including what it depends on.
Summary
- Why
- A token amount shown in a received output is just a claim unless the receiver can independently trace it back through every merge and split to a genuine deploy, and there was no defined bundle or walk for doing that offline.
- What
- BRC-176 defines an offline-checkable bundle of transactions, built on BEEF, that proves specific outputs are genuine BSV-21 or 1Color token outputs of a stated id and amount.
- How
- A verifier loads the BEEF bundle, confirms it is structurally valid Bitcoin data, then recursively walks each subject output's same-token inputs backward until every path reaches a deploy, checking at each step that outputs don't exceed proven inputs.
What this lets you do
- Verify a tip of BSV-21 or 1Color tokens offline, without a global indexer
- Walk backward through a token's transfer history to check conservation at each hop
- Confirm a mint is backed by a proven authority chain to its deploy
- Detect incomplete or tampered proof bundles and fail closed
- Decode either BSV-21 JSON or 1Color binary token outputs into one normalized record
Written by claude-sonnet-5 from the specification text. Where the two differ, the original is correct.
The specification
Abstract
This BRC defines an offline-verifiable packet that proves one or more transaction outputs are valid BSV-21 (BRC-161) or 1Color (BRC-162) token outputs.
The packet is a BRC-62 BEEF. Delivery is transaction-level (BRC-233 Subject BEEF) when proving outputs of one transaction, or BRC-158 Outpoint BEEF when proving a single outpoint. Completeness and validation are this profile — not Atomic BEEF’s SPV-minimal ancestor set.
BRC-161 / BRC-162 say when an output of one transaction is valid given valid inputs. This document says which transactions must be in the bag, and how to check those inputs by walking the bag.
Motivation
BSV-21 balances live in UTXOs. A receiver who is handed a tip (or several tips in one transaction) needs to check that each claimed output is really a token of the named id and amount — without a global indexer.
1Sat solves the analogous problem with a linear tip→origin walk (BRC-159) and a remittance bag (BRC-150). BSV-21 is different:
- Identity is the deploy outpoint, not a sat origin.
- Splits and merges make the ancestry a DAG, not a path.
- Validity is per token id per transaction (conservation / authority), not sat ordering.
- Funding inputs are irrelevant. Token-input source bodies are not.
Atomic BEEF is an SPV package: mined ancestors may be reduced to merkle proofs, and transactions outside the subject’s SPV graph are stripped. Those bodies are not extraneous here. This profile keeps them.
Relationship to other documents
| Concern | Document |
|---|---|
| Token id, ops, per-tx validation (JSON) | BRC-161 |
| Same model, binary prefix (1Color) | BRC-162 |
Basket bsv21 remittance / tags | BRC-163 |
| BEEF / Subject BEEF / Outpoint BEEF encodings | BRC-62, BRC-233, BRC-158 |
| 1Sat tip→origin (not used for BSV-21 validity) | BRC-159 |
Specification
What is proven
Given a packet and a set of subject outpoints, a successful verify means: each subject output is a valid BSV-21 or 1Color output under BRC-161 / BRC-162, of the token id and amount carried on that output, funded by a valid deploy (and, for mints, a valid authority) present in the packet.
The packet proves local lineage of the subjects. It does not prove that the issuer minted nothing else, and it does not prove UTXO set membership.
A declared BRC-207 attested-admission profile may stop a separate token-validity walk at accepted authority attestations. That evidence is not a BRC-176 packet and MUST NOT be reported as one. This specification's completeness requirements remain unchanged; neither an attestation nor token lineage replaces ordinary Bitcoin/SPV evidence.
Packet
There is one packet: a BRC-62 BEEF (V1 or V2) plus a scope — the set of subject outpoints the packet is verified against. The envelope only determines how the scope travels: embedded, or supplied by the caller.
| Envelope | When | Scope |
|---|---|---|
Subject BEEF (0x5709beef + txid) | Proving one or more outputs of one transaction | Embedded: outputs of that transaction id |
Outpoint BEEF (0x16a7beef + txid + vout) | Proving one outpoint | Embedded: that outpoint |
| Bare BEEF | Otherwise (several transactions’ outputs in one bag) | Out-of-band: caller names the subject outpoints |
The verify result is always bound to the scope, however it arrived. A caller MUST prove exactly the outpoints it intends to accept; a proof obtained for one scope says nothing about any other outpoint.
A BRC-176 packet is never BRC-95 Atomic BEEF and MUST NOT be validated, stripped, or round-tripped as ordinary BRC-95 Atomic BEEF: BRC-95 body omission would remove the token-parent bodies this profile requires, and BRC-95 ancestor rejection would reject packets that are complete under this profile.
BRC-96 txid-only entries are not a transaction body. Missing body → that hop cannot be proven.
Completeness
Let S be the subject outpoints. Let T be the set of token ids those outputs name (deploy outputs contribute their own outpoint as id).
The BEEF MUST contain a full transaction body for:
- Every transaction that has a subject outpoint.
- Recursively, the source transaction of every BSV-21 input of a token id in
Tspent in those transactions, until each lineage reaches its deploy.
It need not contain source transactions of non-token inputs (funding, fees, other protocols).
Extra transactions MAY be present. They MUST NOT cause the packet to fail.
To prove even a single transfer output of a transaction, the bag MUST still include every same-id token input of that transaction — conservation is per token id, not per vout.
Decode
Each output is decoded as BSV-21 JSON (BRC-161) or 1Color binary (BRC-162). Either encoding is an output of the same token model. Unrecognized scripts are ignored.
An output that parses under both encodings resolves to binary — BRC-162 recognition rules.
Normalized record:
| Encoding | Fields | id | Kind | amt |
|---|---|---|---|---|
JSON deploy+mint | — | this outpoint | deploy-value | amt |
JSON deploy+auth | — | this outpoint | deploy-auth | 0 |
JSON mint | id | field | mint | amt |
JSON transfer | id | field | transfer | amt |
JSON burn | id | field | burn | amt |
JSON auth | id | field | auth | 0 |
| Binary, empty id, amt > 0 | — | this outpoint | deploy-value | amount |
| Binary, empty id, amt = 0 | — | this outpoint | deploy-auth | 0 |
| Binary, id, amt = 0 | 36-byte id | that outpoint | auth | 0 |
| Binary, id, amt > 0 | 36-byte id | that outpoint | value | amount |
Token ids compare in underscore form txid_vout (BRC-161).
Validation
Parse the envelope; load the BEEF. Structural failure → unproven.
Before any token rule is evaluated, the BEEF itself MUST be valid under BRC-62 / BRC-233 / BRC-158: every txid MUST match its raw transaction body, every included merkle path MUST verify against its trusted header (for unmined transactions, against the caller's trust anchor), and unmined descendants MUST chain to proven ancestors. BSV-21 admission is evaluated only over that verified transaction set. A self-consistent synthetic DAG is not a proof.
Implementations MAY impose resource limits (transaction count, packet size, recursion depth). A tripped limit MUST be reported as unproven, failing closed for the affected subjects.
prove(outpoint) means: this output is a valid BSV-21 token of the id and amt on its script. That is conservation (or a mint from proven authority) at this hop, with every value/auth input itself proven, back to a deploy. A deploy in the bag is not enough.
Walk backward. A same-id token input counts only if prove succeeds on it. Deploy → genesis, done.
Missing source body → that input counts as nothing. A missing body can only reduce I, never increase O: omitting a same-id parent may produce false negatives (unproven), but can never produce a false positive. Missing non-token/funding parents are irrelevant to BSV-21 lineage. Bitcoin transactions cannot cycle; if prove re-enters an outpoint still on the stack, the bag is malformed → unproven.
Conservation is per token id in the transaction: proving one transfer uses the other same-id output amounts on that tx. It does not prove those siblings unless they are subjects too.
function prove(outpoint, beef):
if outpoint is already on the prove stack: fail // malformed BEEF
tx = load outpoint's transaction from beef
if missing: fail
token = decodeBsv21(tx.outputs[outpoint.vout])
if token is none: fail
if token is a deploy: return ok // genesis
I = 0
hasAuth = false
for each input of tx:
sourceTx = load input's source transaction from beef
if missing: continue
spent = decodeBsv21(sourceTx.outputs[input.vout])
if spent is none: continue
if spent is a deploy:
spent.id = input's source outpoint
if spent.id != token.id: continue // other token / funding
if prove(input's source outpoint, beef) fails: continue
if spent is authority: hasAuth = true
else if spent is burn: continue
else: I += spent.amt
// O = same-id amounts this tx moves (not mints). Valid only if I >= O.
O = 0
for each output of tx:
created = decodeBsv21(output)
if created is none: continue
if created is a deploy:
created.id = this output's outpoint
if created.id != token.id: continue
if created is transfer or burn:
O += created.amt
else if created is binary value and not hasAuth:
O += created.amt
if token is transfer or burn or (binary value and not hasAuth):
if I < O: fail
return ok
if token is mint or auth or (binary value and hasAuth):
if not hasAuth: fail
return ok
fail
On failure, the named subjects are unproven. On success, what is proven for each subject is the record (outpoint, id, amt) decoded from its own script in the verified packet — not any externally supplied claim about that outpoint.
What this document does not cover
- Unspent status / UTXO set membership
- Global circulating supply
- Basket remittance, tags, display fields — BRC-163
- 1Sat sat ordering — BRC-159
Examples
Fixed-supply transfer (one parent):
deploy+mint 10_000 at D
→ transfer 4_900 + transfer 5_100
Packet for the 4_900 tip: subject tx + deploy tx. I = 10_000 >= 10_000, so that transfer is valid (the 5_100 sibling is used only for the conservation check).
Merge — both branches required:
D 10_000
→ A 6_000 + B 4_000
→ M 10_000 (spends A and B)
Packet for M MUST include M, A’s tx, B’s tx, and D. Omitting B: I = 6_000, O = 10_000 → unproven. Spend-as-input of A alone is not enough.
Mint:
deploy+auth at D
→ mint 1_000_000 + auth (spends D)
Packet for the mint: subject tx + D. hasAuth from a proven deploy-auth. Value inputs are not required.
Over-transfer (fail):
In: 500
Out: 300 + 400 transfer
I < O → neither transfer is valid.
Security considerations
- Claims — A sender
id/amt/symstring is not this packet. - Issuer mint policy — Authority mints are valid if the auth lineage reaches
deploy+auth. This does not cap global supply. - Unspent status — Not in scope. Same UTXO checks as any BSV payment.