Skip to content

Unit 8: the commitment scheme, spec and record - #67

Merged
EvanWinget merged 8 commits into
mainfrom
unit-8-commitment-scheme
Sep 6, 2026
Merged

Unit 8: the commitment scheme, spec and record#67
EvanWinget merged 8 commits into
mainfrom
unit-8-commitment-scheme

Conversation

@EvanWinget

@EvanWinget EvanWinget commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Phase 3 unit 8, the first of its two PRs: spec/SPEC.md written as a normative design with the vault as the workload, plus the record behind it. Spec only, no code: the reference witness layer, its vectors, and the commitment-hash utility follow in the second PR, and unit 9 re-pins the vault under the scheme and measures every path.

What changed

  • spec/SPEC.md goes from stub to normative in four sections: overview, leaf version and commitment, witness structure, validation pipeline.
  • docs/commitment-record.md is new: provenance, the measurements, eleven decisions, and what stays with Phase 4.
  • The objection register closes O14 and O17 and settles O21's shape, the VM record closes entry 9, and the puzzle and currying docs say what the recorded stand-in now stands in for.

The decisions, one line each

  1. Leaf version 0xd0, provisional until deployment. 0xc2 was the plan's pick until Evan asked whether Liquid or bll had it: bll's author floated 0xc2, Elements uses 0xc4 and 0xbe.
  2. The leaf script is the program's 32-byte tree hash. The serialized program is a witness element checked against it. Serialized bytes as the commitment would break covenant recursion, since nothing serializes in-program.
  3. Digest domains stated: tree-hash preimages begin 0x01 or 0x02, every tag digest in use begins with something else. The vector is owed to the witness layer.
  4. Exactly four witness elements, annex rejected. The signature asserts never commit to the annex, so an ignored one is third-party malleability.
  5. Surplus solution data tolerated inside the element, the element count strict. Tapscript's own split.
  6. Budget derived from the input's own witness weight. The function stays Phase 4.
  7. Per-path leaves are BIP341's, the scheme adds nothing.
  8. Self identity by guess-and-assert, successor roots by reconstruction. The follower path drops from 1,066 to 440 bytes under it. The singleton's posture is re-decided at its re-pin.
  9. Library code revealed per spend. The tagged-hash library the trigger needs is admitted under the language freeze as the scheme's consequence, to be recorded in the plan bullet.
  10. The singleton's constant scriptPubKey stands: the obstacle is the tweak, not the leaf.
  11. All four sections written, not two.

Review order

  1. 4f64700 spec. Read spec/SPEC.md top to bottom. Check it against BIP341 for the fold and the tweak check, and against spec/VALIDATION.md's transaction view for the identity triple.
  2. 259f78b record. Decisions 1, 5, 6, and 8 are the ones with real other sides.
  3. f09c259 pointers. Confirm each closed objection row cites the section that closes it.

Verify

PATH=$PWD/.venv/bin:$PATH ci/lint/lint.sh
.venv/bin/pytest python/tests -q

Lint clean, 861 tests green locally. The measurements in the record's section 2 reproduce from puzzles/vault/vault.bl by keeping one dispatch arm and compiling with bitlisp-compile -I puzzles/lib -I puzzles/vault.

After merge

The execution plan bullet for unit 8 wants its one-line decisions and the library exception, direct to main.

Review round

An independent hostile read against BIP341 and BIP342 found 14 points, folded in as 7ad9e12 (spec) and b876acd (record). The BIP341 restatements and the digest-table bytes checked out. The fixes: the annex rule names the pre-strip witness, the m = 0 case is stated, the digest domains lead with the structural fact and add BIP340's challenge tag, the budget is marked PROVISIONAL, the atom 1 and nil programs are named, the tapscript analogy for surplus data is corrected (its tolerance is opt-in, CLVM's is the default), the annex rationale becomes the readability asymmetry, BIP342's sigops budget is cited as the precedent for a witness-derived budget, and program-level Merkleization is recorded as the declined alternative to per-path leaves. Read the two fold-in commits last.

Review decisions, second round

Three calls by Evan at review, landed as the last two commits (spec, then docs):

  • ASSERT_MY_ANNEX (0x39) replaces annex rejection. The annex is admitted exactly when the condition list asserts its BIP341 sha_annex digest, so keyless paths stay protected by default and the upgrade door stays open under this leaf version. Condition-record decision 30 weighs the three shapes, divergence row C25. The existing gap vector pinning 0x39 invalid still passes against the unchanged implementation and flips in the witness-layer PR.
  • Element bound of 10,000 bytes on the solution, the program, and the annex. Commitment-record decision 12, with the singleton lineage-proof limit it creates recorded in the singleton doc.
  • Compiler-emitted solution shape checks as the default, closing surplus-byte malleability for every compiled program without consensus surface. Decision 5 addendum, landing with unit 9.

Owed to the witness-layer PR: the annex rule vectors in both directions, the bound at and above 10,000 bytes, and the 0x39 implementation.

Phase 3 unit 8. The architecture document goes from stub to
normative: the leaf version (0xd0, provisional until deployment),
what a leaf commits to (the program's tree hash as a 32-byte leaf
script, the serialized program a witness element the validator
checks against it), the tree (any BIP341 arrangement, one leaf
revealed, the execution identity triple read from the control
block as base consensus reads it), the digest domains (tree hashes
and tagged hashes disjoint by first byte, every tag in use
tabulated), the witness (exactly four elements, no annex, strict
at the element boundary and the program's to check inside the
solution), the budget (derived from the input's own witness
weight, the function left to COSTS.md section 9), and the
six-stage pipeline with every failure mode named.

