diff --git a/HASH-PINNING.md b/HASH-PINNING.md new file mode 100644 index 000000000..2b49bed65 --- /dev/null +++ b/HASH-PINNING.md @@ -0,0 +1,267 @@ +# HASH PINNING — `per-table-gpu` under RPX256 + +The integration branch's BLOCK PATH is pinned to RPX256. The workspace default +stays BLAKE3, with its `const` assertions intact; nothing in `crypto/stark` +moves. This file records what the pin is, what enforces it, and what a person +running a box under it must not get wrong. + +**This branch's hash: RPX256** — Rescue-Prime eXtended (XHash12), state 12 / +rate 8 / capacity 4, a **one-cell four-felt digest** against the byte hashes' +two-cell 32-byte one. RPO's geometry with RPO's constants and a different round +schedule, `FB E FB E FB E M`: three of the seven rounds trade the ~2^63-dense +inverse S-box for a seventh power in the degree-3 EXTENSION field, and the last +round is linear. Cheaper on the host and narrower in the AIR than RPO +(`prover/src/lfm/hash.rs`, `HasherKind::Rpx`). + +| pin item | value | +|---|---| +| `BlockStarkHash` | `algebraic_commit::RpxStarkHash` | +| `BlockTranscript` / `block_transcript()` | `algebraic_transcript::AlgebraicTranscript`, built `with_seed(BLOCK_HASHER, seed)` | +| `BLOCK_HASHER` | `hash::HasherKind::Rpx` | + +⚖ **Provenance, stated plainly.** Miden publishes no known-answer table for +RPX — the opposite of RPO's nineteen published vectors — so this hash has a +weaker external anchor than the RPO pin had. What anchors it is the repo's own +host known-answer harness (`make test-rpx-host-kat`; +`crypto/math-cuda/tests/host_kat/rpx_host_kat.cpp` against +`prover/tests/rpx_host_kat_vectors.rs`), which checks the C reference and the +Rust permutation against each other. That is a **self-built oracle**, not an +external one, and it must be described as such. Domain separation is through +the capacity, exactly as for RPO: the three algebraic candidates share one +leaf and parent construction (`prover/src/lfm/algebraic_commit.rs`) and differ +only in the permutation. + +## The pin, mechanically — `prover/src/hash_pin.rs`, and the workspace default does NOT move + +`crypto/stark`'s `DefaultStarkHash` is the *workspace's* default. It names the +hash behind `Commitment`, `BatchedMerkleTree` and every blessed constant in the +repo, and a `const` assertion in `config.rs` makes re-pointing it a compile +error so those artifacts cannot drift. The pin therefore lives one layer up: +`IsStarkProver` is generic over the +configuration, and `prover` names the configuration explicitly at every prove +and verify call site. Collecting those names behind `hash_pin.rs` turns "which +hash does the block path use" into a property of one module. + +**THREE ORTHOGONAL AXES, all named in `hash_pin.rs` and nowhere else:** + +| axis | name | this pin | +|---|---|---| +| what the HOST commits under | `BlockStarkHash` | `algebraic_commit::RpxStarkHash` | +| the Fiat–Shamir transcript OBJECT | `BlockTranscript` / `block_transcript` | `algebraic_transcript::AlgebraicTranscript` | +| the `LFM_HASH` socket permutation | `BLOCK_HASHER` | `hash::HasherKind::Rpx` | + +⚠ **Axis 2 is the dangerous one.** `StarkHash::Transcript` names a *digest* +configuration, which is what GRINDING computes over; the Fiat–Shamir transcript +*object* is built by the caller and handed to `multi_prove`, so the type system +does not force it to match. For the byte hashes the two coincide. For an +algebraic hash they do not, and a branch that pinned only `BlockStarkHash` +would commit under RPX while sponging Fiat–Shamir through bytes — self-consistent +between prover and verifier, and therefore **silent**. + +⚠ **Axis 3 is consulted only by algebraic programs.** Under a byte hash the +emitter's Merkle work lowers to the dedicated KECCAK / `LFM_BLAKE3` chips and +emits no `Instr::Hash` at all, so the socket hasher handed to `execute` is never +consulted and a toy permutation is free and correct. The algebraic arm goes +through `compress` / `permute`, which ARE `Instr::Hash`, executed by whatever is +passed. + +### `REGISTRY_HASHER` and the classification rule + +`registry::build_artifacts` defaults to `REGISTRY_HASHER = HasherKind::Test`, +the permutation `LFM_REGISTRY` is blessed under, and the generator +(`compute_lfm_registry`) imports that same constant, so the blessed value and +the builder default are one definition. The hasher is part of program IDENTITY +(`HasherKind::as_tag` is folded into `lfm_program_id`), so the block path names +its hasher AT THE CALL SITE instead: + +> **A program built at `WrapHash::production()` emits `Instr::Hash` and must be +> proved under `BLOCK_HASHER`. A program that pins a byte hash on its own +> builder emits none, never consults the socket, and is correct at the +> registry's blessed default under every pin.** + +It is *checkable*, not a judgement — read which program the site builds. The 17 +block-path sites (`wrap_tests` 8, `aggregator_tests` 7, `fri_tests` 1, +`join_tests` 1) name `BLOCK_HASHER` through `build_artifacts_with_hasher`; +`wrap_tests`' keccak-chain census site keeps the default because +`keccak_chain_program` pins keccak on its own builder. The registry-identity +suites (`machine_tests`, the chip suites) keep the default too — that is what +`registry_drift_*` compares against. + +⛔ **Do not "fix" a `registry_drift_*` failure by re-blessing the registry under +the pin.** Beyond violating the table's own doctrine — *a second hasher becomes +additional ROWS, never a silent replacement* — it would move registry +identities on the BLAKE3 control, converting "control drifted → STOP and +investigate" into a self-inflicted alarm on the one measurement the comparison +turns on. `build_artifacts` was briefly made to name `BLOCK_HASHER` itself; +that fixed a real aggregator defect at the wrong scope, and every registry +identity moved. + +### Enforcement in-tree + +`prover/src/tests/hash_pin_enumeration.rs` scans the crate for any code line +reaching `DefaultStarkHash`, `DefaultStarkTranscript` or `HasherKind::default()`, +any `Prover::multi_prove` / `Verifier::multi_verify` call that is not the +`BlockProver::` / `BlockVerifier::` spelling, and any item taken from +`stark::config` outside the hash-agnostic allowlist (`Commitment`, +`CommitmentHash`, `StarkHash`, `DeviceTreeBackend`) — an allowlist over a +namespace, because a name list always lags one spelling behind the newest way +to denote the default. A new site fails the test and names itself. What the +gate cannot see is a site that names a hash explicitly and names the *wrong* +one; the instruments for that are `hash_pin::tests` and the differentials in +`algebraic_commit` / `algebraic_transcript`. + +### The arena stride is the BUILDER's digest width + +`SubProofShape::{query_words, opening_words}`, `FriShape::query_words` and +`TableVerifyShape::{opening_words, fri_words}` take the digest width as an +argument. The machine side passes `edsl::digest_words(b)` — the builder's width, +the one every emitter advances its cursor by — and the host side passes +`proof_arena::words_per_root()`, the width it serialises roots at. A shape that +read the configuration instead agreed with a builder at `WrapHash::production()` +and disagreed with any other, and the executor's arena-length check is strict: +a program declaring a roots arena at a literal two words per digest is an +`ArenaLenMismatch` under this pin, not a slow path. + +## `cuda` on an algebraic pin — COMPILES, and cannot prove under the wrong hash + +`--features cuda` builds on this branch. The algebraic backends are +`DeviceTreeBackend`s carrying their own `CommitmentHash` as the device dispatch +key, so the type system pairs a device tree with the permutation it was named +for and cannot produce a keccak tree *labelled* RPX. RPX256 has device kernels +(`math_cuda::rpx`, with the leaf and tree dispatch arms wired), so a GPU run +under THIS pin commits on the device. RPO256 and Poseidon do not yet: a GPU run +under one of those pins aborts at its first device commit with `unimplemented!` +naming the hash — loud, at launch, naming the cause. + +⛔ **Neither a `compile_error!` nor a byte-hash fallback belongs here.** The +first hides the cuda lint arm from the branch, which is how a dispatch +regression would reach main unseen; the second is exactly the silent wrong-hash +build this pin exists to make impossible. A build that aborts is safe; a build +that quietly proves under the wrong hash is not. + +**Lint standard on this branch: BOTH passes gate.** `make lint`'s cuda +combination is a real signal here, unlike on the pre-dispatch `hash-rpo` cut +where it was expected to fail. + +**Consequence for box work:** the RPX pin proves on the GPU; the first +end-to-end GPU prove under it (the wrap under `cuda`) is the milestone gate after +the pin lands. An RPO or Poseidon pin still means CPU-only proving. + +## ⚠ TWO REGENERATIONS — in THIS order, plus one stray constant + +Every root blessed under BLAKE3 has to be regenerated. There are two families of +them and the order between them is load-bearing. This is why the pin PR is large +and mostly generated tables — that is EXPECTED, not a mistake. + +1. **The static preprocessed commitments — FIRST.** FOUR families, not three: + `bitwise`, `keccak_rc`, and `page`'s zero-page AND private-page (OFFSET-only) + constants, each at blowup 2/4/8. Each returns a BLESSED CONSTANT from + `preprocessed_commitment` rather than recomputing, so under a new pin the + prover recomputes an RPX root, compares it against a BLAKE3 constant and + fails with `ProvingError::PrecomputedCommitmentMismatch`. + `cargo run --bin compute_static_commitments --release`, then paste. +2. **`LFM_REGISTRY` — SECOND, only once the statics are in the tree.** The + hasher and the commitment hash are both folded into every `program_id`. + `cargo run --bin compute_lfm_registry --release`. Per entry the `roots`, + `program_id` and `prep_root` move; `log_heights`, `prep_widths`, `chip_set`, + `keccak_rnd_chunks` and `hasher` (`Test`, the registry's own) must not. + ⛔ **Why the order:** `registry.rs` fills slots 13 and 14 of every entry from + `keccak_rc` and `bitwise`'s `preprocessed_commitment` — the blessed constants + of item 1, not a recomputation — and `lfm_program_id` folds every root. A + registry generated before the statics were pasted embeds the OUTGOING hash's + constants in those two slots and in `program_id`, and + `machine_tests::registry_drift_*` fires at exactly slots 13 and 14. ✓ It did, + on this pin's first regeneration. The control-first re-run under the + outgoing pin cannot see this: both tables are self-consistent there. +3. **`SUB_DECODE_COMMITMENT_BLOWUP_2`** (`prover/src/tests/decode_tests.rs`) — + a test-local blessed constant outside both generators: the `sub` ELF's + DECODE commitment at blowup 2, embedded the way the recursion guest embeds + its inner program's. Regenerated by the `#[ignore]` test + `print_decode_commitment_for_sub` (`--ignored --nocapture`). + +That failure is the **trial flip** this pin's PR performs on purpose: flip the +four `hash_pin.rs` lines without regenerating, run the crate's own prove/verify +legs, and expect the mismatch — loud, at prove time, naming the cause. A trial +flip that is GREEN before regeneration means the static-commitment path was not +exercised; treat that as a coverage hole, not as good news. + +★ **Regenerate control-first.** Run each regenerator under the outgoing pin and +confirm it reproduces the existing table byte for byte (rustfmt's trailing +commas are the only expected textual difference) BEFORE trusting it on RPX. + +⛔ **AND THAT IS ALL THE CONTROL PROVES.** `compute_lfm_registry` names +`REGISTRY_HASHER` explicitly and never reads `build_artifacts`, so re-running it +validates the generator **against itself**. When `build_artifacts` was briefly +changed to name `BLOCK_HASHER`, every registry `program_id` moved and this +control reproduced byte-for-byte anyway — it could not have fired. **The check +that fires is `machine_tests::registry_drift_*`**, because it recomputes from +the changed path and compares against the blessed table. A self-consistency +check and an independent check are not substitutes, and quoting the first for a +claim only the second can support is how a green number gets trusted for +something it never examined. A drift failure is investigated, never re-blessed +to silence the test, and neither table is ever hand-edited. + +## ⚠ THE ONE WIDTH DEFECT THAT COULD HAVE PASSED + +Every digest-width defect on the algebraic migration failed loudly, and there +is a reason rather than luck: the machine reconstructs a root matching nothing, +and nothing downstream can proceed. **One shape sidestepped reconstruction +entirely.** `fri_tests`' leaf gate published its digest as two cells and +compared them pairwise. An algebraic `WrapDigest` is ONE cell whose second slot +**repeats the first** (`WrapDigest::from_cell`), so the comparison read one lane +twice and would have **passed on a duplicated value** — a green test asserting +nothing, in the one place whose entire claim is that the machine's leaf IS the +verifier's leaf. It now publishes the digest's own cells. Re-audit any new +comparison that could pass on a repeated cell; `edsl::keccak256` and the BLAKE3 +chain return `[Cell; 2]` because those digests genuinely are two cells. + +## RUNNING UNDER THIS PIN + +- **The gates the pin PR ran** (CPU, box B): `machine_tests::registry_drift_*` + unchanged, `hash_pin`, `tests::hash_pin_enumeration`, + `fri_tests::the_fri_leg_proves_and_verifies`, + `join_tests::the_join_proves_and_verifies`, + `wrap_tests::the_fixture_epoch_wraps`, the four grinding differentials in + `algebraic_commit.rs`, then the full `--lib` suite and `make lint` on both + arms. The GPU wrap under `cuda` is the gate after the merge (see the cuda + section). +- **The arena stride is the BUILDER's digest width on both sides.** Emitters + read `edsl::digest_words(b)`; host serialisers read `words_per_root()` for a + configuration-following program and `commitments_to_arena_for(roots, hash)` + for a program that pins a byte hash on its own builder. A literal two words + per root is an `ArenaLenMismatch` under this pin, not a slow path — the + executor's arena-length check is strict. +- **`P3_ARTIFACT_DIR` must be a FRESH directory for any block run.** The block + driver *loads* cached artifacts when it finds them, so a directory carrying a + BLAKE3 run's bundle and wraps would feed byte-hash proofs to an RPX verifier. + A fresh directory still persists artifacts, so an aggregation OOM does not + cost the first hour again. +- The fixture cache is separate and IS keyed on the pin: + `proof_fixture::cache_format_key()` reads `BLOCK_COMMITMENT_HASH`, so this pin + gets its own blob for free. +- **Proof BYTES do not reproduce run to run** (grinding draws a nonce + non-deterministically); roots do. Never `sha256`-compare proofs. +- **Numbers:** memory and time are separate verdicts on separate lines, block + level only. The BLAKE3 batched record the hash comparison is measured against + (`hash-blake3`, `HASH-PINNING.md` there: 104.2 min wall, 358.2 GiB peak RSS, + 36.9 MB block proof) is the control, and this branch's per-table prover is a + different aggregator from the one that set it — a per-table number and a + batched number are not comparable, and no projected RPX line is carried over + from the RPO pin. + +## KNOWN RED UNDER THIS PIN — out of the pin PR's scope, by decision + +At the pin PR's head the full `--lib` suite is green except for these, each +recorded here so nobody rediscovers them from a red run: + +| test | mechanism | disposition | +|---|---|---| +| `epoch_tests::the_batched_query_census_matches_the_closed_form`, `epoch_tests::the_assembled_carved_batched_epoch_verifier_runs` | the census closures count `Instr::KeccakF` / `Instr::Blake3` only; the algebraic wrap hash is `Instr::Hash`, and `batched_query_permutations_for` has no algebraic arm | byte-hash counter model; algebraic arm owed | +| `fri_tests::the_emitted_permutation_count_meets_the_pinned_prediction`, `fri_tests::the_fri_join_adds_no_second_point_derivation` | "six component byteswaps per layer" and the leaf-swap decompositions are byte-encoding costs; an algebraic leaf needs none | byte-hash counter model; algebraic arm owed | +| `machine_tests::transcript_replay_cell_counts`, `machine_tests::register_derivation_cost` | `wrap_hash_rows` dispatches on `WrapHash::production()`, not on the PROGRAM's own builder hash, so a registry program pinned to BLAKE3 counts zero rows under an algebraic pin | the helper must read the program's hash | +| `machine_tests::the_register_derivation_matches_production`, `machine_tests::the_register_derivation_proves_and_verifies` | `register_derivation_program` is built at `WrapHash::Blake3` and has no algebraic arm, while production's REGISTER commitment now follows the pin | ⚠ FEATURE GAP (the machine REGISTER derivation under an algebraic hash), planned as its own item, not a test fix | +| `per_table_census_tests::the_blake3_tenant_socket_matches_the_record` | lane C's guard, firing correctly: the recorded census — and the lever-0 figure of record it anchors — was produced under the Test/BLAKE3 socket, and this build's socket is RPX | re-record the census under the pin (follow-up); do NOT weaken the guard | +| `epoch_tests::the_closure_rejects_a_moved_index_or_output` | the fixture epoch reports an empty public output; fails at the pre-pin head too | PRE-EXISTING on `per-table-gpu`, not the pin's | + +Poseidon is **UNSHIPPABLE** (broken family, eprint 2026/306 and 2026/1692) and +remains a reference column only; XHash8 is flagged and **not adopted**. diff --git a/prover/src/bin/compute_lfm_registry.rs b/prover/src/bin/compute_lfm_registry.rs index f032825d9..ddc06ef70 100644 --- a/prover/src/bin/compute_lfm_registry.rs +++ b/prover/src/bin/compute_lfm_registry.rs @@ -4,9 +4,16 @@ //! output over the generated block in `prover/src/lfm/registry.rs`. Drift //! tests recompute and compare on every PR; a drift failure is investigated, //! never re-blessed (the `compute_static_commitments` policy). +//! +//! ⚠ ORDER: on a hash-pin change run this AFTER `compute_static_commitments` +//! has been run and its output pasted. Slots 13 and 14 of every entry are +//! `keccak_rc` and `bitwise`'s `preprocessed_commitment`, which return the +//! BLESSED static constants in the tree rather than recomputing, and +//! `program_id` folds every root — so a table generated before the statics +//! embeds the outgoing hash's constants, and `machine_tests::registry_drift_*` +//! fires at exactly those two slots. use lambda_vm_prover::GoldilocksCubicProofOptions; -use lambda_vm_prover::lfm::hash::HasherKind; use lambda_vm_prover::lfm::programs::{ KECCAK_SPONGE_LEN, fri_toy_program, keccak_chain_program, keccak_sponge_program, statement_replay_program, transcript_replay_program, trivial_program, @@ -18,12 +25,10 @@ use lambda_vm_prover::lfm::validate; /// other presets come online). const REGISTRY_BLOWUP_FACTORS: &[u8] = &[2]; -/// The `LFM_HASH` permutation the v0 registry is generated under. -/// -/// Bound into every digest below, so changing it here is a re-blessing of the -/// whole table, not a re-run. A second hasher becomes additional rows, never a -/// silent replacement of these. -const REGISTRY_HASHER: HasherKind = HasherKind::Test; +// The permutation this table is blessed under is `registry::REGISTRY_HASHER` — +// a property of the TABLE rather than of this generator, and the same constant +// `build_artifacts` defaults to, so the two cannot drift apart. +use lambda_vm_prover::lfm::registry::REGISTRY_HASHER; fn fmt_bytes(bytes: &[u8; 32]) -> String { let inner = bytes diff --git a/prover/src/bin/compute_static_commitments.rs b/prover/src/bin/compute_static_commitments.rs index a4de1ddaa..3f7bc9fa7 100644 --- a/prover/src/bin/compute_static_commitments.rs +++ b/prover/src/bin/compute_static_commitments.rs @@ -1,17 +1,25 @@ -//! Prints static `(bitwise, keccak_rc, zero_page)` preprocessed-table commitments -//! for a fixed set of `blowup_factor` values. The output is pasted into the +//! Prints the static preprocessed-table commitments — FOUR families: `bitwise`, +//! `keccak_rc`, and `page`'s zero-page and private-page (OFFSET-only) constants +//! — for a fixed set of `blowup_factor` values. The output is pasted into the //! `static_commitment` match bodies in `prover/src/tables/{bitwise,keccak_rc}.rs` -//! and the `static_zero_page_commitment` match body in `prover/src/tables/page.rs`. +//! and the `static_zero_page_commitment` / `static_private_page_commitment` +//! match bodies in `prover/src/tables/page.rs`. //! The `static_commitments_tests` test suite pins the values so any drift in //! the AIR or FFT pipeline is caught at test time. //! //! Run with: //! cargo run --bin compute_static_commitments --release //! +//! ⚠ On a hash-pin change run this FIRST and paste before `compute_lfm_registry`: +//! the registry embeds these constants (slots 13 and 14 of every entry, and +//! `program_id` folds them), so a registry generated before the paste carries +//! the outgoing hash's statics and the drift gate catches it. +//! //! ⚠️ Do not run this just to silence a failing drift test — see the //! "Regenerating" section on `static_commitment` in `bitwise.rs` / -//! `keccak_rc.rs` and `static_zero_page_commitment` in `page.rs` for when -//! it's actually appropriate to bless new bytes. +//! `keccak_rc.rs` and the two `page.rs` constants for when it's actually +//! appropriate to bless new bytes. A hash-pin change is one such time, and it +//! regenerates all four families together (`prover/src/hash_pin.rs`). use lambda_vm_prover::tables::{STATIC_BLOWUP_FACTORS, bitwise, keccak_rc, page}; use stark::config::Commitment; @@ -37,7 +45,8 @@ fn main() { println!( "// Paste these match arms into the `static_commitment` match bodies\n\ // in `prover/src/tables/{{bitwise,keccak_rc}}.rs` and the\n\ - // `static_zero_page_commitment` match body in `prover/src/tables/page.rs`.\n" + // `static_zero_page_commitment` / `static_private_page_commitment`\n\ + // match bodies in `prover/src/tables/page.rs`.\n" ); let zero_page_config = page::PageConfig::zero_init(0); diff --git a/prover/src/continuation.rs b/prover/src/continuation.rs index db651e6b0..29da614f9 100644 --- a/prover/src/continuation.rs +++ b/prover/src/continuation.rs @@ -2489,7 +2489,15 @@ mod tests { let _ = done_tx.send(r.map(|_| ())); }); let result = done_rx - .recv_timeout(std::time::Duration::from_secs(300)) + // 1800 s of headroom rather than 300. This is a liveness guard — the + // regression it catches wedges the pipeline FOREVER, so any finite + // bound still catches it — and the bound has to clear an honest run + // under load: an algebraic hash pin doubles this test's own proving + // work (alone, three runs each: 7.3-7.6 s at the BLAKE3 default, + // 15.7-15.9 s under RPX), and inside the full `--lib` suite's + // parallel load the old 300 s fired while the test was still making + // progress. + .recv_timeout(std::time::Duration::from_secs(1800)) .expect("prove_continuation wedged: the pipeline did not shut down on error"); let err = result.expect_err("the injected fault must surface as Err"); assert!( diff --git a/prover/src/hash_pin.rs b/prover/src/hash_pin.rs index debe390bb..051e8f96b 100644 --- a/prover/src/hash_pin.rs +++ b/prover/src/hash_pin.rs @@ -56,32 +56,46 @@ //! //! # `cuda` on an algebraic pin //! -//! Compiles, and still cannot prove under the wrong hash. The algebraic -//! backends are `DeviceTreeBackend`s carrying their own `CommitmentHash` as the -//! device dispatch key, and `math-cuda` has no kernels for those permutations -//! yet, so a GPU run under an algebraic pin aborts at its first device commit +//! Compiles, and cannot prove under the wrong hash. The algebraic backends are +//! `DeviceTreeBackend`s carrying their own `CommitmentHash` as the device +//! dispatch key, so a device tree is built by the kernels of the hash it is +//! named for or not built at all. RPX256 has those kernels (`math_cuda::rpx`), +//! so a GPU run under this pin commits on the device; RPO256 and Poseidon do +//! not yet, and a GPU run under one of them aborts at its first device commit //! with `unimplemented!` naming the hash. ⛔ Neither a `compile_error!` nor a //! byte-hash fallback belongs here: the first hides the cuda lint arm from the //! branch, the second is exactly the silent wrong-hash build this module exists -//! to make impossible. Proving a block under an algebraic pin on GPU means -//! landing the kernels, and nothing less. +//! to make impossible. //! -//! # ⚠ TWO regenerations, not one +//! # ⚠ TWO regenerations, not one — in THIS order, plus one stray constant //! //! A pin change is **not** complete until every root blessed under the old hash -//! is regenerated, and there are two families of them: +//! is regenerated. There are two families of them and the order is load-bearing: //! -//! 1. **`LFM_REGISTRY`** — the hasher is folded into every `program_id`. -//! `cargo run --bin compute_lfm_registry --release`. -//! 2. **The static preprocessed commitments** — `bitwise`, `keccak_rc` and -//! `page` each return a BLESSED CONSTANT from `preprocessed_commitment` +//! 1. **The static preprocessed commitments — FIRST.** FOUR families: `bitwise`, +//! `keccak_rc`, and `page`'s zero-page AND private-page constants, at blowup +//! 2/4/8. Each returns a BLESSED CONSTANT from `preprocessed_commitment` //! rather than recomputing, so under a new pin the prover recomputes an -//! RPO root, compares it against a BLAKE3 constant, and fails with +//! algebraic root, compares it against a BLAKE3 constant, and fails with //! `ProvingError::PrecomputedCommitmentMismatch`. -//! `cargo run --bin compute_static_commitments --release`. -//! -//! ✓ VERIFIED (2) empirically: it is exactly how the trial flip failed, and it -//! is the correct failure — loud, at prove time, naming the cause. `registry.rs` +//! `cargo run --bin compute_static_commitments --release`, then paste. +//! 2. **`LFM_REGISTRY` — SECOND, only once the statics are in the tree.** +//! `registry.rs` fills slots 13 and 14 of every entry from `keccak_rc` and +//! `bitwise`'s `preprocessed_commitment` — the blessed constants above, not a +//! recomputation — and `lfm_program_id` folds every root. A registry generated +//! before the statics were pasted therefore embeds the OUTGOING hash's +//! constants, and `machine_tests::registry_drift_*` fires at exactly those two +//! slots. The control-first re-run under the outgoing pin cannot see this: +//! both tables are self-consistent there. +//! `cargo run --bin compute_lfm_registry --release`. +//! 3. **`SUB_DECODE_COMMITMENT_BLOWUP_2`** in `tests/decode_tests.rs` — a +//! test-local blessed constant outside both generators, regenerated by the +//! `#[ignore]` test `print_decode_commitment_for_sub`. +//! +//! ✓ VERIFIED (1) empirically: it is exactly how the trial flip failed, and it +//! is the correct failure — loud, at prove time, naming the cause. ✓ VERIFIED +//! (2) empirically too: the first RPX regeneration ran the registry before the +//! statics and all six drift tests fired at slots 13 and 14. `registry.rs` //! governs both: a drift failure is investigated, never re-blessed to silence //! the test, and neither table is ever hand-edited. @@ -90,14 +104,13 @@ /// Every `multi_prove` / `multi_verify` instantiation in this crate names this /// rather than `stark::config::DefaultStarkHash`, so the two can differ on a /// branch without the workspace default moving. -pub type BlockStarkHash = stark::config::DefaultStarkHash; +pub type BlockStarkHash = crate::lfm::algebraic_commit::RpxStarkHash; /// The Fiat–Shamir transcript OBJECT the block path builds. /// /// See the module header for why this is pinned separately from /// [`BlockStarkHash`] rather than derived from it. -pub type BlockTranscript = - stark::config::DefaultStarkTranscript; +pub type BlockTranscript = crate::lfm::algebraic_transcript::AlgebraicTranscript; /// A fresh block-path transcript over `seed`. /// @@ -106,7 +119,7 @@ pub type BlockTranscript = /// algebraic one absorbs it as its first `append_bytes` call. Callers should not /// have to know which. pub fn block_transcript(seed: &[u8]) -> BlockTranscript { - BlockTranscript::new(seed) + BlockTranscript::with_seed(BLOCK_HASHER, seed) } /// The prover the block path drives, at [`BlockStarkHash`]. @@ -145,7 +158,7 @@ pub type BlockVerifier = /// Every `execute` and prove call on the block path names this rather than a /// literal, so the two axes cannot drift apart in a test harness while /// production stays correct. -pub const BLOCK_HASHER: crate::lfm::hash::HasherKind = crate::lfm::hash::HasherKind::Test; +pub const BLOCK_HASHER: crate::lfm::hash::HasherKind = crate::lfm::hash::HasherKind::Rpx; /// The [`CommitmentHash`] the block path's roots may be called by. /// diff --git a/prover/src/lfm/aggregator_tests.rs b/prover/src/lfm/aggregator_tests.rs index 14d48c405..6183a5f3c 100644 --- a/prover/src/lfm/aggregator_tests.rs +++ b/prover/src/lfm/aggregator_tests.rs @@ -36,7 +36,7 @@ use super::epoch::RootCells; use super::executor::execute; use super::instr::ArenaId; use super::proof::{BatchedLfmProof, aggregation_wrap_options, verify_against_batched}; -use super::registry::{LfmArtifacts, build_artifacts}; +use super::registry::{LfmArtifacts, build_artifacts_with_hasher}; use super::statement::{LFM_MACHINE_VERSION, LFM_STATEMENT_TAG, absorb_lfm_statement}; use super::transcript_replay::{Candidate, TranscriptReplay, assert_canonical, candidate_to_felt}; use super::word::{LfmWord, base_word, ext_word}; @@ -1679,7 +1679,7 @@ fn fixture_leg() -> (RealBatchedLfm, LfmProgram) { let opts = aggregation_wrap_options(); let program = trivial_program(); - let artifacts = build_artifacts(&program, &opts); + let artifacts = build_artifacts_with_hasher(&program, &opts, crate::hash_pin::BLOCK_HASHER); let arenas: Vec> = vec![ (0..4u64) .map(|i| core::array::from_fn(|j| FE::from(1_000 * (i + 1) + j as u64))) @@ -1877,7 +1877,7 @@ fn fixture_wraps() -> ( let mut arenas = super::epoch_tests::batched_epoch_arenas(&e); arenas.push(super::epoch_verify_tests::batched_opening_arena(&e)); arenas.push(super::epoch_verify_tests::batched_fri_arena(&e)); - let artifacts = build_artifacts(&program, &opts); + let artifacts = build_artifacts_with_hasher(&program, &opts, crate::hash_pin::BLOCK_HASHER); let proved = match lfm_prove_batched(&program, &artifacts, &arenas, &opts) { Ok(p) => p, Err(e) => { @@ -1925,7 +1925,7 @@ fn fixture_aggregate() -> FixtureAggregate { let g = real_global(&elf_bytes, &bundle, &inner); let program = global_verifier_program(&g); let arenas = global_arena_words(&g); - let artifacts = build_artifacts(&program, &opts); + let artifacts = build_artifacts_with_hasher(&program, &opts, crate::hash_pin::BLOCK_HASHER); let proved = lfm_prove_batched(&program, &artifacts, &arenas, &opts) .expect("the global wrap must prove batched at the aggregation preset"); let global_wrap = real_batched_lfm(artifacts, opts, &proved); @@ -2195,7 +2195,7 @@ fn the_aggregate_leg_census_matches_the_closed_form() { let opts = aggregation_wrap_options(); let program = trivial_program(); - let artifacts = build_artifacts(&program, &opts); + let artifacts = build_artifacts_with_hasher(&program, &opts, crate::hash_pin::BLOCK_HASHER); let arenas: Vec> = vec![ (0..4u64) .map(|i| core::array::from_fn(|j| FE::from(1_000 * (i + 1) + j as u64))) @@ -2420,7 +2420,8 @@ fn the_real_block_aggregates_end_to_end() { let mut arenas = super::epoch_tests::batched_epoch_arenas(&e); arenas.push(super::epoch_verify_tests::batched_opening_arena(&e)); arenas.push(super::epoch_verify_tests::batched_fri_arena(&e)); - let artifacts = build_artifacts(&program, &agg_opts); + let artifacts = + build_artifacts_with_hasher(&program, &agg_opts, crate::hash_pin::BLOCK_HASHER); let wrap_file = format!("wrap_{k}.rkyv"); let cached = cache_path(&wrap_file).is_some_and(|p| p.exists()); let tp = Instant::now(); @@ -2454,7 +2455,8 @@ fn the_real_block_aggregates_end_to_end() { let g = real_global(&inputs.elf_bytes, &bundle, &inner); let g_program = global_verifier_program(&g); let g_arenas = global_arena_words(&g); - let g_artifacts = build_artifacts(&g_program, &agg_opts); + let g_artifacts = + build_artifacts_with_hasher(&g_program, &agg_opts, crate::hash_pin::BLOCK_HASHER); let g_cached = cache_path("global_wrap.rkyv").is_some_and(|p| p.exists()); let tp = Instant::now(); let g_proved = if g_cached { @@ -2583,7 +2585,8 @@ fn the_real_block_aggregates_end_to_end() { Err(_) => agg_opts.clone(), }; let t = Instant::now(); - let agg_artifacts = build_artifacts(&program, &terminal_opts); + let agg_artifacts = + build_artifacts_with_hasher(&program, &terminal_opts, crate::hash_pin::BLOCK_HASHER); println!( " aggregation artifacts built in {:.1}s", t.elapsed().as_secs_f64() diff --git a/prover/src/lfm/blake3_chip_tests.rs b/prover/src/lfm/blake3_chip_tests.rs index 349b92249..8f639f4e0 100644 --- a/prover/src/lfm/blake3_chip_tests.rs +++ b/prover/src/lfm/blake3_chip_tests.rs @@ -502,7 +502,8 @@ fn tampering_with_the_blake3_witness_is_not_accepted() { let program = blake3_sponge_program(65); let artifacts = build_artifacts(&program, &opts); let exec = execute(&program, &sponge_arenas(&msg), &TestPermutation).expect("execute"); - let mut traces = super::trace::build_traces(&program, &exec.records); + let mut traces = + super::trace::build_traces_with_hasher(&program, &exec.records, artifacts.hasher); // One output byte of the first compression. let col = cols::out_word(0, 0); @@ -720,10 +721,9 @@ fn the_merkle_constructions_agree_with_the_host_under_both_hashes() { b.public(root[1]); let program = compile(b.finish()); - let arena_words: Vec = leaves - .iter() - .flat_map(super::proof_arena::commitment_words) - .collect(); + // At THIS program's width (a byte hash, chosen on its builder above), + // not the configuration's: under an algebraic pin the two differ. + let arena_words: Vec = super::proof_arena::commitments_to_arena_for(&leaves, hash); let exec = execute(&program, &[arena_words], &TestPermutation) .unwrap_or_else(|e| panic!("{hash:?}: the tree build must execute: {e:?}")); assert_eq!( @@ -1596,7 +1596,7 @@ fn blake3_chunking_splits_the_chain_into_uneven_chunks() { &TestPermutation, ) .expect("honest execution"); - let traces = super::trace::build_traces(&program, &exec.records); + let traces = super::trace::build_traces_with_hasher(&program, &exec.records, artifacts.hasher); assert_eq!(traces.blake3.len(), 3, "one LFM_BLAKE3 trace per chunk"); assert_eq!( traces @@ -1780,7 +1780,8 @@ fn a_tampered_non_first_blake3_chunk_rejects() { let artifacts = build_artifacts(&program, &opts); let exec = execute(&program, &sponge_arenas(&msg), &TestPermutation).expect("execute"); - let mut traces = super::trace::build_traces(&program, &exec.records); + let mut traces = + super::trace::build_traces_with_hasher(&program, &exec.records, artifacts.hasher); assert_eq!(traces.blake3.len(), 3); // One output byte of the LAST chunk's first compression — the eleventh of // the twelve, which no other chunk carries. diff --git a/prover/src/lfm/blake3_probe.rs b/prover/src/lfm/blake3_probe.rs index f7b1834c4..6e786f3ad 100644 --- a/prover/src/lfm/blake3_probe.rs +++ b/prover/src/lfm/blake3_probe.rs @@ -26,8 +26,6 @@ //! The permutation count comes from wave 8's rate-parameterised closed form //! and is inherited, not re-established here. //! - **Anything cryptographic about the 6-round variant** (assumption A6R). - -use crypto::fiat_shamir::default_transcript::DefaultTranscript; use crypto::fiat_shamir::is_transcript::IsTranscript; use stark::config::Commitment; use stark::constraints::builder::{ @@ -39,10 +37,10 @@ use stark::lookup::{ }; use stark::proof::options::{GoldilocksCubicProofOptions, ProofOptions}; use stark::proof::view::MultiProofView; -use stark::prover::{IsStarkProver, Prover}; +use stark::prover::IsStarkProver; use stark::trace::TraceTable; use stark::traits::AIR; -use stark::verifier::{IsStarkVerifier, Verifier}; +use stark::verifier::IsStarkVerifier; use crate::tables::bitwise; use crate::tables::types::{BusId, FE, FEE, GoldilocksExtension, GoldilocksField, VmTable}; @@ -70,8 +68,8 @@ fn options() -> ProofOptions { GoldilocksCubicProofOptions::with_blowup(2).expect("probe options") } -fn transcript() -> DefaultTranscript { - let mut t = DefaultTranscript::::new(&[]); +fn transcript() -> crate::hash_pin::BlockTranscript { + let mut t = crate::hash_pin::block_transcript(&[]); t.append_bytes(PROBE_TAG); t } @@ -268,7 +266,7 @@ fn prove_traces( let pairs: Vec<(DynAir, &mut TraceTable, &())> = vec![(chip, t0, &()), (&mirror, t1, &()), (&bw_air, t2, &())]; let mut t = transcript(); - Prover::multi_prove( + crate::hash_pin::BlockProver::multi_prove( pairs, &mut t, #[cfg(feature = "disk-spill")] @@ -289,7 +287,12 @@ fn verify_proof( ); let refs: Vec = vec![chip, &mirror, &bw_air]; let mut vt = transcript(); - Verifier::multi_verify_views(&refs, MultiProofView::Owned(proof), &mut vt, &FEE::zero()) + crate::hash_pin::BlockVerifier::multi_verify_views( + &refs, + MultiProofView::Owned(proof), + &mut vt, + &FEE::zero(), + ) } /// Prove + verify, optionally corrupting the chip trace in between. diff --git a/prover/src/lfm/epoch_tests.rs b/prover/src/lfm/epoch_tests.rs index 0c3872bfb..ae5aaec16 100644 --- a/prover/src/lfm/epoch_tests.rs +++ b/prover/src/lfm/epoch_tests.rs @@ -3207,17 +3207,19 @@ fn the_assembled_batched_epoch_verifier_runs() { /// emitter, so the comparison against the compiled program is absolute. fn expected_batched_arena_words(e: &RealBatchedEpoch, with_legs: bool) -> usize { let num_reg = crate::tables::register::NUM_REGISTER_ADDRESSES; + // A root's width in arena words (see `expected_arena_words`). + let dw = super::proof_arena::words_per_root(); let mut total = 8 + e.statement.public_output_len.div_ceil(4) + 2; - total += 2 * e - .prep_sources - .iter() - .filter(|p| p.is_some_and(PrepSource::is_arena)) - .count(); - total += 2 * usize::from(e.shape.carved_main.is_some()); // the carved root - total += 2; // main_root — ONE, which is the whole batched economy + total += dw + * e.prep_sources + .iter() + .filter(|p| p.is_some_and(PrepSource::is_arena)) + .count(); + total += dw * usize::from(e.shape.carved_main.is_some()); // the carved root + total += dw; // main_root — ONE, which is the whole batched economy total += 2 * num_reg; total += 2; // pc_start - total += 2 * usize::from(e.proof.aux_root.is_some()); + total += dw * usize::from(e.proof.aux_root.is_some()); total += e .proof .tables @@ -3229,13 +3231,13 @@ fn expected_batched_arena_words(e: &RealBatchedEpoch, with_legs: bool) -> usize total += t.trace_ood_next_evaluations.width * t.trace_ood_next_evaluations.height; total += t.composition_poly_parts_ood_evaluation.len(); } - total += 2; // parts_root + total += dw; // parts_root for t in &e.proof.tables { if let Some(coeffs) = t.standalone_final_poly_coeffs.as_ref() { total += coeffs.len(); } } - total += 2 * e.proof.fri_layer_roots.len(); + total += dw * e.proof.fri_layer_roots.len(); total += e.proof.fri_final_poly_coeffs.len(); total += usize::from(e.fri_params.grinding_factor > 0); if with_legs { @@ -3690,7 +3692,10 @@ fn epoch_program_with(e: &RealEpoch, with_legs: bool, split_decode: bool) -> Lfm }) .collect(); // Last in declaration order, so turning the control on shifts no other arena. - let a_split_decode = split_decode.then(|| b.declare_arena(2)); + // One root, at THIS builder's digest width — the width `RootCells::hint` + // reads it back at. + let root_words = RootCells::words_per_root(&b); + let a_split_decode = split_decode.then(|| b.declare_arena(root_words)); // ---- the statement ---- let stmt: Vec<_> = (0..stmt_halves as u32) @@ -4344,33 +4349,38 @@ fn the_spine_hints_each_proof_value_once() { /// comparison against the compiled program is absolute. fn expected_arena_words(e: &RealEpoch, with_legs: bool) -> usize { let num_reg = crate::tables::register::NUM_REGISTER_ADDRESSES; + // A root's width in arena words — the host's counterpart of the emitter's + // `digest_words`: two on a byte hash, one on an algebraic one. The register + // vectors, `pc_start` and the page bases are NOT roots and keep their own + // widths below. + let dw = super::proof_arena::words_per_root(); let mut total = 8 + e.statement.public_output_len.div_ceil(4) + 2; - // ★ Two words per ELF-DEPENDENT preprocessed root and NOT ONE MORE. The - // options-only roots are program text and the REGISTER root is derived, so a - // program that hinted any of them — or that kept a second copy of DECODE for - // the attestation fold — declares more words than this. - total += 2 * e - .phase_a - .iter() - .filter(|(p, _)| p.is_some_and(PrepSource::is_arena)) - .count(); - total += 2 * e.tables.len(); + // ★ One root's width per ELF-DEPENDENT preprocessed root and NOT ONE MORE. + // The options-only roots are program text and the REGISTER root is derived, + // so a program that hinted any of them — or that kept a second copy of + // DECODE for the attestation fold — declares more words than this. + total += dw + * e.phase_a + .iter() + .filter(|(p, _)| p.is_some_and(PrepSource::is_arena)) + .count(); + total += dw * e.tables.len(); total += 2 * num_reg; total += 2; total += 10 * e.page_commitments.len(); for (h, leg) in e.tables.iter().zip(&e.legs) { let s = &h.shape; - total += 2 * usize::from(s.has_aux_root); + total += dw * usize::from(s.has_aux_root); total += usize::from(s.has_contribution); - total += 2; + total += dw; total += s.ood_current_dims.0 * s.ood_current_dims.1; total += s.ood_next_dims.0 * s.ood_next_dims.1; total += s.num_parts; - total += 2 * s.fri.num_committed(); + total += dw * s.fri.num_committed(); total += s.fri.num_terminal_coeffs(); total += usize::from(s.grinding_factor > 0); if with_legs { - total += leg.verify.opening_words() + leg.verify.fri_words(); + total += leg.verify.opening_words(dw) + leg.verify.fri_words(dw); } } total @@ -4411,9 +4421,9 @@ fn the_assembled_verifier_declares_exactly_the_shape_words() { let split_declared: usize = split.arena_schema.lens.iter().map(|l| *l as usize).sum(); assert_eq!( split_declared, - expected_arena_words(&e, false) + 2, - "the split-cell control must declare exactly two surplus words, or it is \ - not the forgery this guard claims to deny" + expected_arena_words(&e, false) + super::proof_arena::words_per_root(), + "the split-cell control must declare exactly one root's width of surplus \ + words, or it is not the forgery this guard claims to deny" ); } diff --git a/prover/src/lfm/epoch_verify.rs b/prover/src/lfm/epoch_verify.rs index aba339816..3138efb14 100644 --- a/prover/src/lfm/epoch_verify.rs +++ b/prover/src/lfm/epoch_verify.rs @@ -127,14 +127,17 @@ impl TableVerifyShape { ); } - /// Arena words this sub-proof's trace openings occupy. - pub fn opening_words(&self) -> usize { - self.num_queries * self.sub.opening_words() + /// Arena words this sub-proof's trace openings occupy, at `digest_words` + /// per sibling digest — the builder's width on the machine side, the host's + /// on the host side (see `SubProofShape::query_words`). + pub fn opening_words(&self, digest_words: usize) -> usize { + self.num_queries * self.sub.opening_words(digest_words) } - /// Arena words this sub-proof's FRI openings occupy. - pub fn fri_words(&self) -> usize { - self.num_queries * self.fri.query_words() + /// Arena words this sub-proof's FRI openings occupy, at `digest_words` per + /// sibling digest. + pub fn fri_words(&self, digest_words: usize) -> usize { + self.num_queries * self.fri.query_words(digest_words) } } @@ -146,8 +149,9 @@ impl TableVerifyShape { /// coefficients — reaches the legs as cells the spine already bound. #[derive(Clone, Copy, Debug)] pub struct TableQueryArenas { - /// Per query, per group: the row-pair values then the sibling digests (two - /// words per level). NO index word — the index is the transcript's. + /// Per query, per group: the row-pair values then the sibling digests + /// (`edsl::digest_words` per level). NO index word — the index is the + /// transcript's. pub openings: ArenaId, /// Per query, per committed FRI layer: the symmetric evaluation then the /// sibling digests. @@ -156,9 +160,10 @@ pub struct TableQueryArenas { /// Declare the query arenas for one sub-proof. pub fn declare_table_arenas(b: &mut LfmBuilder, shape: &TableVerifyShape) -> TableQueryArenas { + let digest_words = super::edsl::digest_words(b) as usize; TableQueryArenas { - openings: b.declare_arena(shape.opening_words() as u32), - fri: b.declare_arena(shape.fri_words() as u32), + openings: b.declare_arena(shape.opening_words(digest_words) as u32), + fri: b.declare_arena(shape.fri_words(digest_words) as u32), } } @@ -310,7 +315,9 @@ pub fn emit_table_verification( }; // ---- (4) per query: authenticate, fold DEEP, then fold FRI. - let stride = shape.sub.opening_words(); + let stride = shape + .sub + .opening_words(super::edsl::digest_words(b) as usize); let mut fri_terminal = Vec::with_capacity(shape.num_queries); for (qi, bits) in challenges.iota_bits.iter().enumerate() { let mut cursor = (qi * stride) as u32; diff --git a/prover/src/lfm/epoch_verify_tests.rs b/prover/src/lfm/epoch_verify_tests.rs index 36b9e86bb..a49f02ab7 100644 --- a/prover/src/lfm/epoch_verify_tests.rs +++ b/prover/src/lfm/epoch_verify_tests.rs @@ -316,7 +316,8 @@ impl TableLegs { } assert_eq!( out.len(), - self.verify.opening_words(), + self.verify + .opening_words(super::proof_arena::words_per_root()), "the opening arena must fill exactly what the shape declares" ); out @@ -333,7 +334,7 @@ impl TableLegs { } assert_eq!( out.len(), - self.verify.fri_words(), + self.verify.fri_words(super::proof_arena::words_per_root()), "the FRI arena must fill exactly what the shape declares" ); out @@ -1078,13 +1079,14 @@ fn the_assembled_verifier_rejects_tampered_leg_data() { let ix = arena_index(&e, t); assert_eq!( good[ix.openings].len(), - leg.verify.opening_words(), + leg.verify + .opening_words(super::proof_arena::words_per_root()), "table {t}: the arena at the computed openings index is not the \ openings arena" ); assert_eq!( good[ix.fri].len(), - leg.verify.fri_words(), + leg.verify.fri_words(super::proof_arena::words_per_root()), "table {t}: the arena at the computed FRI index is not the FRI arena" ); assert_eq!( diff --git a/prover/src/lfm/framework_probe.rs b/prover/src/lfm/framework_probe.rs index 6368c2030..f866d58e7 100644 --- a/prover/src/lfm/framework_probe.rs +++ b/prover/src/lfm/framework_probe.rs @@ -9,8 +9,6 @@ //! a flipped preprocessed root is rejected by the prover (recommit mismatch) //! and by the verifier (root equality), and a tampered witness value breaks //! the bus balance. - -use crypto::fiat_shamir::default_transcript::DefaultTranscript; use crypto::fiat_shamir::is_transcript::IsTranscript; use stark::config::Commitment; use stark::constraints::builder::EmptyConstraints; @@ -20,10 +18,10 @@ use stark::lookup::{ }; use stark::proof::options::{GoldilocksCubicProofOptions, ProofOptions}; use stark::proof::view::MultiProofView; -use stark::prover::{IsStarkProver, Prover}; +use stark::prover::IsStarkProver; use stark::trace::TraceTable; use stark::traits::AIR; -use stark::verifier::{IsStarkVerifier, Verifier}; +use stark::verifier::IsStarkVerifier; use crate::tables::types::{FE, FEE, GoldilocksExtension, GoldilocksField}; @@ -113,8 +111,8 @@ fn prep_root(opts: &ProofOptions) -> Commitment { commit_columns(&[values(), vec![FE::one(); NUM_ROWS]], opts) } -fn transcript() -> DefaultTranscript { - let mut t = DefaultTranscript::::new(&[]); +fn transcript() -> crate::hash_pin::BlockTranscript { + let mut t = crate::hash_pin::block_transcript(&[]); t.append_bytes(PROBE_TAG); t } @@ -128,7 +126,7 @@ fn prove( let pairs: Vec<(DynAir, &mut TraceTable, &())> = vec![(sender, &mut st, &()), (receiver, &mut rt, &())]; let mut t = transcript(); - Prover::multi_prove( + crate::hash_pin::BlockProver::multi_prove( pairs, &mut t, #[cfg(feature = "disk-spill")] @@ -148,7 +146,12 @@ fn b0_preprocessed_multiplicity_round_trips() { let refs: Vec = vec![&sender, &receiver]; let mut vt = transcript(); assert!( - Verifier::multi_verify_views(&refs, MultiProofView::Owned(&proof), &mut vt, &FEE::zero(),), + crate::hash_pin::BlockVerifier::multi_verify_views( + &refs, + MultiProofView::Owned(&proof), + &mut vt, + &FEE::zero(), + ), "honest proof must verify" ); } @@ -180,7 +183,12 @@ fn b0_verifier_rejects_wrong_preprocessed_root() { let refs: Vec = vec![&bad_sender, &receiver]; let mut vt = transcript(); assert!( - !Verifier::multi_verify_views(&refs, MultiProofView::Owned(&proof), &mut vt, &FEE::zero(),), + !crate::hash_pin::BlockVerifier::multi_verify_views( + &refs, + MultiProofView::Owned(&proof), + &mut vt, + &FEE::zero(), + ), "a supplied root differing from the proof's must reject" ); } @@ -200,7 +208,7 @@ fn b0_tampered_witness_value_breaks_balance() { let pairs: Vec<(DynAir, &mut TraceTable, &())> = vec![(&sender, &mut st, &()), (&receiver, &mut rt, &())]; let mut t = transcript(); - let proof = Prover::multi_prove( + let proof = crate::hash_pin::BlockProver::multi_prove( pairs, &mut t, #[cfg(feature = "disk-spill")] @@ -212,7 +220,12 @@ fn b0_tampered_witness_value_breaks_balance() { let refs: Vec = vec![&sender, &receiver]; let mut vt = transcript(); assert!( - !Verifier::multi_verify_views(&refs, MultiProofView::Owned(&proof), &mut vt, &FEE::zero(),), + !crate::hash_pin::BlockVerifier::multi_verify_views( + &refs, + MultiProofView::Owned(&proof), + &mut vt, + &FEE::zero(), + ), "unbalanced bus must reject" ); } diff --git a/prover/src/lfm/fri.rs b/prover/src/lfm/fri.rs index c8be56008..d47a524e7 100644 --- a/prover/src/lfm/fri.rs +++ b/prover/src/lfm/fri.rs @@ -158,10 +158,14 @@ impl FriShape { } /// Arena words one query's FRI opening occupies: per committed layer the - /// symmetric evaluation (one word) and its path (two words per level). - pub fn query_words(self) -> usize { + /// symmetric evaluation (one word) and its path (`digest_words` per level). + /// + /// `digest_words` is the BUILDER's digest width on the machine side + /// (`edsl::digest_words(b)`) and `proof_arena::words_per_root()` on the + /// host side — see `SubProofShape::query_words` for why it is an argument. + pub fn query_words(self, digest_words: usize) -> usize { // The path stride is the DIGEST's width, not a literal two. - self.num_committed() + super::proof_arena::words_per_root() * self.path_steps_per_query() + self.num_committed() + digest_words * self.path_steps_per_query() } /// Keccak permutations the whole sub-proof's FRI costs. @@ -372,7 +376,8 @@ pub fn declare_fri( let roots = b.declare_arena(edsl::digest_words(b) * c as u32); let zetas = b.declare_arena(num_zetas as u32); let coeffs = b.declare_arena(shape.num_terminal_coeffs() as u32); - let queries = b.declare_arena((num_queries * shape.query_words()) as u32); + let queries = + b.declare_arena((num_queries * shape.query_words(edsl::digest_words(b) as usize)) as u32); let layers = (0..c) .map(|i| LayerCommitment::hint(b, roots, edsl::digest_words(b) * i as u32)) @@ -421,7 +426,8 @@ pub fn hint_layer_openings_from( arena: ArenaId, query: usize, ) -> Vec { - let mut cursor = (query * shape.query_words()) as u32; + let stride = shape.query_words(edsl::digest_words(b) as usize); + let mut cursor = (query * stride) as u32; let openings: Vec = (0..shape.num_committed()) .map(|layer| { let sym = b.hint_word(arena, cursor).as_ext(); @@ -439,7 +445,7 @@ pub fn hint_layer_openings_from( .collect(); assert_eq!( cursor as usize, - (query + 1) * shape.query_words(), + (query + 1) * stride, "the emitter's cursor must agree with the declared query stride" ); openings diff --git a/prover/src/lfm/fri_tests.rs b/prover/src/lfm/fri_tests.rs index 71ac14e51..895b6c8ef 100644 --- a/prover/src/lfm/fri_tests.rs +++ b/prover/src/lfm/fri_tests.rs @@ -48,7 +48,6 @@ use super::executor::execute; use super::fri::{ FRI_LEAF_GROUP, FriQuery, FriShape, declare_fri, emit_query_fri, hint_layer_openings, }; -use super::hash::TestPermutation; use super::join_tests::{HostSubProof, build_host_sub_proof}; use super::validator::validate; use super::word::{LfmWord, base_word, ext_word, word_as_ext}; @@ -255,8 +254,8 @@ impl HostFri { /// 48, and all of them move at least one. #[test] fn the_fri_leaf_is_byte_identical_to_productions_own_backends() { + use super::proof_arena::{BlockBatched, BlockPair}; use crypto::merkle_tree::traits::IsMerkleTreeBackend; - use stark::config::{BatchedMerkleTreeBackend, FriLayerMerkleTreeBackend}; // Six distinct components, each with six distinct nonzero bytes in // descending positions, so no two of the 48 bytes agree and no component is @@ -291,20 +290,24 @@ fn the_fri_leaf_is_byte_identical_to_productions_own_backends() { let v0 = b.hint_word(arena, 0); let v1 = b.hint_word(arena, 1); let leaf = super::sub_proof::emit_leaf_hash(&mut b, FRI_LEAF_GROUP, &[v0, v1]); - b.public(leaf[0]); - b.public(leaf[1]); + // ⚠ The digest's OWN width. Two publishes assumed a byte digest; an + // algebraic one is a single cell whose second slot repeats the first, so + // the comparison below would have read one lane twice. + for cell in leaf.cells() { + b.public(*cell); + } let program = compile(b.finish()); validate(&program).expect("the leaf program is admissible"); let mut digests = Vec::new(); for (i, (a, c)) in vectors.iter().enumerate() { let arenas = vec![vec![ext_word(a), ext_word(c)]]; - let exec = execute(&program, &arenas, &TestPermutation).expect("the leaf hash executes"); - let got = [exec.public_words[0].1, exec.public_words[1].1]; + let exec = execute(&program, &arenas, &crate::hash_pin::BLOCK_HASHER) + .expect("the leaf hash executes"); + let got: Vec = exec.public_words.iter().map(|(_, w)| *w).collect(); - let batched = - as IsMerkleTreeBackend>::hash_data(&vec![*a, *c]); - let paired = as IsMerkleTreeBackend>::hash_data(&[*a, *c]); + let batched = as IsMerkleTreeBackend>::hash_data(&vec![*a, *c]); + let paired = as IsMerkleTreeBackend>::hash_data(&[*a, *c]); assert_eq!( batched, paired, "vector {i}: the spec's claim is that the prover's pair backend and \ @@ -622,7 +625,12 @@ fn the_fri_emitter_verifies_every_query_of_a_real_folding_proof() { let h = host_fri(rows, 2); let all: Vec = (0..h.trace.iotas.len()).collect(); let program = fri_only_program(h.shape, all.len()); - let exec = execute(&program, &h.all_arenas(&all), &TestPermutation).expect( + let exec = execute( + &program, + &h.all_arenas(&all), + &crate::hash_pin::BLOCK_HASHER, + ) + .expect( "an honest FRI decommitment must authenticate every layer and reach \ the terminal polynomial", ); @@ -707,7 +715,7 @@ fn the_two_legs_verify_one_real_folding_proof_as_one_program() { let mut arenas = h.trace.arenas(&queries); arenas.extend(h.fri_arenas(&queries)); - let exec = execute(&program, &arenas, &TestPermutation) + let exec = execute(&program, &arenas, &crate::hash_pin::BLOCK_HASHER) .expect("the honest proof must authenticate, fold and reach the terminal"); let codeword = h.terminal_codeword(); @@ -899,16 +907,18 @@ fn the_emitted_permutation_count_meets_the_pinned_prediction() { /// /// ```text /// selects/query = index_bits (pow_bits, once per query) -/// + 2 · merkle_depth · num_groups (trace walks) +/// + w · merkle_depth · num_groups (trace walks) /// + num_committed (FRI leaf ordering) -/// + 2 · path_steps_per_query (FRI walks) +/// + w · path_steps_per_query (FRI walks) /// ``` /// -/// `pow_bits` emits one `Select` per bit (`edsl.rs:257-262`) and each walk level -/// two, since a digest is two words and both must swap on the same bit -/// (`edsl.rs:164-169`). A second derivation makes the measured count exceed the -/// closed form by exactly `index_bits`, and nothing cancels it. Re-falsified in -/// that form: the injected defect now fails with "a surplus of 11 index bits". +/// where `w` is the digest's width in arena words — two on a byte hash, one on +/// an algebraic one. `pow_bits` emits one `Select` per bit (`edsl.rs:257-262`) +/// and each walk level one per digest word, since every word of a digest must +/// swap on the same bit (`edsl.rs:164-169`). A second derivation makes the +/// measured count exceed the closed form by exactly `index_bits`, and nothing +/// cancels it. Re-falsified in that form: the injected defect now fails with "a +/// surplus of 11 index bits". #[test] fn the_fri_join_adds_no_second_point_derivation() { let h = host_fri(2048, 2); @@ -939,10 +949,13 @@ fn the_fri_join_adds_no_second_point_derivation() { let per_query_selects = selects(&two) - selects(&one); let per_query_decs = decs(&two) - decs(&one); + // The digest's width, as the HOST reads it: `emit` builds at + // `WrapHash::production()`, whose builder width this is the counterpart of. + let dw = super::proof_arena::words_per_root(); let expected_selects = h.shape.index_bits() - + 2 * sub.merkle_depth * groups.len() + + dw * sub.merkle_depth * groups.len() + h.shape.num_committed() - + 2 * h.shape.path_steps_per_query(); + + dw * h.shape.path_steps_per_query(); assert_eq!( per_query_selects, expected_selects, @@ -951,11 +964,11 @@ fn the_fri_join_adds_no_second_point_derivation() { steps. A surplus of {} index bits is a second point derivation or a \ second index decomposition", h.shape.index_bits(), - 2 * sub.merkle_depth * groups.len(), + dw * sub.merkle_depth * groups.len(), groups.len(), sub.merkle_depth, h.shape.num_committed(), - 2 * h.shape.path_steps_per_query(), + dw * h.shape.path_steps_per_query(), h.shape.path_steps_per_query(), h.shape.index_bits(), ); @@ -1031,16 +1044,19 @@ fn no_tampered_fri_value_can_pass() { }; let program = fri_only_program(shape, queries.len()); let honest = h.all_arenas(&queries); - execute(&program, &honest, &TestPermutation).expect("the honest run must execute"); + execute(&program, &honest, &crate::hash_pin::BLOCK_HASHER) + .expect("the honest run must execute"); - let stride = h.shape.query_words(); + // Host-side offsets at the host's digest width; the program was built at + // `WrapHash::production()`, which this is the counterpart of. + let dw = super::proof_arena::words_per_root(); + let stride = h.shape.query_words(dw); // (label, arena, word) — arena order is the driver's: deep, roots, zetas, // coeffs, queries. - let bump: Vec<(&str, usize, usize)> = vec![ + let mut bump: Vec<(&str, usize, usize)> = vec![ ("query index", 0, 0), ("layer 0 root", 1, 0), - ("layer 0 root, second word", 1, 1), - ("layer 2 root", 1, 2 * (c - 1)), + ("layer 2 root", 1, dw * (c - 1)), ("zeta_0 (the DEEP fold's challenge)", 2, 0), ("zeta_C (the uncommitted final fold)", 2, c), ("terminal coefficient 0", 3, 0), @@ -1050,16 +1066,20 @@ fn no_tampered_fri_value_can_pass() { ( "layer 0 sibling, top level", 4, - 2 * h.shape.layer_path_len(0) - 1, + dw * h.shape.layer_path_len(0) - 1, ), ("second query's layer 0 evaluation", 4, stride), ]; + if dw == 2 { + // Only a byte digest has a second word to move; an algebraic root is one. + bump.push(("layer 0 root, second word", 1, 1)); + } for (label, arena, word) in bump { let mut tampered = honest.clone(); tampered[arena][word][0] += FE::one(); - let err = execute(&program, &tampered, &TestPermutation).expect_err(&format!( - "moving the {label} must make the program unexecutable" - )); + let err = execute(&program, &tampered, &crate::hash_pin::BLOCK_HASHER).expect_err( + &format!("moving the {label} must make the program unexecutable"), + ); println!(" {label:<40} rejected: {err:?}"); } @@ -1067,7 +1087,7 @@ fn no_tampered_fri_value_can_pass() { // decommitment. Every word is a real prover value. let mut spliced = honest.clone(); let (from, to) = (stride, 0usize); - let len = 1 + 2 * h.shape.layer_path_len(0); + let len = 1 + dw * h.shape.layer_path_len(0); let borrowed: Vec = spliced[4][from..from + len].to_vec(); assert_ne!( borrowed, @@ -1076,7 +1096,7 @@ fn no_tampered_fri_value_can_pass() { splice is a no-op and this vector proves nothing" ); spliced[4][to..to + len].copy_from_slice(&borrowed); - let err = execute(&program, &spliced, &TestPermutation).expect_err( + let err = execute(&program, &spliced, &crate::hash_pin::BLOCK_HASHER).expect_err( "a REAL leaf and a REAL path, at the wrong index, must still be rejected \ — the walk climbs at this query's own bits", ); @@ -1116,7 +1136,8 @@ fn the_shape_pins_the_lengths_production_must_check_at_runtime() { }; let program = fri_only_program(shape, 1); let honest = h.all_arenas(&queries); - execute(&program, &honest, &TestPermutation).expect("the honest run must execute"); + execute(&program, &honest, &crate::hash_pin::BLOCK_HASHER) + .expect("the honest run must execute"); // (label, arena, what the truncation would buy a prover) let attacks: [(&str, usize, &str); 3] = [ @@ -1140,7 +1161,7 @@ fn the_shape_pins_the_lengths_production_must_check_at_runtime() { for (label, arena, mirrors) in attacks { let mut truncated = honest.clone(); truncated[arena].clear(); - let err = execute(&program, &truncated, &TestPermutation) + let err = execute(&program, &truncated, &crate::hash_pin::BLOCK_HASHER) .expect_err(&format!("{label} must be refused")); assert!( matches!(err, LfmExecError::ArenaLenMismatch { .. }), @@ -1174,7 +1195,7 @@ fn the_shape_pins_the_lengths_production_must_check_at_runtime() { #[test] fn the_fri_leg_proves_and_verifies() { use super::proof::{lfm_prove, verify_against}; - use super::registry::build_artifacts; + use super::registry::build_artifacts_with_hasher; let h = host_fri(512, 2); assert_eq!( @@ -1201,7 +1222,11 @@ fn the_fri_leg_proves_and_verifies() { let mut arenas = h.trace.arenas(&queries); arenas.extend(h.fri_arenas(&queries)); - let artifacts = build_artifacts(&program, &opts); + // Built at `WrapHash::production()`, so it emits `Instr::Hash` and the + // artifacts must carry the pin's tenant — the classification rule in + // HASH-PINNING.md. `build_artifacts` defaults to the registry's blessed + // hasher, which is correct for registry programs and wrong for this one. + let artifacts = build_artifacts_with_hasher(&program, &opts, crate::hash_pin::BLOCK_HASHER); let proved = lfm_prove(&program, &artifacts, &arenas, &opts) .expect("the joined trace+DEEP+FRI program must prove"); diff --git a/prover/src/lfm/join_tests.rs b/prover/src/lfm/join_tests.rs index 0459d8569..c9e859811 100644 --- a/prover/src/lfm/join_tests.rs +++ b/prover/src/lfm/join_tests.rs @@ -26,6 +26,7 @@ //! distinguish a per-level walk from a two-level one; it is not enough to catch //! something that only appears past a word boundary in the index. +use crypto::merkle_tree::traits::IsStreamingLeafBackend; use math::field::traits::IsFFTField; use stark::config::Commitment; use stark::domain::new_verifier_domain; @@ -38,7 +39,6 @@ use super::builder::LfmBuilder; use super::compiler::compile; use super::constraint_tests::{deep_shape, open_sub_proof, real_fixture}; use super::executor::execute; -use super::hash::TestPermutation; use super::sub_proof::{ GroupShape, ROWS_PER_LEAF, SubProofShape, emit_sub_proof, emit_sub_proof_with_bits, }; @@ -398,7 +398,8 @@ fn the_join_premises_hold_on_a_real_proof() { for (q, iota) in h.iotas.iter().enumerate() { let arenas = vec![vec![base_word(FE::from(*iota as u64))]]; - let exec = execute(&program, &arenas, &TestPermutation).expect("the derivation executes"); + let exec = execute(&program, &arenas, &crate::hash_pin::BLOCK_HASHER) + .expect("the derivation executes"); assert_eq!( exec.public_words[0].1[0], h.points[q].0, "query {q}: the machine's point must be \ @@ -438,7 +439,7 @@ fn the_join_matches_the_production_verifier_on_every_query() { let program = compile(b.finish()); validate(&program).expect("the joined sub-proof program is admissible"); - let exec = execute(&program, &h.arenas(&all), &TestPermutation) + let exec = execute(&program, &h.arenas(&all), &crate::hash_pin::BLOCK_HASHER) .expect("an honest sub-proof must authenticate and fold"); let mut nonzero = 0usize; @@ -814,7 +815,7 @@ fn join_leg_cost() { use super::builder::{Bit, Cell, Ext, Felt}; use super::deep::{DeepOpening, emit_deep_invariants, emit_deep_point}; use super::proof::{lfm_prove, verify_against}; -use super::registry::build_artifacts; +use super::registry::build_artifacts_with_hasher; use super::sub_proof::{ GroupCommitment, GroupOpening, emit_group_authentication, emit_query_points, }; @@ -856,8 +857,12 @@ fn control_program_source( let uniforms = b.declare_arena(2); let ood = b.declare_arena((shape.deep.num_eval_points * shape.deep.num_total_cols) as u32); let parts_arena = b.declare_arena(shape.deep.num_composition_parts as u32); - let roots = b.declare_arena(2 * groups.len() as u32); - let queries = b.declare_arena(shape.query_words() as u32); + // The roots and the query stride follow THIS builder's digest width, as the + // production emitter's do — a literal two here is a byte-hash assumption + // that the executor's arena-length check refuses under an algebraic pin. + let dw = super::edsl::digest_words(&b); + let roots = b.declare_arena(dw * groups.len() as u32); + let queries = b.declare_arena(shape.query_words(dw as usize) as u32); let extra = b.declare_arena(match control { // A second copy of every folded value, both points. Control::SplitValues => { @@ -887,7 +892,7 @@ fn control_program_source( let commitments: Vec = groups .iter() .enumerate() - .map(|(i, g)| GroupCommitment::hint(&mut b, roots, 2 * i as u32, *g)) + .map(|(i, g)| GroupCommitment::hint(&mut b, roots, dw * i as u32, *g)) .collect(); let inv = emit_deep_invariants(&mut b, &shape.deep, gamma, zeta, &ood_steps, &claimed_parts); @@ -906,10 +911,11 @@ fn control_program_source( .collect(); let siblings: Vec = (0..shape.merkle_depth) .map(|_| { - let lo = b.hint_word(queries, cursor); - let hi = b.hint_word(queries, cursor + 1); - cursor += 2; - super::edsl::WrapDigest::from_pair(lo, hi) + // The stride follows THIS builder's digest width, as the + // production emitter's does — not a literal two. + let d = super::edsl::hint_digest(&mut b, queries, cursor); + cursor += dw; + d }) .collect(); GroupOpening { values, siblings } @@ -1018,7 +1024,11 @@ fn the_join_proves_and_verifies() { b.public(s.as_cell()); } let program = compile(b.finish()); - let artifacts = build_artifacts(&program, &opts); + // Built at `WrapHash::production()`, so it emits `Instr::Hash` and the + // artifacts must carry the pin's tenant — the classification rule in + // HASH-PINNING.md. `build_artifacts` defaults to the registry's blessed + // hasher, which is correct for registry programs and wrong for this one. + let artifacts = build_artifacts_with_hasher(&program, &opts, crate::hash_pin::BLOCK_HASHER); let proved = lfm_prove(&program, &artifacts, &h.arenas(&queries), &opts) .expect("the joined sub-proof must prove"); @@ -1084,7 +1094,8 @@ fn sweep_tampers(h: &HostSubProof, label: &str) { b.public(s.as_cell()); } let program = compile(b.finish()); - let honest = execute(&program, &h.arenas(&[q]), &TestPermutation).expect("honest"); + let honest = + execute(&program, &h.arenas(&[q]), &crate::hash_pin::BLOCK_HASHER).expect("honest"); // Sweep every value slot of every group, so no vector class (first group, // first column, regular point) is silently the only one tested. @@ -1096,14 +1107,15 @@ fn sweep_tampers(h: &HostSubProof, label: &str) { // Offset of this group's value `slot` inside the query arena. let mut off = 1usize; for prior in groups.iter().take(g) { - off += prior.num_values() + 2 * h.shape.merkle_depth; + off += prior.num_values() + + super::proof_arena::words_per_root() * h.shape.merkle_depth; } off + slot }; arenas[4][word_of_slot][0] += FE::one(); // Incoherent: the real roots, a moved leaf. - let err = execute(&program, &arenas, &TestPermutation) + let err = execute(&program, &arenas, &crate::hash_pin::BLOCK_HASHER) .err() .unwrap_or_else(|| { panic!("{label}: group {g} slot {slot}: a moved value must not authenticate") @@ -1121,9 +1133,12 @@ fn sweep_tampers(h: &HostSubProof, label: &str) { let mut coherent_roots = h.roots.clone(); coherent_roots[g] = forged; arenas[3] = commitments_to_arena(&coherent_roots); - let forged_run = execute(&program, &arenas, &TestPermutation).unwrap_or_else(|e| { - panic!("{label}: group {g} slot {slot}: the coherent forgery must execute: {e:?}") - }); + let forged_run = execute(&program, &arenas, &crate::hash_pin::BLOCK_HASHER) + .unwrap_or_else(|e| { + panic!( + "{label}: group {g} slot {slot}: the coherent forgery must execute: {e:?}" + ) + }); // Which of the two points moves is not incidental: a leaf holds // the row PAIR, its first half is the regular point and its second // the symmetric, and folding the halves into the wrong point is a @@ -1171,13 +1186,13 @@ fn sweep_tampers(h: &HostSubProof, label: &str) { for (g, group) in groups.iter().enumerate() { let words = &h.openings[q][g].values; let leaf = if group.is_ext { - type ExtBackend = stark::config::BatchedMerkleTreeBackend; + type ExtBackend = super::proof_arena::BlockBatched; let v: Vec = words.iter().map(|w| FEE::new([w[0], w[1], w[2]])).collect(); - ExtBackend::hash_data_from_slices(&v, &[]) + >::hash_data_from_slices(&v, &[]) } else { - type BaseBackend = stark::config::BatchedMerkleTreeBackend; + type BaseBackend = super::proof_arena::BlockBatched; let v: Vec = words.iter().map(|w| w[0]).collect(); - BaseBackend::hash_data_from_slices(&v, &[]) + >::hash_data_from_slices(&v, &[]) }; coherent_roots[g] = walk_to_root(leaf, bad, &h.openings[q][g].siblings); moved_a_root |= coherent_roots[g] != h.roots[g]; @@ -1188,16 +1203,17 @@ fn sweep_tampers(h: &HostSubProof, label: &str) { trees are degenerate at this index and the walk half of this vector \ tests nothing" ); - execute(&program, &arenas, &TestPermutation) + execute(&program, &arenas, &crate::hash_pin::BLOCK_HASHER) .err() .unwrap_or_else(|| { panic!("{label}: index bit {level}: a moved index must not authenticate") }); arenas[3] = commitments_to_arena(&coherent_roots); - let forged = execute(&program, &arenas, &TestPermutation).unwrap_or_else(|e| { - panic!("{label}: index bit {level}: coherent forgery must execute: {e:?}") - }); + let forged = + execute(&program, &arenas, &crate::hash_pin::BLOCK_HASHER).unwrap_or_else(|e| { + panic!("{label}: index bit {level}: coherent forgery must execute: {e:?}") + }); assert_ne!( forged.public_words[0].1, honest.public_words[0].1, "{label}: index bit {level}: the index derives the evaluation point, so a \ @@ -1215,9 +1231,9 @@ fn sweep_tampers(h: &HostSubProof, label: &str) { siblings[level][0] ^= 1; let mut arenas = h.arenas(&[q]); let base = 1 + groups[0].num_values(); - arenas[4][base..base + 2 * h.shape.merkle_depth] + arenas[4][base..base + super::proof_arena::words_per_root() * h.shape.merkle_depth] .copy_from_slice(&commitments_to_arena(&siblings)); - execute(&program, &arenas, &TestPermutation) + execute(&program, &arenas, &crate::hash_pin::BLOCK_HASHER) .err() .unwrap_or_else(|| { panic!("{label}: sibling level {level}: a moved path must not authenticate") @@ -1231,18 +1247,18 @@ fn sweep_tampers(h: &HostSubProof, label: &str) { /// The leaf hash a tampered opening really produces, under production's own /// backend rather than a local model. fn tampered_leaf(h: &HostSubProof, q: usize, g: usize, slot: usize) -> Commitment { - type BaseBackend = stark::config::BatchedMerkleTreeBackend; - type ExtBackend = stark::config::BatchedMerkleTreeBackend; + type BaseBackend = super::proof_arena::BlockBatched; + type ExtBackend = super::proof_arena::BlockBatched; let group = h.shape.groups()[g]; let words = &h.openings[q][g].values; if group.is_ext { let mut v: Vec = words.iter().map(|w| FEE::new([w[0], w[1], w[2]])).collect(); v[slot] = &v[slot] + FEE::new([FE::one(), FE::zero(), FE::zero()]); - ExtBackend::hash_data_from_slices(&v, &[]) + >::hash_data_from_slices(&v, &[]) } else { let mut v: Vec = words.iter().map(|w| w[0]).collect(); v[slot] += FE::one(); - BaseBackend::hash_data_from_slices(&v, &[]) + >::hash_data_from_slices(&v, &[]) } } } @@ -1266,7 +1282,7 @@ fn the_controls_show_what_the_join_denies() { validate(&program).expect("admissible"); let mut arenas = h.arenas(&[q]); arenas.push(h.split_values(q)); - let clean = execute(&program, &arenas, &TestPermutation) + let clean = execute(&program, &arenas, &crate::hash_pin::BLOCK_HASHER) .expect("the control must accept honest inputs"); assert_eq!( word_as_ext(&clean.public_words[0].1).expect("ext"), @@ -1277,7 +1293,7 @@ fn the_controls_show_what_the_join_denies() { let mut attacked = arenas.clone(); attacked[5][0][0] += FE::one(); - let forged = execute(&program, &attacked, &TestPermutation).expect( + let forged = execute(&program, &attacked, &crate::hash_pin::BLOCK_HASHER).expect( "SplitValues: authenticating one set of values and folding another is \ exactly what this control permits", ); @@ -1298,7 +1314,7 @@ fn the_controls_show_what_the_join_denies() { validate(&program).expect("admissible"); let mut arenas = h.arenas(&[q]); arenas.push(vec![base_word(h.points[q].0), base_word(h.points[q].1)]); - let clean = execute(&program, &arenas, &TestPermutation).expect("honest"); + let clean = execute(&program, &arenas, &crate::hash_pin::BLOCK_HASHER).expect("honest"); assert_eq!( word_as_ext(&clean.public_words[0].1).expect("ext"), h.expected[q].0 @@ -1306,7 +1322,7 @@ fn the_controls_show_what_the_join_denies() { let mut attacked = arenas.clone(); attacked[5] = vec![base_word(h.points[other].0), base_word(h.points[other].1)]; - let forged = execute(&program, &attacked, &TestPermutation).expect( + let forged = execute(&program, &attacked, &crate::hash_pin::BLOCK_HASHER).expect( "HintedPoint: a hinted point is not tied to the authenticated index, \ which is what this control permits", ); @@ -1529,8 +1545,12 @@ fn the_precomputed_group_comes_first_and_that_is_checkable() { } let program = compile(b.finish()); validate(&program).expect("admissible"); - let exec = execute(&program, &h.arenas(&queries), &TestPermutation) - .expect("the four-group sub-proof must authenticate and fold"); + let exec = execute( + &program, + &h.arenas(&queries), + &crate::hash_pin::BLOCK_HASHER, + ) + .expect("the four-group sub-proof must authenticate and fold"); for (k, q) in queries.iter().enumerate() { assert_eq!( word_as_ext(&exec.public_words[2 * k].1).expect("ext"), diff --git a/prover/src/lfm/keccak_probe.rs b/prover/src/lfm/keccak_probe.rs index b8a8dd208..24b511eef 100644 --- a/prover/src/lfm/keccak_probe.rs +++ b/prover/src/lfm/keccak_probe.rs @@ -6,17 +6,15 @@ //! recursion machine's AIR set: it establishes that the family's only coupling //! to the VM is the core chip's two `Keccak` bus tokens, and that a chip owning //! nothing but those tokens is a sufficient driver. - -use crypto::fiat_shamir::default_transcript::DefaultTranscript; use crypto::fiat_shamir::is_transcript::IsTranscript; use stark::constraints::builder::EmptyConstraints; use stark::lookup::{AirWithBuses, AuxiliaryTraceBuildData, NullBoundaryConstraintBuilder}; use stark::proof::options::{GoldilocksCubicProofOptions, ProofOptions}; use stark::proof::view::MultiProofView; -use stark::prover::{IsStarkProver, Prover}; +use stark::prover::IsStarkProver; use stark::trace::TraceTable; use stark::traits::AIR; -use stark::verifier::{IsStarkVerifier, Verifier}; +use stark::verifier::IsStarkVerifier; use crate::tables::types::{FE, FEE, GoldilocksExtension, GoldilocksField, VmTable}; use crate::tables::{bitwise, keccak_rc, keccak_rnd}; @@ -35,8 +33,8 @@ fn options() -> ProofOptions { GoldilocksCubicProofOptions::with_blowup(2).expect("probe options") } -fn transcript() -> DefaultTranscript { - let mut t = DefaultTranscript::::new(&[]); +fn transcript() -> crate::hash_pin::BlockTranscript { + let mut t = crate::hash_pin::block_transcript(&[]); t.append_bytes(PROBE_TAG); t } @@ -115,7 +113,7 @@ fn prove_traces( (&bw_air, t3, &()), ]; let mut t = transcript(); - Prover::multi_prove( + crate::hash_pin::BlockProver::multi_prove( pairs, &mut t, #[cfg(feature = "disk-spill")] @@ -140,7 +138,12 @@ fn verify_proof( ); let refs: Vec = vec![adapter, &rnd_air, &rc_air, &bw_air]; let mut vt = transcript(); - Verifier::multi_verify_views(&refs, MultiProofView::Owned(proof), &mut vt, &FEE::zero()) + crate::hash_pin::BlockVerifier::multi_verify_views( + &refs, + MultiProofView::Owned(proof), + &mut vt, + &FEE::zero(), + ) } /// Prove + verify, optionally corrupting the adapter trace in between. diff --git a/prover/src/lfm/logup_tests.rs b/prover/src/lfm/logup_tests.rs index 76179939f..21dc409c8 100644 --- a/prover/src/lfm/logup_tests.rs +++ b/prover/src/lfm/logup_tests.rs @@ -3,7 +3,7 @@ //! ## The oracles //! //! Two, both production's own. `compute_commit_bus_offset` (`lib.rs`) for the -//! COMMIT-bus target, and `Verifier::multi_verify` for the balance itself — the +//! COMMIT-bus target, and `crate::hash_pin::BlockVerifier::multi_verify` for the balance itself — the //! fixture is a real sender/receiver pair whose bus genuinely closes, and //! production accepting it at target zero is what says so. Nothing here asserts //! a balance this file computed. @@ -23,7 +23,7 @@ use stark::proof::stark::MultiProof; use stark::proof::view::StarkProofView; use stark::traits::AIR; -use stark::verifier::{IsStarkVerifier, Verifier}; +use stark::verifier::IsStarkVerifier; use crate::tables::types::{FE, FEE, GoldilocksExtension, GoldilocksField}; @@ -278,7 +278,7 @@ fn the_closure_matches_a_bus_that_really_balances() { airs.iter().map(|a| &**a).collect(); assert!( - Verifier::multi_verify( + crate::hash_pin::BlockVerifier::multi_verify( &air_refs, &proof, &mut crate::hash_pin::block_transcript(&[]), @@ -757,7 +757,7 @@ fn the_closure_accumulates_per_chunk_not_per_family() { assert_eq!(air_refs.len(), 3, "one sender and two chunks of one family"); assert!( - Verifier::multi_verify( + crate::hash_pin::BlockVerifier::multi_verify( &air_refs, &proof, &mut crate::hash_pin::block_transcript(&[]), @@ -963,7 +963,7 @@ enum RowWitness { /// One REAL continuation epoch — epoch 0 of the LFM fixture guest, built by /// `Traces::from_image_and_logs` and proved over the production epoch AIR set /// (`VmAirs` + the epoch-local L2G table) under the real epoch statement, then -/// ACCEPTED by `Verifier::multi_verify_views` against production's own +/// ACCEPTED by `crate::hash_pin::BlockVerifier::multi_verify_views` against production's own /// `compute_expected_commit_bus_balance_view`. The acceptance is load-bearing /// twice over: it is what makes this "what a verifying epoch proof carries" /// rather than "what some prover run emitted", and it is what runs @@ -1232,7 +1232,7 @@ fn a_zero_row_fixed_table_carries_some_zero_not_none() { ) .expect("the COMMIT-bus target must exist"); assert!( - Verifier::multi_verify_views(&refs, view, &mut seed(), &expected), + crate::hash_pin::BlockVerifier::multi_verify_views(&refs, view, &mut seed(), &expected), "production must ACCEPT this epoch proof — the measurement is about what \ a VERIFYING proof carries, and this is also the run of \ verifier.rs:1238's presence check" @@ -1359,7 +1359,7 @@ fn a_zero_row_fixed_table_carries_some_zero_not_none() { let mut tampered = proof.clone(); tampered.proofs[i].bus_public_inputs = None; assert!( - !Verifier::multi_verify_views( + !crate::hash_pin::BlockVerifier::multi_verify_views( &refs, MultiProofView::Owned(&tampered), &mut seed(), diff --git a/prover/src/lfm/machine_tests.rs b/prover/src/lfm/machine_tests.rs index 614425482..45bf1fde5 100644 --- a/prover/src/lfm/machine_tests.rs +++ b/prover/src/lfm/machine_tests.rs @@ -285,7 +285,7 @@ use super::layout::keccak as klayout; use super::programs::{keccak_chain_program, keccak_chain_program_source}; use super::proof::prove_traces; use super::registry::LfmArtifacts; -use super::trace::{LfmTraces, build_traces}; +use super::trace::{LfmTraces, build_traces_with_hasher}; use super::validator::LfmViolation; use crate::lfm::chips::keccak as kchip; use crate::tables::types::VmTable; @@ -321,7 +321,7 @@ fn prove_keccak_chain_with_tamper( let exec = super::executor::execute(program, &keccak_arenas(seed), &super::hash::TestPermutation) .expect("honest execution"); - let mut traces = build_traces(program, &exec.records); + let mut traces = build_traces_with_hasher(program, &exec.records, artifacts.hasher); mutate(&mut traces); let proof = prove_traces(artifacts, &mut traces, &exec.public_words, &opts)?; Ok((proof, exec.public_words)) @@ -606,10 +606,33 @@ fn sponge_arenas(msg: &[u8]) -> Vec> { vec![halves.into_iter().map(super::word::base_word).collect()] } -/// The 32-byte digest from the two public words: byte `j` is byte `j % 4` of -/// half `j / 4`, and half `h` is lane `h % 4` of word `h / 4`. +/// The 32 bytes a published digest stands for, at whatever width it was +/// published. +/// +/// A BYTE digest is two words read as eight little-endian `u32` halves: byte +/// `j` is byte `j % 4` of half `j / 4`, and half `h` is lane `h % 4` of word +/// `h / 4`. An ALGEBRAIC digest is ONE word of four canonical felts, and its 32 +/// bytes are the backend's own serialisation of them. +/// +/// ⛔ **The discriminator is the slice's own length, NOT +/// `WrapHash::production()`, and that distinction is the whole point.** This +/// helper's callers mix two kinds of program: ones that pin a byte hash on their +/// own builder (`keccak_sponge_program`, `blake3_sponge_program` — always two +/// words, on every branch, because their identity is registry-pinned) and ones +/// that follow the configuration (`merkle_opening_program` — one word on an +/// algebraic arm). Branching on the global configuration would render the first +/// group wrong on an algebraic branch, which is the same scope error as reading +/// a root's width from the configuration instead of from the root. fn digest_bytes(public: &[(u32, LfmWord)]) -> [u8; 32] { use math::field::traits::IsPrimeField; + if public.len() == 1 { + return super::algebraic_commit::digest_to_commitment(&public[0].1); + } + assert_eq!( + public.len(), + 2, + "a digest is one algebraic word or two byte words" + ); let mut out = [0u8; 32]; for h in 0..8 { let lane = public[h / 4].1[h % 4]; @@ -766,7 +789,7 @@ fn tampered_absorb_xor_rejects() { &super::hash::TestPermutation, ) .expect("honest execution"); - let mut traces = build_traces(&program, &exec.records); + let mut traces = build_traces_with_hasher(&program, &exec.records, artifacts.hasher); // Rate byte 5 of the first absorb row: XOR(state, block) no longer holds. let col = kchip::cols::PERM_IN + 5; let old = traces.keccak.main_table.get_row(0)[col]; @@ -873,7 +896,7 @@ fn permute_row_cannot_substitute_the_permuted_state() { exec.records.public[1] = words[0]; exec.records.public[2] = words[1]; - let mut traces = build_traces(&program, &exec.records); + let mut traces = build_traces_with_hasher(&program, &exec.records, artifacts.hasher); let proof = prove_traces(&artifacts, &mut traces, &exec.public_words, &opts) .expect("the prover has no constraint checks, so it accepts"); assert!( @@ -1272,7 +1295,7 @@ fn canonicity_guard_rejects_an_out_of_range_candidate_in_the_proof() { exec.records.public[0] = super::word::base_word(FE::zero()); exec.public_words[0].1 = super::word::base_word(FE::zero()); - let mut traces = build_traces(&program, &exec.records); + let mut traces = build_traces_with_hasher(&program, &exec.records, artifacts.hasher); let proof = prove_traces(&artifacts, &mut traces, &exec.public_words, &opts) .expect("the prover has no constraint checks, so it accepts"); assert!( @@ -2689,7 +2712,7 @@ fn chunking_splits_the_sponge_into_two_uneven_chunks() { &super::hash::TestPermutation, ) .expect("honest execution"); - let traces = build_traces(&program, &exec.records); + let traces = build_traces_with_hasher(&program, &exec.records, artifacts.hasher); assert_eq!(traces.keccak_rnd.len(), 2, "one KECCAK_RND trace per chunk"); assert_eq!( traces @@ -2802,7 +2825,7 @@ fn tampered_second_chunk_permutation_rejects() { ) .expect("honest execution"); - let mut traces = build_traces(&program, &exec.records); + let mut traces = build_traces_with_hasher(&program, &exec.records, artifacts.hasher); assert_eq!(traces.keccak_rnd.len(), 2); // Byte 0 of lane (0,0) on the second chunk's first row: the `Keccak` // receive token no longer matches the send that fed it. @@ -2846,7 +2869,7 @@ fn dropping_the_second_chunks_permutation_rejects() { ) .expect("honest execution"); - let mut traces = build_traces(&program, &exec.records); + let mut traces = build_traces_with_hasher(&program, &exec.records, artifacts.hasher); // Same chunk COUNT — so the AIR set and the digest still match — but the // last chunk is now empty. traces.keccak_rnd[1] = keccak_rnd::generate_keccak_rnd_trace(&[]); @@ -2890,7 +2913,7 @@ fn permutations_may_be_reassigned_across_chunk_boundaries() { let round_ops = round_ops_of(&program, &sponge_arenas(&msg)); assert_eq!(round_ops.len(), 3); - let mut traces = build_traces(&program, &exec.records); + let mut traces = build_traces_with_hasher(&program, &exec.records, artifacts.hasher); // Canonical split is 2 + 1; re-split as 1 + 2. traces.keccak_rnd[0] = keccak_rnd::generate_keccak_rnd_trace(&round_ops[..1]); traces.keccak_rnd[1] = keccak_rnd::generate_keccak_rnd_trace(&round_ops[1..]); @@ -3262,7 +3285,13 @@ fn the_merkle_walk_authenticates_a_real_opening() { // root the host actually built. The keccak instrument cannot, and the name // moved with the hash rather than outliving it. let program = merkle_opening_program(R1F_SHAPE); - let artifacts = build_artifacts(&program, &opts); + // Built at `WrapHash::production()`, so it emits `Instr::Hash` and must be + // proved under the pin's tenant — the classification rule in HASH-PINNING.md. + let artifacts = super::registry::build_artifacts_with_hasher( + &program, + &opts, + crate::hash_pin::BLOCK_HASHER, + ); let proved = lfm_prove(&program, &artifacts, &merkle_arenas(opening, *index), &opts) .expect("the honest opening must execute and prove"); @@ -3313,7 +3342,13 @@ fn tampered_merkle_opening_rejects() { // Same production twin as the honest-path test above — a tamper control is // only a control over the walk the honest path uses. let program = merkle_opening_program(R1F_SHAPE); - let artifacts = build_artifacts(&program, &opts); + // Built at `WrapHash::production()`, so it emits `Instr::Hash` and must be + // proved under the pin's tenant — the classification rule in HASH-PINNING.md. + let artifacts = super::registry::build_artifacts_with_hasher( + &program, + &opts, + crate::hash_pin::BLOCK_HASHER, + ); let honest = lfm_prove(&program, &artifacts, &merkle_arenas(opening, *index), &opts) .expect("honest prove"); @@ -3381,7 +3416,7 @@ fn tampered_merkle_opening_rejects() { ); // Incoherent: still claiming the real root. - let err = super::executor::execute(&program, &arenas, &super::hash::TestPermutation) + let err = super::executor::execute(&program, &arenas, &crate::hash_pin::BLOCK_HASHER) .err() .unwrap_or_else(|| panic!("{what}: claiming the real root must not execute")); println!("R1f tamper {what}: incoherent run rejected with {err:?}"); @@ -3699,16 +3734,12 @@ use super::programs::l2g_binding_program; /// loud — the same discipline `R1F_SHAPE` uses. const R1G_EPOCHS: usize = 2; -/// The `i`-th 32-byte root in a program's published words. +/// The `i`-th 32-byte root in a program's published words, at the root's own +/// width — the L2G binding follows the configuration, so a root is +/// `words_per_root()` words: two byte words or one algebraic word. fn published_root(public: &[(u32, LfmWord)], i: usize) -> [u8; 32] { - use math::field::traits::IsPrimeField; - let mut out = [0u8; 32]; - for h in 0..8 { - let lane = public[2 * i + h / 4].1[h % 4]; - let half = crate::tables::types::GoldilocksField::canonical(lane.value()) as u32; - out[4 * h..4 * h + 4].copy_from_slice(&half.to_le_bytes()); - } - out + let w = super::proof_arena::words_per_root(); + digest_bytes(&public[w * i..w * (i + 1)]) } fn l2g_arenas( @@ -3777,7 +3808,12 @@ fn l2g_binding_proves_and_verifies() { let opts = options(); let (epoch, global) = r1g_l2g_roots(); let program = l2g_binding_program(R1G_EPOCHS); - let artifacts = build_artifacts(&program, &opts); + // A production() program: proved under the pin's tenant, as above. + let artifacts = super::registry::build_artifacts_with_hasher( + &program, + &opts, + crate::hash_pin::BLOCK_HASHER, + ); let proved = lfm_prove(&program, &artifacts, &l2g_arenas(epoch, global), &opts) .expect("the honest binding must execute and prove"); @@ -3814,7 +3850,12 @@ fn tampered_l2g_binding_rejects() { let opts = options(); let (epoch, global) = r1g_l2g_roots(); let program = l2g_binding_program(R1G_EPOCHS); - let artifacts = build_artifacts(&program, &opts); + // A production() program: proved under the pin's tenant, as above. + let artifacts = super::registry::build_artifacts_with_hasher( + &program, + &opts, + crate::hash_pin::BLOCK_HASHER, + ); let honest = lfm_prove(&program, &artifacts, &l2g_arenas(epoch, global), &opts).expect("honest prove"); @@ -3842,7 +3883,7 @@ fn tampered_l2g_binding_rejects() { l2g_arenas(&swapped_one_side, global), ), ] { - let err = super::executor::execute(&program, &arenas, &super::hash::TestPermutation) + let err = super::executor::execute(&program, &arenas, &crate::hash_pin::BLOCK_HASHER) .err() .unwrap_or_else(|| panic!("{what}: must not execute")); println!("R1g tamper {what}: rejected with {err:?}"); diff --git a/prover/src/lfm/programs.rs b/prover/src/lfm/programs.rs index cfdd50d5d..afdec9134 100644 --- a/prover/src/lfm/programs.rs +++ b/prover/src/lfm/programs.rs @@ -948,24 +948,22 @@ pub fn l2g_binding_program_source(num_epochs: usize) -> LfmProgramSource { assert!(num_epochs > 0, "a continuation has at least one epoch"); - let words = 2 * num_epochs as u32; - let mut b = LfmBuilder::new(); + // The L2G roots are the block path's own commitments, so this program + // follows the configuration and reads each root at the DIGEST's width — + // two words on a byte hash, one on an algebraic one — never a literal two. + let mut b = LfmBuilder::new().with_wrap_hash(edsl::WrapHash::production()); + let dw = edsl::digest_words(&b); + let words = dw * num_epochs as u32; let epoch_arena = b.declare_arena(words); let global_arena = b.declare_arena(words); for i in 0..num_epochs as u32 { - let epoch = [ - b.hint_word(epoch_arena, 2 * i), - b.hint_word(epoch_arena, 2 * i + 1), - ]; - let global = [ - b.hint_word(global_arena, 2 * i), - b.hint_word(global_arena, 2 * i + 1), - ]; - edsl::assert_word_eq(&mut b, epoch[0], global[0]); - edsl::assert_word_eq(&mut b, epoch[1], global[1]); - b.public(epoch[0]); - b.public(epoch[1]); + let epoch = edsl::hint_digest(&mut b, epoch_arena, dw * i); + let global = edsl::hint_digest(&mut b, global_arena, dw * i); + for (e, g) in epoch.cells().iter().zip(global.cells()) { + edsl::assert_word_eq(&mut b, *e, *g); + b.public(*e); + } } b.finish() } diff --git a/prover/src/lfm/proof_arena.rs b/prover/src/lfm/proof_arena.rs index 07dd2b096..fd9374a30 100644 --- a/prover/src/lfm/proof_arena.rs +++ b/prover/src/lfm/proof_arena.rs @@ -28,11 +28,24 @@ use super::word::{LfmWord, base_word}; type FE = FieldElement; +/// The BATCHED Merkle backend the block path commits under, over any field. +/// +/// ⛔ Use this rather than `stark::config::BatchedMerkleTreeBackend`, which is +/// `BatchBlake3Backend` by definition — a workspace-default ALIAS, and therefore +/// the same silent spelling of the default that `Prover` and `Verifier` are. A +/// test comparing a machine leaf against that alias compares against BLAKE3 +/// whatever the branch pins. +pub type BlockBatched = + ::Batched; + +/// The PAIR backend FRI layers commit under. See [`BlockBatched`]; the alias it +/// replaces is `stark::config::FriLayerMerkleTreeBackend` = `PairBlake3Backend`. +pub type BlockPair = ::Pair; + /// The Merkle backend the main trace is committed under — the BLOCK PATH's pin, /// not a locally chosen equivalent and no longer `stark`'s default alias, so a /// branch that pins a different hash reaches this module too. -type MainBackend = - ::Batched; +type MainBackend = BlockBatched; /// Halves in one 32-byte commitment. pub const ROOT_HALVES: usize = 8; @@ -359,6 +372,36 @@ pub fn commitments_to_arena(roots: &[Commitment]) -> Vec { roots.iter().flat_map(commitment_words).collect() } +/// [`commitments_to_arena`] at the width of an EXPLICIT wrap hash rather than +/// the configuration's — the host half of the rule that the arena stride is the +/// BUILDER's digest width. +/// +/// A program that pins a byte hash on its own builder reads two words per root +/// whatever the pin says (`edsl::digest_words` of that builder), so the host +/// feeding it must serialise at that width too; under an algebraic pin the +/// configuration-following [`commitments_to_arena`] would hand it one word per +/// root and the executor's arena-length check refuses the program outright. +pub fn commitments_to_arena_for(roots: &[Commitment], hash: super::edsl::WrapHash) -> Vec { + roots + .iter() + .flat_map(|c| commitment_words_for(c, hash)) + .collect() +} + +/// [`commitment_words`] at the width of an explicit wrap hash. See +/// [`commitments_to_arena_for`]. +pub fn commitment_words_for(c: &Commitment, hash: super::edsl::WrapHash) -> Vec { + if hash == super::edsl::WrapHash::Algebraic { + return vec![super::algebraic_commit::commitment_to_digest(c)]; + } + let halves = pack_stream(c); + debug_assert_eq!(halves.len(), ROOT_HALVES); + vec![ + [halves[0], halves[1], halves[2], halves[3]], + [halves[4], halves[5], halves[6], halves[7]], + ] +} + // ==================== the attestation's program id ==================== /// The inner ELF bytes the guest input carries. diff --git a/prover/src/lfm/registry.rs b/prover/src/lfm/registry.rs index 8fad7a29c..a61cf9f16 100644 --- a/prover/src/lfm/registry.rs +++ b/prover/src/lfm/registry.rs @@ -498,20 +498,40 @@ impl LfmArtifacts { /// ever also selects the commitment scheme the roots are committed under, every /// root above moves with it and the tag on its own stops being the whole /// binding. +/// ★ The `LFM_HASH` permutation `LFM_REGISTRY` is blessed under. +/// +/// Bound into every digest in that table — `HasherKind::as_tag` is folded into +/// `lfm_program_id` — so changing it here is a re-blessing of the whole table, +/// not a re-run. **A second hasher becomes additional ROWS, never a silent +/// replacement of these.** +/// +/// ⚠ Lives here rather than in `compute_lfm_registry` because it is a property +/// of the TABLE, not of the generator, and because [`build_artifacts`] has to +/// name it: a default spelled `HasherKind::default()` is a silent global, while +/// one spelled `REGISTRY_HASHER` is a statement about registry identity that a +/// reader can check against the table. +pub const REGISTRY_HASHER: HasherKind = HasherKind::Test; + pub fn build_artifacts(program: &LfmProgram, options: &ProofOptions) -> LfmArtifacts { - // ★ The block path's PINNED socket permutation, not `HasherKind::default()`. + // ⛔ **DEFAULTS TO `Test`, AND MUST.** The hasher is part of program + // IDENTITY — `HasherKind::as_tag` is folded into `lfm_program_id` — and + // `LFM_REGISTRY` is blessed under `compute_lfm_registry`'s + // `REGISTRY_HASHER = Test`, whose own doc calls changing it a re-blessing of + // the whole table rather than a re-run. // - // ⚠ The default is `Test`, a one-round toy, and under a BYTE hash that is - // free and correct: `ByteWrapHash::hash_bytes` lowers to the dedicated - // KECCAK / `LFM_BLAKE3` chips and emits no `Instr::Hash` at all, so the - // socket hasher is never consulted. The algebraic arm goes through - // `compress` / `permute`, which ARE `Instr::Hash` — so a program built here - // would be proved with the toy permutation while the host committed under - // RPO, and every digest downstream would be wrong. It surfaces as the - // grinding check refusing an honest nonce, naming nothing. + // ⚠ This entry point was briefly changed to name `hash_pin::BLOCK_HASHER`, + // to fix a real defect on the AGGREGATOR path where `lfm_prove_batched` + // inherited a toy permutation from a defaulted build. That fix was correct + // about the defect and wrong about its scope: it moved every registry + // program's identity away from the blessed table, and + // `rpo_chip_tests::the_rpo_choice_moves_the_program_digest_and_no_root` + // asserts this function defaults to `Test` in as many words. // - // On a byte pin this is `HasherKind::Test` and the call is unchanged. - build_artifacts_with_hasher(program, options, crate::hash_pin::BLOCK_HASHER) + // ★ The block path names its hasher AT THE CALL SITE + // ([`build_artifacts_with_hasher`]) instead. Registry programs pin a byte + // hash on their own builders, emit no `Instr::Hash`, and never consult the + // socket — they are correct at `Test` under every pin. + build_artifacts_with_hasher(program, options, REGISTRY_HASHER) } /// [`build_artifacts`] for a program proved under an explicitly chosen @@ -779,64 +799,64 @@ pub static LFM_REGISTRY: &[LfmRegistryEntry] = &[ blowup_factor: 2, roots: [ [ - 0xc4, 0x37, 0x4f, 0xb8, 0xb4, 0xfb, 0x57, 0x10, 0xd0, 0x8a, 0x30, 0xd5, 0xfe, 0xee, - 0x32, 0x36, 0x95, 0xfb, 0x55, 0xaa, 0x27, 0x9d, 0x4c, 0x5b, 0xf9, 0xff, 0xd6, 0xed, - 0xc0, 0xb9, 0x9d, 0x4f, + 0x82, 0x54, 0x4b, 0xc4, 0x61, 0x8d, 0x49, 0x29, 0x43, 0x97, 0x57, 0x20, 0xca, 0x82, + 0xd6, 0x74, 0xfe, 0x72, 0x82, 0x09, 0x39, 0x3c, 0xe6, 0x45, 0x42, 0xab, 0xbf, 0x15, + 0x9f, 0xec, 0xad, 0xfd, ], [ - 0x39, 0xa6, 0xc2, 0x1c, 0xed, 0x86, 0x48, 0x8a, 0xcf, 0xdc, 0xa7, 0xcf, 0x82, 0xd0, - 0x2d, 0x16, 0xc7, 0x65, 0xb4, 0x7b, 0x75, 0x79, 0x15, 0x9b, 0xfe, 0xfc, 0xe4, 0x36, - 0x67, 0x6b, 0x9e, 0x6a, + 0xe7, 0x02, 0x4b, 0x94, 0x35, 0x37, 0xe8, 0x74, 0x72, 0x94, 0xc0, 0x66, 0xf7, 0xa7, + 0x40, 0x08, 0xbb, 0x59, 0x87, 0xa4, 0xe2, 0xf6, 0x50, 0x80, 0x4b, 0xe0, 0xb3, 0x67, + 0x01, 0xbc, 0x82, 0x91, ], [ - 0x3a, 0x2f, 0xf0, 0xdd, 0x38, 0x45, 0x69, 0x6f, 0xa9, 0xc9, 0xd3, 0xc7, 0xe4, 0xa1, - 0xc0, 0x46, 0x30, 0x01, 0xb3, 0xd5, 0xad, 0x16, 0xb4, 0x89, 0x25, 0xc6, 0xdc, 0x5a, - 0xa8, 0x47, 0xe9, 0x1c, + 0x05, 0x9b, 0xa3, 0x44, 0xe5, 0x9c, 0x68, 0xbd, 0xa5, 0x82, 0xee, 0xec, 0xca, 0xee, + 0x30, 0xbb, 0x3f, 0x0e, 0x73, 0x19, 0x07, 0x82, 0x52, 0x8d, 0x39, 0x43, 0x78, 0x04, + 0x61, 0x09, 0xc5, 0x26, ], [ - 0xd5, 0x12, 0xa3, 0xb7, 0xcd, 0x73, 0x29, 0xe1, 0x4f, 0x2a, 0x69, 0x49, 0xa2, 0xe2, - 0x1a, 0xbe, 0x4a, 0xf0, 0xf6, 0xd4, 0xd5, 0xd1, 0x21, 0x91, 0x93, 0x55, 0x9e, 0x04, - 0x99, 0xc2, 0x9c, 0xbd, + 0x87, 0xfb, 0xc4, 0x76, 0x2e, 0x30, 0xa3, 0xe6, 0x6e, 0x84, 0x5a, 0xd8, 0xec, 0x2b, + 0x40, 0xe7, 0x45, 0x95, 0xb1, 0xa4, 0x59, 0xbd, 0x1c, 0x4b, 0xed, 0xcc, 0x9d, 0xb1, + 0xa6, 0xdd, 0x7b, 0x52, ], [ - 0xd6, 0xd8, 0x93, 0x9a, 0xcb, 0x60, 0xe0, 0xd5, 0x37, 0x9c, 0x9e, 0xc6, 0x44, 0x9e, - 0xa0, 0x46, 0x6a, 0xb2, 0xc4, 0x2b, 0x53, 0x9a, 0x9f, 0xa7, 0x1d, 0xd8, 0x02, 0x16, - 0x36, 0x9d, 0xf3, 0xdb, + 0x0d, 0x28, 0x93, 0xa3, 0xb5, 0xa3, 0x06, 0x0c, 0xab, 0x08, 0x2f, 0x56, 0x8f, 0x9a, + 0x4b, 0x02, 0x6b, 0x58, 0x55, 0x45, 0xf5, 0xc3, 0xfd, 0x0d, 0x12, 0x39, 0x4d, 0x99, + 0x48, 0x41, 0x4b, 0xf7, ], [ - 0x4d, 0xee, 0x07, 0x52, 0x0b, 0x48, 0xd9, 0x42, 0x4c, 0xb7, 0xc5, 0x5f, 0x70, 0xbf, - 0xbd, 0xed, 0xf1, 0xc5, 0x85, 0xaf, 0xea, 0xdd, 0xfc, 0x8d, 0xb8, 0x0b, 0x7c, 0x64, - 0x35, 0xe2, 0x81, 0x31, + 0xee, 0x9a, 0x81, 0xfb, 0xc8, 0xbd, 0x72, 0x5f, 0x89, 0x27, 0x3f, 0xd4, 0xe8, 0xcb, + 0x20, 0x07, 0xf8, 0x01, 0x7f, 0x7e, 0x78, 0x34, 0x38, 0x9a, 0xef, 0xce, 0xfd, 0x39, + 0xaf, 0xe0, 0x9c, 0x6d, ], [ - 0x80, 0xe6, 0x2e, 0x9d, 0xac, 0x64, 0x93, 0x41, 0xae, 0x34, 0x6a, 0xba, 0xaa, 0x9a, - 0x1f, 0x49, 0x0c, 0x3f, 0xfd, 0xd3, 0x20, 0x6c, 0x97, 0xdc, 0xcd, 0xc1, 0x95, 0x99, - 0x6f, 0xe5, 0x6b, 0x70, + 0x8d, 0xa4, 0x49, 0x54, 0x80, 0xa3, 0xfe, 0x4d, 0x2f, 0xb1, 0xe0, 0x1d, 0xd9, 0x6b, + 0x82, 0xa6, 0x65, 0x36, 0x1f, 0x21, 0xf9, 0x92, 0x67, 0x4f, 0x88, 0x90, 0x70, 0xab, + 0xff, 0x31, 0xec, 0xb2, ], [ - 0xa2, 0xf9, 0x94, 0x17, 0x1f, 0xb2, 0x5f, 0x6c, 0x9d, 0x29, 0x94, 0xb7, 0xc2, 0x7f, - 0x6b, 0x1d, 0x6a, 0xe7, 0xad, 0x66, 0x5b, 0x41, 0xa7, 0xd1, 0xf2, 0x53, 0x80, 0xa6, - 0x34, 0x5d, 0x27, 0x87, + 0x9b, 0xd2, 0x23, 0x26, 0x3c, 0xa7, 0x25, 0xb3, 0xc1, 0x3c, 0x18, 0xe9, 0x9c, 0xb2, + 0x28, 0xdb, 0x4f, 0x29, 0x58, 0xac, 0xdb, 0x52, 0x58, 0xe8, 0x23, 0x2e, 0xb0, 0x8b, + 0x0d, 0xd5, 0xed, 0x41, ], [ - 0x18, 0x61, 0x3e, 0x19, 0xc1, 0x22, 0x91, 0x76, 0xa8, 0x95, 0x99, 0xba, 0x0d, 0x28, - 0x28, 0xad, 0x60, 0xe9, 0x02, 0x41, 0x85, 0x6f, 0xa5, 0xfa, 0x7c, 0xef, 0xd6, 0x90, - 0x97, 0x13, 0x2b, 0x26, + 0xd7, 0x1c, 0x38, 0x03, 0x8f, 0x76, 0x18, 0x6f, 0xde, 0xf9, 0x59, 0x89, 0x9e, 0xc4, + 0x6d, 0xa2, 0x5a, 0x5b, 0x94, 0xd0, 0x5f, 0x34, 0x32, 0x0a, 0xd8, 0xbc, 0xf3, 0x54, + 0xda, 0xf0, 0x81, 0xcb, ], [ - 0xc5, 0x58, 0xc5, 0x04, 0x78, 0xb4, 0x99, 0xd5, 0x48, 0x59, 0x23, 0x3a, 0xed, 0xf0, - 0x78, 0x5e, 0xea, 0xa1, 0x67, 0xb4, 0x9a, 0xec, 0xfc, 0x7e, 0xd5, 0x99, 0x2e, 0xf0, - 0x04, 0xd5, 0xdc, 0x7f, + 0xca, 0x9c, 0x81, 0x3f, 0xdc, 0xd4, 0x49, 0x28, 0x06, 0xd2, 0x9b, 0x22, 0x09, 0x77, + 0x0f, 0x27, 0x87, 0x6e, 0xb1, 0x28, 0xf4, 0x3c, 0x4a, 0x38, 0xf7, 0x57, 0x42, 0x92, + 0x29, 0xeb, 0x86, 0x8c, ], [ - 0x6c, 0xf6, 0x04, 0x71, 0x7c, 0x74, 0x88, 0x1d, 0x1b, 0x74, 0x19, 0x74, 0xbe, 0xfd, - 0x67, 0x74, 0x13, 0xdc, 0xd0, 0x05, 0x4d, 0xcf, 0x8f, 0xbf, 0x98, 0x61, 0xa6, 0x21, - 0x1d, 0x6b, 0xa2, 0xbe, + 0xa3, 0xd2, 0x1d, 0x58, 0xbf, 0x0c, 0x09, 0xb2, 0x14, 0xce, 0xe6, 0x4f, 0x94, 0xf8, + 0x10, 0xc2, 0xc2, 0xf0, 0xcb, 0x13, 0xd8, 0x8a, 0x66, 0xbc, 0x8f, 0x4c, 0x76, 0x72, + 0xe8, 0xc4, 0x17, 0x99, ], [ - 0x5b, 0x89, 0xe1, 0x60, 0xe1, 0xcf, 0x68, 0x81, 0xd2, 0xeb, 0x40, 0x21, 0x62, 0x5f, - 0x7a, 0x76, 0x96, 0x9e, 0xc8, 0x0b, 0x01, 0x3f, 0x0a, 0xfb, 0x3f, 0x24, 0x73, 0xcd, - 0x55, 0xd3, 0xea, 0x27, + 0x4b, 0xd7, 0x50, 0x10, 0xec, 0x2d, 0xfb, 0x77, 0xbd, 0xdd, 0x72, 0x55, 0xec, 0xb4, + 0x35, 0x03, 0xb1, 0x99, 0x66, 0xac, 0xe9, 0x33, 0xe6, 0xcc, 0x08, 0xae, 0xba, 0x7c, + 0x54, 0xdb, 0x18, 0x9c, ], [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -844,14 +864,14 @@ pub static LFM_REGISTRY: &[LfmRegistryEntry] = &[ 0x00, 0x00, 0x00, 0x00, ], [ - 0x48, 0xad, 0x9f, 0x53, 0x9b, 0x8e, 0x46, 0x7e, 0x45, 0x9d, 0x3a, 0x14, 0xe1, 0x22, - 0x80, 0xec, 0xd5, 0xfd, 0x7a, 0x0f, 0x77, 0x84, 0xb8, 0xf0, 0xc4, 0x53, 0x4d, 0xb2, - 0x83, 0x90, 0xcb, 0x99, + 0x0a, 0x3c, 0x86, 0xe2, 0xd3, 0x37, 0x98, 0xf2, 0x68, 0x45, 0x42, 0xbf, 0x0b, 0x3d, + 0x52, 0x02, 0x4b, 0x08, 0x8b, 0x7a, 0x4b, 0xc8, 0xb0, 0x2a, 0xe4, 0x14, 0x86, 0x09, + 0x35, 0xdc, 0xa6, 0x7c, ], [ - 0x9f, 0xbc, 0x26, 0x49, 0xce, 0x62, 0x34, 0xb5, 0x87, 0x3b, 0x91, 0xd8, 0x45, 0x2c, - 0x60, 0x74, 0x9b, 0x28, 0xaf, 0xdb, 0xf5, 0x1f, 0x3b, 0x9d, 0xde, 0x7c, 0xd0, 0xc2, - 0x38, 0x74, 0x8a, 0xeb, + 0x46, 0x8d, 0xfe, 0x65, 0xed, 0x50, 0x25, 0xbf, 0x7e, 0xfa, 0x04, 0x11, 0x28, 0x36, + 0x29, 0x06, 0xbd, 0x97, 0x29, 0xeb, 0x5b, 0x3a, 0xae, 0xbf, 0x66, 0xea, 0x5e, 0x59, + 0xe4, 0xd7, 0x17, 0x6c, ], ], log_heights: [3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 16, 2, 0, 5, 20], @@ -862,14 +882,14 @@ pub static LFM_REGISTRY: &[LfmRegistryEntry] = &[ blake3: false, }, program_id: [ - 0x5b, 0xbd, 0x2f, 0x24, 0x96, 0x05, 0x49, 0x0e, 0x26, 0xb6, 0xd7, 0xaa, 0xcb, 0x7f, - 0xde, 0x61, 0xdb, 0xac, 0x1d, 0xa4, 0x09, 0x69, 0xac, 0x91, 0xdb, 0x48, 0xd6, 0xaa, - 0x5b, 0x23, 0xb9, 0x1c, + 0xff, 0xaf, 0xf6, 0xee, 0xf4, 0xdc, 0x28, 0x7f, 0xf3, 0x94, 0xd1, 0x91, 0x61, 0x3c, + 0xda, 0x00, 0x7d, 0x2e, 0xc7, 0x6d, 0xaa, 0x6e, 0x38, 0x96, 0x64, 0x81, 0xde, 0xed, + 0x27, 0xfd, 0x68, 0xde, ], prep_root: [ - 0x67, 0xb6, 0x24, 0xb7, 0x38, 0x62, 0xfb, 0x62, 0x9d, 0x64, 0x01, 0x95, 0x07, 0xd1, - 0x05, 0xa5, 0x30, 0x8a, 0xd8, 0x7a, 0xdb, 0x50, 0x10, 0xdb, 0x4e, 0x2c, 0x94, 0x0c, - 0x3d, 0xd4, 0xe1, 0x58, + 0x23, 0x57, 0x15, 0x53, 0x9b, 0xdb, 0xf1, 0x9e, 0x9e, 0x6f, 0x9b, 0xce, 0x1d, 0x51, + 0x9e, 0x57, 0x28, 0x28, 0x47, 0x36, 0x03, 0x3b, 0x0b, 0x78, 0xd9, 0xdb, 0x7b, 0x1b, + 0x80, 0x2b, 0xf9, 0xac, ], prep_widths: [6, 10, 11, 8, 134, 13, 56, 12, 2, 3, 1, 20, 0, 0, 0], }, @@ -878,64 +898,64 @@ pub static LFM_REGISTRY: &[LfmRegistryEntry] = &[ blowup_factor: 2, roots: [ [ - 0x70, 0xac, 0x06, 0xa6, 0xd5, 0xad, 0x1b, 0xa7, 0xdf, 0xa1, 0xea, 0x71, 0x28, 0x70, - 0x1b, 0xc8, 0xa4, 0xc5, 0xc3, 0xe4, 0x53, 0x23, 0x4a, 0x30, 0x7c, 0x6d, 0x73, 0x4b, - 0x88, 0xa1, 0x0f, 0x43, + 0x9a, 0x59, 0x5c, 0x71, 0xd1, 0xa7, 0x6a, 0xf8, 0x76, 0x0a, 0x56, 0x5f, 0x3d, 0xf5, + 0x87, 0x9f, 0x71, 0x48, 0xf0, 0xf0, 0xd3, 0x49, 0x38, 0x5c, 0x2b, 0x9a, 0x6d, 0x05, + 0x91, 0x18, 0x93, 0x0d, ], [ - 0xfb, 0x9a, 0x60, 0xbc, 0x12, 0xeb, 0x89, 0x64, 0xf8, 0x55, 0xf3, 0xc7, 0x63, 0xc0, - 0x7c, 0x6a, 0x4b, 0x4c, 0x96, 0xd3, 0x54, 0xfe, 0x10, 0xcb, 0x0a, 0x50, 0xb7, 0x13, - 0xe2, 0xbf, 0x9f, 0x89, + 0xed, 0x75, 0x5c, 0x0a, 0x8b, 0x67, 0x41, 0x15, 0x9c, 0x13, 0xdd, 0xcc, 0xbb, 0x80, + 0x34, 0x20, 0xf9, 0x11, 0x8e, 0x74, 0x4b, 0xe2, 0x4c, 0xc7, 0x79, 0xe0, 0x4f, 0x4d, + 0x76, 0x31, 0x09, 0xd0, ], [ - 0x9f, 0xab, 0xcd, 0xbd, 0x46, 0xd9, 0x37, 0x10, 0x80, 0x58, 0xb4, 0x00, 0x7f, 0x72, - 0x84, 0x15, 0x7f, 0x46, 0xde, 0xc1, 0x09, 0x7a, 0xd9, 0xe5, 0x72, 0xac, 0xae, 0xf0, - 0xfd, 0xf4, 0xc9, 0xb1, + 0x20, 0x8b, 0x3c, 0x06, 0x1e, 0x1c, 0xf4, 0x7a, 0xe3, 0x6b, 0xb6, 0xf3, 0x62, 0x1e, + 0xda, 0x7e, 0x0e, 0x5a, 0x1c, 0x7c, 0x77, 0x4d, 0xf5, 0x1e, 0xb9, 0xd1, 0xad, 0x27, + 0xed, 0xac, 0xba, 0x44, ], [ - 0x63, 0x15, 0x10, 0x60, 0x57, 0x2c, 0xc4, 0x75, 0xb2, 0x86, 0xee, 0x3e, 0x39, 0x3a, - 0xf4, 0xd5, 0x1d, 0xe5, 0x98, 0x6b, 0xe3, 0x22, 0xbd, 0xc5, 0x4e, 0x6f, 0xf9, 0x31, - 0x45, 0xe3, 0x38, 0xd8, + 0x62, 0x88, 0x6d, 0xac, 0x61, 0xce, 0x6f, 0x89, 0x64, 0x26, 0x67, 0x3d, 0xcb, 0x77, + 0x0a, 0xbb, 0x85, 0x53, 0x99, 0xf4, 0x09, 0xc0, 0x33, 0x1b, 0xe6, 0xf7, 0x93, 0x65, + 0x17, 0x7f, 0x15, 0x5c, ], [ - 0xcf, 0xe1, 0xf7, 0xb5, 0xfa, 0x6a, 0xbe, 0xec, 0x67, 0x88, 0xc2, 0xc4, 0x0d, 0x65, - 0xab, 0x7b, 0x94, 0x09, 0x21, 0xcf, 0x71, 0x22, 0x4a, 0xc6, 0x20, 0x96, 0x2d, 0x7d, - 0x50, 0x22, 0xa3, 0xf9, + 0xd9, 0xe6, 0xaf, 0xf7, 0x07, 0x25, 0xaa, 0x8c, 0x15, 0x0f, 0x41, 0x3e, 0xd5, 0x46, + 0x13, 0x06, 0x70, 0x75, 0x63, 0x1b, 0xc0, 0x65, 0x98, 0x0f, 0xa9, 0x25, 0x2e, 0x5f, + 0xdc, 0x62, 0xeb, 0x65, ], [ - 0xfa, 0xf3, 0x6c, 0x05, 0x83, 0x57, 0xa3, 0x6b, 0x72, 0x33, 0x48, 0x07, 0xba, 0xa7, - 0x86, 0x78, 0x1a, 0xa4, 0xcb, 0xe6, 0x49, 0xf4, 0x74, 0x20, 0xe3, 0x49, 0xcb, 0x0e, - 0x18, 0x91, 0xab, 0x72, + 0x7f, 0x87, 0x55, 0xc5, 0x81, 0x3b, 0xeb, 0x94, 0x20, 0x1d, 0xf1, 0xe5, 0x17, 0x93, + 0x1a, 0xe2, 0xe9, 0x46, 0x58, 0xb6, 0x00, 0x04, 0x99, 0xb1, 0x54, 0xdc, 0xa2, 0x7a, + 0xa8, 0xf4, 0xe3, 0xa4, ], [ - 0x80, 0xe6, 0x2e, 0x9d, 0xac, 0x64, 0x93, 0x41, 0xae, 0x34, 0x6a, 0xba, 0xaa, 0x9a, - 0x1f, 0x49, 0x0c, 0x3f, 0xfd, 0xd3, 0x20, 0x6c, 0x97, 0xdc, 0xcd, 0xc1, 0x95, 0x99, - 0x6f, 0xe5, 0x6b, 0x70, + 0x8d, 0xa4, 0x49, 0x54, 0x80, 0xa3, 0xfe, 0x4d, 0x2f, 0xb1, 0xe0, 0x1d, 0xd9, 0x6b, + 0x82, 0xa6, 0x65, 0x36, 0x1f, 0x21, 0xf9, 0x92, 0x67, 0x4f, 0x88, 0x90, 0x70, 0xab, + 0xff, 0x31, 0xec, 0xb2, ], [ - 0xf7, 0x6a, 0x87, 0xdd, 0xf9, 0x66, 0xfc, 0x2f, 0x04, 0x98, 0x52, 0x2a, 0x46, 0xfe, - 0x7c, 0x21, 0x6a, 0x41, 0xc3, 0x7d, 0x29, 0xdd, 0xae, 0xf6, 0xb5, 0xaa, 0x67, 0x27, - 0x2b, 0x76, 0x82, 0x0d, + 0x96, 0x15, 0x02, 0xab, 0x70, 0xc5, 0x29, 0x42, 0xe0, 0xa6, 0xd7, 0xe6, 0x8c, 0x4e, + 0x0e, 0xf6, 0x1f, 0x28, 0x9b, 0xc5, 0xfa, 0x61, 0xa5, 0xc6, 0x96, 0x3a, 0xf1, 0xd4, + 0xe6, 0x91, 0xe5, 0x2b, ], [ - 0xc5, 0x38, 0x40, 0x49, 0xab, 0xb6, 0xa4, 0xb0, 0x38, 0x08, 0x83, 0x85, 0x42, 0x03, - 0x59, 0x34, 0xb4, 0x6d, 0x52, 0xa7, 0x5e, 0xf2, 0x53, 0x93, 0x6a, 0xcf, 0x9b, 0x6f, - 0x28, 0x57, 0x45, 0xe5, + 0xd2, 0x8e, 0xe0, 0xb7, 0x30, 0x2f, 0xc0, 0xf3, 0x1c, 0x75, 0xe3, 0x19, 0x08, 0x9c, + 0xd3, 0x00, 0x7b, 0x8c, 0xbe, 0x40, 0x85, 0xad, 0xf6, 0xbe, 0xb8, 0xc6, 0x2f, 0x46, + 0x8f, 0xfd, 0x03, 0xcd, ], [ - 0x83, 0xdb, 0xad, 0xc4, 0x15, 0x82, 0x82, 0x91, 0x87, 0x33, 0xf4, 0xe2, 0x34, 0x96, - 0xa6, 0xad, 0x5c, 0x29, 0xa9, 0xe4, 0x02, 0x11, 0x47, 0x4e, 0xfc, 0x76, 0x4d, 0xb2, - 0x63, 0x8f, 0xdc, 0x06, + 0x51, 0x61, 0x61, 0x29, 0x2f, 0x64, 0x86, 0xf3, 0x77, 0x19, 0x29, 0xae, 0x32, 0x5d, + 0x7e, 0xbb, 0xe2, 0x8a, 0x8d, 0x56, 0x27, 0x43, 0x1e, 0xf3, 0xd2, 0x3b, 0x82, 0x79, + 0xec, 0x71, 0x01, 0x6f, ], [ - 0x6c, 0xf6, 0x04, 0x71, 0x7c, 0x74, 0x88, 0x1d, 0x1b, 0x74, 0x19, 0x74, 0xbe, 0xfd, - 0x67, 0x74, 0x13, 0xdc, 0xd0, 0x05, 0x4d, 0xcf, 0x8f, 0xbf, 0x98, 0x61, 0xa6, 0x21, - 0x1d, 0x6b, 0xa2, 0xbe, + 0xa3, 0xd2, 0x1d, 0x58, 0xbf, 0x0c, 0x09, 0xb2, 0x14, 0xce, 0xe6, 0x4f, 0x94, 0xf8, + 0x10, 0xc2, 0xc2, 0xf0, 0xcb, 0x13, 0xd8, 0x8a, 0x66, 0xbc, 0x8f, 0x4c, 0x76, 0x72, + 0xe8, 0xc4, 0x17, 0x99, ], [ - 0x5b, 0x89, 0xe1, 0x60, 0xe1, 0xcf, 0x68, 0x81, 0xd2, 0xeb, 0x40, 0x21, 0x62, 0x5f, - 0x7a, 0x76, 0x96, 0x9e, 0xc8, 0x0b, 0x01, 0x3f, 0x0a, 0xfb, 0x3f, 0x24, 0x73, 0xcd, - 0x55, 0xd3, 0xea, 0x27, + 0x4b, 0xd7, 0x50, 0x10, 0xec, 0x2d, 0xfb, 0x77, 0xbd, 0xdd, 0x72, 0x55, 0xec, 0xb4, + 0x35, 0x03, 0xb1, 0x99, 0x66, 0xac, 0xe9, 0x33, 0xe6, 0xcc, 0x08, 0xae, 0xba, 0x7c, + 0x54, 0xdb, 0x18, 0x9c, ], [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -943,14 +963,14 @@ pub static LFM_REGISTRY: &[LfmRegistryEntry] = &[ 0x00, 0x00, 0x00, 0x00, ], [ - 0x48, 0xad, 0x9f, 0x53, 0x9b, 0x8e, 0x46, 0x7e, 0x45, 0x9d, 0x3a, 0x14, 0xe1, 0x22, - 0x80, 0xec, 0xd5, 0xfd, 0x7a, 0x0f, 0x77, 0x84, 0xb8, 0xf0, 0xc4, 0x53, 0x4d, 0xb2, - 0x83, 0x90, 0xcb, 0x99, + 0x0a, 0x3c, 0x86, 0xe2, 0xd3, 0x37, 0x98, 0xf2, 0x68, 0x45, 0x42, 0xbf, 0x0b, 0x3d, + 0x52, 0x02, 0x4b, 0x08, 0x8b, 0x7a, 0x4b, 0xc8, 0xb0, 0x2a, 0xe4, 0x14, 0x86, 0x09, + 0x35, 0xdc, 0xa6, 0x7c, ], [ - 0x9f, 0xbc, 0x26, 0x49, 0xce, 0x62, 0x34, 0xb5, 0x87, 0x3b, 0x91, 0xd8, 0x45, 0x2c, - 0x60, 0x74, 0x9b, 0x28, 0xaf, 0xdb, 0xf5, 0x1f, 0x3b, 0x9d, 0xde, 0x7c, 0xd0, 0xc2, - 0x38, 0x74, 0x8a, 0xeb, + 0x46, 0x8d, 0xfe, 0x65, 0xed, 0x50, 0x25, 0xbf, 0x7e, 0xfa, 0x04, 0x11, 0x28, 0x36, + 0x29, 0x06, 0xbd, 0x97, 0x29, 0xeb, 0x5b, 0x3a, 0xae, 0xbf, 0x66, 0xea, 0x5e, 0x59, + 0xe4, 0xd7, 0x17, 0x6c, ], ], log_heights: [5, 8, 7, 7, 2, 7, 2, 5, 7, 2, 16, 2, 0, 5, 20], @@ -961,14 +981,14 @@ pub static LFM_REGISTRY: &[LfmRegistryEntry] = &[ blake3: false, }, program_id: [ - 0x7e, 0x33, 0xb7, 0xb4, 0xd0, 0x9a, 0xbc, 0xcb, 0xd8, 0xd5, 0xae, 0x44, 0x47, 0xd4, - 0x7a, 0x04, 0x53, 0xd2, 0x29, 0xd8, 0xcd, 0xe8, 0x36, 0x86, 0x8f, 0x7f, 0x1a, 0x83, - 0xed, 0x0c, 0x24, 0xe1, + 0x55, 0xb6, 0x4c, 0xed, 0x57, 0x0c, 0xb1, 0x99, 0xc6, 0x05, 0xd8, 0x19, 0x77, 0x07, + 0x01, 0xc2, 0x3e, 0x0b, 0x93, 0xda, 0x00, 0xc6, 0xb4, 0x6d, 0x99, 0xd5, 0x7e, 0xc0, + 0x6b, 0xb6, 0x49, 0xf4, ], prep_root: [ - 0x26, 0xc7, 0x59, 0x52, 0x12, 0x62, 0x93, 0x9d, 0xb0, 0x2c, 0x6c, 0xe2, 0x1c, 0xa7, - 0x14, 0xa7, 0x08, 0xf6, 0x77, 0x06, 0x7c, 0xfd, 0xdf, 0xce, 0xee, 0x88, 0xfd, 0xf3, - 0xcc, 0xda, 0x67, 0x5d, + 0x81, 0x31, 0x40, 0x97, 0xdc, 0x51, 0x37, 0x09, 0x39, 0x04, 0x60, 0x51, 0xe7, 0x3c, + 0x35, 0x58, 0x21, 0x69, 0xdd, 0x0e, 0x5f, 0xbf, 0x0f, 0x69, 0x1d, 0xb4, 0xff, 0x7a, + 0xae, 0x80, 0x43, 0x5c, ], prep_widths: [6, 10, 11, 8, 134, 13, 56, 12, 2, 3, 1, 20, 0, 0, 0], }, @@ -977,64 +997,64 @@ pub static LFM_REGISTRY: &[LfmRegistryEntry] = &[ blowup_factor: 2, roots: [ [ - 0xff, 0x4b, 0xdf, 0x1d, 0x97, 0x16, 0xf4, 0xea, 0x96, 0x06, 0xb1, 0x02, 0xe2, 0xb9, - 0xdf, 0x8e, 0xb0, 0x65, 0x3f, 0x64, 0x7b, 0xe5, 0x44, 0x92, 0x15, 0x61, 0x44, 0x31, - 0x0f, 0x55, 0x00, 0x9b, + 0xe1, 0xc3, 0x27, 0x68, 0xac, 0x08, 0x46, 0xd6, 0x86, 0xeb, 0xae, 0x4c, 0xff, 0x10, + 0x92, 0x9e, 0x5c, 0xcc, 0x6a, 0xdd, 0xd5, 0xcd, 0xf2, 0xce, 0x5c, 0x1e, 0x60, 0x7d, + 0x6f, 0xd1, 0x77, 0x33, ], [ - 0xfe, 0xd5, 0xf3, 0x81, 0xda, 0x69, 0x9d, 0x2a, 0xad, 0x4f, 0x07, 0x3a, 0xf5, 0x16, - 0xdf, 0xc0, 0x5f, 0x87, 0x08, 0x4a, 0x64, 0x91, 0x48, 0xc7, 0x16, 0xcb, 0xe8, 0x73, - 0x8d, 0x94, 0x28, 0xd5, + 0x31, 0xf9, 0x22, 0xf3, 0x9d, 0xf1, 0xcb, 0xd0, 0xeb, 0x62, 0x25, 0x93, 0xea, 0x75, + 0x43, 0x30, 0x8b, 0xb1, 0x02, 0x45, 0x20, 0xde, 0xf9, 0x17, 0x78, 0x17, 0x27, 0xc6, + 0xc4, 0x9a, 0x99, 0x28, ], [ - 0x03, 0x45, 0x83, 0x23, 0x1a, 0xc2, 0xf0, 0x18, 0x7f, 0xd6, 0x9e, 0xb6, 0x94, 0x3b, - 0xdb, 0xd2, 0x5f, 0x6b, 0x2f, 0x9f, 0x74, 0x1a, 0xa5, 0x7e, 0x8e, 0x2a, 0x45, 0x99, - 0xc5, 0x92, 0x2b, 0x13, + 0x07, 0xdb, 0x70, 0x37, 0x6d, 0xff, 0x1c, 0x50, 0x7f, 0x82, 0xf7, 0x83, 0x73, 0x7c, + 0x2e, 0xad, 0xab, 0x48, 0xbc, 0x68, 0x82, 0xf7, 0xff, 0x66, 0x80, 0x88, 0xe9, 0x2c, + 0xae, 0x17, 0x71, 0x37, ], [ - 0x78, 0xd1, 0xac, 0x7d, 0xcc, 0x52, 0x49, 0x8e, 0x04, 0x25, 0x96, 0xa6, 0x28, 0xfa, - 0x63, 0x7b, 0xfe, 0x0b, 0xbb, 0xa7, 0xff, 0xbf, 0x4e, 0x71, 0x08, 0x2b, 0x29, 0xf4, - 0x9b, 0xfa, 0xab, 0x84, + 0x54, 0xb6, 0x8d, 0xc0, 0x91, 0x2b, 0x77, 0xc9, 0xeb, 0x5e, 0xe5, 0x1a, 0x1d, 0x13, + 0x91, 0x0a, 0x95, 0xdb, 0xec, 0x96, 0x87, 0x73, 0x43, 0x0c, 0x3d, 0xf9, 0xe6, 0x5d, + 0x80, 0xee, 0x1d, 0xd5, ], [ - 0xbd, 0xa4, 0x7d, 0xf2, 0x23, 0x60, 0x44, 0x08, 0xda, 0x87, 0xe5, 0xc6, 0x34, 0xea, - 0xf1, 0xab, 0x25, 0x6c, 0x45, 0xca, 0x50, 0xd0, 0x3f, 0x6a, 0x65, 0x15, 0xb6, 0x25, - 0x80, 0x89, 0xb6, 0xe4, + 0x16, 0x7c, 0xec, 0xa5, 0x7c, 0x2b, 0x9b, 0x02, 0x25, 0x0b, 0x93, 0x86, 0xdc, 0xe0, + 0x05, 0x81, 0xa3, 0x67, 0xf4, 0xbe, 0xd3, 0x4e, 0x06, 0x78, 0xae, 0xb6, 0x52, 0x05, + 0xff, 0xdd, 0xfb, 0xd5, ], [ - 0x8b, 0xac, 0x90, 0x86, 0xc2, 0x4a, 0xed, 0xe7, 0x89, 0xa8, 0x0f, 0x5c, 0x26, 0x4a, - 0x0f, 0x3f, 0x6c, 0xb2, 0x4c, 0xe7, 0x87, 0x31, 0x1b, 0xd7, 0xe7, 0x3d, 0xce, 0xd6, - 0x49, 0x3e, 0xd3, 0xe6, + 0x7d, 0x25, 0xc1, 0xee, 0x40, 0x2b, 0x03, 0x6b, 0xf9, 0x14, 0x9a, 0xa3, 0x50, 0x04, + 0xb8, 0x62, 0x5f, 0x24, 0x5b, 0x6d, 0x11, 0x4b, 0x36, 0xa9, 0xea, 0x0b, 0xd0, 0x85, + 0x58, 0xd8, 0x02, 0xf5, ], [ - 0x46, 0xc6, 0xfc, 0x88, 0xd0, 0xc0, 0x51, 0xec, 0x08, 0x58, 0x81, 0xae, 0xa8, 0x23, - 0xc1, 0xb8, 0x3f, 0x57, 0xb5, 0x55, 0xfb, 0xe0, 0x67, 0xfb, 0x18, 0x54, 0x3b, 0x95, - 0x25, 0x87, 0xe6, 0x15, + 0xfc, 0x62, 0x00, 0xa7, 0x23, 0x66, 0x4f, 0x48, 0xcf, 0xc1, 0x4b, 0xd8, 0xf7, 0x3a, + 0x4c, 0x77, 0x05, 0xe4, 0x5c, 0x49, 0x3c, 0x9f, 0x95, 0x3d, 0x5e, 0xbe, 0x60, 0x95, + 0x38, 0x8e, 0x26, 0xfc, ], [ - 0xa2, 0xf9, 0x94, 0x17, 0x1f, 0xb2, 0x5f, 0x6c, 0x9d, 0x29, 0x94, 0xb7, 0xc2, 0x7f, - 0x6b, 0x1d, 0x6a, 0xe7, 0xad, 0x66, 0x5b, 0x41, 0xa7, 0xd1, 0xf2, 0x53, 0x80, 0xa6, - 0x34, 0x5d, 0x27, 0x87, + 0x9b, 0xd2, 0x23, 0x26, 0x3c, 0xa7, 0x25, 0xb3, 0xc1, 0x3c, 0x18, 0xe9, 0x9c, 0xb2, + 0x28, 0xdb, 0x4f, 0x29, 0x58, 0xac, 0xdb, 0x52, 0x58, 0xe8, 0x23, 0x2e, 0xb0, 0x8b, + 0x0d, 0xd5, 0xed, 0x41, ], [ - 0x6f, 0x6c, 0x7b, 0xdf, 0xd5, 0x99, 0xb3, 0xa8, 0x7f, 0x1a, 0x1a, 0x07, 0x00, 0x5c, - 0xe0, 0xa1, 0x77, 0x77, 0x1a, 0x68, 0xea, 0x04, 0x24, 0xff, 0x55, 0x35, 0xb8, 0x1a, - 0x76, 0xfd, 0x4b, 0x83, + 0x2f, 0x2d, 0xfa, 0xe3, 0x1c, 0xbe, 0x00, 0xe3, 0xdc, 0x4a, 0xa5, 0x48, 0xc6, 0x72, + 0x28, 0x7b, 0x26, 0x05, 0x9a, 0x42, 0xf3, 0x3c, 0x72, 0xb0, 0xbe, 0x5f, 0x73, 0x47, + 0x00, 0x0e, 0xf3, 0xeb, ], [ - 0xe0, 0x47, 0x17, 0x6f, 0x21, 0xe3, 0x91, 0x8d, 0x5d, 0x4b, 0x56, 0xb0, 0x5b, 0x31, - 0x4d, 0x8f, 0x3d, 0x8e, 0xd1, 0x4d, 0xc8, 0x5c, 0xb4, 0x2b, 0x38, 0xc6, 0x9a, 0x4d, - 0x2a, 0x53, 0x36, 0xc3, + 0x9a, 0x39, 0xe1, 0x95, 0x0c, 0x1b, 0x7a, 0x36, 0x16, 0xfc, 0xb5, 0xde, 0xf0, 0x2f, + 0xac, 0x61, 0x3b, 0x32, 0xa3, 0xe9, 0x4a, 0x63, 0xfe, 0x66, 0x59, 0x08, 0x5b, 0x22, + 0xe3, 0x87, 0xab, 0x5c, ], [ - 0x6c, 0xf6, 0x04, 0x71, 0x7c, 0x74, 0x88, 0x1d, 0x1b, 0x74, 0x19, 0x74, 0xbe, 0xfd, - 0x67, 0x74, 0x13, 0xdc, 0xd0, 0x05, 0x4d, 0xcf, 0x8f, 0xbf, 0x98, 0x61, 0xa6, 0x21, - 0x1d, 0x6b, 0xa2, 0xbe, + 0xa3, 0xd2, 0x1d, 0x58, 0xbf, 0x0c, 0x09, 0xb2, 0x14, 0xce, 0xe6, 0x4f, 0x94, 0xf8, + 0x10, 0xc2, 0xc2, 0xf0, 0xcb, 0x13, 0xd8, 0x8a, 0x66, 0xbc, 0x8f, 0x4c, 0x76, 0x72, + 0xe8, 0xc4, 0x17, 0x99, ], [ - 0x5b, 0x89, 0xe1, 0x60, 0xe1, 0xcf, 0x68, 0x81, 0xd2, 0xeb, 0x40, 0x21, 0x62, 0x5f, - 0x7a, 0x76, 0x96, 0x9e, 0xc8, 0x0b, 0x01, 0x3f, 0x0a, 0xfb, 0x3f, 0x24, 0x73, 0xcd, - 0x55, 0xd3, 0xea, 0x27, + 0x4b, 0xd7, 0x50, 0x10, 0xec, 0x2d, 0xfb, 0x77, 0xbd, 0xdd, 0x72, 0x55, 0xec, 0xb4, + 0x35, 0x03, 0xb1, 0x99, 0x66, 0xac, 0xe9, 0x33, 0xe6, 0xcc, 0x08, 0xae, 0xba, 0x7c, + 0x54, 0xdb, 0x18, 0x9c, ], [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1042,14 +1062,14 @@ pub static LFM_REGISTRY: &[LfmRegistryEntry] = &[ 0x00, 0x00, 0x00, 0x00, ], [ - 0x48, 0xad, 0x9f, 0x53, 0x9b, 0x8e, 0x46, 0x7e, 0x45, 0x9d, 0x3a, 0x14, 0xe1, 0x22, - 0x80, 0xec, 0xd5, 0xfd, 0x7a, 0x0f, 0x77, 0x84, 0xb8, 0xf0, 0xc4, 0x53, 0x4d, 0xb2, - 0x83, 0x90, 0xcb, 0x99, + 0x0a, 0x3c, 0x86, 0xe2, 0xd3, 0x37, 0x98, 0xf2, 0x68, 0x45, 0x42, 0xbf, 0x0b, 0x3d, + 0x52, 0x02, 0x4b, 0x08, 0x8b, 0x7a, 0x4b, 0xc8, 0xb0, 0x2a, 0xe4, 0x14, 0x86, 0x09, + 0x35, 0xdc, 0xa6, 0x7c, ], [ - 0x9f, 0xbc, 0x26, 0x49, 0xce, 0x62, 0x34, 0xb5, 0x87, 0x3b, 0x91, 0xd8, 0x45, 0x2c, - 0x60, 0x74, 0x9b, 0x28, 0xaf, 0xdb, 0xf5, 0x1f, 0x3b, 0x9d, 0xde, 0x7c, 0xd0, 0xc2, - 0x38, 0x74, 0x8a, 0xeb, + 0x46, 0x8d, 0xfe, 0x65, 0xed, 0x50, 0x25, 0xbf, 0x7e, 0xfa, 0x04, 0x11, 0x28, 0x36, + 0x29, 0x06, 0xbd, 0x97, 0x29, 0xeb, 0x5b, 0x3a, 0xae, 0xbf, 0x66, 0xea, 0x5e, 0x59, + 0xe4, 0xd7, 0x17, 0x6c, ], ], log_heights: [2, 2, 2, 2, 2, 2, 2, 2, 4, 2, 16, 2, 0, 5, 20], @@ -1060,14 +1080,14 @@ pub static LFM_REGISTRY: &[LfmRegistryEntry] = &[ blake3: false, }, program_id: [ - 0xb5, 0x6b, 0xe4, 0x22, 0xcb, 0xc2, 0x5f, 0x19, 0x0b, 0xb8, 0x11, 0xd0, 0xfa, 0x5d, - 0x36, 0xae, 0x7a, 0x40, 0xb0, 0xd8, 0x73, 0xbd, 0x83, 0x10, 0x68, 0x1b, 0xf2, 0x04, - 0xe4, 0x31, 0x71, 0x97, + 0x55, 0x79, 0xf4, 0xc0, 0x15, 0xb3, 0xd9, 0x73, 0xc1, 0x7d, 0xe0, 0x41, 0x58, 0xaa, + 0x06, 0xff, 0x4e, 0x56, 0x57, 0x50, 0x3c, 0x9e, 0x51, 0xc9, 0xe0, 0x40, 0x3c, 0xc8, + 0x58, 0xd4, 0x2a, 0x33, ], prep_root: [ - 0x32, 0x84, 0x97, 0xf3, 0x4e, 0x2c, 0x81, 0xd6, 0x0c, 0x22, 0xa6, 0x5f, 0xc6, 0xe5, - 0x52, 0xc5, 0xe2, 0x5a, 0x79, 0xfe, 0xd7, 0xc1, 0x9b, 0x4e, 0x9c, 0xe8, 0x2c, 0xe6, - 0x0e, 0x5b, 0x74, 0x63, + 0x17, 0xb9, 0x2d, 0x29, 0xbd, 0x27, 0x65, 0xb1, 0x9f, 0xb3, 0xe7, 0x4e, 0x89, 0xb8, + 0x89, 0x66, 0xc6, 0xd1, 0xc5, 0x63, 0x0f, 0x8f, 0x12, 0x0b, 0x4e, 0xff, 0x73, 0x86, + 0x1f, 0x03, 0xf2, 0x5b, ], prep_widths: [6, 10, 11, 8, 134, 13, 56, 12, 2, 3, 1, 20, 0, 0, 0], }, @@ -1076,64 +1096,64 @@ pub static LFM_REGISTRY: &[LfmRegistryEntry] = &[ blowup_factor: 2, roots: [ [ - 0xb0, 0xf7, 0xa5, 0x7c, 0x6f, 0xd3, 0x93, 0xd2, 0xd4, 0xc2, 0x23, 0x68, 0x02, 0x9b, - 0xa1, 0xa6, 0x76, 0x27, 0x91, 0x9b, 0xc8, 0x82, 0xba, 0xfb, 0x98, 0xb0, 0x13, 0x48, - 0x76, 0x86, 0x23, 0x9a, + 0xc7, 0xa2, 0x00, 0xa8, 0x75, 0xaa, 0x69, 0x79, 0xe0, 0x0a, 0x38, 0x87, 0x0a, 0xb4, + 0x1b, 0x04, 0x7a, 0x8c, 0xa0, 0x57, 0x10, 0x3f, 0xbc, 0xb8, 0x28, 0xa8, 0x2c, 0xc8, + 0x53, 0x41, 0x3b, 0xf5, ], [ - 0x6d, 0x44, 0x6e, 0x89, 0x9c, 0xb6, 0xb0, 0x08, 0x0d, 0x3d, 0x95, 0x93, 0xd1, 0xa3, - 0x5a, 0x92, 0xd8, 0x11, 0x45, 0x20, 0xbc, 0x34, 0x82, 0xfe, 0xa9, 0x2e, 0x3d, 0x50, - 0xe6, 0x6f, 0xfc, 0xae, + 0x13, 0xf7, 0xa3, 0xbe, 0x73, 0x3b, 0xec, 0x61, 0xcf, 0xd6, 0xc1, 0x69, 0x8c, 0xef, + 0x90, 0x3a, 0x89, 0x8b, 0x76, 0xeb, 0x9f, 0x0d, 0x1d, 0x87, 0x7d, 0xe5, 0xac, 0x22, + 0x82, 0xef, 0x4f, 0xd8, ], [ - 0x03, 0x45, 0x83, 0x23, 0x1a, 0xc2, 0xf0, 0x18, 0x7f, 0xd6, 0x9e, 0xb6, 0x94, 0x3b, - 0xdb, 0xd2, 0x5f, 0x6b, 0x2f, 0x9f, 0x74, 0x1a, 0xa5, 0x7e, 0x8e, 0x2a, 0x45, 0x99, - 0xc5, 0x92, 0x2b, 0x13, + 0x07, 0xdb, 0x70, 0x37, 0x6d, 0xff, 0x1c, 0x50, 0x7f, 0x82, 0xf7, 0x83, 0x73, 0x7c, + 0x2e, 0xad, 0xab, 0x48, 0xbc, 0x68, 0x82, 0xf7, 0xff, 0x66, 0x80, 0x88, 0xe9, 0x2c, + 0xae, 0x17, 0x71, 0x37, ], [ - 0x78, 0xd1, 0xac, 0x7d, 0xcc, 0x52, 0x49, 0x8e, 0x04, 0x25, 0x96, 0xa6, 0x28, 0xfa, - 0x63, 0x7b, 0xfe, 0x0b, 0xbb, 0xa7, 0xff, 0xbf, 0x4e, 0x71, 0x08, 0x2b, 0x29, 0xf4, - 0x9b, 0xfa, 0xab, 0x84, + 0x54, 0xb6, 0x8d, 0xc0, 0x91, 0x2b, 0x77, 0xc9, 0xeb, 0x5e, 0xe5, 0x1a, 0x1d, 0x13, + 0x91, 0x0a, 0x95, 0xdb, 0xec, 0x96, 0x87, 0x73, 0x43, 0x0c, 0x3d, 0xf9, 0xe6, 0x5d, + 0x80, 0xee, 0x1d, 0xd5, ], [ - 0xbd, 0xa4, 0x7d, 0xf2, 0x23, 0x60, 0x44, 0x08, 0xda, 0x87, 0xe5, 0xc6, 0x34, 0xea, - 0xf1, 0xab, 0x25, 0x6c, 0x45, 0xca, 0x50, 0xd0, 0x3f, 0x6a, 0x65, 0x15, 0xb6, 0x25, - 0x80, 0x89, 0xb6, 0xe4, + 0x16, 0x7c, 0xec, 0xa5, 0x7c, 0x2b, 0x9b, 0x02, 0x25, 0x0b, 0x93, 0x86, 0xdc, 0xe0, + 0x05, 0x81, 0xa3, 0x67, 0xf4, 0xbe, 0xd3, 0x4e, 0x06, 0x78, 0xae, 0xb6, 0x52, 0x05, + 0xff, 0xdd, 0xfb, 0xd5, ], [ - 0x8b, 0xac, 0x90, 0x86, 0xc2, 0x4a, 0xed, 0xe7, 0x89, 0xa8, 0x0f, 0x5c, 0x26, 0x4a, - 0x0f, 0x3f, 0x6c, 0xb2, 0x4c, 0xe7, 0x87, 0x31, 0x1b, 0xd7, 0xe7, 0x3d, 0xce, 0xd6, - 0x49, 0x3e, 0xd3, 0xe6, + 0x7d, 0x25, 0xc1, 0xee, 0x40, 0x2b, 0x03, 0x6b, 0xf9, 0x14, 0x9a, 0xa3, 0x50, 0x04, + 0xb8, 0x62, 0x5f, 0x24, 0x5b, 0x6d, 0x11, 0x4b, 0x36, 0xa9, 0xea, 0x0b, 0xd0, 0x85, + 0x58, 0xd8, 0x02, 0xf5, ], [ - 0xf9, 0x99, 0x84, 0x64, 0x5f, 0x19, 0xc7, 0x85, 0x09, 0x04, 0xa0, 0x3b, 0x36, 0x8f, - 0xab, 0xd8, 0xda, 0x0a, 0x67, 0xd8, 0x92, 0x7e, 0x0b, 0xd3, 0x89, 0xf4, 0xff, 0xab, - 0x79, 0x43, 0xe6, 0x6b, + 0xde, 0x04, 0x16, 0x1b, 0x9a, 0x9a, 0x84, 0x02, 0xd9, 0x21, 0x50, 0xa3, 0x6b, 0x62, + 0xb6, 0x0a, 0x0e, 0x69, 0x9d, 0x6f, 0xe3, 0xea, 0x80, 0x0d, 0x0a, 0x9b, 0x76, 0xee, + 0x82, 0x89, 0x46, 0xf2, ], [ - 0xd2, 0x56, 0xf8, 0xf5, 0x6b, 0x34, 0xaf, 0xf5, 0xa6, 0x1d, 0xf5, 0xe2, 0x8a, 0x4a, - 0x99, 0xef, 0xed, 0xf2, 0x55, 0x7c, 0xdb, 0xf3, 0xdd, 0x80, 0x5c, 0x09, 0x55, 0xd6, - 0x47, 0x75, 0xbc, 0x4f, + 0x5e, 0x14, 0xfe, 0xa2, 0x64, 0x4f, 0x4a, 0x70, 0xbf, 0x0e, 0x0d, 0x93, 0x09, 0xd8, + 0xb4, 0xbb, 0xba, 0xd1, 0xe6, 0x91, 0x4a, 0x71, 0x7b, 0xec, 0x11, 0xf5, 0x37, 0x3b, + 0x3f, 0xe9, 0xe8, 0x5a, ], [ - 0x4d, 0x97, 0x51, 0x99, 0xc3, 0xc6, 0x4e, 0x95, 0xa1, 0xc3, 0xb8, 0xcc, 0x2e, 0x18, - 0x29, 0xfc, 0x24, 0xd6, 0xf7, 0x97, 0x30, 0x6f, 0x83, 0xab, 0x0b, 0x69, 0x23, 0xd9, - 0x58, 0x46, 0xbe, 0xb3, + 0x5a, 0xa5, 0xed, 0xac, 0xa3, 0x06, 0xbb, 0x97, 0x08, 0xc3, 0xe3, 0x1e, 0x0d, 0x8c, + 0x00, 0x1d, 0xed, 0xd2, 0x6e, 0x49, 0x6d, 0xd9, 0xc7, 0x85, 0xd6, 0xd4, 0xd6, 0xbf, + 0x83, 0x2d, 0x6d, 0xb9, ], [ - 0xce, 0xa8, 0x2c, 0x02, 0x28, 0x09, 0xb9, 0xdb, 0xc9, 0x9c, 0xe3, 0x56, 0x3b, 0xde, - 0xad, 0x9b, 0x5d, 0x87, 0x02, 0xed, 0xa6, 0x33, 0x14, 0x17, 0xb2, 0x86, 0xf6, 0xb1, - 0x93, 0xea, 0xdc, 0xef, + 0xec, 0x03, 0x54, 0x70, 0x9b, 0x7f, 0x21, 0x5b, 0xf9, 0x48, 0x5c, 0xd8, 0x64, 0xa8, + 0xb5, 0x2b, 0x6e, 0x22, 0x79, 0x74, 0x50, 0x93, 0xbb, 0x21, 0x04, 0x53, 0x80, 0xd7, + 0x99, 0x26, 0x5d, 0xd3, ], [ - 0x6c, 0xf6, 0x04, 0x71, 0x7c, 0x74, 0x88, 0x1d, 0x1b, 0x74, 0x19, 0x74, 0xbe, 0xfd, - 0x67, 0x74, 0x13, 0xdc, 0xd0, 0x05, 0x4d, 0xcf, 0x8f, 0xbf, 0x98, 0x61, 0xa6, 0x21, - 0x1d, 0x6b, 0xa2, 0xbe, + 0xa3, 0xd2, 0x1d, 0x58, 0xbf, 0x0c, 0x09, 0xb2, 0x14, 0xce, 0xe6, 0x4f, 0x94, 0xf8, + 0x10, 0xc2, 0xc2, 0xf0, 0xcb, 0x13, 0xd8, 0x8a, 0x66, 0xbc, 0x8f, 0x4c, 0x76, 0x72, + 0xe8, 0xc4, 0x17, 0x99, ], [ - 0x5b, 0x89, 0xe1, 0x60, 0xe1, 0xcf, 0x68, 0x81, 0xd2, 0xeb, 0x40, 0x21, 0x62, 0x5f, - 0x7a, 0x76, 0x96, 0x9e, 0xc8, 0x0b, 0x01, 0x3f, 0x0a, 0xfb, 0x3f, 0x24, 0x73, 0xcd, - 0x55, 0xd3, 0xea, 0x27, + 0x4b, 0xd7, 0x50, 0x10, 0xec, 0x2d, 0xfb, 0x77, 0xbd, 0xdd, 0x72, 0x55, 0xec, 0xb4, + 0x35, 0x03, 0xb1, 0x99, 0x66, 0xac, 0xe9, 0x33, 0xe6, 0xcc, 0x08, 0xae, 0xba, 0x7c, + 0x54, 0xdb, 0x18, 0x9c, ], [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1141,14 +1161,14 @@ pub static LFM_REGISTRY: &[LfmRegistryEntry] = &[ 0x00, 0x00, 0x00, 0x00, ], [ - 0x48, 0xad, 0x9f, 0x53, 0x9b, 0x8e, 0x46, 0x7e, 0x45, 0x9d, 0x3a, 0x14, 0xe1, 0x22, - 0x80, 0xec, 0xd5, 0xfd, 0x7a, 0x0f, 0x77, 0x84, 0xb8, 0xf0, 0xc4, 0x53, 0x4d, 0xb2, - 0x83, 0x90, 0xcb, 0x99, + 0x0a, 0x3c, 0x86, 0xe2, 0xd3, 0x37, 0x98, 0xf2, 0x68, 0x45, 0x42, 0xbf, 0x0b, 0x3d, + 0x52, 0x02, 0x4b, 0x08, 0x8b, 0x7a, 0x4b, 0xc8, 0xb0, 0x2a, 0xe4, 0x14, 0x86, 0x09, + 0x35, 0xdc, 0xa6, 0x7c, ], [ - 0x9f, 0xbc, 0x26, 0x49, 0xce, 0x62, 0x34, 0xb5, 0x87, 0x3b, 0x91, 0xd8, 0x45, 0x2c, - 0x60, 0x74, 0x9b, 0x28, 0xaf, 0xdb, 0xf5, 0x1f, 0x3b, 0x9d, 0xde, 0x7c, 0xd0, 0xc2, - 0x38, 0x74, 0x8a, 0xeb, + 0x46, 0x8d, 0xfe, 0x65, 0xed, 0x50, 0x25, 0xbf, 0x7e, 0xfa, 0x04, 0x11, 0x28, 0x36, + 0x29, 0x06, 0xbd, 0x97, 0x29, 0xeb, 0x5b, 0x3a, 0xae, 0xbf, 0x66, 0xea, 0x5e, 0x59, + 0xe4, 0xd7, 0x17, 0x6c, ], ], log_heights: [2, 2, 2, 2, 2, 2, 2, 5, 6, 2, 16, 2, 0, 5, 20], @@ -1159,14 +1179,14 @@ pub static LFM_REGISTRY: &[LfmRegistryEntry] = &[ blake3: false, }, program_id: [ - 0xb9, 0xf8, 0xcf, 0x8a, 0xcb, 0x89, 0x30, 0xc9, 0xe9, 0xfd, 0x1c, 0xc7, 0x5d, 0x7e, - 0xac, 0x51, 0x66, 0x66, 0x6c, 0x74, 0xb6, 0x46, 0x67, 0x94, 0x55, 0xbd, 0x3a, 0x04, - 0x5a, 0x60, 0x29, 0x8f, + 0x0a, 0x94, 0x02, 0xe9, 0x21, 0x34, 0xae, 0xbc, 0x2a, 0xcb, 0xbb, 0x94, 0x2b, 0xdf, + 0xa9, 0x91, 0xaf, 0x2c, 0xd1, 0xc7, 0xb2, 0xff, 0xbb, 0xf9, 0x14, 0xed, 0x40, 0xde, + 0xf6, 0x54, 0x80, 0xf0, ], prep_root: [ - 0xd4, 0x11, 0x7a, 0x0a, 0x76, 0x8a, 0xb9, 0x41, 0xcc, 0xf2, 0x3d, 0xa8, 0x1a, 0x06, - 0x35, 0x72, 0x70, 0x10, 0xc2, 0x12, 0x9f, 0x7f, 0xd9, 0x8e, 0x22, 0x12, 0x8c, 0x47, - 0x07, 0xf0, 0xae, 0x7f, + 0xa6, 0x14, 0xdf, 0x60, 0xda, 0x68, 0x8c, 0xfc, 0x67, 0x5d, 0x4b, 0x31, 0xaa, 0xce, + 0xa4, 0x82, 0x1e, 0xf0, 0xfb, 0x02, 0x08, 0xf4, 0x0e, 0x4b, 0xd4, 0x6f, 0xba, 0x2e, + 0x85, 0x07, 0xb8, 0xe2, ], prep_widths: [6, 10, 11, 8, 134, 13, 56, 12, 2, 3, 1, 20, 0, 0, 0], }, @@ -1175,64 +1195,64 @@ pub static LFM_REGISTRY: &[LfmRegistryEntry] = &[ blowup_factor: 2, roots: [ [ - 0x04, 0x22, 0x0e, 0xfa, 0xe5, 0xda, 0xac, 0xb8, 0x36, 0x5f, 0xe8, 0x15, 0xbb, 0xa7, - 0x04, 0xff, 0x25, 0x84, 0x0b, 0x22, 0x3b, 0xda, 0xf7, 0x13, 0xab, 0x56, 0x5e, 0x50, - 0x53, 0x2d, 0x63, 0x45, + 0x05, 0xd2, 0x17, 0x2e, 0x98, 0xf4, 0x8e, 0xc9, 0xb2, 0x7e, 0xd4, 0xbc, 0xd9, 0x4a, + 0xba, 0x0d, 0x16, 0x43, 0x99, 0xcf, 0x6e, 0x13, 0xe6, 0xa5, 0x44, 0x4e, 0xda, 0x9d, + 0xfe, 0xb3, 0x6e, 0xed, ], [ - 0x93, 0xec, 0x2a, 0x0e, 0x5b, 0x45, 0x00, 0x32, 0x31, 0x49, 0x52, 0x1e, 0xd5, 0x0e, - 0x83, 0x9d, 0xd5, 0x7d, 0xb8, 0xe3, 0x1f, 0xe3, 0xf7, 0xa4, 0xa9, 0xa9, 0x00, 0x85, - 0x22, 0x2e, 0xe6, 0x05, + 0x89, 0x96, 0xd2, 0xab, 0x4d, 0xee, 0xd9, 0x5d, 0x70, 0x0b, 0xae, 0x67, 0xe9, 0xca, + 0xb1, 0xf4, 0xbf, 0x1b, 0x70, 0x6a, 0x58, 0x4c, 0xc0, 0xfe, 0x29, 0x05, 0xb8, 0xea, + 0x74, 0xd6, 0xb5, 0xfe, ], [ - 0x03, 0x45, 0x83, 0x23, 0x1a, 0xc2, 0xf0, 0x18, 0x7f, 0xd6, 0x9e, 0xb6, 0x94, 0x3b, - 0xdb, 0xd2, 0x5f, 0x6b, 0x2f, 0x9f, 0x74, 0x1a, 0xa5, 0x7e, 0x8e, 0x2a, 0x45, 0x99, - 0xc5, 0x92, 0x2b, 0x13, + 0x07, 0xdb, 0x70, 0x37, 0x6d, 0xff, 0x1c, 0x50, 0x7f, 0x82, 0xf7, 0x83, 0x73, 0x7c, + 0x2e, 0xad, 0xab, 0x48, 0xbc, 0x68, 0x82, 0xf7, 0xff, 0x66, 0x80, 0x88, 0xe9, 0x2c, + 0xae, 0x17, 0x71, 0x37, ], [ - 0xf7, 0x3a, 0x8e, 0x97, 0xe7, 0xd3, 0x89, 0xc7, 0x6e, 0xed, 0x62, 0x87, 0x7d, 0x85, - 0x82, 0x88, 0xdc, 0x5d, 0x45, 0xc8, 0x12, 0xe2, 0x2d, 0x1d, 0xb5, 0x82, 0x61, 0xb1, - 0x48, 0xb4, 0x97, 0xc6, + 0xc4, 0xed, 0x3f, 0xac, 0x6e, 0x05, 0x97, 0xc7, 0x01, 0x3c, 0xba, 0x47, 0x1a, 0x9e, + 0x0d, 0xfc, 0x71, 0x40, 0x55, 0x2d, 0xa8, 0xec, 0xa4, 0xee, 0xd2, 0x13, 0x17, 0xd9, + 0x7f, 0xb8, 0x62, 0x61, ], [ - 0x4a, 0x32, 0x91, 0x14, 0x1b, 0x2a, 0x59, 0x9e, 0xe1, 0x97, 0x2f, 0xff, 0x81, 0x63, - 0xbe, 0x90, 0x03, 0xda, 0x0f, 0xc3, 0x8a, 0x95, 0x3f, 0xb5, 0x58, 0x08, 0xda, 0x0b, - 0xb7, 0x64, 0xfb, 0xab, + 0x2a, 0x1f, 0xcd, 0x6c, 0x67, 0x77, 0xdc, 0x14, 0x3c, 0x7b, 0xb8, 0x48, 0x24, 0xdb, + 0xde, 0x29, 0x86, 0xaa, 0x0a, 0x0b, 0xe4, 0x82, 0xcd, 0x83, 0x67, 0xbe, 0xff, 0xbe, + 0xda, 0x77, 0x03, 0x1c, ], [ - 0x8b, 0xac, 0x90, 0x86, 0xc2, 0x4a, 0xed, 0xe7, 0x89, 0xa8, 0x0f, 0x5c, 0x26, 0x4a, - 0x0f, 0x3f, 0x6c, 0xb2, 0x4c, 0xe7, 0x87, 0x31, 0x1b, 0xd7, 0xe7, 0x3d, 0xce, 0xd6, - 0x49, 0x3e, 0xd3, 0xe6, + 0x7d, 0x25, 0xc1, 0xee, 0x40, 0x2b, 0x03, 0x6b, 0xf9, 0x14, 0x9a, 0xa3, 0x50, 0x04, + 0xb8, 0x62, 0x5f, 0x24, 0x5b, 0x6d, 0x11, 0x4b, 0x36, 0xa9, 0xea, 0x0b, 0xd0, 0x85, + 0x58, 0xd8, 0x02, 0xf5, ], [ - 0x80, 0xe6, 0x2e, 0x9d, 0xac, 0x64, 0x93, 0x41, 0xae, 0x34, 0x6a, 0xba, 0xaa, 0x9a, - 0x1f, 0x49, 0x0c, 0x3f, 0xfd, 0xd3, 0x20, 0x6c, 0x97, 0xdc, 0xcd, 0xc1, 0x95, 0x99, - 0x6f, 0xe5, 0x6b, 0x70, + 0x8d, 0xa4, 0x49, 0x54, 0x80, 0xa3, 0xfe, 0x4d, 0x2f, 0xb1, 0xe0, 0x1d, 0xd9, 0x6b, + 0x82, 0xa6, 0x65, 0x36, 0x1f, 0x21, 0xf9, 0x92, 0x67, 0x4f, 0x88, 0x90, 0x70, 0xab, + 0xff, 0x31, 0xec, 0xb2, ], [ - 0x06, 0x87, 0x09, 0xed, 0x6f, 0x37, 0xa4, 0x63, 0xed, 0x6e, 0x7d, 0xbb, 0x3b, 0x6c, - 0xca, 0x14, 0x0c, 0xe5, 0x32, 0xf5, 0x61, 0x6b, 0xb0, 0xe4, 0x51, 0x9c, 0x7c, 0x81, - 0x29, 0xad, 0x0d, 0x75, + 0xef, 0x55, 0xdd, 0x07, 0x83, 0xed, 0x34, 0xf5, 0xd0, 0xed, 0xd3, 0x97, 0x85, 0xf8, + 0x0c, 0x84, 0x2e, 0x58, 0xd2, 0x5b, 0xfc, 0x7f, 0x13, 0x5f, 0x80, 0x2f, 0xca, 0xd9, + 0x33, 0xb3, 0x4a, 0x79, ], [ - 0xd8, 0x44, 0x51, 0x6e, 0x0d, 0x66, 0xaf, 0x31, 0x9d, 0x5b, 0xd1, 0x15, 0xdf, 0x75, - 0x56, 0x35, 0xd2, 0x85, 0x9c, 0x22, 0xe2, 0x93, 0x96, 0x46, 0xc9, 0x0b, 0x4f, 0x1a, - 0xec, 0x20, 0x23, 0xcc, + 0xf5, 0x03, 0xc2, 0x09, 0xbd, 0xdd, 0x4a, 0x2c, 0x01, 0x55, 0xba, 0x0a, 0x7a, 0x8d, + 0xe6, 0x7c, 0xf7, 0xeb, 0x3a, 0x3e, 0x66, 0xb9, 0x55, 0x99, 0x8c, 0xab, 0x9b, 0x6f, + 0xde, 0x6a, 0xa9, 0x80, ], [ - 0x4f, 0x83, 0x48, 0x8c, 0x38, 0xb0, 0x33, 0x1f, 0x1e, 0xea, 0x4a, 0x30, 0x36, 0x4b, - 0xc7, 0x90, 0x55, 0x6d, 0x21, 0x5d, 0xf3, 0xc9, 0xd7, 0x30, 0xc0, 0x2a, 0x60, 0x5a, - 0x78, 0x32, 0x13, 0x32, + 0x58, 0x9a, 0x27, 0xbe, 0x91, 0xff, 0x77, 0x5d, 0x0c, 0xba, 0x66, 0x5d, 0x35, 0x7b, + 0xa0, 0x91, 0x86, 0x81, 0x5a, 0xf6, 0x84, 0x3b, 0xd1, 0x35, 0x66, 0x51, 0x91, 0xd6, + 0x63, 0x4e, 0xbd, 0xec, ], [ - 0x6c, 0xf6, 0x04, 0x71, 0x7c, 0x74, 0x88, 0x1d, 0x1b, 0x74, 0x19, 0x74, 0xbe, 0xfd, - 0x67, 0x74, 0x13, 0xdc, 0xd0, 0x05, 0x4d, 0xcf, 0x8f, 0xbf, 0x98, 0x61, 0xa6, 0x21, - 0x1d, 0x6b, 0xa2, 0xbe, + 0xa3, 0xd2, 0x1d, 0x58, 0xbf, 0x0c, 0x09, 0xb2, 0x14, 0xce, 0xe6, 0x4f, 0x94, 0xf8, + 0x10, 0xc2, 0xc2, 0xf0, 0xcb, 0x13, 0xd8, 0x8a, 0x66, 0xbc, 0x8f, 0x4c, 0x76, 0x72, + 0xe8, 0xc4, 0x17, 0x99, ], [ - 0x3a, 0x8f, 0xde, 0xb3, 0x01, 0x7d, 0x7b, 0xce, 0xc3, 0x96, 0xac, 0xb8, 0xf1, 0x55, - 0x6b, 0xe6, 0x62, 0x5f, 0xa0, 0xb1, 0x00, 0x0c, 0x4d, 0x48, 0xfd, 0x66, 0x94, 0xb4, - 0x42, 0x68, 0x42, 0x44, + 0x42, 0xd6, 0x1f, 0x87, 0x71, 0x67, 0xbf, 0xcf, 0x68, 0x1b, 0xb3, 0x7d, 0x5f, 0xfb, + 0x2e, 0x0f, 0x03, 0x3f, 0x1b, 0x4b, 0xbe, 0x5b, 0xb4, 0x01, 0x3e, 0x47, 0x88, 0x43, + 0x6e, 0xd9, 0xfc, 0xdd, ], [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1240,14 +1260,14 @@ pub static LFM_REGISTRY: &[LfmRegistryEntry] = &[ 0x00, 0x00, 0x00, 0x00, ], [ - 0x48, 0xad, 0x9f, 0x53, 0x9b, 0x8e, 0x46, 0x7e, 0x45, 0x9d, 0x3a, 0x14, 0xe1, 0x22, - 0x80, 0xec, 0xd5, 0xfd, 0x7a, 0x0f, 0x77, 0x84, 0xb8, 0xf0, 0xc4, 0x53, 0x4d, 0xb2, - 0x83, 0x90, 0xcb, 0x99, + 0x0a, 0x3c, 0x86, 0xe2, 0xd3, 0x37, 0x98, 0xf2, 0x68, 0x45, 0x42, 0xbf, 0x0b, 0x3d, + 0x52, 0x02, 0x4b, 0x08, 0x8b, 0x7a, 0x4b, 0xc8, 0xb0, 0x2a, 0xe4, 0x14, 0x86, 0x09, + 0x35, 0xdc, 0xa6, 0x7c, ], [ - 0x9f, 0xbc, 0x26, 0x49, 0xce, 0x62, 0x34, 0xb5, 0x87, 0x3b, 0x91, 0xd8, 0x45, 0x2c, - 0x60, 0x74, 0x9b, 0x28, 0xaf, 0xdb, 0xf5, 0x1f, 0x3b, 0x9d, 0xde, 0x7c, 0xd0, 0xc2, - 0x38, 0x74, 0x8a, 0xeb, + 0x46, 0x8d, 0xfe, 0x65, 0xed, 0x50, 0x25, 0xbf, 0x7e, 0xfa, 0x04, 0x11, 0x28, 0x36, + 0x29, 0x06, 0xbd, 0x97, 0x29, 0xeb, 0x5b, 0x3a, 0xae, 0xbf, 0x66, 0xea, 0x5e, 0x59, + 0xe4, 0xd7, 0x17, 0x6c, ], ], log_heights: [5, 10, 2, 4, 4, 2, 2, 6, 6, 3, 16, 3, 0, 5, 20], @@ -1258,14 +1278,14 @@ pub static LFM_REGISTRY: &[LfmRegistryEntry] = &[ blake3: true, }, program_id: [ - 0x36, 0xaf, 0x61, 0xc0, 0xec, 0x4b, 0x4e, 0x1e, 0xc0, 0xae, 0x1d, 0x11, 0x87, 0xbe, - 0xb6, 0xfb, 0xaf, 0x59, 0x85, 0xf4, 0xd6, 0x03, 0xc3, 0x23, 0x0e, 0x31, 0xdc, 0x0b, - 0x43, 0x46, 0xdf, 0x0e, + 0x4d, 0xf6, 0x91, 0xc0, 0x40, 0xd6, 0x21, 0x39, 0xb8, 0x4a, 0x72, 0xe0, 0xcc, 0x1c, + 0x08, 0x77, 0xa5, 0x67, 0x4e, 0x53, 0x1e, 0x36, 0xdd, 0x56, 0x89, 0xc9, 0x4c, 0xc9, + 0x88, 0x15, 0x64, 0x4f, ], prep_root: [ - 0x80, 0xe1, 0x7a, 0x56, 0x9f, 0xc7, 0x1a, 0x5a, 0x59, 0x09, 0x88, 0x65, 0x2a, 0x45, - 0x22, 0xf3, 0x94, 0x53, 0x11, 0x52, 0x77, 0x88, 0x06, 0x24, 0x5f, 0x96, 0x06, 0x8a, - 0x6d, 0xb8, 0x33, 0x6b, + 0x77, 0x62, 0x5c, 0x36, 0x2d, 0xd7, 0xe8, 0xbf, 0xbf, 0x58, 0x2e, 0xdd, 0x42, 0x73, + 0x72, 0x7c, 0x5d, 0xf0, 0x74, 0x17, 0xb2, 0xdb, 0xa3, 0xbf, 0x11, 0x8f, 0x30, 0xfe, + 0x20, 0xab, 0x63, 0x4e, ], prep_widths: [6, 10, 11, 8, 134, 13, 56, 12, 2, 3, 1, 20, 0, 0, 0], }, @@ -1274,64 +1294,64 @@ pub static LFM_REGISTRY: &[LfmRegistryEntry] = &[ blowup_factor: 2, roots: [ [ - 0xab, 0xd6, 0xc8, 0xc9, 0x6c, 0x1b, 0x74, 0x41, 0xf1, 0xb4, 0x6f, 0xe5, 0xb5, 0x6e, - 0xef, 0xe4, 0xcd, 0x4c, 0x4a, 0x4a, 0x44, 0x4a, 0x04, 0xbd, 0x9c, 0x37, 0xbb, 0x3d, - 0x3f, 0xcd, 0x15, 0xb1, + 0x8e, 0x12, 0x29, 0x48, 0x9e, 0xac, 0x16, 0x2c, 0x45, 0x9a, 0x99, 0xbf, 0xde, 0x4a, + 0x9a, 0xd5, 0x57, 0x3c, 0xff, 0x61, 0xee, 0x7a, 0xd5, 0x20, 0xa0, 0x1d, 0x7a, 0x27, + 0x4c, 0x93, 0xef, 0xb4, ], [ - 0xbb, 0x71, 0x84, 0xe7, 0xbd, 0x9d, 0x5d, 0xed, 0x6d, 0x19, 0x39, 0xe3, 0xb1, 0x43, - 0x1a, 0x9b, 0xdb, 0x96, 0x47, 0xf3, 0x9e, 0x40, 0x12, 0xec, 0x5d, 0xc0, 0xa4, 0xa4, - 0x41, 0x0b, 0xad, 0x74, + 0x79, 0x6f, 0x6d, 0xe6, 0xb0, 0x27, 0x4a, 0x4f, 0x35, 0x50, 0x4f, 0x02, 0x76, 0x26, + 0x3c, 0x73, 0x2b, 0xbe, 0xa6, 0xdd, 0xab, 0xb3, 0x9e, 0xf7, 0x4a, 0xce, 0x51, 0xc1, + 0x12, 0xa2, 0x80, 0x40, ], [ - 0x03, 0x45, 0x83, 0x23, 0x1a, 0xc2, 0xf0, 0x18, 0x7f, 0xd6, 0x9e, 0xb6, 0x94, 0x3b, - 0xdb, 0xd2, 0x5f, 0x6b, 0x2f, 0x9f, 0x74, 0x1a, 0xa5, 0x7e, 0x8e, 0x2a, 0x45, 0x99, - 0xc5, 0x92, 0x2b, 0x13, + 0x07, 0xdb, 0x70, 0x37, 0x6d, 0xff, 0x1c, 0x50, 0x7f, 0x82, 0xf7, 0x83, 0x73, 0x7c, + 0x2e, 0xad, 0xab, 0x48, 0xbc, 0x68, 0x82, 0xf7, 0xff, 0x66, 0x80, 0x88, 0xe9, 0x2c, + 0xae, 0x17, 0x71, 0x37, ], [ - 0x87, 0x12, 0x45, 0xcc, 0xe4, 0xa3, 0x38, 0x2c, 0xbc, 0x5d, 0xe4, 0x20, 0xa7, 0x6d, - 0xac, 0xc3, 0x53, 0x00, 0x51, 0xbb, 0x11, 0xff, 0x1e, 0x1e, 0x65, 0xff, 0x0b, 0x57, - 0x2b, 0x36, 0xf1, 0x8f, + 0x06, 0xfe, 0x81, 0xac, 0x1e, 0xd6, 0x2e, 0x90, 0xec, 0xd5, 0x9a, 0x03, 0x33, 0xe2, + 0xdf, 0xeb, 0xa5, 0xa5, 0x42, 0xe4, 0x7a, 0xec, 0x27, 0x4c, 0xda, 0x74, 0x87, 0x31, + 0xe1, 0x40, 0xe0, 0x58, ], [ - 0x9b, 0xfb, 0xe0, 0xa9, 0x13, 0x3e, 0xc0, 0x12, 0x95, 0x8f, 0xe6, 0x04, 0xd8, 0xe6, - 0x04, 0xcd, 0x08, 0xd8, 0x24, 0xc9, 0x7f, 0x51, 0x39, 0xb6, 0xd6, 0x4d, 0x1d, 0xab, - 0xf6, 0x35, 0x00, 0x1d, + 0x60, 0xdf, 0x05, 0x40, 0x1e, 0x94, 0x4d, 0x5a, 0x43, 0xd7, 0x0b, 0xe9, 0x8e, 0x38, + 0x1e, 0x3d, 0x22, 0xa5, 0x91, 0xeb, 0xa4, 0x1f, 0xf8, 0xff, 0x1f, 0x96, 0x8b, 0x53, + 0xb6, 0x48, 0x97, 0xd7, ], [ - 0x8b, 0xac, 0x90, 0x86, 0xc2, 0x4a, 0xed, 0xe7, 0x89, 0xa8, 0x0f, 0x5c, 0x26, 0x4a, - 0x0f, 0x3f, 0x6c, 0xb2, 0x4c, 0xe7, 0x87, 0x31, 0x1b, 0xd7, 0xe7, 0x3d, 0xce, 0xd6, - 0x49, 0x3e, 0xd3, 0xe6, + 0x7d, 0x25, 0xc1, 0xee, 0x40, 0x2b, 0x03, 0x6b, 0xf9, 0x14, 0x9a, 0xa3, 0x50, 0x04, + 0xb8, 0x62, 0x5f, 0x24, 0x5b, 0x6d, 0x11, 0x4b, 0x36, 0xa9, 0xea, 0x0b, 0xd0, 0x85, + 0x58, 0xd8, 0x02, 0xf5, ], [ - 0x80, 0xe6, 0x2e, 0x9d, 0xac, 0x64, 0x93, 0x41, 0xae, 0x34, 0x6a, 0xba, 0xaa, 0x9a, - 0x1f, 0x49, 0x0c, 0x3f, 0xfd, 0xd3, 0x20, 0x6c, 0x97, 0xdc, 0xcd, 0xc1, 0x95, 0x99, - 0x6f, 0xe5, 0x6b, 0x70, + 0x8d, 0xa4, 0x49, 0x54, 0x80, 0xa3, 0xfe, 0x4d, 0x2f, 0xb1, 0xe0, 0x1d, 0xd9, 0x6b, + 0x82, 0xa6, 0x65, 0x36, 0x1f, 0x21, 0xf9, 0x92, 0x67, 0x4f, 0x88, 0x90, 0x70, 0xab, + 0xff, 0x31, 0xec, 0xb2, ], [ - 0x36, 0x22, 0xcc, 0x16, 0x23, 0x02, 0xe9, 0xdb, 0x18, 0x9b, 0x32, 0xc1, 0x20, 0x33, - 0x4d, 0xf0, 0x22, 0x34, 0xe6, 0x72, 0xf1, 0xf4, 0x30, 0xd1, 0x5d, 0x23, 0xa1, 0x56, - 0xcb, 0x0f, 0x89, 0xf4, + 0x92, 0x13, 0x7a, 0xa0, 0xb8, 0xe0, 0xe3, 0xb7, 0xb0, 0x4e, 0x49, 0x3d, 0x2b, 0x8f, + 0x7d, 0x1a, 0xef, 0x0b, 0x47, 0x68, 0x28, 0xd7, 0x23, 0x8c, 0xa4, 0x4c, 0x10, 0x2c, + 0x2e, 0x1c, 0x6d, 0x0c, ], [ - 0x34, 0x94, 0x94, 0xe2, 0xfb, 0x65, 0xc5, 0x44, 0x3a, 0xd7, 0x02, 0x77, 0x34, 0xfb, - 0xc7, 0xab, 0x39, 0xfc, 0xda, 0x74, 0x14, 0x2b, 0xfc, 0x66, 0xdc, 0x70, 0x49, 0x93, - 0x04, 0xda, 0xcc, 0x55, + 0x8a, 0xc9, 0x0a, 0xc6, 0x8d, 0x5c, 0x71, 0xf7, 0x0d, 0x60, 0x13, 0x14, 0x21, 0xcc, + 0x03, 0x06, 0x79, 0x30, 0x77, 0x81, 0xd9, 0xc1, 0xc4, 0x1c, 0x9f, 0x55, 0xa8, 0x02, + 0xc5, 0xb2, 0x5e, 0x21, ], [ - 0xad, 0xea, 0x6a, 0xb5, 0xcd, 0x68, 0x68, 0x36, 0x0d, 0x92, 0x94, 0xfb, 0x39, 0xc6, - 0xeb, 0xd1, 0x5d, 0x18, 0x25, 0xf8, 0x08, 0xe8, 0x2e, 0x9a, 0x48, 0x20, 0x62, 0x8c, - 0x27, 0x4a, 0x4d, 0x4b, + 0xe5, 0x6d, 0x6f, 0x2a, 0x3b, 0x38, 0x2f, 0xd0, 0xcf, 0x6f, 0xcb, 0x53, 0xb3, 0xfa, + 0x03, 0x1b, 0x4e, 0x99, 0xda, 0x4a, 0xa2, 0xf7, 0x82, 0x23, 0x9f, 0x03, 0x59, 0x06, + 0x51, 0xec, 0x4d, 0x4f, ], [ - 0x6c, 0xf6, 0x04, 0x71, 0x7c, 0x74, 0x88, 0x1d, 0x1b, 0x74, 0x19, 0x74, 0xbe, 0xfd, - 0x67, 0x74, 0x13, 0xdc, 0xd0, 0x05, 0x4d, 0xcf, 0x8f, 0xbf, 0x98, 0x61, 0xa6, 0x21, - 0x1d, 0x6b, 0xa2, 0xbe, + 0xa3, 0xd2, 0x1d, 0x58, 0xbf, 0x0c, 0x09, 0xb2, 0x14, 0xce, 0xe6, 0x4f, 0x94, 0xf8, + 0x10, 0xc2, 0xc2, 0xf0, 0xcb, 0x13, 0xd8, 0x8a, 0x66, 0xbc, 0x8f, 0x4c, 0x76, 0x72, + 0xe8, 0xc4, 0x17, 0x99, ], [ - 0x7d, 0xff, 0xd0, 0x62, 0xc9, 0x18, 0x47, 0x3a, 0xdb, 0xe7, 0x43, 0xe8, 0x4e, 0x4c, - 0xe2, 0x6a, 0xb4, 0x60, 0x6c, 0x03, 0x9a, 0x88, 0xce, 0x38, 0xb2, 0x33, 0xb1, 0xcb, - 0x7c, 0xc3, 0x82, 0x46, + 0x6a, 0xc2, 0xa2, 0x99, 0xe6, 0x48, 0x90, 0x8b, 0xe6, 0xa1, 0xca, 0xbf, 0x87, 0x9c, + 0x70, 0x73, 0xe2, 0x70, 0x50, 0x82, 0xf9, 0xeb, 0xdf, 0xeb, 0x61, 0x21, 0x06, 0x7f, + 0x37, 0x78, 0x2a, 0x1d, ], [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1339,14 +1359,14 @@ pub static LFM_REGISTRY: &[LfmRegistryEntry] = &[ 0x00, 0x00, 0x00, 0x00, ], [ - 0x48, 0xad, 0x9f, 0x53, 0x9b, 0x8e, 0x46, 0x7e, 0x45, 0x9d, 0x3a, 0x14, 0xe1, 0x22, - 0x80, 0xec, 0xd5, 0xfd, 0x7a, 0x0f, 0x77, 0x84, 0xb8, 0xf0, 0xc4, 0x53, 0x4d, 0xb2, - 0x83, 0x90, 0xcb, 0x99, + 0x0a, 0x3c, 0x86, 0xe2, 0xd3, 0x37, 0x98, 0xf2, 0x68, 0x45, 0x42, 0xbf, 0x0b, 0x3d, + 0x52, 0x02, 0x4b, 0x08, 0x8b, 0x7a, 0x4b, 0xc8, 0xb0, 0x2a, 0xe4, 0x14, 0x86, 0x09, + 0x35, 0xdc, 0xa6, 0x7c, ], [ - 0x9f, 0xbc, 0x26, 0x49, 0xce, 0x62, 0x34, 0xb5, 0x87, 0x3b, 0x91, 0xd8, 0x45, 0x2c, - 0x60, 0x74, 0x9b, 0x28, 0xaf, 0xdb, 0xf5, 0x1f, 0x3b, 0x9d, 0xde, 0x7c, 0xd0, 0xc2, - 0x38, 0x74, 0x8a, 0xeb, + 0x46, 0x8d, 0xfe, 0x65, 0xed, 0x50, 0x25, 0xbf, 0x7e, 0xfa, 0x04, 0x11, 0x28, 0x36, + 0x29, 0x06, 0xbd, 0x97, 0x29, 0xeb, 0x5b, 0x3a, 0xae, 0xbf, 0x66, 0xea, 0x5e, 0x59, + 0xe4, 0xd7, 0x17, 0x6c, ], ], log_heights: [5, 12, 2, 4, 7, 2, 2, 6, 6, 2, 16, 4, 0, 5, 20], @@ -1357,14 +1377,14 @@ pub static LFM_REGISTRY: &[LfmRegistryEntry] = &[ blake3: true, }, program_id: [ - 0x07, 0xef, 0x50, 0xba, 0xed, 0x14, 0x7d, 0x67, 0x83, 0xaf, 0xa4, 0x38, 0x56, 0x17, - 0x5f, 0xb7, 0x22, 0x89, 0x94, 0x25, 0xa9, 0x2a, 0x66, 0x90, 0x27, 0x8e, 0x5a, 0x04, - 0x9c, 0x75, 0xcc, 0x95, + 0xd9, 0x80, 0x4c, 0xab, 0xc9, 0xa2, 0xbb, 0x15, 0xb2, 0x36, 0xe3, 0x4b, 0x50, 0x05, + 0x97, 0x07, 0x99, 0xf6, 0x54, 0xa3, 0x87, 0x07, 0x34, 0xa5, 0x14, 0x8a, 0xd5, 0x63, + 0x21, 0xd6, 0xa6, 0x4d, ], prep_root: [ - 0xc6, 0xdf, 0x01, 0xb3, 0x62, 0x13, 0x01, 0x8f, 0xef, 0xd9, 0xf0, 0x81, 0xcc, 0xea, - 0x15, 0x7b, 0x84, 0xc3, 0xf4, 0x5b, 0x96, 0x60, 0x23, 0xde, 0x38, 0xde, 0xbe, 0x08, - 0x70, 0x1d, 0x90, 0xc6, + 0x47, 0x89, 0xd7, 0x30, 0x6e, 0x18, 0xd5, 0x29, 0x48, 0x34, 0x27, 0x88, 0x91, 0x55, + 0x33, 0x26, 0x90, 0xf5, 0x33, 0x4d, 0x88, 0xe8, 0xdb, 0x90, 0x73, 0xff, 0x38, 0xc1, + 0xae, 0xc4, 0xf4, 0xc9, ], prep_widths: [6, 10, 11, 8, 134, 13, 56, 12, 2, 3, 1, 20, 0, 0, 0], }, diff --git a/prover/src/lfm/sub_proof.rs b/prover/src/lfm/sub_proof.rs index 787b224f3..663441405 100644 --- a/prover/src/lfm/sub_proof.rs +++ b/prover/src/lfm/sub_proof.rs @@ -132,9 +132,18 @@ impl SubProofShape { } /// Arena words one query's openings occupy — every group's values, plus - /// the index and the sibling digests (two words per level per group). - pub fn query_words(&self) -> usize { - 1 + self.opening_words() + /// the index and the sibling digests (`digest_words` per level per group). + /// + /// ★ `digest_words` is the DIGEST's width in arena words, and it is an + /// argument rather than a read of the configuration: the machine side + /// passes `edsl::digest_words(b)` — the BUILDER's width, the one every + /// emitter advances its cursor by — and the host side passes + /// `proof_arena::words_per_root()`, the width it serialises roots at. A + /// shape that read the configuration here would agree with a builder at + /// `WrapHash::production()` and disagree with any other, and the + /// disagreement would surface as the emitter's own stride assertion. + pub fn query_words(&self, digest_words: usize) -> usize { + 1 + self.opening_words(digest_words) } /// [`Self::query_words`] WITHOUT the index word. @@ -143,10 +152,9 @@ impl SubProofShape { /// all but the transcript's own bits, so the arena carries only the opened /// values and the paths. An arena that still carried an index would be /// offering the prover a second one. - pub fn opening_words(&self) -> usize { + pub fn opening_words(&self, digest_words: usize) -> usize { let values: usize = self.groups().iter().map(GroupShape::num_values).sum(); - let siblings = - super::proof_arena::words_per_root() * self.merkle_depth * self.groups().len(); + let siblings = digest_words * self.merkle_depth * self.groups().len(); values + siblings } @@ -570,7 +578,8 @@ pub fn emit_sub_proof_with_bits( let ood = b.declare_arena((shape.deep.num_eval_points * shape.deep.num_total_cols) as u32); let parts = b.declare_arena(shape.deep.num_composition_parts as u32); let roots = b.declare_arena(edsl::digest_words(b) * groups.len() as u32); - let queries = b.declare_arena((num_queries * shape.query_words()) as u32); + let queries = + b.declare_arena((num_queries * shape.query_words(edsl::digest_words(b) as usize)) as u32); let arenas = SubProofArenas { uniforms, ood, @@ -644,7 +653,7 @@ pub fn emit_sub_proof_with_bits( } assert_eq!( cursor as usize, - num_queries * shape.query_words(), + num_queries * shape.query_words(edsl::digest_words(b) as usize), "the emitter's cursor must agree with the declared query stride" ); diff --git a/prover/src/lfm/wrap_tests.rs b/prover/src/lfm/wrap_tests.rs index 127cbbba0..b2f119611 100644 --- a/prover/src/lfm/wrap_tests.rs +++ b/prover/src/lfm/wrap_tests.rs @@ -23,10 +23,17 @@ //! //! ## What this module cannot see //! -//! The hash. Every permutation here is `TestPermutation` inside the LFM chips -//! plus the production keccak family hosted for `keccak256`; the point of -//! measuring cells at all is to have the first column of a matrix whose other -//! columns (blake, Poseidon) do not exist yet. It also cannot see prove time or +//! The hash — MOSTLY. This module was written when every permutation here was +//! `TestPermutation` inside the LFM chips plus the production keccak family +//! hosted for `keccak256`, and the point of measuring cells at all was to have +//! the first column of a matrix whose other columns did not exist yet. Those +//! columns exist now (BLAKE3, RPO, RPX, Poseidon), so +//! ⚠ [`the_census_agrees_with_the_traces_the_prover_builds`] takes its tenant +//! from `artifacts.hasher` rather than defaulting: it compares a census, a trace +//! set and an AIR set, and the socket chip's WIDTH is tenant-dependent, so three +//! defaults against one pinned artifact set is an out-of-bounds index rather +//! than a disagreement. Everything else here is still tenant-agnostic. It also +//! cannot see prove time or //! peak memory as a property of the machine — those are measured around the //! process, by the harness that runs it, and are reported as observations of one //! box rather than as machine invariants. @@ -40,10 +47,9 @@ use super::compiler::LfmProgram; use super::edsl::WrapHash; use super::epoch_tests::EpochInputs; use super::executor::execute; -use super::hash::TestPermutation; use super::instr::Instr; use super::proof::{LfmProveError, lfm_prove, lfm_prove_with_residency, verify_against}; -use super::registry::build_artifacts; +use super::registry::{build_artifacts, build_artifacts_with_hasher}; use crate::tables::types::FE; @@ -731,7 +737,7 @@ fn the_wrap_reports_gpu_counters() { let program = super::epoch_tests::epoch_program(&e, true); let arenas = super::epoch_tests::epoch_arena_words(&e, true); let opts = wrap_options(); - let artifacts = build_artifacts(&program, &opts); + let artifacts = build_artifacts_with_hasher(&program, &opts, crate::hash_pin::BLOCK_HASHER); println!(" chip log-heights: {:?}", artifacts.log_heights); g::reset_all_gpu_call_counters(); @@ -922,7 +928,7 @@ fn wrap_run_from(inner: ProofOptions, inputs: EpochInputs) { report_ratio(&e, main, aux); let opts = wrap_options(); - let artifacts = build_artifacts(&program, &opts); + let artifacts = build_artifacts_with_hasher(&program, &opts, crate::hash_pin::BLOCK_HASHER); println!( " wrap options: blowup {}, {} queries, grinding {}\n chip log-heights: {:?}", opts.blowup_factor, opts.fri_number_of_queries, opts.grinding_factor, artifacts.log_heights @@ -1101,7 +1107,7 @@ fn the_wrap_commitments_match_across_residency_modes() { let program = super::epoch_tests::epoch_program(&e, true); let arenas = super::epoch_tests::epoch_arena_words(&e, true); let opts = wrap_options(); - let artifacts = build_artifacts(&program, &opts); + let artifacts = build_artifacts_with_hasher(&program, &opts, crate::hash_pin::BLOCK_HASHER); let prove_under = |residency: ResidencyMode| { let t = Instant::now(); @@ -1452,9 +1458,19 @@ fn the_census_agrees_with_the_traces_the_prover_builds() { let state: [u64; 25] = core::array::from_fn(|i| 0x9E37_79B9_7F4A_7C15u64.wrapping_mul(i as u64 + 1)); let arenas = vec![super::keccak_adapter::state_to_words(&state).to_vec()]; - let exec = execute(&program, &arenas, &TestPermutation).expect("the chain program runs"); - let traces = super::trace::build_traces(&program, &exec.records); - let census = lfm_chip_census(&program); + // ⚠ ONE tenant for all four of execution, traces, census and AIRs, taken + // from the artifacts this test is about to compare against. The four used to + // default to `HasherKind::Test` while `build_artifacts` named the pin, and a + // trace built for one tenant against constraints built for another is an + // out-of-bounds index inside `HashConstraints::eval` — the socket chip's + // width is tenant-dependent (436 columns for RPO against 3,056 for BLAKE3). + // It surfaces as a bounds panic in a rayon worker, which reaches the test as + // "a scoped thread panicked" and names nothing at all. + let opts = wrap_options(); + let artifacts = build_artifacts(&program, &opts); + let exec = execute(&program, &arenas, &artifacts.hasher).expect("the chain program runs"); + let traces = super::trace::build_traces_with_hasher(&program, &exec.records, artifacts.hasher); + let census = super::airs::lfm_chip_census_with_hasher(&program, artifacts.hasher); // The frozen AIR order, as the census emits it and `air_trace_pairs` proves // it. Built from the trace set so a chip whose height the census got from the @@ -1517,12 +1533,11 @@ fn the_census_agrees_with_the_traces_the_prover_builds() { } // ---- the AIR set: the names and the widths, in the frozen order. - let opts = wrap_options(); - let artifacts = build_artifacts(&program, &opts); - let airs = super::airs::LfmAirs::new( + let airs = super::airs::LfmAirs::new_with_hasher( &artifacts.roots, &opts, artifacts.keccak_rnd_chunks, + artifacts.hasher, artifacts.chip_set, ); let refs = airs.air_refs(); @@ -1781,7 +1796,7 @@ fn batched_wrap_run_from(inner: ProofOptions, inputs: EpochInputs) { ); let opts = wrap_options(); - let artifacts = build_artifacts(&program, &opts); + let artifacts = build_artifacts_with_hasher(&program, &opts, crate::hash_pin::BLOCK_HASHER); println!( " wrap options: blowup {}, {} queries, grinding {}\n chip log-heights: {:?}", opts.blowup_factor, opts.fri_number_of_queries, opts.grinding_factor, artifacts.log_heights @@ -1944,7 +1959,7 @@ fn the_fixture_continuation_epoch_wraps_batched_from_proofs() { arenas.push(super::epoch_verify_tests::batched_opening_arena(&e)); arenas.push(super::epoch_verify_tests::batched_fri_arena(&e)); let opts = wrap_options(); - let artifacts = build_artifacts(&program, &opts); + let artifacts = build_artifacts_with_hasher(&program, &opts, crate::hash_pin::BLOCK_HASHER); let proved = lfm_prove(&program, &artifacts, &arenas, &opts).expect("the carved wrap must prove"); @@ -1962,19 +1977,17 @@ fn the_fixture_continuation_epoch_wraps_batched_from_proofs() { "the carved wrap of the final epoch must verify" ); - // The published-word schema's aggregator-facing check: the last 8 words - // are the carved L2G root, byte-equal to the bundle's claimed root. + // The published-word schema's aggregator-facing check: the last + // `lanes_per_root()` words are the carved L2G root — eight byte halves on a + // byte hash, four felts on an algebraic one — equal to the bundle's claimed + // root as the host publishes it. let root = bundle.epoch_view(n - 1).l2g_root(); - let published_root: Vec = proved.public_words[proved.public_words.len() - 8..] + let lanes = super::proof_arena::lanes_per_root(); + let published_root: Vec = proved.public_words[proved.public_words.len() - lanes..] .iter() - .map(|w| super::word::word_as_base(&w.1).expect("a root half is a base word")) - .collect(); - let expected_root: Vec = root - .chunks(4) - .map(|c: &[u8]| { - FE::from(u32::from_le_bytes(c.try_into().expect("a root is 32 bytes")) as u64) - }) + .map(|w| super::word::word_as_base(&w.1).expect("a root lane is a base word")) .collect(); + let expected_root: Vec = super::proof_arena::commitment_lanes(&root); assert_eq!( published_root, expected_root, "the wrap must publish the carved L2G root it verified under" @@ -2027,7 +2040,7 @@ fn the_from_proof_final_epoch_wraps() { let program = super::epoch_tests::epoch_program(&e, true); let arenas = super::epoch_tests::epoch_arena_words(&e, true); let opts = wrap_options(); - let artifacts = build_artifacts(&program, &opts); + let artifacts = build_artifacts_with_hasher(&program, &opts, crate::hash_pin::BLOCK_HASHER); let t = Instant::now(); let proved = lfm_prove(&program, &artifacts, &arenas, &opts).expect("the wrap must prove"); @@ -2181,7 +2194,8 @@ fn the_real_block_proves_and_wraps_end_to_end() { .unwrap_or_else(|err| panic!("epoch {i} must reconstruct from the bundle: {err}")); let program = super::epoch_tests::epoch_program(&e, true); let arenas = super::epoch_tests::epoch_arena_words(&e, true); - let artifacts = build_artifacts(&program, &wrap_opts); + let artifacts = + build_artifacts_with_hasher(&program, &wrap_opts, crate::hash_pin::BLOCK_HASHER); let c = t.elapsed().as_secs_f64(); construct_secs += c; @@ -2383,7 +2397,8 @@ fn the_real_block_proves_and_wraps_end_to_end_batched() { let mut arenas = super::epoch_tests::batched_epoch_arenas(&e); arenas.push(super::epoch_verify_tests::batched_opening_arena(&e)); arenas.push(super::epoch_verify_tests::batched_fri_arena(&e)); - let artifacts = build_artifacts(&program, &wrap_opts); + let artifacts = + build_artifacts_with_hasher(&program, &wrap_opts, crate::hash_pin::BLOCK_HASHER); let c = t.elapsed().as_secs_f64(); construct_secs += c; diff --git a/prover/src/tables/bitwise.rs b/prover/src/tables/bitwise.rs index fede150c2..dc5f73f0f 100644 --- a/prover/src/tables/bitwise.rs +++ b/prover/src/tables/bitwise.rs @@ -194,19 +194,19 @@ pub const fn is_preprocessed() -> bool { fn static_commitment(blowup_factor: u8) -> Option { match blowup_factor { 2 => Some([ - 0x9f, 0xbc, 0x26, 0x49, 0xce, 0x62, 0x34, 0xb5, 0x87, 0x3b, 0x91, 0xd8, 0x45, 0x2c, - 0x60, 0x74, 0x9b, 0x28, 0xaf, 0xdb, 0xf5, 0x1f, 0x3b, 0x9d, 0xde, 0x7c, 0xd0, 0xc2, - 0x38, 0x74, 0x8a, 0xeb, + 0x46, 0x8d, 0xfe, 0x65, 0xed, 0x50, 0x25, 0xbf, 0x7e, 0xfa, 0x04, 0x11, 0x28, 0x36, + 0x29, 0x06, 0xbd, 0x97, 0x29, 0xeb, 0x5b, 0x3a, 0xae, 0xbf, 0x66, 0xea, 0x5e, 0x59, + 0xe4, 0xd7, 0x17, 0x6c, ]), 4 => Some([ - 0x26, 0xe3, 0xea, 0x30, 0xb6, 0xbe, 0x1c, 0xf8, 0xd3, 0x90, 0x3b, 0x41, 0xf2, 0x11, - 0xec, 0xcb, 0x42, 0x63, 0xd1, 0x77, 0x5b, 0x9d, 0x81, 0x1b, 0x13, 0xfc, 0x7b, 0x87, - 0xa7, 0xae, 0xb7, 0x1d, + 0x88, 0x7b, 0x04, 0x68, 0xdd, 0x29, 0xf5, 0x0a, 0x87, 0x05, 0xcc, 0xdb, 0x48, 0xce, + 0x38, 0x11, 0xf5, 0x49, 0x2f, 0xe2, 0x73, 0x15, 0x03, 0xa7, 0xb9, 0x86, 0x04, 0x60, + 0xcc, 0x28, 0xa5, 0x9b, ]), 8 => Some([ - 0x9e, 0x11, 0x8d, 0x0c, 0x06, 0x45, 0x1c, 0x8c, 0x25, 0x1e, 0x16, 0x40, 0x17, 0x94, - 0xf5, 0x88, 0x03, 0x26, 0xba, 0x0c, 0xa4, 0x13, 0xbe, 0x82, 0x48, 0x50, 0xa1, 0xd1, - 0xe4, 0xc2, 0x30, 0xe0, + 0x7d, 0x85, 0xb2, 0x07, 0x0e, 0xdb, 0x9c, 0x89, 0xd1, 0x91, 0xda, 0x78, 0xe7, 0x11, + 0x13, 0x77, 0xe3, 0x1e, 0xe9, 0xbe, 0x3e, 0x3d, 0xd4, 0x26, 0x86, 0xbb, 0x4a, 0xe9, + 0x2c, 0x51, 0x1e, 0x44, ]), _ => None, } diff --git a/prover/src/tables/keccak_rc.rs b/prover/src/tables/keccak_rc.rs index d8d3525f3..bc01003ee 100644 --- a/prover/src/tables/keccak_rc.rs +++ b/prover/src/tables/keccak_rc.rs @@ -96,19 +96,19 @@ pub const fn generate_row(round: usize) -> [u64; NUM_PRECOMPUTED_COLS] { fn static_commitment(blowup_factor: u8) -> Option { match blowup_factor { 2 => Some([ - 0x48, 0xad, 0x9f, 0x53, 0x9b, 0x8e, 0x46, 0x7e, 0x45, 0x9d, 0x3a, 0x14, 0xe1, 0x22, - 0x80, 0xec, 0xd5, 0xfd, 0x7a, 0x0f, 0x77, 0x84, 0xb8, 0xf0, 0xc4, 0x53, 0x4d, 0xb2, - 0x83, 0x90, 0xcb, 0x99, + 0x0a, 0x3c, 0x86, 0xe2, 0xd3, 0x37, 0x98, 0xf2, 0x68, 0x45, 0x42, 0xbf, 0x0b, 0x3d, + 0x52, 0x02, 0x4b, 0x08, 0x8b, 0x7a, 0x4b, 0xc8, 0xb0, 0x2a, 0xe4, 0x14, 0x86, 0x09, + 0x35, 0xdc, 0xa6, 0x7c, ]), 4 => Some([ - 0xbf, 0x23, 0x00, 0x05, 0x30, 0x52, 0x47, 0x59, 0xff, 0xf4, 0xec, 0x7c, 0x40, 0x89, - 0x15, 0xd7, 0x0e, 0x85, 0x24, 0x75, 0x28, 0xdd, 0xb2, 0x3f, 0x01, 0x83, 0x7c, 0x61, - 0x45, 0x0c, 0x56, 0x4e, + 0xe9, 0xd8, 0x8a, 0xfe, 0x76, 0xf2, 0x68, 0x30, 0x55, 0xf6, 0xe7, 0x7c, 0x5e, 0x21, + 0x91, 0x7e, 0xa6, 0x59, 0x3b, 0xb1, 0x4e, 0xaa, 0xc0, 0x29, 0xf5, 0x00, 0xc0, 0x13, + 0xca, 0xc6, 0x6d, 0x10, ]), 8 => Some([ - 0xf4, 0xc0, 0x24, 0x38, 0xf9, 0x16, 0x8d, 0x35, 0x9f, 0xad, 0x2a, 0xc2, 0x79, 0x07, - 0x07, 0x7a, 0xa6, 0x0b, 0xa0, 0x4d, 0x7b, 0x71, 0x0e, 0xb9, 0xb4, 0x35, 0x5e, 0x84, - 0x71, 0xfa, 0xcc, 0x68, + 0x78, 0xfe, 0x1a, 0x99, 0xd6, 0x8b, 0x7d, 0x79, 0xd7, 0x3c, 0xd9, 0x8d, 0xc4, 0x81, + 0x82, 0x63, 0x82, 0x9a, 0x01, 0x3f, 0xed, 0x73, 0xab, 0xf9, 0x9d, 0xfa, 0xc7, 0xa1, + 0x99, 0x0f, 0xdf, 0xc2, ]), _ => None, } diff --git a/prover/src/tables/page.rs b/prover/src/tables/page.rs index 07f7e9a25..06c33309f 100644 --- a/prover/src/tables/page.rs +++ b/prover/src/tables/page.rs @@ -410,19 +410,19 @@ pub fn generate_page_trace_from_dense( pub(crate) fn static_zero_page_commitment(blowup_factor: u8) -> Option { match blowup_factor { 2 => Some([ - 0x17, 0xca, 0x82, 0xd0, 0x53, 0x18, 0x0e, 0x72, 0x0d, 0xa6, 0x33, 0x27, 0xe8, 0x64, - 0xb4, 0x61, 0xe5, 0xfa, 0x96, 0x65, 0x09, 0xa3, 0x41, 0x09, 0xff, 0x3f, 0x62, 0x81, - 0x3a, 0xe8, 0x8c, 0x62, + 0x3b, 0x8d, 0xfb, 0xfc, 0xab, 0xcb, 0x13, 0x98, 0x1b, 0xec, 0xe7, 0x72, 0x38, 0x50, + 0xcc, 0xd1, 0x9a, 0x95, 0xe6, 0xcc, 0x6c, 0xf7, 0xf0, 0x4a, 0x49, 0xc9, 0x5d, 0x52, + 0x42, 0x2b, 0x8c, 0x4b, ]), 4 => Some([ - 0x7a, 0x23, 0x4c, 0x3d, 0x8c, 0x24, 0xcc, 0x88, 0x6c, 0xb6, 0xe4, 0xd5, 0xc9, 0xe3, - 0xf0, 0x4e, 0x87, 0x56, 0xb5, 0xe0, 0x17, 0x98, 0x45, 0xd3, 0x7c, 0x3e, 0x4d, 0xcc, - 0x08, 0x66, 0xc3, 0xab, + 0x0b, 0x95, 0xb7, 0xe1, 0x62, 0xe4, 0xec, 0x59, 0xd5, 0xb7, 0x41, 0x7f, 0xc6, 0xe7, + 0x03, 0x7c, 0xee, 0x3c, 0xc2, 0x10, 0x29, 0xea, 0xbf, 0xae, 0x5a, 0xfd, 0x5e, 0x5d, + 0x00, 0x0b, 0x41, 0xdb, ]), 8 => Some([ - 0x9d, 0xc4, 0x23, 0xf5, 0xad, 0x9c, 0x10, 0x12, 0xc5, 0x5d, 0xa5, 0x9f, 0xd4, 0x0e, - 0x55, 0x99, 0x0a, 0x9a, 0x3c, 0x3a, 0x77, 0xd7, 0x27, 0xfb, 0xbe, 0xd1, 0xc8, 0x5a, - 0xb0, 0x45, 0x62, 0xf6, + 0x20, 0x86, 0x24, 0x23, 0x30, 0x0a, 0xea, 0xdc, 0x58, 0x37, 0x88, 0xf7, 0x33, 0x39, + 0x89, 0x2c, 0xf5, 0x2b, 0xfa, 0x89, 0x8b, 0x06, 0x60, 0x4d, 0x6f, 0x21, 0xc7, 0xfb, + 0x6f, 0xcb, 0xac, 0x84, ]), _ => None, } @@ -436,19 +436,19 @@ pub(crate) fn static_zero_page_commitment(blowup_factor: u8) -> Option Option { match blowup_factor { 2 => Some([ - 0xbc, 0x98, 0x15, 0x92, 0xb7, 0xfb, 0x9b, 0x50, 0x5a, 0xfb, 0xa7, 0x48, 0x40, 0x0a, - 0x67, 0xb9, 0x56, 0x2d, 0x54, 0x6e, 0x56, 0xb0, 0x32, 0xf1, 0x19, 0x57, 0x86, 0x36, - 0x30, 0xa0, 0x76, 0x42, + 0x04, 0x31, 0x10, 0xb3, 0x61, 0xbd, 0xd4, 0x6d, 0x92, 0x65, 0x0e, 0x2d, 0xc9, 0xe6, + 0x1d, 0x20, 0x78, 0xc3, 0xd6, 0xc6, 0x8f, 0x12, 0xb0, 0x45, 0xf0, 0x76, 0x68, 0x27, + 0x5e, 0x2b, 0xa7, 0xbd, ]), 4 => Some([ - 0x1a, 0x60, 0x17, 0xe1, 0x43, 0x1b, 0x15, 0xe5, 0xe8, 0x33, 0x39, 0xae, 0xc6, 0x6a, - 0x62, 0x72, 0x2b, 0xd0, 0x95, 0xbe, 0xb3, 0x05, 0x06, 0x9f, 0x72, 0x32, 0xea, 0xac, - 0x07, 0x0a, 0xfa, 0x6f, + 0xef, 0x71, 0xa3, 0x11, 0x4a, 0xb3, 0xf7, 0xd6, 0x77, 0xe4, 0x79, 0x0e, 0xd1, 0x96, + 0xf3, 0x36, 0x19, 0x70, 0x6b, 0xd7, 0x2e, 0x04, 0x86, 0x13, 0xfd, 0x81, 0xd1, 0xf4, + 0x26, 0xca, 0xd2, 0x41, ]), 8 => Some([ - 0xb5, 0x27, 0xbd, 0x0d, 0xa1, 0x14, 0xfe, 0xe0, 0x45, 0xe6, 0x32, 0xb7, 0x86, 0xf0, - 0x68, 0x26, 0x05, 0x19, 0xa1, 0x57, 0x46, 0x24, 0xc3, 0x0d, 0x2f, 0xdd, 0x16, 0xe3, - 0x72, 0x3a, 0xbf, 0x3b, + 0xf7, 0x2d, 0x07, 0xc5, 0x84, 0x5c, 0xc8, 0x92, 0x79, 0x75, 0xf3, 0x56, 0xb7, 0xf9, + 0xe5, 0xd5, 0xb5, 0x8f, 0x62, 0x63, 0x36, 0x8f, 0xd0, 0x8c, 0x42, 0xce, 0xfa, 0x2c, + 0xd2, 0x79, 0xd2, 0x8e, ]), _ => None, } diff --git a/prover/src/tests/bitwise_bus_tests.rs b/prover/src/tests/bitwise_bus_tests.rs index 1782bd0fc..93f83e607 100644 --- a/prover/src/tests/bitwise_bus_tests.rs +++ b/prover/src/tests/bitwise_bus_tests.rs @@ -4,12 +4,10 @@ //! - Completeness: Valid lookups to BITWISE are accepted //! - Soundness: Invalid lookups to BITWISE are rejected +use math::field::element::FieldElement; use stark::constraints::builder::EmptyConstraints; use std::collections::HashMap; -use crypto::fiat_shamir::default_transcript::DefaultTranscript; -use math::field::element::FieldElement; - use stark::lookup::{ AirWithBuses, AuxiliaryTraceBuildData, BusInteraction, BusValue, Multiplicity, NullBoundaryConstraintBuilder, Packing, @@ -17,7 +15,7 @@ use stark::lookup::{ use stark::proof::options::ProofOptions; use stark::trace::TraceTable; use stark::traits::AIR; -use stark::verifier::{IsStarkVerifier, Verifier}; +use stark::verifier::IsStarkVerifier; use crate::tables::types::{BusId, FE, GoldilocksExtension, GoldilocksField, alu_op}; use crate::test_utils::multi_prove_ram; @@ -195,15 +193,15 @@ fn prove_and_verify(sender_lookups: &[(u8, u8, u8)]) -> bool { ]; let multi_proof = - multi_prove_ram(air_trace_pairs, &mut DefaultTranscript::::new(&[])).unwrap(); + multi_prove_ram(air_trace_pairs, &mut crate::hash_pin::block_transcript(&[])).unwrap(); let airs: Vec<&dyn AIR> = vec![&sender_air, &receiver_air]; - Verifier::multi_verify( + crate::hash_pin::BlockVerifier::multi_verify( &airs, &multi_proof, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &FieldElement::zero(), ) } @@ -305,15 +303,15 @@ fn prove_and_verify_custom( ]; let multi_proof = - multi_prove_ram(air_trace_pairs, &mut DefaultTranscript::::new(&[])).unwrap(); + multi_prove_ram(air_trace_pairs, &mut crate::hash_pin::block_transcript(&[])).unwrap(); let airs: Vec<&dyn AIR> = vec![&sender_air, &receiver_air]; - Verifier::multi_verify( + crate::hash_pin::BlockVerifier::multi_verify( &airs, &multi_proof, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &FieldElement::zero(), ) } diff --git a/prover/src/tests/bitwise_tests.rs b/prover/src/tests/bitwise_tests.rs index c824764d3..920a1dbd8 100644 --- a/prover/src/tests/bitwise_tests.rs +++ b/prover/src/tests/bitwise_tests.rs @@ -415,16 +415,15 @@ fn test_preprocessed_commitment_is_nonzero() { #[cfg(test)] mod soundness_tests { use super::*; - use crypto::fiat_shamir::default_transcript::DefaultTranscript; use stark::lookup::{ AirWithBuses, AuxiliaryTraceBuildData, BusInteraction, BusValue, Multiplicity, NullBoundaryConstraintBuilder, Packing, }; use stark::proof::options::ProofOptions; - use stark::prover::{IsStarkProver, Prover}; + use stark::prover::IsStarkProver; use stark::trace::TraceTable; use stark::traits::AIR; - use stark::verifier::{IsStarkVerifier, Verifier}; + use stark::verifier::IsStarkVerifier; use crate::tables::types::{GoldilocksExtension, GoldilocksField}; @@ -558,8 +557,14 @@ mod soundness_tests { let dummy_air = create_receiver_air(proof_options); // Use the prover's commitment computation (3 precomputed cols: X, Y, AND) - Prover::compute_precomputed_commitment_for_testing(trace, &dummy_air, 3) - .expect("Failed to compute commitment") + // — the PINNED prover, so the commitment the AIRs declare is built under + // the same hash `multi_prove_ram` recomputes it with. The workspace + // `Prover` alias is BLAKE3 whatever the pin says, and under an algebraic + // pin it makes the honest arm fail with PrecomputedCommitmentMismatch. + crate::hash_pin::BlockProver::compute_precomputed_commitment_for_testing( + trace, &dummy_air, 3, + ) + .expect("Failed to compute commitment") } fn create_sender_trace(x: u8, y: u8, claimed_result: u8) -> TraceTable { @@ -626,15 +631,15 @@ mod soundness_tests { ]; let multi_proof = - multi_prove_ram(air_trace_pairs, &mut DefaultTranscript::::new(&[])).unwrap(); + multi_prove_ram(air_trace_pairs, &mut crate::hash_pin::block_transcript(&[])).unwrap(); let airs: Vec<&dyn AIR> = vec![&sender_air, &receiver_air]; - let result = Verifier::multi_verify( + let result = crate::hash_pin::BlockVerifier::multi_verify( &airs, &multi_proof, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &FieldElement::zero(), ); @@ -674,15 +679,15 @@ mod soundness_tests { ]; let multi_proof = - multi_prove_ram(air_trace_pairs, &mut DefaultTranscript::::new(&[])).unwrap(); + multi_prove_ram(air_trace_pairs, &mut crate::hash_pin::block_transcript(&[])).unwrap(); let airs: Vec<&dyn AIR> = vec![&sender_air, &receiver_air]; - let result = Verifier::multi_verify( + let result = crate::hash_pin::BlockVerifier::multi_verify( &airs, &multi_proof, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &FieldElement::zero(), ); @@ -744,16 +749,16 @@ mod soundness_tests { ]; let multi_proof = - multi_prove_ram(air_trace_pairs, &mut DefaultTranscript::::new(&[])).unwrap(); + multi_prove_ram(air_trace_pairs, &mut crate::hash_pin::block_transcript(&[])).unwrap(); // Verifier uses DIFFERENT AIR with honest commitment let verifier_airs: Vec<&dyn AIR> = vec![&sender_air, &verifier_receiver_air]; - let result = Verifier::multi_verify( + let result = crate::hash_pin::BlockVerifier::multi_verify( &verifier_airs, &multi_proof, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &FieldElement::zero(), ); diff --git a/prover/src/tests/branch_bus_tests.rs b/prover/src/tests/branch_bus_tests.rs index ee81ebb5a..c5b3897ff 100644 --- a/prover/src/tests/branch_bus_tests.rs +++ b/prover/src/tests/branch_bus_tests.rs @@ -6,12 +6,10 @@ //! - Padding: Auto-padding to power of 2 works correctly //! - Border cases: Edge values (0, MAX, signed boundaries) work +use math::field::element::FieldElement; use stark::constraints::builder::EmptyConstraints; use std::collections::HashMap; -use crypto::fiat_shamir::default_transcript::DefaultTranscript; -use math::field::element::FieldElement; - use stark::lookup::{ AirWithBuses, AuxiliaryTraceBuildData, BusInteraction, BusValue, LinearTerm, Multiplicity, NullBoundaryConstraintBuilder, Packing, @@ -19,7 +17,7 @@ use stark::lookup::{ use stark::proof::options::ProofOptions; use stark::trace::TraceTable; use stark::traits::AIR; -use stark::verifier::{IsStarkVerifier, Verifier}; +use stark::verifier::IsStarkVerifier; use crate::tables::branch::{BranchOperation, cols, generate_branch_trace}; use crate::tables::types::{BusId, FE, GoldilocksExtension, GoldilocksField}; @@ -336,15 +334,15 @@ fn prove_and_verify(ops: &[BranchOperation]) -> bool { ]; let multi_proof = - multi_prove_ram(air_trace_pairs, &mut DefaultTranscript::::new(&[])).unwrap(); + multi_prove_ram(air_trace_pairs, &mut crate::hash_pin::block_transcript(&[])).unwrap(); let airs: Vec<&dyn AIR> = vec![&sender_air, &receiver_air]; - Verifier::multi_verify( + crate::hash_pin::BlockVerifier::multi_verify( &airs, &multi_proof, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &FieldElement::zero(), ) } @@ -426,15 +424,15 @@ fn prove_and_verify_custom(ops: &[BranchOperation], receiver_rows: &[CustomBranc ]; let multi_proof = - multi_prove_ram(air_trace_pairs, &mut DefaultTranscript::::new(&[])).unwrap(); + multi_prove_ram(air_trace_pairs, &mut crate::hash_pin::block_transcript(&[])).unwrap(); let airs: Vec<&dyn AIR> = vec![&sender_air, &receiver_air]; - Verifier::multi_verify( + crate::hash_pin::BlockVerifier::multi_verify( &airs, &multi_proof, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &FieldElement::zero(), ) } diff --git a/prover/src/tests/decode_tests.rs b/prover/src/tests/decode_tests.rs index 4f7fa0221..44aa9ae14 100644 --- a/prover/src/tests/decode_tests.rs +++ b/prover/src/tests/decode_tests.rs @@ -237,13 +237,18 @@ fn decode_commitment_zero_bytes_rejects() { } /// DECODE preprocessed commitment for the `sub` asm test ELF at blowup=2, -/// computed offline once. Mirrors how the recursion guest embeds the -/// commitment as a compile-time constant for its inner program. If the -/// AIR or FFT pipeline changes, this drifts and the test fails — -/// regenerate via the `print_decode_commitment_for_sub` helper below. +/// computed offline once UNDER THE BLOCK PIN (`hash_pin::BLOCK_STARK_HASH`, +/// RPX256 here). Mirrors how the recursion guest embeds the commitment as a +/// compile-time constant for its inner program. +/// +/// ⚠ A fifth blessed constant, outside the four families +/// `compute_static_commitments` regenerates: it moves with the pin exactly as +/// they do, and `HASH-PINNING.md` lists it with them. If the pin, the AIR or the +/// FFT pipeline changes, this drifts and the test fails — regenerate via the +/// `print_decode_commitment_for_sub` helper below (`--ignored --nocapture`). const SUB_DECODE_COMMITMENT_BLOWUP_2: [u8; 32] = [ - 0xcd, 0xf1, 0xa8, 0xdf, 0x5b, 0x32, 0x67, 0xa4, 0x3c, 0x3d, 0x6b, 0x60, 0x1e, 0xe2, 0xbd, 0x22, - 0xb1, 0xc1, 0x7d, 0xb7, 0x8f, 0x67, 0x59, 0x70, 0x99, 0xbb, 0xa4, 0xfd, 0x0b, 0xca, 0x01, 0x2d, + 0xe6, 0xa9, 0x9f, 0x70, 0xfd, 0xdc, 0x7a, 0x0a, 0x65, 0xe1, 0x00, 0x56, 0x03, 0x0a, 0xe5, 0xc9, + 0x25, 0xef, 0xa9, 0x02, 0xe1, 0xd9, 0xd9, 0xd7, 0x62, 0xb1, 0x64, 0x11, 0x65, 0xe3, 0xa1, 0x5e, ]; #[test] diff --git a/prover/src/tests/hash_pin_enumeration.rs b/prover/src/tests/hash_pin_enumeration.rs index 3f358d5c2..d0dd85b6a 100644 --- a/prover/src/tests/hash_pin_enumeration.rs +++ b/prover/src/tests/hash_pin_enumeration.rs @@ -24,6 +24,92 @@ use std::collections::BTreeSet; use std::path::Path; +/// ★★ Calls that reach a prover or verifier through the WORKSPACE ALIAS. +/// +/// ⛔ `stark::prover::Prover` and `stark::verifier::Verifier` **are** +/// `GenericProver` / `GenericVerifier` AT `DefaultStarkHash`. They are the +/// SILENT spelling of the first symbol below, and this gate's first version +/// omitted them — so it searched for the class by the one name the class never +/// uses. Nineteen files kept calling the BLAKE3 alias against artifacts that +/// follow the pin, on branches whose entire purpose is that the two differ, and +/// the gate reported green. +/// +/// Matched as call forms rather than as bare identifiers, because `Prover` and +/// `Verifier` appear inside `IsStarkProver`, `BlockProver` and ordinary prose. +/// A line naming the pin is excluded by [`PIN_CALLS`] rather than by the pattern. +/// `compute_precomputed_commitment_for_testing` joined the list when a test +/// declared BLAKE3 preprocessed commitments through it while the pinned prover +/// recomputed them under RPX — the same alias, one more call form. +const ALIAS_CALLS: &[&str] = &[ + "Prover::multi_prove", + "Verifier::multi_verify", + "Prover::compute_precomputed_commitment_for_testing", +]; + +/// The pinned spellings, which contain [`ALIAS_CALLS`] as substrings. +const PIN_CALLS: &[&str] = &["BlockProver::", "BlockVerifier::"]; + +/// ★★ Items `prover` may name from `stark::config` — the hash-AGNOSTIC three. +/// +/// **Everything else that module exports is a spelling of the workspace +/// DEFAULT**, so this is an ALLOWLIST over a namespace rather than a list of +/// forbidden names, and that difference is the point. +/// +/// ⚠ The three misses this gate has had — `DefaultStarkHash` as a substring, +/// then the `Prover`/`Verifier` call forms, then the Merkle backend type +/// aliases — share one root: **the gate is lexical and the class is +/// type-level**, so a name list always lags one spelling behind the newest way +/// to denote the default. An allowlist cannot lag: a new alias added to +/// `stark::config` is flagged the first time `prover` names it, without anyone +/// remembering to extend this file. +/// +/// ⚖ Deletion would be stronger still — let the compiler refuse the spelling +/// rather than a test — and it was considered and is NOT available: +/// `BatchedMerkleTreeBackend` and `FriLayerMerkleTreeBackend` have twelve +/// legitimate consumers inside `crypto/stark` itself (`commitment.rs`, +/// `gpu_lde.rs`, the cuda tests), where the workspace default IS the correct +/// hash. `#[deprecated]` would fire on those under `-D warnings`, in the very +/// crate that must keep using them. +/// `DeviceTreeBackend` is allowed for the same reason: it is the marker a Merkle +/// backend implements to name its OWN hash as the device dispatch key, so it +/// carries no default — `algebraic_commit` implements it for the algebraic +/// backends, which is the opposite of reaching a default. +const CONFIG_ALLOWED: &[&str] = &[ + "Commitment", + "CommitmentHash", + "StarkHash", + "DeviceTreeBackend", +]; + +/// Every item named from `stark::config` on this line, `use` lists included. +fn config_items(code: &str) -> Vec { + const PREFIX: &str = "stark::config::"; + let mut out = Vec::new(); + let mut rest = code; + while let Some(i) = rest.find(PREFIX) { + rest = &rest[i + PREFIX.len()..]; + if let Some(stripped) = rest.strip_prefix('{') { + let end = stripped.find('}').unwrap_or(stripped.len()); + for part in stripped[..end].split(',') { + let name = part.split_whitespace().next().unwrap_or(""); + if !name.is_empty() { + out.push(name.to_string()); + } + } + rest = &stripped[end.min(stripped.len())..]; + } else { + let end = rest + .find(|c: char| !c.is_alphanumeric() && c != '_') + .unwrap_or(rest.len()); + if end > 0 { + out.push(rest[..end].to_string()); + } + rest = &rest[end..]; + } + } + out +} + /// The symbols that silently select a hash when nobody names one. const IMPLIED_HASH_SYMBOLS: &[&str] = &[ // The workspace's commitment configuration, and the `Prover` / `Verifier` @@ -39,6 +125,25 @@ const IMPLIED_HASH_SYMBOLS: &[&str] = &[ /// Files allowed to mention an implied-hash symbol, each with its reason. /// +/// ⚠⚠ **THE QUESTION THIS LIST ANSWERS IS "IS THIS DEFAULT PAIRED WITH A +/// NON-DEFAULT?", NOT "IS THIS REACHABLE FROM PRODUCTION?"** The first version +/// asked the second, and every entry's reasoning was *true* and one scope too +/// wide. `build_traces` really is test-only and production really does reach +/// `build_traces_with_hasher` — and twelve tests still built traces at +/// `HasherKind::Test` while proving against artifacts that followed the pin, +/// which is an out-of-bounds index inside `HashConstraints::eval` because the +/// socket chip's width is tenant-dependent. +/// +/// ★ **"Test-only" is not "safe" — it is only "production-safe."** Before +/// blessing an entry, name the CONSUMER the default is handed to and check what +/// tenant *it* follows. +/// +/// ⚖ And note the subtlest part: that mismatch did not pre-exist. Before +/// `build_artifacts` was pinned, `artifacts.hasher` was ALSO `Test`, so the pair +/// agreed **by both being wrong**. A correct fix to one half of a +/// wrong-but-consistent pair CREATES the failure — so a red test after such a +/// fix is evidence the fix worked, not that it broke something. +/// /// Paths are relative to `prover/src`. Two files are excluded from the scan /// rather than blessed: `hash_pin.rs`, because naming the default is what it is /// FOR, and this file, because it has to spell the symbols it searches for. @@ -92,20 +197,6 @@ const BLESSED: &[(&str, &str)] = &[ "Host-side BYTE-transcript differentials: the oracle for the machine's \ byte `TranscriptReplay` arm is deliberately the byte transcript.", ), - ( - "tests/prove_elfs_tests.rs", - "Names `DefaultStarkTranscript` deliberately — its header records that \ - the production path's transcript must be the one the default \ - commitment configuration names, and the test exists to hold that.", - ), - ( - "tests/recursion_soundness_gap_poc.rs", - "A proof-of-concept against the workspace default configuration.", - ), - ( - "tests/page_offset_forgery_poc.rs", - "As `recursion_soundness_gap_poc.rs`.", - ), ]; /// Every `.rs` under `dir`, relative to `root`. @@ -166,7 +257,13 @@ fn no_call_site_outside_the_pin_reaches_a_default_alias() { let text = std::fs::read_to_string(root.join(rel)).expect("a readable source file"); for line in text.lines() { let Some(code) = code_of(line) else { continue }; - if IMPLIED_HASH_SYMBOLS.iter().any(|s| code.contains(s)) { + let implied = IMPLIED_HASH_SYMBOLS.iter().any(|s| code.contains(s)) + || config_items(code) + .iter() + .any(|item| !CONFIG_ALLOWED.contains(&item.as_str())); + let aliased = ALIAS_CALLS.iter().any(|s| code.contains(s)) + && !PIN_CALLS.iter().any(|s| code.contains(s)); + if implied || aliased { found.insert(rel.to_string_lossy().replace('\\', "/")); } } diff --git a/prover/src/tests/local_to_global_bus_tests.rs b/prover/src/tests/local_to_global_bus_tests.rs index 8025596d6..7908ca2c2 100644 --- a/prover/src/tests/local_to_global_bus_tests.rs +++ b/prover/src/tests/local_to_global_bus_tests.rs @@ -5,12 +5,10 @@ //! program-end receiver (final value of each cell). The bus balances iff every //! epoch's `fini` matches the next epoch's `init` (the cross-epoch telescoping). +use math::field::element::FieldElement; use stark::constraints::builder::EmptyConstraints; use std::collections::HashMap; -use crypto::fiat_shamir::default_transcript::DefaultTranscript; -use math::field::element::FieldElement; - use stark::config::Commitment; use stark::lookup::{ AirWithBuses, AuxiliaryTraceBuildData, BusInteraction, BusValue, Multiplicity, @@ -21,7 +19,7 @@ use stark::proof::stark::MultiProof; use stark::proof::view::MultiProofView; use stark::trace::TraceTable; use stark::traits::AIR; -use stark::verifier::{IsStarkVerifier, Verifier}; +use stark::verifier::IsStarkVerifier; use crate::tables::bitwise::{BitwiseOperation, BitwiseOperationType}; use crate::tables::local_to_global::{ @@ -340,12 +338,12 @@ fn prove_verify_memory(l2g_boundary: &[CellBoundary], memw_boundary: &[CellBound _, _, )> = vec![(&l2g, &mut l2g_trace, &()), (&memw, &mut memw_trace, &())]; - let proof = multi_prove_ram(pairs, &mut DefaultTranscript::::new(&[])).unwrap(); + let proof = multi_prove_ram(pairs, &mut crate::hash_pin::block_transcript(&[])).unwrap(); let airs: Vec<&dyn AIR> = vec![&l2g, &memw]; - Verifier::multi_verify( + crate::hash_pin::BlockVerifier::multi_verify( &airs, &proof, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &FieldElement::zero(), ) } @@ -367,13 +365,13 @@ fn prove_verify_l2g_range_with_trace( (&l2g, l2g_trace, &()), (&receiver, &mut receiver_trace, &()), ]; - let proof = multi_prove_ram(pairs, &mut DefaultTranscript::::new(&[])).unwrap(); + let proof = multi_prove_ram(pairs, &mut crate::hash_pin::block_transcript(&[])).unwrap(); let airs: Vec<&dyn AIR> = vec![&l2g, &receiver]; - Verifier::multi_verify( + crate::hash_pin::BlockVerifier::multi_verify( &airs, &proof, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &FieldElement::zero(), ) } @@ -407,7 +405,7 @@ fn l2g_root(boundary: &[CellBoundary]) -> Commitment { _, _, )> = vec![(&air, &mut trace, &())]; - let proof = multi_prove_ram(pairs, &mut DefaultTranscript::::new(&[])).unwrap(); + let proof = multi_prove_ram(pairs, &mut crate::hash_pin::block_transcript(&[])).unwrap(); proof.proofs[0].lde_trace_main_merkle_root } @@ -468,7 +466,7 @@ pub(crate) fn prove_global(boundaries: &[Vec]) -> MultiProof::new(&[])).unwrap() + multi_prove_ram(air_trace_pairs, &mut crate::hash_pin::block_transcript(&[])).unwrap() } pub(crate) fn prove_and_verify(boundaries: &[Vec]) -> bool { @@ -489,10 +487,10 @@ pub(crate) fn prove_and_verify(boundaries: &[Vec]) -> bool { airs.push(&genesis_anchor); airs.push(&program_end_anchor); - Verifier::multi_verify( + crate::hash_pin::BlockVerifier::multi_verify( &airs, &proof, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &FieldElement::zero(), ) } @@ -593,12 +591,12 @@ fn prove_verify_memory_with_trace( _, _, )> = vec![(&l2g, l2g_trace, &()), (&memw, &mut memw_trace, &())]; - let proof = multi_prove_ram(pairs, &mut DefaultTranscript::::new(&[])).unwrap(); + let proof = multi_prove_ram(pairs, &mut crate::hash_pin::block_transcript(&[])).unwrap(); let airs: Vec<&dyn AIR> = vec![&l2g, &memw]; - Verifier::multi_verify( + crate::hash_pin::BlockVerifier::multi_verify( &airs, &proof, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &FieldElement::zero(), ) } @@ -656,7 +654,8 @@ fn prove_and_verify_global_with_traces( air_trace_pairs.push((&genesis_anchor, &mut genesis_trace, &())); air_trace_pairs.push((&program_end_anchor, &mut program_end_trace, &())); - let proof = multi_prove_ram(air_trace_pairs, &mut DefaultTranscript::::new(&[])).unwrap(); + let proof = + multi_prove_ram(air_trace_pairs, &mut crate::hash_pin::block_transcript(&[])).unwrap(); let mut airs: Vec<&dyn AIR> = l2g_airs .iter() @@ -665,10 +664,10 @@ fn prove_and_verify_global_with_traces( airs.push(&genesis_anchor); airs.push(&program_end_anchor); - Verifier::multi_verify( + crate::hash_pin::BlockVerifier::multi_verify( &airs, &proof, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &FieldElement::zero(), ) } diff --git a/prover/src/tests/lt_bus_tests.rs b/prover/src/tests/lt_bus_tests.rs index e95a81285..7f21cd2db 100644 --- a/prover/src/tests/lt_bus_tests.rs +++ b/prover/src/tests/lt_bus_tests.rs @@ -6,12 +6,10 @@ //! - Padding: Auto-padding to power of 2 works correctly //! - Border cases: Edge values (0, MAX, signed boundaries) work +use math::field::element::FieldElement; use stark::constraints::builder::EmptyConstraints; use std::collections::HashMap; -use crypto::fiat_shamir::default_transcript::DefaultTranscript; -use math::field::element::FieldElement; - use stark::lookup::{ AirWithBuses, AuxiliaryTraceBuildData, BusInteraction, BusValue, Multiplicity, NullBoundaryConstraintBuilder, Packing, @@ -19,7 +17,7 @@ use stark::lookup::{ use stark::proof::options::ProofOptions; use stark::trace::TraceTable; use stark::traits::AIR; -use stark::verifier::{IsStarkVerifier, Verifier}; +use stark::verifier::IsStarkVerifier; use crate::tables::lt::{LtOperation, cols, generate_lt_trace}; use crate::tables::types::{BusId, FE, GoldilocksExtension, GoldilocksField}; @@ -289,15 +287,15 @@ fn prove_and_verify(ops: &[LtOperation]) -> bool { ]; let multi_proof = - multi_prove_ram(air_trace_pairs, &mut DefaultTranscript::::new(&[])).unwrap(); + multi_prove_ram(air_trace_pairs, &mut crate::hash_pin::block_transcript(&[])).unwrap(); let airs: Vec<&dyn AIR> = vec![&sender_air, &receiver_air]; - Verifier::multi_verify( + crate::hash_pin::BlockVerifier::multi_verify( &airs, &multi_proof, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &FieldElement::zero(), ) } @@ -373,15 +371,15 @@ fn prove_and_verify_custom(ops: &[LtOperation], receiver_rows: &[CustomLtRow]) - ]; let multi_proof = - multi_prove_ram(air_trace_pairs, &mut DefaultTranscript::::new(&[])).unwrap(); + multi_prove_ram(air_trace_pairs, &mut crate::hash_pin::block_transcript(&[])).unwrap(); let airs: Vec<&dyn AIR> = vec![&sender_air, &receiver_air]; - Verifier::multi_verify( + crate::hash_pin::BlockVerifier::multi_verify( &airs, &multi_proof, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &FieldElement::zero(), ) } diff --git a/prover/src/tests/page_offset_forgery_poc.rs b/prover/src/tests/page_offset_forgery_poc.rs index fe676df63..a42e09056 100644 --- a/prover/src/tests/page_offset_forgery_poc.rs +++ b/prover/src/tests/page_offset_forgery_poc.rs @@ -37,16 +37,15 @@ // Fiat-Shamir from the hash production replays (`DefaultStarkHash`), or every // honest proof it builds is rejected at challenge derivation. Same half-flip // `config.rs` warns about; the warning applies to test harnesses too. -use stark::config::DefaultStarkTranscript as DefaultTranscript; use stark::proof::options::ProofOptions; -use stark::prover::{IsStarkProver, Prover}; +use stark::prover::IsStarkProver; use crate::statement::{StatementKind, absorb_statement}; use crate::tables::bitwise::{cols as bw_cols, row_index as bw_row_index}; use crate::tables::page::cols as page_cols; use crate::tables::trace_builder::Traces; use crate::tables::types::{FE, VmTable}; -use crate::test_utils::{E, asm_elf_bytes}; +use crate::test_utils::asm_elf_bytes; use crate::{MaxRowsConfig, VmAirs, VmProof}; use executor::elf::Elf; @@ -196,7 +195,7 @@ fn craft_proof( .filter(|c| c.is_private_input) .count(); - let mut transcript = DefaultTranscript::::new(&[]); + let mut transcript = crate::hash_pin::block_transcript(&[]); absorb_statement( &mut transcript, StatementKind::Monolithic, @@ -208,7 +207,7 @@ fn craft_proof( options.fri_final_poly_log_degree, ); - let proof = Prover::multi_prove( + let proof = crate::hash_pin::BlockProver::multi_prove( airs.air_trace_pairs(&mut traces), &mut transcript, #[cfg(feature = "disk-spill")] @@ -701,7 +700,7 @@ fn craft_proof_with_duplicate_page( None, ); - let mut transcript = DefaultTranscript::::new(&[]); + let mut transcript = crate::hash_pin::block_transcript(&[]); absorb_statement( &mut transcript, StatementKind::Monolithic, @@ -713,7 +712,7 @@ fn craft_proof_with_duplicate_page( options.fri_final_poly_log_degree, ); - let proof = Prover::multi_prove( + let proof = crate::hash_pin::BlockProver::multi_prove( airs.air_trace_pairs(&mut traces), &mut transcript, #[cfg(feature = "disk-spill")] diff --git a/prover/src/tests/prove_elfs_tests.rs b/prover/src/tests/prove_elfs_tests.rs index a7ab54ff0..e0102b728 100644 --- a/prover/src/tests/prove_elfs_tests.rs +++ b/prover/src/tests/prove_elfs_tests.rs @@ -27,7 +27,6 @@ // about exactly this half-flip ("the type system cannot force this; naming the // alias is what makes the production path follow DefaultStarkHash"); the warning // applies to the test harness too. -use stark::config::DefaultStarkTranscript as DefaultTranscript; use math::field::element::FieldElement; use stark::constraints::builder::EmptyConstraints; @@ -35,7 +34,7 @@ use stark::lookup::{AirWithBuses, AuxiliaryTraceBuildData}; use stark::proof::options::ProofOptions; use stark::proof::view::{MultiProofView, StarkProofView}; use stark::traits::AIR; -use stark::verifier::{IsStarkVerifier, Verifier}; +use stark::verifier::IsStarkVerifier; use crate::VmProof; use crate::tables::MaxRowsConfig; @@ -84,11 +83,11 @@ fn prove_and_verify_vm_minimal(elf: &Elf, traces: &mut Traces) -> bool { // Build air_trace_pairs for all tables let air_trace_pairs = airs.air_trace_pairs(traces); - let multi_proof = match multi_prove_ram(air_trace_pairs, &mut DefaultTranscript::::new(&[])) - { - Ok(proof) => proof, - Err(_) => return false, - }; + let multi_proof = + match multi_prove_ram(air_trace_pairs, &mut crate::hash_pin::block_transcript(&[])) { + Ok(proof) => proof, + Err(_) => return false, + }; // Compute the verifier-side expected COMMIT bus balance from public output bytes let views: Vec> = multi_proof @@ -107,10 +106,10 @@ fn prove_and_verify_vm_minimal(elf: &Elf, traces: &mut Traces) -> bool { .expect("fingerprint collision in test"); // Verify using centralized air_refs() which includes all tables - Verifier::multi_verify_views( + crate::hash_pin::BlockVerifier::multi_verify_views( &airs.air_refs(), &views, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &expected_bus_balance, ) } @@ -143,7 +142,7 @@ fn prove_vm_minimal(elf_bytes: &[u8], private_inputs: &[u8], max_rows: &MaxRowsC let runtime_page_ranges = traces.runtime_page_ranges(); let proof = multi_prove_ram( airs.air_trace_pairs(&mut traces), - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), ) .expect("prove"); let num_private_input_pages = traces @@ -201,10 +200,10 @@ fn verify_vm_minimal(vm_proof: &VmProof, elf_bytes: &[u8]) -> bool { &mut replay_transcript, ) .expect("fingerprint collision in test"); - Verifier::multi_verify_views( + crate::hash_pin::BlockVerifier::multi_verify_views( &air_refs, &views, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &expected_bus_balance, ) } @@ -255,15 +254,15 @@ fn test_cpu_only_no_bus() { _, )> = vec![(&cpu_air, &mut cpu_trace, &())]; - let multi_proof = multi_prove_ram(air_trace_pairs, &mut DefaultTranscript::::new(&[])) + let multi_proof = multi_prove_ram(air_trace_pairs, &mut crate::hash_pin::block_transcript(&[])) .expect("Prover failed"); let airs: Vec<&dyn AIR> = vec![&cpu_air]; assert!( - Verifier::multi_verify( + crate::hash_pin::BlockVerifier::multi_verify( &airs, &multi_proof, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &FieldElement::zero(), ), "CPU-only verification failed" @@ -1919,7 +1918,7 @@ fn test_prove_elfs_test_commit_4_wrong_pages_rejected() { ); let proof = multi_prove_ram( prover_airs.air_trace_pairs(&mut traces), - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), ) .expect("Prover failed"); @@ -1951,10 +1950,10 @@ fn test_prove_elfs_test_commit_4_wrong_pages_rejected() { ) .expect("fingerprint collision in test"); - let verified = Verifier::multi_verify_views( + let verified = crate::hash_pin::BlockVerifier::multi_verify_views( &verifier_air_refs, &views, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &expected_bus_balance, ); assert!( @@ -2678,7 +2677,7 @@ fn test_deep_stack_runtime_pages_roundtrip() { ); let proof = multi_prove_ram( prover_airs.air_trace_pairs(&mut traces), - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), ) .expect("Prover failed"); // Verifier reconstructs from ELF + runtime_page_ranges hint @@ -2710,10 +2709,10 @@ fn test_deep_stack_runtime_pages_roundtrip() { ) .expect("fingerprint collision in test"); - let verified = Verifier::multi_verify_views( + let verified = crate::hash_pin::BlockVerifier::multi_verify_views( &verifier_air_refs, &views, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &expected_bus_balance, ); assert!( @@ -2755,7 +2754,7 @@ fn test_deep_stack_missing_pages_rejected() { ); let proof = multi_prove_ram( prover_airs.air_trace_pairs(&mut traces), - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), ) .expect("Prover failed"); // Verifier uses EMPTY runtime_page_ranges → missing stack/heap pages @@ -2786,10 +2785,10 @@ fn test_deep_stack_missing_pages_rejected() { ) .expect("fingerprint collision in test"); - let verified = Verifier::multi_verify_views( + let verified = crate::hash_pin::BlockVerifier::multi_verify_views( &verifier_air_refs, &views, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &expected_bus_balance, ); assert!( @@ -2866,7 +2865,7 @@ fn test_heap_alloc_runtime_pages_roundtrip() { ); let proof = multi_prove_ram( prover_airs.air_trace_pairs(&mut traces), - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), ) .expect("Prover failed"); // Verifier reconstructs from ELF + runtime hint (ranges decoded to pages) @@ -2898,10 +2897,10 @@ fn test_heap_alloc_runtime_pages_roundtrip() { ) .expect("fingerprint collision in test"); - let verified = Verifier::multi_verify_views( + let verified = crate::hash_pin::BlockVerifier::multi_verify_views( &verifier_air_refs, &views, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &expected_bus_balance, ); assert!( @@ -3079,15 +3078,15 @@ fn test_crafted_zero_count_proof_must_not_verify() { (airs.decode.as_ref(), &mut decode_trace, &()), ]; - let proof = multi_prove_ram(pairs, &mut DefaultTranscript::::new(&[])) + let proof = multi_prove_ram(pairs, &mut crate::hash_pin::block_transcript(&[])) .expect("Proof generation should succeed"); assert_eq!(proof.proofs.len(), 2); - let verified = Verifier::multi_verify( + let verified = crate::hash_pin::BlockVerifier::multi_verify( &verifier_air_refs, &proof, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &FieldElement::zero(), ); @@ -3546,7 +3545,7 @@ fn test_prove_first_epoch_without_halt() { let multi_proof = multi_prove_ram( airs.air_trace_pairs(&mut traces), - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), ) .expect("first epoch failed to prove"); @@ -3566,10 +3565,10 @@ fn test_prove_first_epoch_without_halt() { .expect("fingerprint collision in test"); assert!( - Verifier::multi_verify_views( + crate::hash_pin::BlockVerifier::multi_verify_views( &airs.air_refs(), &views, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &expected_bus_balance, ), "first epoch (HALT excluded) failed to verify" @@ -3635,7 +3634,7 @@ fn test_prove_second_epoch_from_snapshot() { let multi_proof = multi_prove_ram( airs.air_trace_pairs(&mut traces), - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), ) .expect("second epoch failed to prove"); @@ -3655,10 +3654,10 @@ fn test_prove_second_epoch_from_snapshot() { .expect("fingerprint collision in test"); assert!( - Verifier::multi_verify_views( + crate::hash_pin::BlockVerifier::multi_verify_views( &airs.air_refs(), &views, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &expected_bus_balance, ), "second epoch (register init from snapshot) failed to verify" @@ -3749,7 +3748,7 @@ fn test_epoch_proof_commits_l2g() { let mut pairs = airs.air_trace_pairs(&mut traces); pairs.push((&inert_l2g_air, &mut l2g_trace, &())); - let multi_proof = multi_prove_ram(pairs, &mut DefaultTranscript::::new(&[])) + let multi_proof = multi_prove_ram(pairs, &mut crate::hash_pin::block_transcript(&[])) .expect("epoch proof with inert L2G failed to prove"); let mut refs = airs.air_refs(); @@ -3771,10 +3770,10 @@ fn test_epoch_proof_commits_l2g() { .expect("fingerprint collision in test"); assert!( - Verifier::multi_verify_views( + crate::hash_pin::BlockVerifier::multi_verify_views( &refs, &views, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &expected_bus_balance, ), "epoch proof with inert L2G failed to verify" @@ -3911,7 +3910,7 @@ fn test_continuation_pipeline_end_to_end() { let mut pairs = airs.air_trace_pairs(&mut traces); pairs.push((&inert_l2g_air, &mut l2g_trace, &())); - let multi_proof = multi_prove_ram(pairs, &mut DefaultTranscript::::new(&[])) + let multi_proof = multi_prove_ram(pairs, &mut crate::hash_pin::block_transcript(&[])) .expect("epoch proof failed to prove"); let mut refs = airs.air_refs(); @@ -3931,10 +3930,10 @@ fn test_continuation_pipeline_end_to_end() { ) .expect("fingerprint collision in test"); assert!( - Verifier::multi_verify_views( + crate::hash_pin::BlockVerifier::multi_verify_views( &refs, &views, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &expected_bus_balance, ), "epoch {i} failed to verify" @@ -4054,7 +4053,7 @@ fn test_epoch_memory_bus_with_l2g_bookend() { let mut pairs = airs.air_trace_pairs(&mut traces); pairs.push((&l2g_air, &mut l2g_trace, &())); - let multi_proof = multi_prove_ram(pairs, &mut DefaultTranscript::::new(&[])) + let multi_proof = multi_prove_ram(pairs, &mut crate::hash_pin::block_transcript(&[])) .expect("epoch with L2G memory bookend failed to prove"); let mut refs = airs.air_refs(); @@ -4075,10 +4074,10 @@ fn test_epoch_memory_bus_with_l2g_bookend() { .expect("fingerprint collision in test"); assert!( - Verifier::multi_verify_views( + crate::hash_pin::BlockVerifier::multi_verify_views( &refs, &views, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &expected_bus_balance, ), "epoch Memory bus must balance with L2G bookend + PAGE excluding touched cells" @@ -4290,7 +4289,7 @@ fn a_blake3_workload_claiming_no_blake3_table_is_rejected() { assert!(!airs.include_blake3, "the forged shape must omit the table"); let pairs = airs.air_trace_pairs(&mut traces); - let proved = multi_prove_ram(pairs, &mut DefaultTranscript::::new(&[])); + let proved = multi_prove_ram(pairs, &mut crate::hash_pin::block_transcript(&[])); let verified = match &proved { Err(_) => false, @@ -4300,7 +4299,7 @@ fn a_blake3_workload_claiming_no_blake3_table_is_rejected() { .iter() .map(StarkProofView::Owned) .collect(); - let mut replay = DefaultTranscript::::new(&[]); + let mut replay = crate::hash_pin::block_transcript(&[]); match crate::compute_expected_commit_bus_balance_view( &airs.air_refs(), &views, @@ -4309,10 +4308,10 @@ fn a_blake3_workload_claiming_no_blake3_table_is_rejected() { &mut replay, ) { None => false, - Some(expected) => Verifier::multi_verify_views( + Some(expected) => crate::hash_pin::BlockVerifier::multi_verify_views( &airs.air_refs(), &views, - &mut DefaultTranscript::::new(&[]), + &mut crate::hash_pin::block_transcript(&[]), &expected, ), } @@ -4368,7 +4367,7 @@ fn the_blake3_count_is_bound_into_the_statement() { }; let challenge_for = |counts: &crate::TableCounts| { - let mut t = DefaultTranscript::::new(&[]); + let mut t = crate::hash_pin::block_transcript(&[]); absorb_statement( &mut t, StatementKind::Monolithic, diff --git a/prover/src/tests/recursion_soundness_gap_poc.rs b/prover/src/tests/recursion_soundness_gap_poc.rs index 5c6dc3395..05260a1f2 100644 --- a/prover/src/tests/recursion_soundness_gap_poc.rs +++ b/prover/src/tests/recursion_soundness_gap_poc.rs @@ -21,13 +21,11 @@ use std::path::PathBuf; // Fiat-Shamir from the hash production replays (`DefaultStarkHash`), or every // honest proof it builds is rejected at challenge derivation. Same half-flip // `config.rs` warns about; the warning applies to test harnesses too. -use stark::config::DefaultStarkTranscript as DefaultTranscript; -use stark::prover::{IsStarkProver, Prover}; +use stark::prover::IsStarkProver; use crate::recursion::{MIN_PROOF_OPTIONS, precomputed_commitments}; use crate::statement::{StatementKind, absorb_statement, elf_digest}; use crate::tables::trace_builder::Traces; -use crate::test_utils::E; use crate::{MaxRowsConfig, VmAirs, VmProof}; use executor::elf::Elf; @@ -172,7 +170,7 @@ fn custom_prove_with_statement_elf( .filter(|c| c.is_private_input) .count(); - let mut transcript = DefaultTranscript::::new(&[]); + let mut transcript = crate::hash_pin::block_transcript(&[]); absorb_statement( &mut transcript, StatementKind::Monolithic, @@ -184,7 +182,7 @@ fn custom_prove_with_statement_elf( opts.fri_final_poly_log_degree, ); - let proof = Prover::multi_prove( + let proof = crate::hash_pin::BlockProver::multi_prove( airs.air_trace_pairs(&mut traces), &mut transcript, #[cfg(feature = "disk-spill")]