Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/condition-comparison.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ is no intra-transaction chaining.
| Capability | Chia | BitLisp |
| --- | --- | --- |
| announcements | `CREATE_COIN_ANNOUNCEMENT` 60, `ASSERT_COIN_ANNOUNCEMENT` 61, `CREATE_PUZZLE_ANNOUNCEMENT` 62, `ASSERT_PUZZLE_ANNOUNCEMENT` 63 | `ANNOUNCE` 0x40 and `ASSERT_ANNOUNCEMENT` 0x41, normative, transaction-scoped, namespacing first-class in the arguments rather than payload prefix bytes, announcer precision chosen by the assert through the specifier grammar (decisions 10 and 16, divergence C11) |
| messages | `SEND_MESSAGE` 66, `RECEIVE_MESSAGE` 67 (CHIP-0025), mode flags select which sender and receiver fields the pairing commits to, paired within the surrounding block | `SEND_MESSAGE` and `RECEIVE_MESSAGE` at the same numeric opcodes, normative, strictly transaction-scoped counted balance with fields re-addressed to prevout data (decision 16, divergences C8 to C10) |
| messages | `SEND_MESSAGE` 66, `RECEIVE_MESSAGE` 67 (CHIP-0025), mode flags select which sender and receiver fields the pairing commits to, paired within the surrounding block | `ASSURE` and `REQUIRE` at the same numeric opcodes (renamed 2026-08-20, decision 27), normative, strictly transaction-scoped counted balance with fields re-addressed to prevout data plus the execution-identity pair, tapleaf hash and merkle root, at bits 3 and 4 of each five-bit half (decisions 16 and 26, divergences C8 to C10 and C23) |
| concurrency asserts | `ASSERT_CONCURRENT_SPEND` 64, `ASSERT_CONCURRENT_PUZZLE` 65, another spend with the named coin id or puzzle hash occurs alongside this one | not in the v0 plan |

The scoping difference is the architectural one: Chia validates a
Expand Down Expand Up @@ -214,7 +214,7 @@ specs of the same shape.
normative and the v0 table is complete: `CREATE_OUTPUT`,
`CREATE_OUTPUT_TAPROOT`, the eight signature asserts (decision
23), the four time asserts, the five self asserts (decision
20), the message family (`SEND_MESSAGE`, `RECEIVE_MESSAGE`,
20), the message family (`ASSURE`, `REQUIRE`,
`ANNOUNCE`, `ASSERT_ANNOUNCEMENT`), `RESERVE_FEE` (decision
21), and the seal pair (decision 24). All eight validation
rules are normative, rule 5 landing last by design so costing
Expand Down
51 changes: 51 additions & 0 deletions docs/condition-record.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Section 4 registers the rules that have no external reference at all.
| C20 | the seal family | no equivalent: coin identity is content-derived from parent, program hash, and amount, so a spend's authorization is indifferent to which aggregate bundle carries it and bundles need no seal | SEAL and SEAL_OUTPUTS, asserts pinning the spending transaction's own txid and its BIP 341 outputs hash | A pure addition, not a divergence of shared semantics. Bitcoin output identity is positional: the txid commits to the whole transaction, so an intercepted covenant spend can be rebuilt around a grafted output with every condition still holding, the surplus-capture gap rule 7's acceptance vector pins. The seal is the consensus answer to the aggregation qualification in section 4.4 of the evaluation doc. Ratified 2026-08-09, decision 24. | `validation/seals.json` |
| C21 | per-spend base cost | 450,000 (SPEND_COST) charged for each coin spend under the hard fork 2 pricing flag, before any condition's own cost | no per-spend constant, conditions alone charge | A Bitcoin input's fixed overhead, prevout fetch and per-input iteration, is work base consensus performs and prices in weight for every input of every transaction, so a cost-unit constant would charge it twice. Declined with a falsifier pre-registered: the Phase 4 measurement pass isolates per-spend overhead Chia-style, n spends of k conditions against nk conditions in fewer spends, and a measured non-byte-proportional overhead introduces the constant then, a tightening that is cheap before publication. Ratified 2026-08-09, decision 25. | `conditions/costs.json` totals contain no per-list constant |
| 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 |

## 2. Reference provenance

Expand Down Expand Up @@ -1472,6 +1473,56 @@ Section 4 registers the rules that have no external reference at all.
budget argument explicitly, mirroring run, so no future
caller can forget the budget silently.

26. **Execution-identity commitment for rule 3 specifiers.**
RATIFIED (decision by Evan, 2026-08-18, landed 2026-08-20).
The specifier table gains two execution-identity fields, the
executing leaf's tapleaf hash (bit 3) and the spending path's
merkle root (bit 4), composed onto the eight prevout rows,
commitment values 0 to 31, the mode repacked as assurer times
32 plus requirer, announcements sharing the table. The
transaction view's BitLisp input carries both fields,
authenticated by base consensus against the spent scriptPubKey
through the control block, so every composed value is a
validator-filled binding puzzle authors do not hand-roll.
Rationale: taproot splits Chia's puzzle-hash identity into the
coin's script commitment and the executing program, and the C9
mapping carried only the first, so program-to-program trust had
no faithful addressing field. The widening was chosen over
repurposing an existing value: the uniform bit rule reads as
one spec sentence, every deployed-Chia combination survives,
and the re-encode is cheapest before anything ships. Two
caveats are stated in rule 3's author guidance and pinned by
vectors:
- **Grafted leaf.** A tapleaf hash identifies a program, not a
coin: the same leaf grafted into any other tree, under any
internal key, produces the same leaf hash, so a tapleaf-only
specifier is matched by any input an adversary arranges to
execute that program.
- **Moved root.** A merkle root identifies a tree, not an
output: the same tree under a different internal key produces
the same root. Programs meaning "this program, in that coin"
compose an execution-identity bit with the prevout fields
that pin the coin.
Internal-key addressing is declined on soundness rather than
deferred: BIP341 requires no possession proof for the internal
key, so anyone can place a foreign public key in their own
control block over their own tree, and an IPK specifier would
read as key-holder identity while proving nothing. Both
specifier fields are deliberately IPK-independent.

27. **The addressed pair renamed to ASSURE and REQUIRE.** RATIFIED
(decision by Evan, 2026-08-20). SEND_MESSAGE and
RECEIVE_MESSAGE become ASSURE and REQUIRE at the unchanged
opcodes, and the mode halves follow: the sender half is the
assurer half, the receiver half the requirer half. Semantics
are untouched. Bitcoin-native terminology work under the
standing vocabulary policy: the pair reads as what each
condition demands of the transaction rather than as a
transport metaphor, which the balanced-both-ways ledger never
quite matched. The glossary maps Chia's CHIP-0025 names, and
the language-level rename is a deliberate source-compatibility
break pinned by the loader vocabulary.

## 4. Novel-layer register

The validation rules have no external reference: no deployed system
Expand Down
2 changes: 1 addition & 1 deletion docs/execution-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ descriptions, and commit messages*

**Done 2026-08-09:** 184 tests, 984 vector cases, lint and diff harness clean at a fresh seed. Owed forward: the fresh-reader predictability exercise transfers to Phase 5 hostile-review preparation.

- [ ] **Amendment, execution-identity commitment (2026-08-18, decision by Evan, skeleton change under ground rule 6):** the rule 3 specifier table gains two execution-identity fields, the executing leaf's tapleaf hash and the spending path's merkle root. Commitment values widen to 5 bits, bit 3 composing the tapleaf and bit 4 the merkle root onto values 0 to 7, the mode repacking as sender times 32 plus receiver, announcements sharing the table. Taproot splits Chia's puzzle-hash identity into the coin's script commitment and the executing program, and the C9 mapping carried only the first. Internal-key addressing is declined on soundness: the control block proves no possession of the internal key, so an IPK specifier is attacker-satisfiable. Own PR after PR 57 merges and before the payment pool, with spec, implementation, and vector commits separate, packed-mode constants re-encoded in the same PR, and the decisions plus the grafted-leaf and moved-root caveats recorded in `docs/condition-record.md`.
- [x] **Amendment, execution-identity commitment (2026-08-18, decision by Evan, skeleton change under ground rule 6):** landed 2026-08-20. The rule 3 specifier table gains two execution-identity fields, the executing leaf's tapleaf hash and the spending path's merkle root, carried on the transaction view's BitLisp input. Commitment values widen to 5 bits, bit 3 composing the tapleaf and bit 4 the merkle root onto values 0 to 7, the mode repacking as assurer times 32 plus requirer, announcements sharing the table. Taproot splits Chia's puzzle-hash identity into the coin's script commitment and the executing program, and the C9 mapping carried only the first. Internal-key addressing is declined on soundness: the control block proves no possession of the internal key, so an IPK specifier is attacker-satisfiable. Landed with the same-PR rename of the addressed pair to ASSURE and REQUIRE, the halves following as assurer and requirer (decision by Evan, 2026-08-20). Decisions 26 and 27 plus the grafted-leaf and moved-root caveats recorded in `docs/condition-record.md`, divergence row C23, packed-mode constants re-encoded corpus-wide including the vault's, whose instances re-pinned.

**Claude Code fit:** strong. Design decisions stay in Fable 5 sessions and land in spec prose first; Claude Code implements and generates vectors.

Expand Down
4 changes: 3 additions & 1 deletion docs/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ A term enters this table in the same PR that introduces it.
| spend | input plus its witness | coin spend | one input's evaluation and conditions |
| reserved condition | upgradable NOP | unknown condition | priced forward-compatibility hatch, invalid-by-default outside it |
| internal key, merkle root, tweak | BIP341 terms, used as defined there | none | see the CREATE_OUTPUT_TAPROOT entry |
| SEND_MESSAGE, RECEIVE_MESSAGE | no direct equivalent | SEND_MESSAGE, RECEIVE_MESSAGE (CHIP-0025) | landed 2026-08-07, opcodes numerically Chia's, transaction-scoped counted balance (condition-record decision 16) |
| 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 and merkle root of the script-path spend | puzzle hash (one field carries both meanings) | the pair of specifier fields addressing the executing program and its tree, validator-filled from the control block (condition-record decisions 26 and 27, divergence C23) |
| 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) |
Expand Down
33 changes: 17 additions & 16 deletions docs/puzzles/vault.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,19 +155,19 @@ management. The choice is per instance.

**Path 3, consolidation follower.** ARGS is `(MY_SPK)`. Emitted
conditions: the taproot assert, `ASSERT_MY_SCRIPTPUBKEY MY_SPK`,
and `SEND_MESSAGE 26 () MY_SPK`. The scriptPubKey arrives in the
and `ASSURE 98 () MY_SPK`. The scriptPubKey arrives in the
solution because no operator derives it, and the assert proves it
is the coin's own. Mode 26 puts the sender half at commitment 3,
the sending input's own scriptPubKey and amount filled by the
validator from real prevout data, and the receiver half at
is the coin's own. Mode 98 puts the assurer half at commitment 3,
the assuring input's own scriptPubKey and amount filled by the
validator from real prevout data, and the requirer half at
commitment 2, addressing the shared scriptPubKey.

**Path 4, consolidation leader.** ARGS is
`(MY_SPK MY_AMT OUT_AMT FOLLOWER_AMOUNTS)`. The program rejects an
empty follower list and `MY_AMT + sum(FOLLOWER_AMOUNTS) > OUT_AMT`.
Emitted conditions: the taproot assert,
`ASSERT_MY_SCRIPTPUBKEY MY_SPK`, `ASSERT_MY_AMOUNT MY_AMT`, one
`RECEIVE_MESSAGE 26 () MY_SPK <amount>` per listed follower
`REQUIRE 98 () MY_SPK <amount>` per listed follower
amount, and `CREATE_OUTPUT MY_SPK OUT_AMT`.

An unknown path raises.
Expand Down Expand Up @@ -255,28 +255,29 @@ work.

The spender, who needs no key, picks one coin as leader. Every
other coin spends its follower path and sends one message
addressed to the shared scriptPubKey, the sender half carrying its
addressed to the shared scriptPubKey, the assurer half carrying its
own scriptPubKey and amount from prevout data the validator fills.
The leader lists the follower amounts in its solution, receives
The leader lists the follower amounts in its solution, requires
one matching message per listed amount, and claims one output
paying the shared scriptPubKey at least its own amount plus the
listed sum.

The message ledger makes the sum honest. Message records balance
only when k identical sends meet exactly k identical receives, so:
only when k identical ASSURE records meet exactly k identical
REQUIRE records, so:

- An omitted follower leaves an unmatched send. Its coin cannot be
silently absorbed.
- An omitted follower leaves an unmatched ASSURE. Its coin cannot
be silently absorbed.
- A phantom or misstated listed amount creates an unmatched
receive. The sum cannot be inflated or deflated against the
REQUIRE. The sum cannot be inflated or deflated against the
coins actually present.
- Two followers of equal amount produce one record at weight two,
and the leader must list the amount twice.
- A hostile input at a foreign scriptPubKey cannot forge a
follower record, because the sender half is filled from its real
prevout data, and cannot intercept the followers as a fake
leader, because its receive records carry its own scriptPubKey
in the receiver half while the followers addressed the vault's.
follower record, because the assurer half is filled from its
real prevout data, and cannot intercept the followers as a fake
leader, because its REQUIRE records carry its own scriptPubKey
in the requirer half while the followers addressed the vault's.
- A negative or non-minimal listed amount dies at condition
parsing, before the ledger runs.
- Two independently balanced consolidations at the same
Expand Down Expand Up @@ -329,7 +330,7 @@ The mod hashes, pinned in `python/tests/test_vault_puzzles.py`:

```
$ bitlisp-compile -T puzzles/vault/vault.bl -I puzzles/lib -I puzzles/vault
15884715af56b2851e9e1359b11d7090623168ddc2d84fc4785b75e904c2294f
2fc2096c3167018bc0210e061d4add87f081360a77ec352bcfb1456243ca34a2
$ bitlisp-compile -T puzzles/vault/triggered.bl -I puzzles/lib -I puzzles/vault
214b0347c7df10d8d7c95769dd4cc3e0fdcee183b281b3d96ebda71bb739ec1b
```
Expand Down
21 changes: 11 additions & 10 deletions puzzles/vault/vault.bl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
;
; Consolidation is keyless: the follower's message names its own
; scriptPubKey and amount from real prevout data, the leader must
; receive exactly one such message per follower it sums, and the
; require exactly one such message per follower it sums, and the
; message ledger balances only when the leader's list matches the
; followers present, so the merged output can never pay less than
; every merged coin's value.
Expand All @@ -36,9 +36,10 @@
(defconstant FOLLOW_PATH 3)
(defconstant LEAD_PATH 4)

; Sender half 3 commits the sending input's own scriptPubKey and
; amount, receiver half 2 addresses a scriptPubKey argument.
(defconstant CONSOLIDATE_MODE (+ (* 8 3) 2))
; spk_amount-spk: assurer half 3 commits the assuring input's own
; scriptPubKey and amount, requirer half 2 addresses a
; scriptPubKey argument, packed as assurer times 32 plus requirer.
(defconstant CONSOLIDATE_MODE 98)
(defconstant CONSOLIDATE_PAYLOAD ())

; This instance's own curried tree hash, the merkle root its
Expand Down Expand Up @@ -77,21 +78,21 @@

; No operator derives the 34-byte taproot scriptPubKey, so the
; shared scriptPubKey arrives in the solution and is proven equal
; to the coin's own before it addresses or receives anything.
; to the coin's own before it assures or requires anything.
(defun follow-path (VROOT IK (MY_SPK))
(list
(list ASSERT_MY_TAPROOT IK VROOT)
(list ASSERT_MY_SCRIPTPUBKEY MY_SPK)
(list SEND_MESSAGE CONSOLIDATE_MODE CONSOLIDATE_PAYLOAD MY_SPK)))
(list ASSURE CONSOLIDATE_MODE CONSOLIDATE_PAYLOAD MY_SPK)))

(defun sum-amounts (AMOUNTS)
(if AMOUNTS (+ (f AMOUNTS) (sum-amounts (r AMOUNTS))) 0))

(defun receive-conditions (SPK AMOUNTS TAIL)
(defun require-conditions (SPK AMOUNTS TAIL)
(if AMOUNTS
(c (list RECEIVE_MESSAGE CONSOLIDATE_MODE CONSOLIDATE_PAYLOAD
(c (list REQUIRE CONSOLIDATE_MODE CONSOLIDATE_PAYLOAD
SPK (f AMOUNTS))
(receive-conditions SPK (r AMOUNTS) TAIL))
(require-conditions SPK (r AMOUNTS) TAIL))
TAIL))

; A leader without followers is rejected, so every consolidation
Expand All @@ -104,7 +105,7 @@
(c (list ASSERT_MY_TAPROOT IK VROOT)
(c (list ASSERT_MY_SCRIPTPUBKEY MY_SPK)
(c (list ASSERT_MY_AMOUNT MY_AMT)
(receive-conditions MY_SPK FOLLOWER_AMOUNTS
(require-conditions MY_SPK FOLLOWER_AMOUNTS
(list (list CREATE_OUTPUT MY_SPK OUT_AMT))))))))

; Exists to name VROOT once for every branch.
Expand Down
Loading