The signature assert family - #41
Merged
Merged
Conversation
Eight conditions at 0x10 to 0x17, offset continuity 0x10+k with Chia's AGG_SIG opcodes 43+k in Chia's order. Each is an assert carrying (pubkey, message, signature) and is satisfied exactly when the signature verifies under BIP340 over a tagged-hash digest of the binding data and the message. Validation rule 8 checks the family at stage 5, and the stages intro now states stage 5's real character: it adds no context beyond stage 2. Design ratified in chat 2026-08-09 (decision 23 in docs/condition-record.md, landing in the docs commit): BIP340 x-only Schnorr with the signature as third operand, program-composed messages with the delegated-program idiom as guidance, tagged-hash domain separation replacing Chia's suffix construction, the full eight-variant binding menu under the self-assert MY naming convention, ASSERT_SIG_RAW adopted as the attestation-import mode. The MY names resolve the creating-txid vs spending-txid ambiguity Evan caught in spec review. Five-agent review responses folded in: the family states its argument check order (arity first, then operands in order, the message-family precedent, a vector pinned it unsaid), the family block table row renamed signature asserts, the stages amendment trimmed to behavior (the expense rationale lives in the decision record), the bad_condition_arg marker covers all three operands, and verification names BIP340's Verify directly. Chia parity: opcode continuity, two-operand shape extended by the signature operand, the 1024-byte message cap, and per-variant domain separation. Divergences C16 to C19 in the docs commit: per-triple secp verification replaces aggregated BLS, tagged-hash prefix replaces the suffix construction, the AGG_SIG_UNSAFE suffix firewall is unnecessary by construction, and duplicate triples are idempotent facts rather than counted aggregate terms.
The signature assert family's decision record: scheme and carrier (BIP340 triples, the annex and external-section declines), the program-composed digest shape with the delegated-program idiom and its pre-registered reserved-tier alternative, tagged-hash domain separation with the probe-found injectivity wart in Chia's suffix construction as evidence, the full binding menu under the MY naming convention with the decision 18 precedent applied twice, the RAW adoption on batching-complement grounds, the composition and MEV analysis, and the two-PR sequencing that spawns the seal unit. Provenance: the 2026-08-09 research pass, source read at the 0.46.0 tag plus probes through validate_clvm_and_signature, the entry point that runs Chia's real aggregate verification, with all eight final-message constructions confirmed end to end. The novel-layer register gains rule 8's row and closes decision 12's obligation. Glossary rows for the family, RAW, the delegated-program idiom, and the planned seal pair. Comparison table flipped from planned to landed, execution plan amended. Five-agent review responses folded in: two stale comparison paragraphs rewritten (the open-item sentence and the vocabulary-gap observation), the execution plan's 2026-08-06 digest-shape bullet amended (conditions hash declined, triples not pairs), C17 states ME's verbatim-challenge suffix and UNSAFE's suffixlessness, the register rows describe exactly what the corpus pins, register row 4 lists the signature duplicates cases, the glossary stage row matches the amended stages preamble, and the family row's parenthetical no longer swallows RAW.
Parsing: eight opcodes at 0x10 to 0x17 share one AssertSig shape, three operands checked in order (pubkey exactly 32 bytes, message at most 1024, signature exactly 64), arity first, every defect bad_condition_arg. A shape-legal pubkey that lifts to no curve point is deliberately not a parse error: BIP340 defines lift failure as verification failure, and the family error unsatisfied_sig_assert covers it at stage 5. Checking: rule 8 verifies each triple against the tagged-hash digest of its variant, sha256(sha256(tag) twice, then the fixed-length binding fields, then the message), fields read from the carrying input's own prevout data. Verification is ordered last in validate_transaction: it is the costly check and runs only on transactions every other rule accepts, the stage 5 contract. The binding-field dispatch names every field and raises on an unknown one, a review-response hardening so a future table typo fails loudly instead of silently emitting outpoint bytes. The digest reuses secp256k1.verify, the same BIP340 relation the VM's secp_verify runs, so the official BIP340 vectors bind this path through the shared implementation. Spec: CONDITIONS.md signature assert family, VALIDATION.md rule 8.
One shape for all eight opcodes, {opcode, pubkey, message,
signature} with the operands hex, since the variant lives in the
opcode byte.
Signatures produced by the vendored Bitcoin Core framework signer, the recorded secp_verify signing oracle, digests computed from the spec's construction independently of the implementation. Parse layer (15 cases): every variant's parsed form, operand shape and arity errors in the spec's pinned check order, pair-typed operands rejected in every slot, the off-curve pubkey parsing as shape-legal (verification owns lift failure), the 1024-byte boundary, and the 0x18 family gap. Validation layer (38 cases): a satisfied case per variant and the eight-variant conjunction, variant separation in five directions (txid vs outpoint, raw vs bound both ways, and each single-field variant against the two-field variant extending it), failing cases for all eight variants, operand byte flips, outpoint index binding with the high-byte counterexample and the txid mode ignoring the index, amount binding at zero and above 2^32 with the truncation counterexample, the shared-tail scriptPubKey twin, sibling-input isolation, RAW replay acceptance and the no-firewall tag-suffix acceptance, the decision 23 footgun pinned as what the layer does not protect, transaction field isolation, and the two-signer conjunction. Duplicates (2 cases): identical triples idempotent within an input, the copied condition diverging across inputs. Five-agent review responses: the coverage lens ran fifteen mutants and two survived the original corpus, both now killed deterministically. The off-curve cases carry the BIP340 official off-curve x coordinate (the first draft's 0x01-bytes key lifts to a real curve point, so the lift-failure-as-success mutant, an accept-invalid bug class, had passed 874 cases) plus an above-field-prime companion, and the ninth-position failing triple kills the check-only-the-first-k loop mutant the corpus never exercised. The s-at-group-order case pins a value-defect rejection through the composed path.
Six properties over the family: the spec's variant table restated as a literal and asserted equal to the implementation's (a review-response hardening, the suite previously imported the table, so a field-order mutant passed every invariant), environment independence, a lone triple satisfied exactly when the carrier's digest fields match the signed coin's (RAW satisfied everywhere), operand byte-flip metamorphic rejection, variant separation across all opcode pairs, and rule 4 in-place duplication invariance. Signatures come from the vendored Core framework signer and digests from the literal table, so a digest divergence between spec and implementation fails here. The two coins differ in every binding field including an index and an amount above one byte and 2^32 respectively, the truncating-compare separators. Example counts are capped because pure-Python verification dominates each example and the mutation surface is small and discrete.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The signature assert family: eight conditions at 0x10 to 0x17, ASSERT_SIG_MY_TXID, ASSERT_SIG_MY_SCRIPTPUBKEY, ASSERT_SIG_MY_AMOUNT, the three two-field combinations, ASSERT_SIG_RAW, and ASSERT_SIG_MY_OUTPOINT. Each carries (pubkey, message, signature) and asserts BIP340 Schnorr verification over a per-variant tagged-hash digest of the carrying input's own prevout data and the program's message. New validation rule 8, stage 5 occupied, decision 23 with divergences C16 to C19.
Spec authority: spec/CONDITIONS.md signature assert family (encoding, digest table, operands), spec/VALIDATION.md rule 8 (the fact, idempotence, guidance), the amended stages preamble, and three new invariants. Design ratified in chat 2026-08-09, recorded as decision 23 in docs/condition-record.md.
Review guide
Read the commits in order:
Verify independently:
Five-agent Fable review, run pre-PR, all catches folded in
Declined finding, flagged for you: the docs lens noted decision 11's historical text still says stage 5 collects "(pk, message) pairs". Left per the earlier-entries-keep-their-wording convention, with the current contract stated in decision 23 and the glossary row. Say the word if you want an amendment note under decision 11 instead.
Confidence: 90 percent
What supports it: the digest construction is triple-checked (implementation, an independent hand-transcribed probe grid, and vector signatures generated from a third restatement), the verification relation is bound to the official BIP340 vectors through the shared secp_verify implementation, the adversarial pass found nothing, and every identified mutant now dies deterministically.
The residual 10 percent, named: the digest construction is novel consensus surface with no external oracle (no deployed system to diff against, the ground-rule-4 situation, mitigated by invariants and the adversarial corpus rather than eliminated), the footgun boundary is a designed-in non-protection whose safety depends on Phase 3 tooling defaults that do not exist yet, and cross-rule error precedence remains unpinned by design (decision 22), which is recorded freedom rather than risk but keeps one degree of implementation variance alive.