Subtree Data Frame Format
The list of transaction hashes making up a subtree also needs to travel over an ordinary byte stream, not just one-to-many delivery. This defines that form and how a receiver takes it in.
Summary
- Why
- A receiver that gets a batch of transaction hashes pushed to it unsolicited needs the batch's identifying merkle root carried in the same delivery, since there is no separate request that already named it.
- What
- BRC-143 defines the byte layout of a pushed subtree frame: a merkle root followed by a count and an ordered list of transaction node hashes.
- How
- A sender writes the 32-byte merkle root, an 8-byte big-endian node count, then that many 32-byte node hashes (with an all-0xFF hash standing in for the coinbase in a block's first subtree); the receiver reads the count, consumes exactly that many hashes, rebuilds the tree through its subtree node API, and checks the…
What this lets you do
- Push a subtree to a peer with the identifying root carried in the same frame
- Verify a received subtree by recomputing its merkle root from the node hashes
- Detect the coinbase slot by value (0xFF x 32) instead of a flag field
- Read a variable-length frame using only the node count, no extra length prefix
- Fetch member transaction bytes separately, since the frame carries hashes only
Written by claude-sonnet-5 from the specification text. Where the two differ, the original is correct.
Reference for an AI
Everything an assistant needs to answer questions about BRC-143 accurately, including what it depends on.
The specification
Abstract
This BRC specifies the push wire form of a subtree — an ordered list of transaction node hashes plus the merkle root that identifies it — for delivery and ingest over a byte stream. A subtree names its member transactions by hash; a receiver reconstructs the subtree and verifies it against the carried root. All frame integers are fixed-width big-endian; there is no length prefix beyond the node count.
Copyright
This BRC is licensed under the Open BSV License.
Motivation
A subtree is a batch of transactions committed by a merkle root, and a block references its subtrees in order. In an announce/pull system a receiver fetches a subtree by its root hash — the root is the request key, carried out-of-band in the request path — and the transferred bytes are a bare list of 32-byte node hashes.
A push delivery has no request path: the subtree arrives unsolicited, so the identifying root must travel in-band. BRC-143 is that frame — the merkle root followed by the ordered node hashes — and nothing a receiver recomputes locally. Per-node fee and size, the aggregate totals, and the conflict set are deliberately omitted: a receiver's transaction-meta store supplies fee and size, and validation does not consume the others on the ingest path, so carrying them would only inflate an already large object.
Specification
Frame layout
All frame integers are big-endian. Node hashes are carried in internal
(chainhash) byte order — not display order.
| Offset | Size | Field | Notes |
|--------|--------|-------------------|--------------------------------------------------------------------------|
| 0 | 32 | SubtreeMerkleRoot | Merkle root of the node hashes: the subtree identity and verify target. |
| 32 | 8 | NodeCount (N) | uint64 BE. Number of node hashes that follow. Delimits the body. |
| 40 | 32 × N | NodeHashes | Ordered node hashes, each 32 bytes. Includes the coinbase placeholder. |
Total header: 40 bytes. NodeCount is the sole delimiter — a reader consumes
the 40-byte header, then exactly NodeCount × 32 bytes.
Coinbase placeholder
A block's first subtree begins with a coinbase placeholder: 32 bytes of
0xFF at NodeHashes[0]. This is not zeros — a zero hash is the merkle
padding value, and placing zeros where the coinbase leaf belongs corrupts the
root. The placeholder is self-describing and detected by value; there is no
flag. A non-first subtree carries no placeholder.
Reconstruction and verification
A receiver rebuilds the subtree through the subtree node API, then verifies:
- For each hash in order: if it equals
0xFF × 32, insert it as the coinbase node; otherwise insert it as an ordinary node, sourcing fee and size from the local transaction-meta store (or zero when only the root is being checked). - Assert the recomputed merkle root equals
SubtreeMerkleRoot.
Tree height is derived as ceil(log2(N)); the merkle computation pads to the
next power of two with zero-hash leaves, and an odd node is hashed with itself.
No height or capacity field is carried — it would fight that derivation.
Do not byte-splice. This wire frame is not a storage serialization; a receiver rebuilds via the node API and never copies these bytes into a deserializer.
Member transactions
A subtree references its members by hash and does not carry them. A receiver validating the subtree also needs the member transaction bytes (for script/UTXO/parent checks); those are supplied from the receiver's own transaction feed or fetched out of band. BRC-143 proper is hashes-only.
Identity
Subtree identity is SubtreeMerkleRoot — the merkle root over NodeHashes, with
the coinbase placeholder participating as the first leaf. A receiver recomputes
it from the hashes to verify the frame; there is no separate identifier field.
References
- BRC-144: Block Frame Format — carries the ordered subtree list (the block↔subtree association) that this frame deliberately omits
- BRC-12: Raw Transaction Format / BRC-30: Transaction Extended Format (EF) — the member transactions a subtree references by hash
Constants Reference
| Name | Value | Description |
|---|---|---|
| SubtreeHeaderSize | 40 | Root + NodeCount, in bytes |
| CoinbasePlaceholder | 0xFF × 32 | Node-hash value marking the coinbase slot |