diff --git a/README.md b/README.md index 0ab622f..4629924 100644 --- a/README.md +++ b/README.md @@ -204,6 +204,7 @@ BRC | Standard 138 | [Single-Use Signed Proofs for Request Authentication](./peer-to-peer/0138.md) 139 | [Multicast Shard Manifest Announcement Protocol](./transactions/0139.md) 140 | [Threshold Key Sharing and Backup via Shamir's Secret Sharing Scheme](./key-derivation/0140.md) +143 | [Subtree Data Frame Format](./transactions/0143.md) ## License diff --git a/SUMMARY.md b/SUMMARY.md index a6f5c88..5511b89 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -72,6 +72,7 @@ * [Multicast Anchor Transaction Frame Format](./transactions/0134.md) * [Multicast Block Header Format](./transactions/0135.md) * [Multicast Shard Manifest Announcement Protocol](./transactions/0139.md) +* [Subtree Data Frame Format](./transactions/0143.md) ## Scripts diff --git a/transactions/0143.md b/transactions/0143.md new file mode 100644 index 0000000..aabfdf6 --- /dev/null +++ b/transactions/0143.md @@ -0,0 +1,103 @@ +# BRC-143: Subtree Data Frame Format + +Jeff Harris (jeff@lightweb.net) + +## 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. + +```text +| 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](./0144.md) — carries the ordered subtree list + (the block↔subtree association) that this frame deliberately omits +- [BRC-12: Raw Transaction Format](./0012.md) / + [BRC-30: Transaction Extended Format (EF)](./0030.md) — 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 | diff --git a/transactions/README.md b/transactions/README.md index de07c8e..9b30559 100644 --- a/transactions/README.md +++ b/transactions/README.md @@ -34,3 +34,4 @@ BRC | Standard 134 | [Multicast Anchor Transaction Frame Format](./0134.md) 135 | [Multicast Block Header Format](./0135.md) 139 | [Multicast Shard Manifest Announcement Protocol](./0139.md) +143 | [Subtree Data Frame Format](./0143.md)