Beersy
BRC-176

BSV-21 — Validity Proofs

Someone hands you an output claiming to be a token worth a certain amount, but there is no central ledger to look it up in. Tokens can be split and merged from other tokens across many past transactions, so checking one claim can mean checking a whole tangle of prior transactions, not just a single line of history. Without a defined way to package and check that history, a receiver either has to trust the sender or run a full indexer.

Open Protocol Labs, David Casechanged 1 Sept 20268 min read

Reference for an AI

Everything an assistant needs to answer questions about BRC-176 accurately, including what it depends on.

Summary

Why
A receiver needs a way to check, offline and without a global indexer, that a token output someone handed them is genuinely backed by valid and authority all the way back to its deploy.
What
BRC-176 defines an offline-verifiable proof packet, built as a plus a , that shows one or more BSV-21 token outputs are valid back to their deploy.
How
A verifier loads the BEEF packet, checks it is structurally valid, then walks backward from each subject through same-token-id inputs, checking conservation or authority at each hop, until every branch reaches a deploy.

What this lets you do

  • Package a proof of one or more BSV-21 outputs as a BEEF bundle
  • Verify a token output's amount and id trace back to a valid deploy
  • Check split and merge histories that form a DAG rather than a line
  • Detect incomplete packets that omit required same-id parent transactions
  • Bind a verify result to a specific named set of subject outpoints

Written by claude-sonnet-5 from the specification text. Where the two differ, the original is correct.

containerrecordfield

This diagram is a placeholder based on the topic. A diagram drawn from this standard has not been generated yet.

The specification

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 . Delivery is transaction-level (BRC-95 encoding) when proving outputs of one transaction, or BRC-158 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 (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→ 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 ( / authority), not sat ordering.
  • Funding inputs are irrelevant. Token-input source bodies are not.

Atomic BEEF is an package: mined ancestors may be reduced to , and transactions outside the subject’s SPV graph are stripped. Those bodies are not extraneous here. This profile keeps them.

Relationship to other documents

ConcernDocument
Token id, ops, per-tx validation (JSON)BRC-161
Same model, binary prefixBRC-162
bsv21 remittance / tagsBRC-163
BEEF / Atomic BEEF / Outpoint BEEF encodingsBRC-62, BRC-95, BRC-158
→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 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 travels: embedded, or supplied by the caller.

EnvelopeWhenScope
Atomic BEEF (0x01010101 + txid)Proving one or more outputs of one transactionEmbedded: outputs of that transaction id
Outpoint BEEF (0x16a7beef + txid + vout)Proving one outpointEmbedded: that outpoint
Bare BEEFOtherwise (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 ( contribute their own outpoint as id).

The BEEF MUST contain a full transaction body for:

  1. Every transaction that has a subject outpoint.
  2. Recursively, the source transaction of every BSV-21 input of a token id in T spent 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:

EncodingFieldsidamt
JSON deploy+mintthis outpointdeploy-valueamt
JSON deploy+auththis outpointdeploy-auth0
JSON mintidfieldmintamt
JSON transferidfieldtransferamt
JSON burnidfieldburnamt
JSON authidfieldauth0
Binary, empty id, amt > 0this outpointdeploy-valueamount
Binary, empty id, amt = 0this outpointdeploy-auth0
Binary, id, amt = 036-byte idthat outpointauth0
Binary, id, amt > 036-byte idthat outpointvalueamount

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 ), 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 / sym string 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.

Implementations

References

  1. BRC-161 — BSV-21 Fungible Tokens (JSON / Legacy)
  2. BRC-162 — BSV-21 Fungible Tokens (Binary)
  3. BRC-163 — BSV-21 Basket Profile
  4. BRC-62 — BEEF
  5. BRC-95 — Atomic BEEF
  6. BRC-96 — BEEF V2 Txid Only
  7. BRC-158 — Outpoint BEEF
  8. BRC-159 — 1Sat origin tracking
  9. BRC-150 — 1Sat provenance remittance
Was this helpful?

Search Beersy

Search standards by number, title, author or topic