diff --git a/CLAUDE.md b/CLAUDE.md index 41ec514..012ede6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -62,7 +62,8 @@ In practice this means: touching semantics references its spec section. The spec states behavior only, and stays complete enough on its own to predict every vector's outcome. Rationale, oracle provenance, and decision - records live in `docs/` (for the VM, `docs/vm-record.md`), with + records live in `docs/` (for the VM, `docs/vm-record.md`, for the + commitment scheme, `docs/commitment-record.md`), with no exception: a curation-note carve-out was ratified 2026-07-29 and reversed 2026-08-08 (decisions 5 and 17 in `docs/condition-record.md`). diff --git a/docs/README.md b/docs/README.md index d228009..f06d734 100644 --- a/docs/README.md +++ b/docs/README.md @@ -14,6 +14,10 @@ behind spec/CONDITIONS.md and spec/VALIDATION.md, the divergence-from-Chia table, reference provenance, decision record, and the novel-layer register. Cited by CLAUDE.md ground rule 3. +- [commitment-record.md](commitment-record.md): the record behind + spec/SPEC.md, the leaf version and commitment scheme, its + reference provenance, the measurements it rests on, and the + decision record. - [opcode-comparison.md](opcode-comparison.md): informative side-by-side of the CLVM, bllsh, and BitLisp operator sets. - [objection-register.md](objection-register.md): every objection on diff --git a/docs/commitment-record.md b/docs/commitment-record.md new file mode 100644 index 0000000..7b72ccf --- /dev/null +++ b/docs/commitment-record.md @@ -0,0 +1,322 @@ +# Commitment record + +The rationale, reference provenance, and decision record for the +commitment scheme (`spec/SPEC.md`): the leaf version, what a leaf +commits to, the witness structure, and the validation pipeline. The +spec states behavior only. This record says why, what the evidence +was, and what the scheme leaves to Phase 4. It is the counterpart of +the VM record (`docs/vm-record.md`) and the condition record +(`docs/condition-record.md`) for the layer that joins the VM and the +validator to base consensus. + +## 1. Reference provenance + +- **BIP341.** The leaf version derivation, the annex rule, the + control block, the TapLeaf and TapBranch folds, and the tweak + check are BIP341's, restated in the spec so a reader predicts a + vector without leaving the tree. The leaf version pool is BIP341's + footnote on leaf versions, read 2026-09-06: a leaf version is + even, never `0x50`, and the values recommended for use are the + even bytes from `0xc0` to `0xfe` plus `0x66`, `0x7e`, `0x80`, + `0x84`, `0x96`, `0x98`, `0xba`, `0xbc`, and `0xbe`, chosen so that + neither the version nor the version with its parity bit set is a + byte that can begin a valid P2WPKH pubkey or a valid P2WSH + script, which lets a script-path spend be recognized without the + spent output. +- **BIP342.** The sigops budget, 50 plus the serialized size of + the input's witness in bytes with its compact-size prefix, + decremented by 50 per signature check, the precedent for a + per-input budget derived from the input's own witness. Also the + initial-stack bounds tapscript keeps, 520 bytes per element and + 1,000 elements, which the scheme does not. +- **BIP341 wallet test vectors**, vendored under + `vectors/upstream/bip341/`. Their script-tree cases carry leaf + hashes and merkle roots for leaf versions 192 and 250, an + independent oracle for the tagged leaf and branch construction + under a leaf version other than tapscript's. +- **Known leaf version claims**, checked 2026-09-06: tapscript is + `0xc0` (BIP342). Elements assigns `0xc4` to its tapscript and + `0xbe` to Simplicity (`src/script/interpreter.h` at commit + `c7e856fab1b0c4d37005e25c0940184d812a26a0`). The project's + synthesis of the 2022 to 2024 public threads (git-ignored + `references/discussions/`) records the introspection Lisp's + author floating `0xc2` for bll, the message itself not pinned: + neither delving 636 nor delving 682 names a byte, and the + vendored bllsh test framework uses tapscript's `0xc0`. No + registry exists. +- **Chia puzzle hashes.** A Chia coin commits to its puzzle by tree + hash, and a puzzle recomputes its own and its successors' puzzle + hashes in-program from curried values, the pattern + `puzzles/lib/curry-hash.blib` reproduces. CHIP-0049 moves Chia's + last serialization-bound identity, the block generator's, to the + tree hash as well. + +## 2. Measurements + +Program bytes as compiled on 2026-09-06 from the sources in +`puzzles/`, the evidence the design decisions below cite. Witness +bytes weigh one unit each, four to a vbyte. + +| artifact | bytes | +| --- | --- | +| vault, curried instance | 2,562 | +| vault, uncurried | 2,346 | +| vault with a single path kept, trigger | 1,508 | +| vault with a single path kept, recovery | 1,190 | +| vault with a single path kept, follower | 1,066 | +| vault with a single path kept, leader | 1,342 | +| `curry-hash.blib` alone, seven values | 499 | +| follower path, root supplied in the solution and asserted, guards kept | 440 | +| follower path, no guards, no reconstruction | 121 | + +Two facts fall out. Splitting the vault into per-path leaves alone +roughly halves the reveal, because every path that reconstructs its +own root carries the 499-byte library beside the domain guards and +the seven curried values. Supplying a program's own root in the +solution and asserting it removes the library from every path that +does not build a successor, which for the vault is every path but +the trigger. + +## 3. Design decision record + +1. **The leaf version byte.** RATIFIED (decision by Evan, + 2026-09-06): `0xd0`, PROVISIONAL until deployment. The unit plan + proposed `0xc2`, the lowest compliant value above tapscript, and + Evan asked whether Liquid or bll had taken it. They had, in + effect: `0xc2` is the byte the thread synthesis records as + floated for bll and `0xc4` is Elements' tapscript, so the run + above `0xc0` is spoken for and a + sequential pick invites collision with whichever of them deploys + first. `0xd0` is in BIP341's recommended pool, clear of every + known claim, and visibly distant from the run. The byte is the + one constant in the scheme that a signet or Inquisition + deployment can reassign without touching anything else, which is + why it carries the PROVISIONAL marker and nothing else in the + spec depends on its value. + +2. **The leaf commits to the tree hash.** RATIFIED (decision by + Evan, 2026-09-06, via the approved unit plan). The leaf script is + the program's 32-byte tree hash and the serialized program is a + witness element the validator checks against it. The + alternative, the serialized program as the leaf script, saves 33 + witness bytes, the leaf script element and its length prefix, + and one tree hash per spend, and was declined because it prices + covenant recursion out: the vault's trigger path must compute + the triggered coin's root, which under a bytes-committed leaf is + a tagged hash over the successor's serialization. No VM operator + serializes a node, and a program serializing its successor + in-language would carry the serializer's code and pay its cost + on every spend. Under a tree-hash commitment the successor's + identity is `curried-tree-hash` over values the program holds, + then two tagged hashes with `sha256`. This is the Chia identity model, + corroborated by CHIP-0049 spending a hard fork to remove the one + serialization-bound identity Chia had left. D5's one-spelling + rule stays the wire rule: the tree hash is over the node, and + the node has one serialization. + +3. **Domain separation is stated and pinned.** RATIFIED (decision + by Evan, 2026-09-06, via the approved unit plan). Objection O17 + asked that a hash tree living beside taproot commitments hash + distinctly from the TapLeaf and TapBranch tags. Spec section 2.4 + answers twice. Structurally, a tree hash never enters the script + tree as a node: it is the leaf script, wrapped under TapLeaf + with the leaf version, so no tree hash is ever a merkle node. + Then by preimage, over every tag a BitLisp validator hashes + under, BIP340's challenge tag included: a tree hash's preimage + begins with `0x01` or `0x02` and a tagged hash's with the tag + digest's first byte, none of which is `0x01` or `0x02`. The + table is exhaustive over those tags and gains a row with any new + one, the check that keeps the argument true. TapSighash is base + consensus's key-path digest, computed by no BitLisp rule, and + its first byte is `0xf4` regardless. The vector pinning the + table lands with the reference witness layer. + +4. **The witness is four elements, and an annex only under a self + assert.** RATIFIED (decision by Evan, 2026-09-06, revising the + approved unit plan at the PR's review). Solution, program, leaf + script, control block, in BIP341's order. A fifth element or a + missing one is `bad_witness`. The unit plan rejected the annex + outright, on an asymmetry with the solution: a program can read + any solution byte and so commit to it, while no operator reads + the annex, so nothing could commit to one and an ignored annex + would be bytes any relay peer could attach to a signed input to + change its wtxid. Review priced what rejection costs: a soft + fork only tightens, so a later annex use for BitLisp inputs + would need a new leaf version, where tapscript's + commit-and-reserve idiom keeps it under one. Binding an annex + hash into the signature digests, tapscript's shape, was weighed + and declined because it protects signed spends only, and this + vocabulary is built for keyless paths. The annex is instead + admitted exactly when the condition list carries + `ASSERT_MY_ANNEX` over its BIP341 `sha_annex` digest, the + condition-record's decision 30 and divergence C25: keyless + paths are protected by default, a program that wants an annex + commits to it, and the door stays open under this leaf version. + Tapscript also bounds each initial stack element at 520 bytes + and the stack at 1,000 elements. The scheme keeps neither as + such: a program element cannot fit in 520 bytes, the element + count is fixed at four, and decision 12 sets the bound the + scheme does keep. + +5. **Surplus solution data is tolerated, at the element boundary + it is not.** RATIFIED (decision by Evan, 2026-09-06, via the + approved unit plan), closing entry 9 of the VM record and + objection O14. Both sides as recorded there: the deviation would + reject solution bytes evaluation never touched, closing a + wtxid-malleability vector authors cannot reliably close alone, + at the cost of defining "touched", which entangles the + evaluation-order question of entry 8. The default keeps CLVM's + tolerance. Bitcoin's own precedent decided the boundary: + tapscript fails a spend unless exactly one element remains, so + a third party can add no element, and a witness element the + script drops unread is tolerated because the author wrote the + drop. The scheme matches that boundary exactly, the element + count strict. Inside the element the defaults differ and this + record says so: tapscript is strict unless a script opts out + with a drop, while CLVM's destructuring ignores a solution tail + unless a program opts in with a shape check. The standard-layer + obligation is that opt-in: templates check the shape of the + solution they consume, and the vault does at its re-pin. + Addendum (decision by Evan, 2026-09-06, at the PR's review): + the compiler emits that shape check by default for every + `program` form's parameter list, so strictness becomes the + default for every compiled program at a few bytes each with no + new consensus surface. A compiler behavior change, not a + language surface, landing with unit 9. A seal does not close + this: seals commit to the transaction's outputs, non-witness + data, and read no witness byte. + +6. **The budget derives from witness weight.** RATIFIED (decision + by Evan, 2026-09-06, via the approved unit plan). Objection O21 + holds both sides: a declared budget makes cost knowable before + deserialization and reads as an explicit fee commitment, a + derived budget adds no witness element and no contested + namespace. Derived was taken: the per-input budget is a function + of the input's own witness weight, fixed in COSTS.md section 9 + at Phase 4, and no other input's witness moves it, which keeps + the per-input independence the condition layer's composition + guarantee relies on. BIP342's sigops budget is the precedent, + 50 plus the input's serialized witness size in bytes, so the + shape has deployed history under taproot itself. Section 9 + decides the two parameters that precedent settles for tapscript, + whether length prefixes count and whether a constant offset + applies. Deserialization and the leaf check are uncharged + because the weight mapping already prices every witness byte + and both are linear in those bytes. + +7. **Per-path leaves are BIP341's, not the scheme's.** RATIFIED + (decision by Evan, 2026-09-06, via the approved unit plan). Once + a leaf commits to one program, a puzzle with several spend paths + is several curried programs in several leaves of one tree, and a + spend reveals the executed path plus 32 bytes of control block + per tree level. The scheme adds no mechanism: spec section 2.3 + states that a tree holds any number of BitLisp leaves beside + leaves of other versions, and the puzzle discipline follows. The + measured saving on the vault is 840 to 1,280 bytes per spend + before the discipline in decision 8 is applied. The alternative + a Chia reader raises is program-level Merkleization: one leaf + committing to a root over sub-programs, the executed + sub-program and its path proof arriving in the solution. It + saves the same bytes and keeps one tree hash as the puzzle's + identity. Declined: the taproot path costs the same 32 bytes + per level and base consensus verifies it at no VM cost, where + an in-program proof carries a verifier in every leaf and pays a + `sha256` per level, and `ASSERT_MY_TAPTREE` already binds the + whole tree at one condition. A puzzle that needs one identity + for its tree has it: the merkle root. + +8. **Self-identity is guess-and-assert, successors are + reconstructed.** RATIFIED (decision by Evan, 2026-09-06, via the + approved unit plan) as the standard-layer rule, applied to the + vault at unit 9 with the bytes reported. A program that names its + own root takes it from the solution and asserts it with + `ASSERT_MY_TAPTREE`, which proves the coin's actual root equals + the supplied one at 32 solution bytes and no library code. A + program that builds another program's root, the trigger building + the triggered coin's, reconstructs it from curried values, the + only place `curry-hash.blib` and the tagged leaf and branch + hashing are needed. A leaf that never reconstructs anything can + have its leaf hash curried into its siblings as a constant: the + triggered coin's recovery leaf does not depend on the target, so + the vault's trigger can carry that leaf hash as a value and + reconstruct only the withdrawal leaf. The other side, kept for + the singleton: reconstruction proves the tree is exactly this + program under the nothing-up-my-sleeve key and nothing else, + which an owner-funded coin does not need (the wallet checks the + curry before funding, the posture `docs/puzzles/vault.md` already + records) but a coin third parties rely on may. The singleton's + posture is re-decided at its re-pin. + +9. **Shared library code is revealed per spend.** RATIFIED + (decision by Evan, 2026-09-06, via the approved unit plan). The + only way v0 consensus could commit library code once is a + validator-known library, the standard-layer shorthand + experiment already parked in Phase 4, and decision 8 removes + most of the need. The tagged leaf and branch hashing a successor + builder needs is a small library file beside `curry-hash.blib`, + its tag digests compile-time constants as the curry library's + are. That file is a new library surface under the language + freeze, admitted as the scheme's own consequence and recorded + with this unit in the execution plan. + +10. **The singleton's constant scriptPubKey stands.** RATIFIED + (decision by Evan, 2026-09-06, via the approved unit plan). The + plan asked the scheme to answer the singleton's workaround + first. It cannot: the obstacle is the tweak, not the leaf. + State placed in any leaf moves the root and the scriptPubKey + with it, and no operator recognizes a parent's tweaked key, so + the parent's scriptPubKey stays unrecognizable however the + leaves are arranged. The workaround stands, the singleton + changes only in how its leaf hashes, and the revisit of the + tweak operator (condition-record decision 3) stays at the + Phase 4 gate. + +11. **The whole architecture document is written, not two + sections.** RATIFIED (decision by Evan, 2026-09-06, via the + approved unit plan). The unit's charter named sections 2 and 3. + Sections 1 and 4 were written with them because section 4 is + the enumeration of failure modes over stages already stated in + VALIDATION.md plus the two the scheme adds, `bad_witness` and + `leaf_mismatch`, and a stub status line in a normative document + is the placeholder the quality mandate forbids. + +12. **Every spender-chosen element is bounded at 10,000 bytes.** + RATIFIED (decision by Evan, 2026-09-06, at the PR's review, + revising the approved unit plan). The solution, the program, + and the annex are each at most `MAX_WITNESS_ELEMENT_SIZE = + 10,000` bytes. The plan set no bound, on the argument that the + derived budget and the block weight limit price every byte + and deserialization is linear. Review asked for a limit as + the deserialization-side counterpart of tapscript's 520-byte + element bound, a constant a reviewer can check rather than an + argument. The number is Bitcoin's own legacy script cap, + already reused by the condition layer as the scriptPubKey + operand bound, so the scheme adds no new magnitude. It binds + the singleton: its solution carries two full transaction + serializations, so a lineage whose parent or grandparent + transaction is large cannot be proven, a limit the owner + controls and the singleton doc records. Both node elements + and the annex share the bound so the rule is one sentence. The + leaf script's width and the control block's length were + already fixed. + +## 4. Carried to Phase 4 + +- The weight mapping (COSTS.md section 9): the budget function and + the per-byte witness charge. Nothing about fees or the gate can be + said before it exists. +- The standard-layer shorthand experiment: whether a validator-known + library is worth its consensus surface, measured after the + benchmark puzzles are re-pinned under decision 8. +- The tweak operator revisit, condition-record decision 3, at the + asset token study. +- Relay policy for the leaf version: today's policy discourages + unknown leaf versions, a deployment question for Phase 6. +- The commitment-hash utility: a command printing a leaf hash, a + merkle root, and a scriptPubKey from program sources, lands with + the reference witness layer now that the scheme fixes its output. +- Vectors owed to the reference witness layer: every stage 1 and 2 + failure mode including the element bound at and above 10,000 + bytes, the digest-domain table, the two programs spec section + 3.3 names, the atom `1` and nil, and the annex rule in both + directions with the ASSERT_MY_ANNEX cases. diff --git a/docs/condition-comparison.md b/docs/condition-comparison.md index 747656c..dcc434a 100644 --- a/docs/condition-comparison.md +++ b/docs/condition-comparison.md @@ -102,6 +102,7 @@ delegate to. | own amount | `ASSERT_MY_AMOUNT` 73 | `ASSERT_MY_AMOUNT` `0x33`, normative | | own taproot components | absent | removed (decision 29): `ASSERT_MY_TAPTREE` `0x38` subsumes the derivation assert, `0x37` now a visible gap | | own taptree | absent | `ASSERT_MY_TAPTREE` `0x38`, normative, reads the control block's internal key and merkle root at the generic cost (decision 28) | +| own annex | absent | `ASSERT_MY_ANNEX` `0x39`, normative, reads the annex's BIP341 sha_annex digest at the generic cost, the annex admitted only under it (decision 30) | | own birth time or height | `ASSERT_MY_BIRTH_SECONDS` 74, `ASSERT_MY_BIRTH_HEIGHT` 75 | declined, a chain read outside the transaction view (C13) | | ephemerality | `ASSERT_EPHEMERAL` 76, the coin was created in the same block it is spent | declined, structurally inexpressible within one transaction (C14) | diff --git a/docs/condition-record.md b/docs/condition-record.md index af74c71..4f30a9f 100644 --- a/docs/condition-record.md +++ b/docs/condition-record.md @@ -39,6 +39,7 @@ Section 4 registers the rules that have no external reference at all. | C22 | signature-condition cost | 1,200,000 (AGG_SIG_COST) per occurrence in every regime, pricing one BLS pairing toward the bundle aggregate | 1,300,000 (CONDITION_SIG_ASSERT_COST), equal to the VM's SECP_VERIFY_COST, PROVISIONAL | Both layers of BitLisp price the same BIP340 verification, so the condition constant ties to the operator constant rather than to Chia's BLS figure: one Phase 4 measurement settles both, and a program can never buy the same verification cheaper in one layer than the other. Chia's magnitude corroborates the range, their pairing being work of the same order. Ratified 2026-08-09, decision 25. | `conditions/costs.json` signature cases | | C23 | execution-identity addressing | none: puzzle hash is both the coin's script commitment and the executing program, one field carries both meanings | the specifier table composes the executing leaf's tapleaf hash (bit 3) and the spending path's merkle root (bit 4) onto the prevout rows, commitment values 0 to 31, the mode packed as assurer times 32 plus requirer, both fields validator-filled from the control block. The pair itself is renamed ASSURE and REQUIRE (decision 27) | Taproot splits Chia's one identity into two, and the C9 mapping carried only the script commitment, so program-to-program trust had no faithful addressing field. Internal-key addressing declined as attacker-satisfiable (no possession proof in BIP341). Grafted-leaf and moved-root caveats recorded in decision 26 and in rule 3's author guidance. Ratified 2026-08-18, decision 26, landed 2026-08-20. | `validation/messages.json` identity cases, `conditions/messages.json` composed-mode cases | | C24 | the cheap identity assert | none: ASSERT_MY_PUZZLEHASH reads the one identity field, and no derive-versus-read split exists because a puzzle hash is a plain tree hash | ASSERT_MY_TAPTREE, a self assert over the control block's internal key and merkle root at the generic cost, the transaction view's identity widened to a triple. ASSERT_MY_TAPROOT, the derivation assert it subsumes, removed once the puzzle rework landed (decision 29) | Taproot's identity is a tweak preimage the control block reveals and base consensus authenticates, so the assert reads it at 200 where the derivation assert re-derives it at a point multiplication, the largest single cost in both landed puzzles. Internal key read by self asserts only: decision 26's attacker-satisfiable objection concerns a counterpart's key, not the spending input's own. Ratified 2026-08-22, decision 28. | `validation/self-asserts.json` taptree cases, `conditions/self-asserts.json` and `conditions/costs.json` taptree cases | +| C25 | the annex assert | none: Chia has no annex | ASSERT_MY_ANNEX, a self assert over the BIP341 `sha_annex` digest of the spending input's annex at the generic cost, the annex admitted on a BitLisp spend only under it | Bitcoin's annex is a witness element no operator reads and no BitLisp signature digest commits to, so an ignored annex is third-party malleability on every spend, and a rejected one closes the upgrade path under this leaf version, since a soft fork cannot loosen. Admitting it only under a self assert commits the spender to it, keeps keyless paths safe by default, and leaves the door open. Ratified 2026-09-06, decision 30. | owed to the reference witness layer: `validation/self-asserts.json` and `conditions/self-asserts.json` annex cases | ## 2. Reference provenance @@ -1609,6 +1610,41 @@ Section 4 registers the rules that have no external reference at all. with no intermediate assert. CREATE_OUTPUT_TAPROOT and the derivation machinery are untouched. +30. **ASSERT_MY_ANNEX, the annex under a self assert.** RATIFIED + (decision by Evan, 2026-09-06, at the review of the + commitment-scheme PR, revising the approved unit plan). A + seventh self assert at 0x39, `(0x39 annex_hash)`, over the + BIP341 `sha_annex` digest of the spending input's annex, the + transaction view's BitLisp input gaining an optional + `annexHash`. An annex is admitted exactly when the list + carries the assert (`unasserted_annex` otherwise), and the + assert is unsatisfied without an annex or with another's hash. + Three shapes were weighed: + - Reject every annex, the unit plan's choice. Closes + malleability on every spend including keyless ones, but a + soft fork can only tighten, so any later annex use for + BitLisp inputs would need a new leaf version. + - Bind an annex hash into every signature-assert digest, + tapscript's own commit-and-reserve idiom. Keeps the door + open but protects signed spends only: a stranger can still + attach an annex to a keyless recovery, a consolidation, or + a singleton spend sealed through its inner program, and it + re-pins every signature vector. + - Admit the annex only under a self assert. Keyless paths are + protected by default because they carry no assert, a + program that wants an annex commits to it in one condition, + and a future soft fork can give annex contents meaning to + programs that opt in, under this leaf version. + The third was taken. Cost: one vocabulary entry for a feature + no puzzle uses yet, accepted because the alternative is a + leaf version. The hash mirrors BIP341's `sha_annex`, length + prefix and `0x50` included, so any taproot tooling computes + the same value. 0x39 follows the taptree assert, 0x34 to 0x37 + staying the visible gap. Error `unsatisfied_annex_assert` for + the condition and `unasserted_annex` for the witness rule, + each its own vector, both owed to the reference witness layer + with the implementation. + ## 4. Novel-layer register The validation rules have no external reference: no deployed system diff --git a/docs/glossary.md b/docs/glossary.md index 6b8292e..584d701 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -29,6 +29,8 @@ A term enters this table in the same PR that introduces it. | ASSERT_MY_SCRIPTPUBKEY | asserts the spent output's scriptPubKey | ASSERT_MY_PUZZLEHASH | raw script bytes per divergence C1, empty script allowed as prevout content | | ASSERT_MY_AMOUNT | asserts the spent output's amount | ASSERT_MY_AMOUNT | numeric equality in satoshis | | ASSERT_MY_TAPTREE | asserts the internal key and merkle root the control block carries | ASSERT_MY_PUZZLEHASH is the nearest, reading the one identity field | proves the spent output is the taproot output of its operands by reading the identity base consensus authenticated, no derivation, the self-propagation covenant primitive beside CREATE_OUTPUT_TAPROOT (condition-record decisions 28 and 29, divergence C24) | +| ASSERT_MY_ANNEX | asserts the BIP341 sha_annex digest of the input's own annex | none: Chia has no annex | the annex is admitted on a BitLisp spend only under this assert, so it is committed data the spender chose (spec/SPEC.md section 3.4, condition-record decision 30, divergence C25), glossed 2026-09-06 | +| annex | BIP341 term, the optional last witness element beginning `0x50`, reserved for future use | none | admitted on a BitLisp spend only under ASSERT_MY_ANNEX and bounded at 10,000 bytes like the solution and program elements (spec/SPEC.md sections 3.1 and 3.4), glossed 2026-09-06 | | satisfier | compare miniscript satisfactions, which are spender-side | none | the transaction resource assigned to a claim, one per claim. A miniscript satisfaction is what the spender gives a script, a satisfier is what the transaction gives a claim | | composition guarantee | batching and coinjoin practice, no consensus equivalent | spend bundle aggregation, offers | two valid transactions with disjoint outpoints, same-typed locktimes, and no seal condition concatenate into a valid transaction (VALIDATION.md preamble, condition-record decisions 14, 15, and 24) | | spend | input plus its witness | coin spend | one input's evaluation and conditions | @@ -37,6 +39,9 @@ A term enters this table in the same PR that introduces it. | ASSURE, REQUIRE | no direct equivalent | SEND_MESSAGE, RECEIVE_MESSAGE (CHIP-0025) | landed 2026-08-07 as SEND_MESSAGE and RECEIVE_MESSAGE, opcodes numerically Chia's, transaction-scoped counted balance (condition-record decision 16), renamed 2026-08-20 (decision 27): the pair reads as what each condition demands rather than as a transport metaphor | | assurer half, requirer half | no direct equivalent | the mode's sender and receiver bit fields | the two commitment values a mode packs, assurer times 32 plus requirer since the execution-identity widening (condition-record decision 26) | | execution identity | tapleaf hash, merkle root, and internal key of the script-path spend | puzzle hash (one field carries both meanings) | the triple validator-filled from the control block: the tapleaf hash and merkle root are specifier fields addressing the executing program and its tree, the internal key is read by self asserts only (condition-record decisions 26 to 28, divergences C23 and C24) | +| leaf version | BIP341 term, the control block's first byte with its low bit cleared | none: a coin's puzzle is not versioned | `BITLISP_LEAF_VERSION = 0xd0`, provisional until deployment, the byte that makes a script-path spend a BitLisp spend (spec/SPEC.md section 2.1), glossed 2026-09-06 with the commitment scheme | +| leaf script | BIP341 term, the script a leaf commits to and a spend reveals | puzzle hash, as the value a coin commits to | the 32-byte tree hash of the committed program, never executed, the program itself a separate witness element the validator checks against it (spec/SPEC.md section 2.2), glossed 2026-09-06 | +| per-path leaf | one tapleaf per spending condition, the taproot idiom | none: a puzzle's paths share one puzzle hash | a spend path written as its own curried program in its own leaf, so a spend reveals the executed path and 32 bytes of control block per tree level (spec/SPEC.md section 2.3, commitment-record decisions 7 and 8), glossed 2026-09-06 | | ANNOUNCE, ASSERT_ANNOUNCEMENT | no direct equivalent | the four announcement conditions (coin and puzzle, create and assert) | landed 2026-08-07, the unaddressed pair, namespace first-class, announcer precision chosen by the assert (condition-record decisions 10 and 16) | | message record | no direct equivalent | send and receive balancing in chia_rs | the third condition sort beside claims and asserts, a weighted ledger entry (+1 or -1) whose record must net to zero (VALIDATION.md rule 3, condition-record decision 16) | | RESERVE_FEE | no direct opcode, the fee itself is implicit (inputs minus outputs) | RESERVE_FEE 52 | landed 2026-08-09 at 0x50, demands the transaction's fee reach the summed reservations (VALIDATION.md rule 7, condition-record decision 21) | diff --git a/docs/lang/curry.md b/docs/lang/curry.md index 9b6ffab..e8fd552 100644 --- a/docs/lang/curry.md +++ b/docs/lang/curry.md @@ -98,9 +98,10 @@ to the inner program and every fixed value at once, which is what makes currying the specialization step: template plus parameters becomes one committed identity. -What a scriptPubKey commits to on chain is decided by the -commitment scheme and measured in Phase 4. The tree hash is the -program-level identity those commitments build on. +What a scriptPubKey commits to on chain is the commitment scheme +of `spec/SPEC.md`: a leaf whose script is the program's tree hash, +the curried tree hash for a curried program. The tree hash is the +program-level identity that commitment builds on. ## The command surfaces diff --git a/docs/objection-register.md b/docs/objection-register.md index 54c9828..f03ef00 100644 --- a/docs/objection-register.md +++ b/docs/objection-register.md @@ -64,14 +64,14 @@ vector, or an open entry. | O11 | CLVM serialization is non-canonical (several encodings of one value) and verbose (100 kB of block CLVM gzips to 60 kB). | Towns, bitcoin-dev (2022), delving 682 (2024) | Canonicality is answered: non-minimal length prefixes, trailing bytes, and back-references are `bad_encoding`, one accepted spelling per program. Density is not: BitLisp keeps CLVM's wire format so the serializer stays inside the diffable intersection, and the witness-size question is obligation 3, measured against the 526 vb gate in Phase 4. A bespoke format is the introspection side's answer and adds a format to review, with its own `rd` and `wr` still marked broken in the 2024 artifact. | PARTIAL, canonicality ANSWERED, density OPEN to Phase 4 | D5, `spec/VM.md` section 2, `vectors/vm/serialize.json`, evaluation doc section 5 obligation 3 | | O12 | CLVM costs total allocation, never live memory. A recursive doubling program builds a 200 MB live string inside the mempool cost limit, where Script holds live memory near 500 kB. Fixing it means costing memory, which fixes a consensus-preferred evaluation order. | Towns, bitcoin-dev (2022), delving 682 and 1224 (2024) | Open. BitLisp inherits the total-allocation model and states no point-in-time memory bound. Both sides are recorded: keep the model and prove the reachable peak acceptable under the Phase 4 budget mapping, or adopt a peak bound and pin evaluation order as observable behavior. The measurement that sizes the reachable peak is a Phase 4 bullet, and the 200 MB construction is a mandatory adversarial vector class for whichever side wins. | OPEN, Phase 4 | `docs/vm-record.md` entry 8, execution plan Phase 4 | | O13 | Evaluation order is consensus surface. Eager and lazy differ in memory profile, the cost result "must not be changed as a result of any optimisations", and the introspection prototype flipped order twice. Mandate lazy evaluation as spec. | Towns, delving 1224 (2024). ZmnSCPxj, bitcoin-dev (2022) | The order is stated and pinned: arguments evaluate right to left in the CLVM stack-machine order, `i` evaluates all three arguments with no lazy branch, the rightmost failing argument's error wins, and cost accrues in evaluation order against the budget. Laziness is an authoring-layer idiom (`if` compiles to the apply-over-quoted-branches shape), the same split the introspection side settled on. What the order implies for memory is O12. | ANSWERED for order, the memory consequence OPEN under O12 | `spec/VM.md` section 3.2, `vectors/vm/dispatch.json`, `docs/lang/language.md` `if` | -| O14 | Chialisp tolerates surplus solution arguments. In Bitcoin that is third-party wtxid malleability: a relay peer mutates unconsumed witness bytes and produces a conflicting wtxid at a lower effective feerate. | Towns, delving 636 (2024) | Open, with the serialization half closed. D5 gives each program one accepted spelling. The semantic half (solution bytes that deserialize canonically and are never consumed) is recorded with both sides steelmanned, and it interacts with O12 because "consumed" depends on evaluation order. Until decided, no standard-layer template may depend on surplus tolerance. The decision is owed before the Phase 4 witness format freezes. | OPEN, Phase 4 | `docs/vm-record.md` entry 9, D5 | +| O14 | Chialisp tolerates surplus solution arguments. In Bitcoin that is third-party wtxid malleability: a relay peer mutates unconsumed witness bytes and produces a conflicting wtxid at a lower effective feerate. | Towns, delving 636 (2024) | Answered at the element boundary and by precedent inside it. D5 gives each program one accepted spelling, and the witness is exactly four elements with a fifth rejected, so nothing rides beside the solution. Inside the solution element the program decides. Tapscript set the boundary (exactly one element may remain, so nothing can be added) and tolerates a witness element a script drops unread, an opt-in the author writes. CLVM's default is the reverse, a tail ignored unless checked, and the compiler emits that check by default for every program form (commitment-record decision 5 addendum, landing with unit 9), the vault first. | ANSWERED | `spec/SPEC.md` section 3.1, commitment-record decision 5, `docs/vm-record.md` entry 9, D5 | | O15 | A soft-fork operator must be NOP-shaped with an explicit version bound and a statically knowable cost, never SUCCESS-shaped. Chia's own `softfork` was unimplemented in 2022. | Towns, bitcoin-dev (2022) and delving 636 (2024). ZmnSCPxj (2022) on static cost and version | The NOP-shaped mechanism exists at the condition layer: reserved conditions carry a declared cost that every validator charges before and after assignment, with a floor against free spam, so a future assignment can only tighten. An in-VM guard was declined as redundant with tapleaf versions plus reserved conditions, then reopened on the argument that a guard is cheaper than spending leaf versions, with a pre-registered deciding test (a concrete upgrade the guard delivers that the reserved tier cannot) and no deadline. Until decided, nothing in the tree assumes a guard. | ANSWERED for the mechanism, the guard sub-decision OPEN with no deadline | `spec/VALIDATION.md` rule 6, C4, `docs/vm-record.md` decision 1 and its addenda | | O16 | CLVM's defect history: the negative-division inconsistency, CATbleed (insufficient hash-input domain separation), unmetered internal recursion depth, and operators checked only after their arguments evaluate. Each deserves a regression vector. | Towns, delving 636 (2024), the primer's defect section | Negative division: floor semantics matching the consensus binary, the Python library's deprecation treated as policy (D6, pinned and diffed). CATbleed: BitLisp has no `coinid` and no in-VM identity derivation, program identity is the tree hash whose leaf and pair tags are distinct and whose child hashes are fixed width, and the CREATE_OUTPUT_TAPROOT and self assert derivations use BIP 341's tagged hashes. Argument-first evaluation: an unknown operator fails before its arguments evaluate and a reserved atom fails after, both stated. Recursion depth: the spec sets no depth limit and the cost budget is the only bound, so the safe depth for a recursive implementation is a Phase 4 bound to record beside D8, and the C++ implementation must be non-recursive or prove the reachable depth safe. | PARTIAL, three of four ANSWERED, depth owed to Phase 4 and 6 | D6, D8, D9, `spec/VM.md` sections 3.2 and 4, `vectors/vm/arith.json`, `vectors/vm/sha256tree.json` | -| O17 | A state tree living beside taproot commitments must hash distinctly from the BIP 341 TapLeaf and TapBranch tags, "to ensure they can't be misinterpreted as taproot scripts, possibly enabling theft of funds." | Towns, delving 1275 (2024) | Argued and unstated. The tree hash is Chia's untagged construction with one-byte `0x01` and `0x02` node prefixes, structurally disjoint from BIP 341 preimages that begin with two 32-byte tag digests, so no confusion is apparent. The spec statement and the vector pinning the disjointness argument are owed with the Phase 4 commitment scheme, the point at which the tree hash and the taproot tags meet in one tree. | OPEN, Phase 4 | `docs/vm-record.md` D9 addendum | +| O17 | A state tree living beside taproot commitments must hash distinctly from the BIP 341 TapLeaf and TapBranch tags, "to ensure they can't be misinterpreted as taproot scripts, possibly enabling theft of funds." | Towns, delving 1275 (2024) | Stated as a first-byte fact over every digest kind consensus computes. A tree hash's preimage begins with `0x01` or `0x02`, a tagged hash's with the first byte of the tag digest, and the table of tags in use shows none of those is `0x01` or `0x02`, so no preimage is valid under both kinds. The table gains a row with every new tag. The vector lands with the reference witness layer. | ANSWERED, the vector owed with the witness layer | `spec/SPEC.md` section 2.4, commitment-record decision 3, `docs/vm-record.md` D9 addendum | | O18 | Numbers. Truncating u64 arithmetic cannot hold satoshi amounts, arbitrary-size sign-bit numbers match CScriptNum, and the muladd scalar endianness divergence should go by using big-endian throughout. | Towns (self-adjustment, delving 682). ZmnSCPxj, bitcoin-dev (2022) | Integers are arbitrary-size signed big-endian two's complement atoms, the CLVM rule, so amounts never truncate and there is no little-endian anywhere in the VM. Redundant encodings are accepted as operands and cost at actual length, results are always minimal. Condition-layer amounts carry the field's own domain, 0 to MAX_MONEY, rejected at parse outside it. | ANSWERED | `spec/VM.md` section 1, `vectors/vm/arith.json`, C9, C15 | | O19 | BLS is a Chia artifact. Bitcoin has secp256k1 only, no deployed aggregation, and a second signature scheme is attack surface. | Towns, bitcoin-dev (2022), the first of his two listed changes | The BLS operators are absent (D1). The one signature operator is BIP340 Schnorr over secp256k1 with tapscript's tri-state rule, ECDSA and the r1 curve declined (D2). At the condition layer each signature assert carries its own 64-byte signature and verifies as a self-contained triple (C16), digests are BIP340 tagged hashes with fixed-width binding fields (C17), which also closes a non-injective message concatenation found in the deployed BLS design during the port. The aggregate-verification saving Chia gets is conceded as unavailable until Bitcoin has a deployed aggregation scheme. | ANSWERED, aggregation CONCEDED | D1, D2, C16 to C19, `vectors/vm/secp.json`, `vectors/validation/signature-asserts.json` | | O20 | Costing is unfinished in every public artifact. No published cost schedule exists for the introspection Lisp, and the stated methodology (implement in C++, benchmark, derive) has not been run. | The threads' own record, evaluation doc 8.5 item 4 | BitLisp states a complete v0 schedule (core, every operator family, every condition) inherited from CLVM's deployed table and anchored to CHIP-0049's condition pricing, with every constant marked PROVISIONAL, a pre-registered Phase 4 measurement pass that re-prices each one and isolates per-spend overhead, and a weight mapping still TODO with one constraint already normative (the per-byte witness cost floor the flat condition costs rely on). Upstream's February 2026 modpow repricing sets the re-validation bar and is recorded as such. | OPEN, Phase 4, the schedule stated and the measurement owed | `spec/COSTS.md` sections 1 to 10, decision 25, C21, C22, evaluation doc section 5 obligation 2 | -| O21 | Cost must be knowable before evaluation. A committed maximum operation count that adds to transaction weight makes every evaluation total, and Bitcoin deliberately conflates bytes with CPU. | ZmnSCPxj, OP_FOLD and jets threads (2022), bitcoin-dev (2022) | Every evaluation is total under the budget regardless (O6). Whether the budget is derived from weight or declared and committed is the Phase 4 weight-mapping decision, and the annex-declared shape has one precedent in the record: the taproot annex was declined as a signature carrier (C16) as a contested namespace under today's relay policy, which weighs against an annex-declared budget too. The jets question does not arise: the operator set is closed and no relay-level compression is planned. | OPEN, Phase 4 | `spec/COSTS.md` section 9, C16, execution plan Phase 4 | +| O21 | Cost must be knowable before evaluation. A committed maximum operation count that adds to transaction weight makes every evaluation total, and Bitcoin deliberately conflates bytes with CPU. | ZmnSCPxj, OP_FOLD and jets threads (2022), bitcoin-dev (2022) | Every evaluation is total under the budget regardless (O6). The budget is derived from the input's own witness weight, no declared operand and no annex, so cost is a function of bytes the way Bitcoin already conflates them, and no other input's witness moves it. BIP342's sigops budget, 50 plus the input's witness size, is the deployed precedent. The function itself is the Phase 4 weight mapping. The jets question does not arise: the operator set is closed and no relay-level compression is planned. | ANSWERED for the shape, the mapping OPEN to Phase 4 | `spec/SPEC.md` section 3.2, commitment-record decision 6, `spec/COSTS.md` section 9, C16 | | O22 | The introspection Lisp's own VM divergences from CLVM (reverse cons, balanced-tree environments, `partial`, dropped division, u64 bitwise operators, ascending string compare, eager `i` with lazy `if` only in the surface language) each carry a rationale that a CLVM-faithful design must answer. | Towns, delving 682 and 1224 (2024) | Each stays at the CLVM default under ground rule 3, recorded side by side with its bllsh counterpart in the opcode inventory. The eager `i` with surface-level `if` is shared. The rest are declined without a first-principles case having been made against the CLVM form, and the inventory names the capability each would add so a later case can be made against a row rather than a memory. | RECORDED | `docs/opcode-comparison.md`, ground rule 3 | ## 3. Objections to the conditions architecture @@ -113,11 +113,12 @@ Phase 4: - O11, witness density against the 526 vb gate (obligation 3). - O12, the reachable live-memory peak and the evaluation-order decision (VM record entry 8). -- O14, surplus solution data (VM record entry 9). - O16, the safe recursion depth beside the D8 bounds. -- O17, the tree-hash and taproot-tag disjointness statement. +- O17, the disjointness vector, owed with the reference witness + layer (the statement is in `spec/SPEC.md` section 2.4). - O20, the measured cost schedule and the weight mapping. -- O21, derived versus committed budget. +- O21, the budget function (the derived shape is decided, + commitment-record decision 6). - O28, the witness-byte cost of computed-over-context claims. - The hardware-signer digest scheme (section 5): whether and how a signing device learns the outpoint-bound condition digests, the diff --git a/docs/puzzles/singleton.md b/docs/puzzles/singleton.md index e9fa892..3f162c7 100644 --- a/docs/puzzles/singleton.md +++ b/docs/puzzles/singleton.md @@ -196,7 +196,11 @@ that would decide whether the Chia shape is worth the operator. A singleton lineage is the wrapper program curried with two fixed values. Its coins' scriptPubKey is the taproot output of the BIP341 nothing-up-my-sleeve point tweaked with the curried program's tree -hash as the merkle root, the same identity convention as the vault. +hash as the merkle root, the same identity convention as the vault +and the same recorded stand-in: under `spec/SPEC.md` the root is +the tagged leaf hash over that tree hash, and the sources move to +it when the singleton is re-pinned, where its reconstruction +posture is re-decided (commitment-record decision 8). The vault curries its internal key. The wrapper fixes it as a constant instead, because a singleton's guarantees are relied on by third parties, and every one of them rests on a coin at the lineage @@ -287,6 +291,13 @@ reliance on CREATE_COIN creating exactly the coin it names, which validation rule 1's index-free claims and rule 2's free outputs do not provide. +The commitment scheme bounds the solution element at 10,000 bytes, +so both serializations and the rest of the solution must fit in it +together. A lineage whose parent or grandparent transaction is +large enough to break the bound cannot be proven, a limit the +owner controls by keeping the lineage's own spends small and +choosing a small launch transaction. + ## The state output The creating transaction commits the child's state through one diff --git a/docs/puzzles/vault.md b/docs/puzzles/vault.md index c3238df..088d618 100644 --- a/docs/puzzles/vault.md +++ b/docs/puzzles/vault.md @@ -68,11 +68,15 @@ is rejected by the sequence assert it feeds, which would brick withdrawal alone. With the guards a malformed instance fails on its first spend of any kind, before it has a history. -This identity convention is a recorded stand-in: the Phase 4 -commitment scheme decides how programs are really committed under -the new tapleaf version, and it may replace the bare curried tree -hash with a tagged leaf construction. The convention is confined -to the `curry-hash.blib` call sites. +This identity convention is a recorded stand-in. The commitment +scheme of `spec/SPEC.md` commits a program in a leaf whose script +is the curried program's tree hash, so a coin's merkle root is the +tagged leaf hash over that tree hash, or a tagged branch fold when +the paths sit in leaves of their own. The sources here still use +the bare tree hash as the root, confined to the `curry-hash.blib` +call sites, until the vault is re-pinned under the scheme in its +own unit, where each path becomes a leaf and only the trigger path +keeps reconstruction (commitment-record decisions 7 and 8). ## The two programs diff --git a/docs/vm-record.md b/docs/vm-record.md index 8505fa8..8dd3e2e 100644 --- a/docs/vm-record.md +++ b/docs/vm-record.md @@ -422,7 +422,10 @@ marked open name the phase that owes the answer. which traded protocol-level domain separation for the released oracle on the grounds that the commitment context provides the separation. The owed statement is that context argument, written - down and pinned. + down and pinned. Written down 2026-09-06: `spec/SPEC.md` + section 2.4 states the disjointness as a first-byte fact over + every tag in use (commitment-record decision 3), and the vector + lands with the reference witness layer. 8. **Peak memory and evaluation order.** OPEN, owed with the Phase 4 budget mapping (recorded 2026-08-16 from the public record in ยง8.5 of the evaluation doc). CLVM costing charges total @@ -479,3 +482,10 @@ marked open name the phase that owes the answer. Both sides are steelmanned at decision time and the call is Evan's. Until decided, no standard-layer template may depend on surplus tolerance. + + CLOSED (decision by Evan, 2026-09-06, commitment-record decision + 5): the default side, on tapscript's own precedent. The witness + is strict at the element boundary and the solution element's + content is the program's to check, stated in `spec/SPEC.md` + section 3.1. Standard-layer templates check the shape of the + solution they consume. diff --git a/spec/CONDITIONS.md b/spec/CONDITIONS.md index 24e657c..198a3ff 100644 --- a/spec/CONDITIONS.md +++ b/spec/CONDITIONS.md @@ -83,6 +83,7 @@ block without a vocabulary entry are invalid, not reserved: | `0x32` | `ASSERT_MY_SCRIPTPUBKEY` | | `0x33` | `ASSERT_MY_AMOUNT` | | `0x38` | `ASSERT_MY_TAPTREE` | +| `0x39` | `ASSERT_MY_ANNEX` | | `0x40` | `ANNOUNCE` | | `0x41` | `ASSERT_ANNOUNCEMENT` | | `0x42` | `ASSURE` | @@ -603,6 +604,35 @@ charged after every argument check. **Validation rule.** The assert clause of VALIDATION.md (claims and asserts, rule 2). Stage 2. +### ASSERT_MY_ANNEX (`0x39`) + +`(0x39 annex_hash)` + +**Semantics.** Claims nothing. Asserts that the spending input's +witness carries an annex whose hash equals `annex_hash` +byte-exact, read from the transaction view's `annexHash` field +(`unsatisfied_annex_assert`). The hash is +`sha256(compact_size(len(annex)) || annex)` over the annex +element's bytes including its leading `0x50` byte, the `sha_annex` +value of BIP341. An input without an annex carries no `annexHash` +and fails the assert. + +The annex is admitted only under this assert: an input whose +witness carries an annex and whose condition list holds no +ASSERT_MY_ANNEX is invalid (`unasserted_annex`, SPEC.md section +3.4). No rule reads the annex's content. The assert commits the +spending input to the annex bytes it chose, so no third party can +attach, remove, or alter an annex without invalidating the spend. + +**Arguments.** `annex_hash` is an atom of exactly 32 bytes +(`bad_condition_arg`). Exactly one argument. + +**Cost.** `CONDITION_GENERIC_COST` = 200 (COSTS.md section 10), +charged after every argument check. + +**Validation rule.** The assert clause of VALIDATION.md +(claims and asserts, rule 2). Stage 2. + ### Message family (`0x40` to `0x43`) The four conditions of this family coordinate the inputs of a diff --git a/spec/SPEC.md b/spec/SPEC.md index ee8fe35..52c3777 100644 --- a/spec/SPEC.md +++ b/spec/SPEC.md @@ -1,33 +1,308 @@ # BitLisp Architecture -Status: stub. Filled in across Phases 1 and 2. Nothing normative yet. - -This document specifies how a BitLisp program is committed to on chain and -how a spend supplies it: the taproot leaf version, the commitment scheme, -and the witness structure. Evaluator semantics live in [VM.md](VM.md), -the condition vocabulary in [CONDITIONS.md](CONDITIONS.md), transaction -condition validation in [VALIDATION.md](VALIDATION.md), and the cost model in +This document specifies how a BitLisp program is committed to on +chain and how a spend supplies it: the taproot leaf version, the +commitment scheme, the witness structure, and the validation +pipeline that runs over them. Evaluator semantics live in +[VM.md](VM.md), the condition vocabulary in +[CONDITIONS.md](CONDITIONS.md), transaction condition validation in +[VALIDATION.md](VALIDATION.md), and the cost model in [COSTS.md](COSTS.md). ## 1. Overview -TODO: one-page architecture. Program committed in the output, -witness arguments supplied in the witness, evaluation yields a -condition list, conditions are matched against the transaction context. +A BitLisp program is committed in a taproot output as one leaf of +the output's script tree, under the leaf version section 2 assigns. +The leaf commits to the program by its tree hash. A spend of that +leaf is a BIP341 script-path spend whose witness carries four +elements: the solution, the serialized program, the leaf script, +and the control block, with an annex after them only when the +program commits to one. Base consensus authenticates the leaf +against the spent output exactly as it does for every script-path +spend, and the rules of this document then run: the program must +match the leaf, the program evaluates over the solution under a +cost budget derived from the witness weight, the value it returns +is parsed as a condition list, and the condition list is validated +against the spending transaction together with every other input's. + +``` + witness [solution] [program] [leaf script] [control block] + | | | | + | | +-------+-------+ + | | | + | | base consensus (BIP341): + | | tapleaf hash, path fold, + | | tweak check against the + | | spent scriptPubKey + | | | + | tree hash of the +--> execution identity + | program equals the (tapleaf, merkleRoot, + | leaf script? internalKey) + | | | + +-----+-----+ | + | | + run(program, solution, budget) | + | | + value ---- parse as a condition list | + | | + +---------+---------+ + | + VALIDATION.md over the whole transaction +``` + +The key path of a BitLisp output is base consensus's key path, +unchanged: a spend by signature under the output key runs no +program and this document places no rule on it. A program learns +that no key path exists only by asserting the internal key it was +committed under, the `ASSERT_MY_TAPTREE` entry of CONDITIONS.md. ## 2. Leaf version and commitment -TODO: new tapleaf version number, program hash commitment scheme, whether -standard-layer shorthands are part of the commitment scheme (Phase 4 -experiment feeds this). +### 2.1 Leaf version + +`BITLISP_LEAF_VERSION = 0xd0`. PROVISIONAL: the value is fixed at +deployment and nothing else in this document depends on which +compliant byte it is. + +A script-path spend is a BitLisp spend exactly when the leaf +version of its control block, the first byte with the low bit +cleared, equals `BITLISP_LEAF_VERSION`. Every BitLisp spend is +subject to this document. Spends under every other leaf version +are unaffected by it. + +### 2.2 The leaf + +The leaf script `s` of a BitLisp leaf is exactly 32 bytes: the tree +hash of the committed program, the `sha256tree` digest VM.md +section 4 defines over the program's node. The leaf script is never +executed. Its tapleaf hash is + +`tapleaf = tagged_hash("TapLeaf", BITLISP_LEAF_VERSION || 0x20 || s)` + +where `tagged_hash(tag, m)` is `sha256(sha256(tag) || sha256(tag) || +m)` with `tag` the ASCII bytes of the tag name, as the +CREATE_OUTPUT_TAPROOT entry of CONDITIONS.md defines it, and `0x20` +is the compact-size encoding of the 32-byte script length. + +A program is identified by its tree hash and by nothing else. The +tree hash is a function of the program's node, and VM.md section 2 +gives every node exactly one serialization, so a program has one +identity however it is written down. A curried program is one +program: a leaf commits to the curried program's tree hash, fixed +values included, and the uncurried program has no separate +standing in the commitment. + +### 2.3 The tree + +A BitLisp leaf is an ordinary BIP341 leaf. A script tree holds any +number of leaves in any arrangement BIP341 admits, BitLisp leaves +and leaves of other versions alike, and a spend reveals exactly one +leaf. Two BitLisp leaves of one tree are two independently +spendable programs of the same output. + +A BitLisp spend's execution identity, the triple VALIDATION.md's +transaction view carries, is read from the control block `c` of +`33 + 32m` bytes, `m` from 0 to 128 inclusive, as base consensus +reads it: + +- `internalKey` is `c[1..33]`, the 32-byte x-only internal key. +- `tapleaf` is the tapleaf hash of section 2.2 over the revealed + leaf script. +- `merkleRoot` is the fold of the path: `k_0 = tapleaf`, and for + each `j` from 0 to `m - 1`, with `e_j` the `j`th 32-byte element + of `c[33..]`, `k_(j+1) = tagged_hash("TapBranch", k_j || e_j)` when + `k_j` sorts before `e_j` lexicographically and + `tagged_hash("TapBranch", e_j || k_j)` otherwise. `merkleRoot` is + `k_m`. + +When `m` is 0 the merkle root is the tapleaf hash itself, so a +BitLisp spend's `merkleRoot` is never empty: the empty root the +CREATE_OUTPUT_TAPROOT entry admits names an output with no script +tree, which has no leaf to execute. + +Base consensus accepts the spend only when the spent scriptPubKey +is `0x51 0x20` followed by the x coordinate of `P + t*G`, where `P` +is the point with x coordinate `internalKey` and even y, `t` is +`tagged_hash("TapTweak", internalKey || merkleRoot)`, and the low +bit of `c[0]` equals the parity of that point's y coordinate. This +document takes the triple as authenticated by that check and +derives nothing further from it. + +### 2.4 Digest domains + +A tree hash never enters a script tree as a node. The leaf script +is the tree hash as data, and the tree's nodes are BIP341's tagged +hashes over it: the tapleaf hash wraps the leaf script under +`BITLISP_LEAF_VERSION`, and every branch and the tweak wrap tagged +hashes again. The two kinds of digest a BitLisp validator computes +are further disjoint by preimage, and no preimage is valid under +both: + +- A tree hash. Its preimage begins with the byte `0x01` (an atom) + or `0x02` (a pair). +- A tagged hash. Its preimage begins with the 64-byte prefix + `sha256(tag) || sha256(tag)`, whose first byte is the first byte + of `sha256(tag)`. + +Every tag a BitLisp validator hashes under, and the first byte of +each tag digest: + +| tag | first byte | +| --- | --- | +| `TapLeaf` | `0xae` | +| `TapBranch` | `0x19` | +| `TapTweak` | `0xe8` | +| `BIP340/challenge` | `0x07` | +| `BitLisp/sig/my_txid` | `0x54` | +| `BitLisp/sig/my_scriptpubkey` | `0xe3` | +| `BitLisp/sig/my_amount` | `0xdf` | +| `BitLisp/sig/my_scriptpubkey_amount` | `0xec` | +| `BitLisp/sig/my_txid_amount` | `0xff` | +| `BitLisp/sig/my_txid_scriptpubkey` | `0xe5` | +| `BitLisp/sig/raw` | `0x56` | +| `BitLisp/sig/my_outpoint` | `0xda` | + +None is `0x01` or `0x02`, so no tree-hash preimage is a tagged-hash +preimage and no tagged-hash preimage is a tree-hash preimage. A +tag added to any companion document adds a row here. + +`BIP340/challenge` is the challenge tag of BIP340's Verify, which +`secp_verify` and the signature asserts compute. The three other +digests the companion documents read, the txid and the outputs +hash of VALIDATION.md's transaction view and the annex hash of +section 3.4, are Bitcoin's own untagged constructions. None +enters a script tree, no rule compares any of them against a +digest of the two kinds above, and each reaches a rule only as a +value compared against a condition operand or bound as data into +a tagged signature digest. ## 3. Witness structure -TODO: serialized program + witness-arguments layout, size limits, currying -discipline. +### 3.1 Elements + +The witness of a BitLisp spend is exactly four elements, in BIP341's +stack order with the control block last, followed by an annex only +when section 3.4 admits one: + +| position | element | content | +| --- | --- | --- | +| 1 | solution | the serialization of the solution node | +| 2 | program | the serialization of the program node | +| 3 | leaf script | the 32-byte tree hash of section 2.2 | +| 4 | control block | `33 + 32m` bytes, as base consensus defines it | + +Position 1 is the first element of the input's witness as the +transaction serializes it, and the control block is the last. + +Base consensus checks the control block before any rule below +runs: its length, the lift of the internal key, and the tweak +check of section 2.3. This document adds no rule over its bytes. +The rules over the witness, in order: + +1. **The annex.** In the witness as the transaction serializes + it, if there are at least two elements and the last begins with + the byte `0x50`, that element is the annex, as BIP341 defines + it. It is set aside before the rules below count elements, and + section 3.4 governs it. +2. **Exactly four elements** remain, else `bad_witness`. +3. **The leaf script** is exactly 32 bytes, else `bad_witness`. +4. **Element sizes.** The solution, the program, and the annex + when present are each at most `MAX_WITNESS_ELEMENT_SIZE = + 10,000` bytes, else `bad_witness`. +5. **The program element** deserializes under VM.md section 2 to + a node, else `bad_encoding`, and that node's tree hash equals + the leaf script, else `leaf_mismatch`. The node is the program: + an atom or a pair, with no further constraint on its shape. +6. **The solution element** deserializes under VM.md section 2 to + a node, else `bad_encoding`. That node is the environment the + program evaluates over. Its content is otherwise unconstrained: + the program reads what it reads, and solution data the program + never reads does not invalidate the spend. + +An empty element is not the serialization of any node. Nil is the +one-byte element `0x80`. + +The leaf script's width and the element bound of rule 4 are the +only sizes this document fixes, and the control block's length is +base consensus's. Every witness byte is priced by the weight +mapping of COSTS.md section 9, and within the bound the budget of +section 3.2 is the only limit evaluation places on a spend. + +### 3.2 Budget + +The cost budget of a BitLisp spend is a function of the weight of +its own input's witness alone, the function fixed in COSTS.md +section 9. The witness carries no budget declaration, and no other +input's witness changes the budget. Deserializing the two node +elements and hashing the program tree are not charged against the +budget: the weight mapping prices those bytes. + +PROVISIONAL until COSTS.md section 9 fixes the function. Until it +does, a vector under this document states the budget it runs +under explicitly. + +### 3.3 Evaluation and the condition list + +The spend evaluates `run(program, solution, budget)` under VM.md +section 3. An error is a spend failure under the error's own name. +The returned value is parsed as a condition list under CONDITIONS.md, +and the conditions are costed against the remaining budget under +VALIDATION.md rule 5. The input then enters the transaction view +carrying its condition list and its execution identity, and +VALIDATION.md's rules run over the assembled transaction. + +Two programs follow from these rules and are named so they are +pinned. The program that is the atom `1` returns its solution, so +a leaf committing to it accepts any condition list the spender +supplies: the anyone-can-spend leaf. The program nil returns nil, +the empty list, which CONDITIONS.md rejects as +`bad_condition_list`, so a leaf committing to nil has no valid +script-path spend. + +### 3.4 The annex + +An annex is admitted on a BitLisp spend exactly when the input's +condition list carries `ASSERT_MY_ANNEX` over its hash. The annex +hash is + +`annexHash = sha256(compact_size(len(annex)) || annex)` + +over the annex element's bytes including its leading `0x50`, the +`sha_annex` value of BIP341. The transaction view's BitLisp input +carries `annexHash` when the witness carries an annex and nothing +otherwise. No rule reads the annex's content. + +- An input whose witness carries an annex and whose condition list + holds no `ASSERT_MY_ANNEX` is invalid, `unasserted_annex`. +- An `ASSERT_MY_ANNEX` on an input without an annex, or whose + annex hashes to a different value, is unsatisfied, + `unsatisfied_annex_assert`, the CONDITIONS.md entry. + +Together the two rules make an input's annex committed data the +spender chose: no third party can attach, remove, or alter one +without invalidating the spend. ## 4. Validation pipeline -TODO: deserialize, evaluate under cost budget, parse condition list, -match against transaction. Each stage's failure modes are enumerated and -every failure mode has a vector. +A BitLisp spend passes through six stages. The first five are +per-input work that reads the input's own witness and prevout, and +run independently for every BitLisp input of a transaction. The +sixth reads the assembled transaction. A failure at any stage +invalidates the transaction, and the error named is diagnostic: +the consensus fact is invalidity, and the names distinguish the +failure modes so each has its own vector. + +| stage | work | failure modes | +| --- | --- | --- | +| 1 | witness shape: the annex set aside, four elements, a 32-byte leaf script, element sizes | `bad_witness` | +| 2 | program decode and the leaf check | `bad_encoding`, `leaf_mismatch` | +| 3 | solution decode | `bad_encoding` | +| 4 | evaluation under the budget | the VM.md section 5 taxonomy, `cost_exceeded` among them | +| 5 | condition-list parsing and costing, then the annex rule of section 3.4 | the CONDITIONS.md parse errors, `cost_exceeded`, `unasserted_annex` | +| 6 | transaction validation | the VALIDATION.md rule errors | + +Stages 1 to 3 precede VALIDATION.md's stage 1, stages 4 and 5 are +that stage, the stateless per-spend work, and stage 6 is its +stages 2 to 5. The control block's own +checks, base consensus's, precede stage 1: a spend that fails them +never reaches this document. diff --git a/spec/VALIDATION.md b/spec/VALIDATION.md index 9e70fc1..2152342 100644 --- a/spec/VALIDATION.md +++ b/spec/VALIDATION.md @@ -45,7 +45,9 @@ A transaction is: evaluation produced and its execution identity: `tapleaf`, the 32-byte leaf hash of the executing leaf, `merkleRoot`, the 32-byte merkle root of the spending path, and `internalKey`, the - 32-byte x-only internal key the control block carries. Base + 32-byte x-only internal key the control block carries, and, when + its witness carries an annex, `annexHash`, the 32-byte + `sha_annex` digest of BIP341 over it, absent otherwise. Base consensus authenticates all three during every script-path spend, hashing the revealed leaf into `tapleaf`, combining it with the control block's path into `merkleRoot`, lifting @@ -56,8 +58,8 @@ A transaction is: from it: no rule recomputes the scriptPubKey from the key and the root. Rule 3's specifiers read `tapleaf` and `merkleRoot`, the self assert family of `spec/CONDITIONS.md` reads - `internalKey` and `merkleRoot`, and no specifier reads - `internalKey`. Only conditions reach these fields: an input + `internalKey`, `merkleRoot`, and `annexHash`, and no specifier + reads `internalKey` or `annexHash`. Only conditions reach these fields: an input without a condition list, whether a non-BitLisp input or a BitLisp spend whose evaluation has not yet produced one, may carry the triple or not, and no rule reads it from either. @@ -707,6 +709,10 @@ it enforceable, and lands with that rule: - An ASSERT_MY_TAPTREE's outcome is unchanged by the spent scriptPubKey: it reads the execution identity and nothing else (self asserts). +- An ASSERT_MY_ANNEX is satisfied exactly when its input carries + an annex of its operand's hash, and an input carrying an annex + is valid only when its list carries an ASSERT_MY_ANNEX (self + asserts, SPEC.md section 3.4). - On an input whose spent scriptPubKey is the taproot output of its `internalKey` tweaked with its `merkleRoot`, the shape of every input base consensus admits, ASSERT_MY_TAPTREE over an