Two errors are new: bad_witness for the shape rules and
leaf_mismatch for a program whose tree hash is not the leaf
script. The rest of the pipeline is the existing VM, condition,
and validation documents, cited by section.

The tree-hash commitment follows Chia's puzzle-hash identity
model, corroborated by CHIP-0049. The leaf version pool is BIP341's
own footnote, and the byte was chosen clear of the known claims
(tapscript 0xc0, bll's floated 0xc2, Elements' 0xc4 and 0xbe).
Rationale and the measurements behind the design are in the
commitment record, landing in the next commit.
The rationale behind spec/SPEC.md, the counterpart of the VM and
condition records: reference provenance (BIP341's leaf-version
footnote, the vendored BIP341 vectors as the tagged-hash oracle,
the known leaf-version claims, Chia's puzzle hashes and CHIP-0049),
the vault measurements the design rests on (2,562 curried bytes
whole, 1,066 to 1,508 per path, 499 for the curry library, 440 for
a follower that asserts a solution-supplied root), and eleven
decisions: the leaf byte, tree-hash commitment, stated domain
separation, four elements and no annex, surplus tolerated inside
the solution element only, the derived budget, per-path leaves as
BIP341's own, guess-and-assert for self identity with
reconstruction for successors, libraries revealed per spend, the
singleton's workaround standing, and the whole document written.

Three glossary rows (leaf version, leaf script, per-path leaf),
the README entry, and the ground rule 1 pointer to the record.
Objection register: O14 answered (surplus solution data, decision
5), O17 answered with its vector owed to the witness layer
(decision 3), O21 answered for the budget's shape with the mapping
still Phase 4 (decision 6), and the owed list updated to match.
VM record: entry 9 closed by decision 5, the D9 addendum's owed
statement marked written. The vault and singleton identity
paragraphs say what the stand-in now stands in for and when the
sources move, and the currying doc names the commitment its tree
hash builds.
From an independent hostile read against BIP341 and BIP342. The
annex rule names the witness it examines, the serialized one
before base consensus strips an annex, with BIP341's two-element
condition. The control block's checks move out of the ordered rule
list, since they precede it. Element position 1 is defined as the
first element of the serialized witness. The m = 0 case is stated,
which is what makes ASSERT_MY_TAPTREE's never-empty root true. The
digest-domain section leads with the structural fact, a tree hash
is never a merkle node, scopes its table to every tag a BitLisp
validator hashes under, and gains BIP340's challenge tag. The
budget section is marked PROVISIONAL until COSTS.md section 9
exists, vectors stating their budget until then. Section 3.3
names the two programs the rules single out, the atom 1 as the
anyone-can-spend leaf and nil as unspendable by script path. The
stage cross-reference to VALIDATION.md is corrected.
The tapscript analogy in decision 5 and objection O14 is
corrected: tapscript's boundary is strict and a dropped element is
an opt-in the author writes, where CLVM's tolerance of a solution
tail is the default, so the standard-layer shape check is the
opt-in. Decision 4's annex rationale becomes the readability
asymmetry, a program can commit to any solution byte and nothing
can commit to an annex, and records the 520-byte and 1,000-element
tapscript bounds the scheme drops. Decision 6 and O21 cite BIP342's
sigops budget as the deployed precedent for a witness-derived
budget and name the two parameters COSTS.md section 9 must fix.
Decision 7 records program-level Merkleization as the alternative
and why taproot-level leaves won. Decision 2 counts the saved
bytes as 33 and states serialization as a cost, not an
impossibility. Decision 3 matches the spec's rescoped table.
Provenance pins the Elements commit and says plainly that the bll
leaf-version float is our synthesis's reading, unpinned. The owed
vectors gain the two named programs.
A soft fork only tightens, so an annex use for BitLisp inputs later
needs a new leaf version, where tapscript's commit-and-reserve idiom
keeps it under one. Decision 4 states the trade and why rejection
was still taken.
Two review decisions (decision by Evan, 2026-09-06). The annex is
no longer rejected: it is set aside as BIP341 defines it and
admitted exactly when the input's condition list carries
ASSERT_MY_ANNEX (0x39, one 32-byte operand, generic cost) over the
BIP341 sha_annex digest of it, an annex without the assert being
unasserted_annex and the assert without a matching annex being
unsatisfied_annex_assert. The transaction view's BitLisp input
gains an optional annexHash the self assert family reads and no
specifier does. Keyless paths stay protected by default, a
program that wants an annex commits to it, and the upgrade door
stays open under this leaf version, where outright rejection
would have needed a new one.

The solution, the program, and the annex are each bounded at
MAX_WITNESS_ELEMENT_SIZE = 10,000 bytes, Bitcoin's legacy script
cap and the condition layer's existing scriptPubKey operand
bound, else bad_witness. The stage table and the digest-domain
paragraph name the annex hash beside the txid and outputs hash.

The gap vector pinning 0x39 invalid stays as the implementation
has it and flips with the reference witness layer.
Condition record: decision 30 with the three annex shapes weighed
(reject, bind into signature digests, admit under a self assert)
and divergence row C25. Commitment record: decision 4 rewritten to
the revised outcome with the plan's rejection and its priced
upgrade cost kept as history, decision 5 gains the compiler
default for solution shape checks and the note that a seal reads
no witness byte, decision 12 records the 10,000-byte bound and
the singleton lineage-proof limit it creates, and the owed
vectors gain both. Glossary rows for the assert and the annex, a
comparison row, the singleton doc's bound paragraph, and the O14
row naming the compiler default.
@EvanWinget
EvanWinget merged commit b35e19f into main Sep 6, 2026
2 checks passed
@EvanWinget
EvanWinget deleted the unit-8-commitment-scheme branch September 6, 2026 19:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant