Skip to content

Execution-identity specifiers and the ASSURE/REQUIRE rename - #58

Merged
EvanWinget merged 10 commits into
mainfrom
message-execution-identity
Aug 21, 2026
Merged

Execution-identity specifiers and the ASSURE/REQUIRE rename#58
EvanWinget merged 10 commits into
mainfrom
message-execution-identity

Conversation

@EvanWinget

Copy link
Copy Markdown
Owner

Lands the 2026-08-18 execution-identity amendment (decision by Evan, recorded in the execution plan) plus the 2026-08-20 rename of the addressed message pair.

What changed

  • Rename. SEND_MESSAGE and RECEIVE_MESSAGE become ASSURE and REQUIRE at the unchanged opcodes 0x42 and 0x43. The mode halves follow: assurer half (high bits) and requirer half (low bits). Ledger semantics untouched.
  • Execution identity. The rule 3 specifier table composes two new fields onto the eight prevout rows: bit 3 the executing leaf's tapleaf hash, bit 4 the spending path's merkle root. Commitment values 0 to 31, modes repack as assurer times 32 plus requirer (0 to 1023), announcements share the table (0 to 31). The transaction view's BitLisp input carries the pair, authenticated by base consensus through the control block. Internal-key addressing is declined as attacker-satisfiable.
  • Corpus. Every condition-carrying vector input gains the now-required identity fields, every packed mode re-encodes, and the vault re-pins: the mode constant changed the compiled vault body, so the vault mod hash, curried instances, roots, and scriptPubKeys regenerate from source. New vectors pin the composed-mode parse surface, the identity-addressed ledger semantics, and the grafted-leaf discrimination case.

Spec sections

CONDITIONS.md message family (specifier operand domains, mode ranges), VALIDATION.md transaction view and rule 3 (specifier table, ledger, stability classes, author guidance). Decisions 26 and 27 and divergence row C23 in docs/condition-record.md.

Read the commits in this order

  1. spec: rename the addressed message pair to ASSURE and REQUIRE
  2. spec: the specifier table gains execution identity, modes widen to five-bit halves
  3. conditions: execution-identity specifiers, ten-bit modes, the pair renamed
  4. puzzles: the vault re-encodes its consolidation mode, pair renamed
  5. vectors: the corpus re-pins under execution identity and the rename
  6. tests: identity fields on every BitLisp fixture, coverage for the new rules
  7. docs: decisions 26 and 27, divergence C23, glossary and corpus shape

Verify independently

.venv/bin/pytest python/tests                 # 788 pass, incl. vault closure tests
.venv/bin/python tools/run_vectors.py         # 36 files, 1075 cases
ci/lint/lint.sh

The vault closure tests (test_vm_vectors_match_source, test_validation_vectors_match_source) recompile the puzzle sources and recompute every pinned program and conditions payload, so the regenerated vault vectors are checked against source both directions. In the vault vector diff, the triggered-program values and every signature are byte-identical to before, which is visible directly: only VAULT-side hex moves. The VM diff harness is untouched by this PR (no operator changes).

Notes for review

  • The mode re-encode preserves semantics: old mode m maps to (m >> 3) * 32 + (m & 7), and modes 0 to 7 are unchanged. The old boundary cases (mode 64, announcement mode 8) move to the new boundaries (1024, 32).
  • The model now refuses a condition-carrying input without the identity pair, which is why every vector input and test fixture gained the two fields. Vault instance inputs carry their curried root as both values (single-leaf tree), hostile foreign-script inputs carry filler no case reads.
  • The language-level rename breaks source compatibility for programs spelling the old names, the same deliberate break as the prior reserved-word changes, pinned by the compiler vocabulary.

SEND_MESSAGE becomes ASSURE and RECEIVE_MESSAGE becomes REQUIRE at
the unchanged opcodes 0x42 and 0x43, across CONDITIONS.md,
VALIDATION.md rule 3, and the COSTS.md constant table. The mode
halves follow the pair: the sender half is now the assurer half
and the receiver half the requirer half, in the record tuple, the
operand lists, and the signature lines. The ledger semantics are
untouched: ASSURE contributes weight +1 and REQUIRE weight -1, and
the record vocabulary (message record, message ledger) keeps its
names.

The rename is Bitcoin-native terminology work: the pair reads as
what each condition demands of the transaction rather than as a
transport metaphor, and the halves read as the two roles the mode
packs. Chia's CHIP-0025 names stay mapped in the glossary, updated
in the docs commit of this PR.

Decision by Evan, 2026-08-20.
…ve-bit halves

The 2026-08-18 amendment, decided by Evan and recorded in the
execution plan. Taproot splits Chia's puzzle-hash identity into
the coin's script commitment and the executing program, and the
specifier table carried only the first, so program-to-program
trust had no faithful addressing field.

VALIDATION.md changes:

- The transaction view's BitLisp input gains its execution
  identity: tapleaf, the executing leaf's 32-byte leaf hash, and
  merkleRoot, the spending path's 32-byte root. Base consensus
  authenticates both against the spent scriptPubKey through the
  control block, so the pair is validator-filled and unforgeable.
- Rule 3 commitment values widen from 0..7 to 0..31: bit 3
  composes tapleaf and bit 4 merkleRoot onto the eight prevout
  rows, each set bit appending its operand after the low bits'
  operands, tapleaf first. Announcements share the table.
- The mode repacks as assurer times 32 plus requirer, ten bits.
- The stability table gains both fields as content-class rows,
  with the per-spend caveat: a tapleaf commitment addresses the
  program the counterpart runs in this transaction, not the coin.
- Author guidance gains the grafted-leaf and moved-root cautions:
  a leaf hash survives grafting into any tree and a root survives
  rehoming under any internal key, so execution identity alone
  never pins a coin. Internal-key addressing is declined on
  soundness, not deferred: the control block proves no possession
  of the internal key, so an IPK specifier would read as
  key-holder identity while proving nothing.

CONDITIONS.md changes: the two specifier operand domains (exactly
32 bytes each, merkleRoot never empty since a BitLisp counterpart
always executes a leaf of some tree), ASSERT_ANNOUNCEMENT mode
0..31, ASSURE and REQUIRE mode 0..1023 with five-bit halves.

Every deployed-Chia mode combination survives the re-encode at
its new packing. The widening was chosen over repurposing an
existing value because specifier fields are validator-filled, the
uniform bit rule reads as one spec sentence, and the re-encode is
cheapest before anything ships.
…named

Implements the two spec commits. The specifier table composes the
tapleaf hash (bit 3) and merkle root (bit 4) onto the eight
prevout rows, both operands exactly 32 bytes, appended after the
base operands with tapleaf first. ASSURE and REQUIRE modes widen
to 0..1023 with five-bit halves, ASSERT_ANNOUNCEMENT to 0..31.

The transaction model's input gains tapleaf and merkle_root,
required together with a condition list: a BitLisp input always
executes a leaf of some tree, so the model refuses the impossible
shape. Self specifiers fill the new fields from the input, and the
corpus loader accepts the two keys, with the runner demanding them
on its target input before installing computed conditions.

SEND_MESSAGE and RECEIVE_MESSAGE become ASSURE and REQUIRE
throughout: the opcode constants, the dataclasses, the compiler's
condition vocabulary, and the runner's display names. The record
fields follow the halves rename, assurer_commitment and requirer
on ASSURE, assurer and requirer_commitment on REQUIRE, and the
vector harness pins the renamed keys. The language-level name
change breaks source compatibility for programs spelling the old
names, the same deliberate break as prior reserved-word changes.
CONSOLIDATE_MODE repacks from 8 times 3 plus 2 to 32 times 3 plus
2 under the five-bit halves, the same assurer half 3 (own
scriptPubKey and amount) and requirer half 2 (a scriptPubKey
argument). SEND_MESSAGE and RECEIVE_MESSAGE become ASSURE and
REQUIRE at the call sites. The compiled bytes change only at the
mode constant, so the vault mod hash, every curried instance, and
every derived scriptPubKey re-pin in the vector commit.
Mechanical re-encode, spec commits authorize every change here:

- Every condition-carrying input in the validation corpus gains
  the now-required tapleaf and merkle_root fields. Vault instance
  inputs carry their curried root as both values (a single-leaf
  tree's root equals its leaf hash), the hostile foreign-script
  inputs and the family suites carry fixed filler, which no case
  reads through an execution-identity mode.
- Every ASSURE and REQUIRE mode re-packs from three-bit to
  five-bit halves, identical semantics at the new encoding.
- The vault files re-pin every VAULT-side value: the mode
  constant changed the compiled vault body, so the vault mod
  hash, both curried instances, their roots, scriptPubKeys, and
  every emitted condition list are regenerated from source. The
  triggered program, targets, and signatures are byte-identical
  to before, visible in the diff as unchanged hex.
- The old mode-domain boundary cases move to the new boundaries:
  mode 64 becomes 1024 for the pair, announcement mode 8 becomes
  32, each still pinning bad_condition_arg.
- New conditions cases pin the execution-identity operands: parse
  at both new bits including mode 1023 and announcement mode 31,
  operand order (base fields, then tapleaf, then merkle root),
  and wrong-size rejection for both fields.
- New validation cases pin the ledger semantics: tapleaf and
  merkle-root addressed pairs balance, a mismatched identity
  never balances, same-script inputs with different tapleaves do
  not satisfy each other (the grafted-leaf discrimination), and
  announcement asserts at an identity mode match and miss.
- Parsed-expectation keys follow the rename: assurer_commitment,
  requirer, assurer, requirer_commitment.
… rules

Every test fixture constructing a condition-carrying input gains
the execution-identity pair the model now requires: fixed filler
in the family invariant suites, per-input distinct values in the
message invariants so composed self specifiers discriminate, and
the honest instance roots in the vault suite.

The message invariants' mode pool extends past the prevout values
to tapleaf alone, merkle root alone, both, and both over prevout
fields, so every rule 3 property now also quantifies over
execution-identity commitments.

New coverage: the model refuses a condition-carrying input without
the pair and either field at the wrong size, the corpus loader
maps those defects to context errors, and the runner refuses a
target input missing the pair before computing conditions.

The vault suite re-pins the vault mod hash after the mode
re-encode and spells the hand-assembled consolidation lists at the
ten-bit mode. Renamed fields and condition names follow the spec
throughout.
The condition record gains decision 26 (the execution-identity
amendment with the grafted-leaf and moved-root caveats and the IPK
decline), decision 27 (the ASSURE and REQUIRE rename with the
assurer and requirer halves), and divergence row C23. The glossary
maps the renamed pair to CHIP-0025's names and adds the half and
execution-identity terms. The comparison doc's message row states
the new packing. The vault doc re-pins the vault mod hash, spells
the consolidation paths at mode 98, and reads the ledger
consequences in the new vocabulary. The vectors README documents
the identity keys and renamed expectation fields. The execution
plan flips the amendment bullet to landed.
… residue swept

Ten verified findings from the pre-merge review round, applied:

- The five-bit half width and mode bound now derive from the
  specifier table (SPECIFIER_COMMITMENT_MAX, _MESSAGE_MODE_MAX)
  instead of six scattered literals, and the dead high-half mask
  is gone. Boundary behavior and error text unchanged, pinned by
  the existing 1023/1024 and 31/32 vectors.
- self_specifier raises on an unknown field kind instead of
  silently falling through to outpoint bytes, matching the sig
  digest builder one screen down.
- The three 32-byte specifier operand checks merged into one
  branch with identical rendered errors.
- bitlisp-run's help documents the execution-identity keys its
  target input now requires, so exit 2 matches the shape text.
- Rename residue swept everywhere the old transport vocabulary
  survived: parse helper names, docstrings, the message and
  duplicate invariant suites' DSL, vault.bl comments and its
  require-conditions helper, and the corpus case names
  (send/recv to assure/require). The vault recompiles
  byte-identical, mod hash unchanged, so no vector hex moved.
- TxInput's identity checks use direct attribute access like
  their neighbors, the corpus loader hoists its key sets above
  the input loop, and the seal and merge property helpers use
  dataclasses.replace so future TxInput fields carry through.
- The vault tests' thirteen hand-expanded inputs collapse into a
  bl_input helper stating the single-leaf fact (leaf hash equals
  root) once.

Flagged for Evan, deliberately not taken here: exporting a
specifier radix constant to the language would break the pinned
one-name-per-opcode compiler contract (vault.bl keeps its bare
32 with a comment), and rejecting identity fields on
non-condition inputs would break the runner's pre-evaluation
target, a spec-governed shape that stays permitted.
One transaction-view clarification, decided by Evan 2026-08-20
closing the review round's converse-guard flag: 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
execution-identity pair or not, and no rule reads it from either.
The state is physically real (every taproot script-path input has
a leaf and a root) and load-bearing for the single-spend runner,
whose target input holds identity before evaluation installs its
conditions. The strict direction stays closed: a condition-carrying
input must carry the pair.
Decision by Evan, 2026-08-20, closing the review round's radix
flag by matching deployed Chialisp practice (ground rule 3): the
one production message user in the vendored corpora, tibetswap's
v2r_rebase.clsp, writes the packed mode as a bare literal with a
comment naming the halves (18 ; puzzle-puzzle), no radix constant
and no packing helper. CONSOLIDATE_MODE follows: the literal 98
with the halves named in the comment, replacing the derived
arithmetic. defconstant evaluates at declaration, so the compiled
bytes, the mod hash, and every vector are unchanged, pinned by
test_mod_hashes_pinned. Upstream source informed this choice:
references/tibet at the pinned clone.
@EvanWinget
EvanWinget merged commit 689158a into main Aug 21, 2026
2 checks passed
@EvanWinget
EvanWinget deleted the message-execution-identity branch August 21, 2026 04:13
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