Skip to content

Add sha256, the crypto family's intersection operator - #9

Merged
EvanWinget merged 3 commits into
mainfrom
crypto-sha256
Jul 29, 2026
Merged

EvanWinget merged 3 commits into
mainfrom
crypto-sha256

Conversation

@EvanWinget

Copy link
Copy Markdown
Owner

What changed

The sha256 operator (0x0b) lands as the intersection half of the crypto family: spec rows in VM.md and COSTS.md, the operator implementation, pinning vectors, and diff-harness coverage. The divergent half, secp_verify (D2), follows in its own PR.

Spec sections

  • VM.md section 4: the 0x0b table row, the crypto family prose, and the arg_not_atom taxonomy row.
  • COSTS.md section 8 (new): the cost formula, and the section 1 interleaving bullet (concat-shaped loop with a trailing malloc).
  • VM.md D3: sha256 leaves the list of consensus-known opcodes BitLisp classes as unknown. No new divergence: sha256 is bit-for-bit intersection behavior.

Reading order

  1. 88b30ce Spec: operator table row, family prose, cost formula, D3 update. Every claim is probe-backed against chia-rs 0.46.0 flags 0 (probe details in the commit message).
  2. 5ee7f2a Implementation: op_sha256 as a concat-shaped loop, constants in costs.py.
  3. 51f58b8 Vectors and harness: crypto.json (expectations produced by the oracle, not by bitlisp), generator emits 0x0b, and a harness classifier fix. The first fresh-seed run misclassified the oracle's "sha256 used on list" message through the bare InvalidOperatorArg catch-all as arg_too_long. The fix adds the sha256 fragments to both oracle tables.

Verify independently

.venv/bin/pytest python/tests
.venv/bin/python tools/run_vectors.py
.venv/bin/python tools/diff_clvm.py --count 10000 --seed <any fresh seed>
ci/lint/lint.sh

Two 10k harness runs (seeds 728281 and 314159) pass with zero failures. The vector suite is 416 cases across 9 files, 18 of them new in crypto.json.

The crypto family's intersection half. VM.md section 4 gains the 0x0b
row and the family prose (variadic, atom arguments only, digest of the
concatenation, zero arguments hash the empty string), the error
taxonomy covers sha256 under arg_not_atom, and the D3 row no longer
lists sha256 among the consensus-known opcodes BitLisp classes as
unknown. COSTS.md gains the crypto family section (base 87, 134 per
argument, 2 per byte, flat 320 malloc on the 32-byte digest) and the
concat-shaped interleaving bullet. Both status headers now defer to
the implemented-family list at the head of VM.md section 4 instead of
repeating it.

Every claim is probe-backed against chia-rs 0.46.0 flags 0: totals for
zero through five arguments including nil and redundantly encoded
atoms, the inclusive budget boundary at 724/723 on the worked example,
a pair in the first argument reported at a budget of 41 where the base
cost alone would burst it, and a pair in the second argument reported
only once the first argument's charge fits (266/265).
The concat-shaped loop from the spec: the base cost 87 accrues without
a budget check and rides on the first argument's charge, each
argument's atom check precedes its own charge of 134 plus 2 per byte,
and the 32-byte digest's malloc is one final charge after the loop.
Hashing consumes each argument atom's actual bytes, redundant
encoding bytes included, exactly the bytes the per-byte cost counts.

Cross-checked against chia-rs 0.46.0 flags 0 on the full probe
battery: totals for zero through five arguments including nil,
redundantly encoded, and 5120-byte atoms, inclusive budget boundaries
at 408/407, 566/565, and 724/723, and the pair-versus-cost_exceeded
interleaving at budgets 40/41 and 265/266. Zero disagreements.
vectors/vm/crypto.json pins the sha256 rows of VM.md section 4 and
COSTS.md section 8: the worked example at 724, zero arguments hashing
the empty string, a nil argument reaching the same digest at a
different cost, redundant versus minimal encodings hashing to
distinct digests, inclusive budget boundaries (724/723, 408/407), and
the interleaving boundaries where a pair argument beats or loses to
cost_exceeded (41/40 and 266/265). Expected results and costs were
produced by chia-rs 0.46.0 flags 0, not by bitlisp, so the file
cannot self-confirm.

The diff harness generator now emits 0x0b as a variadic operator, and
both oracle error tables classify the sha256 pair-argument messages
(chia-rs "sha256 used on list", clvm "sha256 on list") as
arg_not_atom. Without the chia-rs fragment the message fell through
to the bare InvalidOperatorArg catch-all and misclassified as
arg_too_long, which the first fresh-seed run surfaced as ten
mismatches. Two 10k runs, seeds 728281 and 314159, now pass with
zero failures.
@EvanWinget

Copy link
Copy Markdown
Owner Author

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@EvanWinget

Copy link
Copy Markdown
Owner Author

Review evidence, following the post-implementation cross-check practice from PRs 7 and 8.

Five independent review passes (CLAUDE.md compliance, shallow bug scan, git history, prior-PR guidance, code-comment contracts) found no issues. Empirical checks run during review, beyond the PR's own verification:

  • Every digest in crypto.json recomputed independently, and every cost recomputed from the COSTS.md section 8 formula. All 18 match.
  • Exhaustive budget sweeps (every budget from 1 to cost plus 3) on eight program shapes including pair-in-first/second/third argument, improper tail, and a 300-byte atom, bitlisp versus chia-rs flags 0: zero mismatches.
  • Three additional fresh harness seeds (998877, 990731, 424242) beyond the PR's two: zero failures.
  • Both new classifier fragments probed against the pinned wheels: chia-rs emits "InvalidOperatorArg: sha256 used on list" and the Python oracle emits "sha256 on list", exactly the strings added, with the chia-rs entry correctly placed before the bare InvalidOperatorArg catch-all.

Upstream source cross-check (reading disclosure per the reference-material policy, behavior was established by binary probes first): clvm_rs more_ops.rs op_sha256 uses the same constants (87 base, 134 per argument, 2 per byte), accrues the base cost unchecked, runs each argument's atom check before that argument's checked charge, and charges malloc on the finalized 32-byte digest. This matches the COSTS.md section 8 formula and the op_sha256 loop shape line for line.

@EvanWinget
EvanWinget merged commit 66a5a57 into main Jul 29, 2026
2 checks passed
@EvanWinget
EvanWinget deleted the crypto-sha256 branch July 29, 2026 03:10
@EvanWinget
EvanWinget restored the crypto-sha256 branch July 29, 2026 03:10
@EvanWinget
EvanWinget deleted the crypto-sha256 branch July 29, 2026 03:11
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