Abstract
This BRC defines an offline-verifiable packet that proves one or more transaction outputs are valid BRC-161 / BRC-162 BSV-21 tokens.
The packet is a BRC-62 BEEF. Delivery is transaction-level (BRC-95 Atomic BEEF encoding) 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 | BRC-162 |
Basket bsv21 remittance / tags |
BRC-163 |
| BEEF / Atomic BEEF / Outpoint BEEF encodings | BRC-62, BRC-95, 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 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.
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 |
|---|---|---|
Atomic BEEF (0x01010101 + 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.
Atomic BEEF encoding is reused — only its byte framing. These BRC-95 rules do not apply to a BRC-176 packet:
- Fail if a transaction is not an SPV ancestor of the subject.
- Omit a transaction body once a merkle proof exists.
A BRC-176 packet 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 binary (BRC-162). Either encoding is a BSV-21 output. 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-95 / 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.