Skip to content

Add InputOutputMismatch error.#6

Merged
srinathsetty merged 1 commit into
microsoft:mainfrom
porcuquine:mismatch-error
Oct 6, 2021
Merged

Add InputOutputMismatch error.#6
srinathsetty merged 1 commit into
microsoft:mainfrom
porcuquine:mismatch-error

Conversation

@porcuquine

@porcuquine porcuquine commented Oct 6, 2021

Copy link
Copy Markdown
Contributor

Add a new error, InputOutputMismatch for the case in which the current input and previous output do not match, when folding instances.

@srinathsetty
srinathsetty merged commit cf5d708 into microsoft:main Oct 6, 2021
@porcuquine
porcuquine deleted the mismatch-error branch October 19, 2021 00:22
asn-d6 pushed a commit to asn-d6/Nova that referenced this pull request Apr 23, 2023
…o/fix/name_space_of_witness

Fix namespace of the witness
huitseeker added a commit to huitseeker/Nova that referenced this pull request Aug 23, 2023
* doc: Improve code comments by adding backticks for formatting

- Back-ticks have been added around relevant technical terms, type names, and code references within the comments to better highlight them while reading.

* refactor: Refactor Spartan polynomial file organization

- Refactor the structure of Spartan SNARK related modules, mainly moving and renaming `polynomial.rs` to `polys/mlpoly.rs` and introducing three new submodules under `polys`: `eq_poly`, `mlpoly`, and `unipoly`.
- Implement a new module `polys` that houses refactored and newly introduced polynomial types used in the Spartan SNARK, leading to import refactors across numerous files.
- Establish a new file `unipoly.rs` responsible for housing the main structs `UniPoly` and `CompressedUniPoly`

* refactor: Fix comment in UniPoly storage and enhance testing

- Fix comment about ordering of coefficient storage protocol in `UniPoly` and `CompressedUniPoly` structures for improved processing.
- Integrated unit tests for 'from_evals_quad' and 'from_evals_cubic' in context of `pallas::Scalar` and `bn256::Scalar` types,

* test: Add multilinear polynomial evaluation test

- Introduced new test for multilinear polynomial evaluation in the mlpoly.rs file.

* chore: rename to eq / multilinear / univariate

* chore: remove unnecessary qualificaitons
marcus-sa added a commit to marcus-sa/Nova that referenced this pull request May 8, 2026
…_lookup

Multi-table verify path per design pin §5.1 (API). Per-table
(C)-binding `∀j: poly_lookup_j(0) + poly_lookup_j(1) == T_lookup_running_j`
enforced HERE — this is what M.4 owes that M.2's fold did not.

Per pin §2.2 the verifier mirrors the prover's transcript schedule
step-by-step: per-table `comm_L`/value-cols/`comm_ts` loop at Step 2;
gated per-table α at Step 5a; sequenced per-table `r_logup_j` at Step
5b (sequencing IS domain separation per §2.3 — no per-table separator
scalar absorbed); batched `comm_inv_w[*]` then `comm_inv_t[*]` at
Step 6. Verifier reads the multi-table NIFS extension fields
(`Vec`-typed since M.1) directly; `LookupPayloadPublicMultiTable<E>`
carries only the public projection per pin §5.1.

Pin §1.3 cross-table-cancellation forgery is closed STRUCTURALLY: any
single-table (C)-binding failure rejects the whole step, regardless
of whether a hypothetical SUM-aggregate would have cancelled it.

Tests:
- k=1 round-trip (`m4_round_trip_k1_homogeneous`) — regression vs
  the existing single-table multi-column round-trip; confirms the
  multi-table path accepts on a satisfying witness at k=1.
- k=2 round-trip (`m4_round_trip_k2_homogeneous`) — homogeneous
  random tables, n_1 = n_2 = 64, distinct queries per table, asserts
  the per-table running-scalar VECTOR threads independently
  (`T_lookup[0] != T_lookup[1]`).
- k=2 hard-reject (`m4_per_table_c_binding_hard_rejects_corrupted_t_lookup`)
  — two honest fold steps; corrupt `folded_U_s1.T_lookup[1]` with a
  non-zero offset; step-2 verify with the corrupted U1 must fail at
  the per-table (C)-binding for table_id=1
  (`InvalidSumcheckProof`). Smaller version of pin §5.2 microsoft#6 (full
  cross-table cancellation negative test lives at M.14 in inumbra
  harness).

All 125 vendor/nova lib tests pass under --features lookup-fold
--release per .claude/rules/testing.md (143s).

GH-microsoft#2 sub-stage M.4 of M.1–M.16. M.6 (in-circuit
AllocatedNIFS::verify_with_multi_table_lookup) follows in next dispatch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
marcus-sa added a commit to marcus-sa/Nova that referenced this pull request May 8, 2026
…ect (pin §5.2 microsoft#6)

Lands the canonical regression check that the VECTOR running-claim
composition closes pin §1.3's cross-table cancellation forgery vector
at the per-table (C)-binding inside `verify_with_multi_table_lookup`.

Test: `m14_cross_table_cancellation_hard_rejects`
(`vendor/nova/src/neutron/nifs.rs::tests`, `#[cfg(feature = "lookup-fold")]`).

## Pin §5.2 microsoft#6 obligation

Synthesise a multi-table fold step where the prover supplies bundles
for T_1 and T_2 with deliberately-corrupted witnesses such that
`Σ inv_w_1 − Σ inv_t_1 = +Δ` and `Σ inv_w_2 − Σ inv_t_2 = −Δ`. A
hypothetical SUM-aggregate (C)-binding `Σ_j (poly_j(0) + poly_j(1))
== Σ_j T_running_j` would succeed by cancellation; assert
`verify_with_multi_table_lookup` REJECTS at the per-table (C)-binding
for T_1.

## Test shape (mirrors the M.4 hard-reject pattern)

1. k=2 homogeneous-size random tables (n_1 = n_2 = 64). The
   cancellation property is size-independent per pin §1.3, so
   homogeneous sizes preserve narrative coherence with the M.4
   sibling without weakening the obligation.
2. Honest step 1 prove → honest `folded_U_s1.T_lookup =
   [t1_HONEST, t2_HONEST]`.
3. Honest step 2 prove + verify (positive control: must accept).
4. **Direction A — forward cancellation** (+Δ on T_1, −Δ on T_2):
   pre-step-2-verify, mutate `folded_U_s1.T_lookup` to
   `[t1_HONEST + Δ, t2_HONEST − Δ]`. Aggregate `(t1+Δ) + (t2−Δ) =
   t1 + t2` is preserved by construction. Verify rejects with
   `NovaError::InvalidSumcheckProof` at the per-table (C)-binding
   loop (`nifs.rs:1977-1990`, first-fire at j=0).
5. **Direction B — reverse cancellation** (−Δ' on T_1, +Δ' on T_2):
   sign-swap symmetry control. Different Δ' ChaCha20Rng draw,
   distinct corruption payload, same per-table reject semantics.

Both directions emit explicit `aggregate_honest == aggregate_corrupt`
assertions before the verify call, making the structural property
pin §1.3 names as the forgery condition (the SUM-aggregate
cancellation invariant) audit-visible.

## VECTOR composition closure

The per-table (C)-binding loop at `nifs.rs:1977-1990` checks
`poly_lookup_j(0) + poly_lookup_j(1) == T_lookup_running_j` per j and
returns `Err(InvalidSumcheckProof)` on the first failing table. M.4's
sibling `m4_per_table_c_binding_hard_rejects_corrupted_t_lookup`
covers single-table corruption (Δ on T_2 alone, T_1 honest); this
M.14 test covers the symmetric cancellation pattern. M.4 closes
"single-table corruption is caught"; M.14 closes "cross-table
cancellation does not bypass the per-table check".

The j=0 first-fire is structural (the loop is j-ascending). For
documentation: T_2's check would also reject if reached, since
`poly_lookup_2(0) + poly_lookup_2(1) = t2_HONEST` mismatches the
corrupt running scalar `t2_HONEST − Δ` (or `t2_HONEST + Δ'` in
Direction B).

## Determinism

Seed `0xC1BE_CA1E` ("cancellation") via `ChaCha20Rng::seed_from_u64`,
per `.claude/rules/cryptography.md` reviewer-reproducibility
discipline (US-05). Both Δ values drawn from the same stream;
`assert_ne!(delta, Scalar::ZERO)` guards against vacuous corruption.

## Audit-trail framing

Test header cites pin §5.2 microsoft#6 (line 561), pin §1.3 forgery vector
(line 90), and pin §3 VECTOR composition for the auditor's hand-off.
The (C)-binding call-site is named explicitly
(`vendor/nova/src/neutron/nifs.rs:1989`).

## Verification

```
cargo test --release -p nova-snark --features lookup-fold m14_cross_table_cancellation_hard_rejects
```

passes in 0.20s release-mode. Full neutron-suite regression
(`cargo test --release -p nova-snark --features lookup-fold neutron::`)
passes 40/40, no regressions in M.4 / M.6 / M.6.5 / M.12 family.

## Halt-and-ask trigger (per dispatch)

If `verify_with_multi_table_lookup` ACCEPTS either corrupted bundle
pair, this is a soundness-class incident — pin §1.3's forgery vector
is open at vendor HEAD. The `assert!(matches!(... Err(...)))` panic
IS the halt signal. (Did not fire; the per-table check rejects both
directions as designed.)

## Refs

- Pin: `docs/research/cryptography/c1-beta-multi-table-fold-design-pin-2026-05-08.md`
  §5.2 microsoft#6 (line 561), §1.3 (line 90), §3 (VECTOR composition).
- Cryptography rule: `.claude/rules/cryptography.md` (Gadget contract,
  ≥1000-iter behavioural earned-trust adjacent layer; this single
  test is the *negative* arm complementing the M.8 ≥1000-iter
  positive arm).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
marcus-sa added a commit to marcus-sa/Nova that referenced this pull request May 9, 2026
…h Y extension to T_lookup_per_table

- default_with_lookup_k: allocate T_lookup_zero = alloc_zero(...) once,
  clone across k slots; each T_lookup_per_table[j] inherits
  (0)·(0) = T_lookup_zero zero-binding constraint
- Variable-identity binding test (extends M.GH5.0 STAGE 0 module)
- pp_digest baselines unchanged (default_with_lookup_k is #[allow(dead_code)]
  until M.GH5.3 wires synthesize_base_case through this constructor; the
  production augmented circuit's R1CS shape is not affected at this
  milestone — Pallas/Bn256IPA/Secp256k1 expects all hold)

Closes the IVC-anchor constraint-binding gap surfaced during M.GH5.2
review. Same class as Corrigendum microsoft#4's X-side fix; same precedent for
test-shape adaptation. Halpert-ratified per Corrigendum microsoft#6.

Test extension (m_gh5_0_path_y_x_t_aliasing_binding):
  + Cases (1, Some(2)) and (2, Some(2)) — k=2 production arity per ADR-0021
  + Variable-identity binding: T_lookup_per_table[j].get_variable() ==
    T_lookup_per_table[0].get_variable() for all j ∈ [1, k) — intra-Vec
    aliasing form (TestConstraintSystem does not support post-allocation
    witness mutation; Corrigendum microsoft#4-A precedent for adapted assertion)
  + Distinctness from T: T_lookup_per_table[0].get_variable() !=
    T.get_variable() — confirms Halpert's recommended SEPARATE
    T_lookup_zero (not aliased to T)
  + Constraint-count floor: default_with_lookup_k MUST emit ≥ 6
    constraints (default(num_io) baseline 5 + 1 from T_lookup_zero
    alloc_zero), strictly exceeding the default(num_io) baseline

Pre/post numbers (TestConstraintSystem at num_io=1, k=2, Bn256EngineKZG):
  pre-fix:  num_constraints=5, T_lookup_per_table[0]=Aux(5),
            T_lookup_per_table[1]=Aux(6) — distinct fresh aux per slot,
            ZERO new constraints (the gap Corrigendum microsoft#6 closes)
  post-fix: num_constraints=6, T_lookup_per_table[0]=Aux(5),
            T_lookup_per_table[1]=Aux(5) — shared T_lookup_zero,
            (0)·(0) = T_lookup_zero constraint enforced

Adversary-side: TestConstraintSystem does not expose post-allocation
witness mutation; per Corrigendum microsoft#4-A precedent the witness-injection
negative test is a structural counterfactual under Path Y. The
variable-identity assertion algebraically subsumes it: a malicious
prover attempting T_lookup_per_table[j] = N ≠ 0 would attempt
T_lookup_zero = N, rejected directly by alloc_zero's row at synthesis.

Pin: docs/research/cryptography/microsoftgh-5-augmented-circuit-wireup-design-pin-2026-05-08.md §1.4 Corrigendum microsoft#6
Roadmap: docs/feature/microsoftgh-5-augmented-circuit-wireup/deliver/roadmap.json row 01-03

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
marcus-sa added a commit to marcus-sa/Nova that referenced this pull request May 10, 2026
…p-fold wire-up

Wires NeutronAugmentedCircuit::synthesize_non_base_case through
verify_with_multi_table_lookup and synthesize_base_case through
default_with_lookup_k under the lookup-fold cfg + runtime gate. Closes
(W3) plumbing and (W4) confirmation per pin §1.4.

Vendor-side surface:
- NeutronAugmentedCircuitInputs: +public_bundles_multi_table field
  (Option<Vec<LookupPayloadPublicMultiTable<E>>>) per pin §3.1
- NeutronAugmentedCircuit struct: +shape_registry, +lookup_fold_k,
  +index_n_bits fields; with_lookup_fold(k, registry, bits) builder
  (#[allow(dead_code)] until M.GH5.4 inumbra-side wire-up)
- alloc_with_k_hint: T_lookup_per_table allocation branches on
  inst.t_lookup() Some/None × k > 0 hint
- synthesize_base_case: under #[cfg(feature = "lookup-fold")] dispatches
  to default_with_lookup_k(cs, 1, k) when lookup_fold_k > 0; else
  default(cs, 1)
- synthesize_non_base_case: under same cfg dispatches to new private
  synthesize_non_base_case_lookup_fold method when lookup_fold_k > 0;
  else legacy nifs.verify
- synthesize_non_base_case_lookup_fold: allocates chunk_index_in_z =
  U.X[0], shape_registry, AllocatedLookupNIFSMultiTable, per-bundle
  AllocatedLookupPayloadPublicMultiTable; invokes
  verify_with_multi_table_lookup; rebinds post-fold T_lookup_per_table
  via from_lookup_fold_output (no-constraint structural rewire)
- AllocatedLookupPayloadPublicMultiTable::alloc helper

Halpert ratification (2026-05-09 dispatch, all 6 questions RATIFY,
High confidence):
- Q1 Option B (cfg + runtime lookup_fold_k > 0 gate): selected per
  Principle 7 + ADR-0023 vendor-fork separability. lookup_fold_k is
  construction-time pinned and pp_digest-shape-bound; no downgrade
  attack possible.
- Q2 Argument-order canonical, table_id-traversal mechanical
  (Vec iter preserves order at every fold depth).
- Q3 lookup_fold_k as usize correct at M.GH5.3; per-position
  uniformity assertion deferred to M.GH5.4 per Auditor Obligation 10.
- Q4 M.7 shape-registry assertion fires BEFORE pp_digest.absorb per
  pin §2.5 (vendor/nova/src/neutron/circuit/nifs.rs:689-698 direct
  verification). Dispatch text's "AFTER" formulation was a misstatement;
  STOP-AND-ASK NOT triggered.
- Q5 (W4) Phase-1↔Phase-2 binding closed: clone() on AllocatedNum
  preserves Variable identity, so Phase-1 hash binding of
  U.T_lookup_per_table transitively binds Phase-2 verifier inputs.
- Q6 from_lookup_fold_output no-constraint rewire is sound;
  (C)-binding at nifs.rs:872-897 takes the binding role at fold-depth
  ≥ 1 that alloc_zero plays at base case. Path Y / microsoft#4-A invariants
  are base-case-only.

Test posture preserved at M.GH5.2 close baseline:
- vendor --release --features lookup-fold: 152/0/7
- vendor --release (default): 80/0/6
- inumbra-spend-circuits: 87/0/9 lib + 4 RED scaffolds
- inumbra-spend-harness: 13/0
- M.GH5.0 STAGE 0 + Corrigendum microsoft#4-A + Corrigendum microsoft#6 tests: pass
- pp_digest baselines unchanged (vendor IVC tests on legacy path;
  refresh deferred to M.GH5.4 when inumbra-side wires
  with_lookup_fold(2, ...))
- Determinism verified across 2 consecutive runs.

Pin: docs/research/cryptography/microsoftgh-5-augmented-circuit-wireup-design-pin-2026-05-08.md
     §3.1 + §3.2 + §3.4 + §5.1 + §5.5 row M.GH5.3
Roadmap: docs/feature/microsoftgh-5-augmented-circuit-wireup/deliver/roadmap.json row 01-04

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
marcus-sa added a commit to marcus-sa/Nova that referenced this pull request May 11, 2026
… threads shape_registry to NeutronAugmentedCircuit::new

Per pin §5.5 row M.GH5.4 (post-Corrigendum-microsoft#8 reverted scope) sub-
deliverable 2:

- Extend `PublicParams::setup` and `setup_with_ptau_dir` under
  `feature = "lookup-fold"` to accept `(shape_registry: Vec<E1::Scalar>,
  lookup_fold_k: usize, index_n_bits: usize)`. Threaded to the shape-
  derivation `NeutronAugmentedCircuit::new` call via
  `with_lookup_fold(...)`. Non-`lookup-fold` builds keep the pre-M.GH5.4
  signature (vendor examples / benches build unchanged).

- Store the registry triple on `PublicParams` under `lookup-fold`,
  `#[serde(skip)]`-gated so the bincode encoding (and therefore
  `pp_digest`) is byte-equivalent to pre-M.GH5.4 at the default
  `(vec![], 0, 0)` state. Vendor-internal `test_pp_digest` baselines
  do NOT refresh — confirmed empirically: 152/0/7 release sweep
  matches pre-M.GH5.4 baseline byte-for-byte.

- `RecursiveSNARK::new` (base case) and `RecursiveSNARK::prove_step`
  read the triple from `pp` and chain `with_lookup_fold(pp.lookup_fold_k,
  &pp.shape_registry, pp.index_n_bits)` on each `NeutronAugmentedCircuit::new`.
  This closes the four invocation sites the pin §3.1 binding requires
  (the augmented-circuit shape-derivation site + the prove-step site
  must emit byte-equivalent R1CS, which they do because both threads
  the same triple from `pp`).

- Lift `#[allow(dead_code)] until M.GH5.4` on
  `NeutronAugmentedCircuit::with_lookup_fold` (vendor
  `circuit/mod.rs:268`). The `with_multi_table_bundles` annotation
  (`circuit/mod.rs:139`) stays — its per-step consumer wires up at
  M.GH5.5 (the inumbra-side fold-of-N happy-path differential).

- Vendor-internal `TrivialCircuit` / `CubicCircuit` test sites updated
  to pass `(vec![], 0, 0)` under `lookup-fold` (the default lookup-
  fold-OFF state); shape and `pp_digest` byte-equivalent to pre-M.GH5.4.

Sub-deliverable 1 (inumbra-side consumer wire-up) is a no-op at this
milestone: there is no inumbra-side consumer of `neutron::PublicParams::setup`
yet (wrap.rs is on `nova_snark::nova::*` per Corrigendum microsoft#8; will stay
through M.GH5.9; Stage K migration dispatched as separate GH-microsoft#7).
`build_chunk_step_registry` (M.10 / `shape_registry.rs:224`) is ready
to be invoked by M.GH5.5 when the inumbra-side fold-of-N differential
dispatches against the augmented-circuit IVC. Auditor Obligation 10
confirmed by construction at `shape_registry.rs:347`: `vec![merged_mc,
chunk_mc]` (length 2) inside the `0..NUM_CHUNK_POSITIONS=16` loop —
k=2 uniform across all 16 chunked-Strauss-Shamir positions per ADR-0021.

Test results (release-mode per `.claude/rules/testing.md`):
- vendor `cargo test --release -p nova-snark --features lookup-fold`:
  152/0/7 (matches pre-M.GH5.4 baseline; no `pp_digest` refresh
  needed — the `#[serde(skip)]` design preserves the bincode encoding).
- inumbra-spend-circuits lib: 87/0/9 + 4 RED scaffolds (baseline).
- inumbra-spend-harness: 13/0 (baseline).
- M.GH5.0 STAGE 0 + Corrigendum microsoft#4-A + Corrigendum microsoft#6 tests pass.

Closes (W4) plumbing per pin §5.5. Wire-up consumer at M.GH5.5.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
marcus-sa added a commit to marcus-sa/Nova that referenced this pull request May 12, 2026
…commitment Vec extension

Widens `FoldedInstance::{comm_L, comm_ts, comm_inv_w, comm_inv_t}` at
`vendor/nova/src/neutron/relation.rs:246-264` from scalar
`Option<Commitment<E>>` to per-table `Option<Vec<Commitment<E>>>`,
mirroring the already-landed `T_lookup: Option<Vec<E::Scalar>>` extension
at line 265 (GH-microsoft#2 M.2 structural mirror). Lifts the per-`X` scalar
`fold_one` body at `relation.rs:802-826` to a per-`j` loop running the
linear-combination `comm_X_j_fold = (1 - r_b) * comm_X_j_running + r_b *
payload_per_table[j].comm_X` independently for each
`(X, j) ∈ {L, ts, inv_w, inv_t} × {0, ..., k-1}` under common `r_b`.

This is a **HARD PREREQUISITE** for M.GH7.0 — without the per-table Vec
extension, the §1.2(b)/(c) Spartan bridge-reduction has no per-table
running commitments to consume.

## Soundness sketch (Corrigendum microsoft#6 §1.3 lines 166-241)

FoldedInstance-level per-table-independent-fold-under-common-`r_b`
binding reduces to four paper-grounded primitives composed:

1. **PCS binding at each `(X, j)` pair independently** — Pedersen
   homomorphism (1991) + KZG eprint 2010/096.
2. **RO2 collision-resistance over `table_id`-canonical absorption
   ordering** — Fischlin 2005 / Lasso §2.3 FS analysis. Per-step NIFS
   absorbs `comm_inv_w_vec[0..k]` then `comm_inv_t_vec[0..k]` in
   canonical order at `nifs.rs:1497-1556` BEFORE `r_b` is squeezed; the
   FoldedInstance-level fold preserves the order.
3. **No cross-`j` term under common `r_b`** — the fold body
   `comm_X_j_fold = (1 - r_b) * comm_X_j_1 + r_b * comm_X_j_2` is a
   linear combination over a SINGLE table's commitments at each `j`
   (Falsifier B verified-absent at the algebra).
4. **Path β per-step binding composes into FoldedInstance level** —
   GH-microsoft#2 Path β at `c1-beta-multi-table-fold-design-pin-2026-05-08.md`
   §A.2.4 (per-step ratified) extended upward via Nova binding-by-hash
   composition (eprint 2021/370 v3 §4).

Both falsifiers verified-absent at vendor HEAD per Corrigendum microsoft#6: (A)
per-step NIFS-message Vec-typed inverse commitments persist across
steps at `nifs.rs:256-260`; (B) primitive 3's per-`j` independence is
structural at the algebra level. Confidence promoted Medium → Medium-
High per §6.1 row M.GH7.0a.

## Implementation

- `relation.rs:246-264`: four `comm_X` fields widened to
  `Option<Vec<Commitment<E>>>`. `T_lookup_per_table` at line 265
  unchanged (already Vec-typed since GH-microsoft#2 M.2).
- `relation.rs:fold_with_lookup`: signature change from singular
  `payload: &LookupPayload<E>` to per-table `payload_per_table:
  &[LookupPayload<E>]`; body lifted from per-`X` scalar to per-`(X, j)`
  Vec loop. Length-mismatch fails-closed with `NovaError::InvalidStructure`.
- `relation.rs:fold` (R1CS-only): per-table Vecs passed through via
  `.clone()` (Vec is not `Copy`).
- `nifs.rs:prove_with_multi_table_lookup_inner` + `verify_with_multi_table_lookup`:
  replaced the singular `effective_payload = bundles[0]` collapse (k=1
  byte-equivalence routing per pre-M.GH7.0a M.3 commentary at
  `nifs.rs:2039-2045`) with per-table construction in `table_id`-canonical
  order. K=1 single-table degenerates byte-identically via
  `std::slice::from_ref(&effective_payload)`.
- Single-table callers (`prove_with_lookup`, `verify_with_lookup`,
  `prove_with_multi_column_lookup`, `verify_with_multi_column_lookup`):
  wrap singular `effective_payload` with `std::slice::from_ref` at the
  fold-with-lookup call site.

**No `AllocatedFoldedInstance` extension needed** per Corrigendum microsoft#1 —
binding-via-hash at GH-microsoft#5 (W1) carries the four `comm_X` fields through
`u.X[0]`; `AllocatedFoldedInstance` at `circuit/relation.rs:26-56` does
not hold the four commitments and does not need to.

## Tests

Three tests added to `neutron::relation::tests` module
(`vendor/nova/src/neutron/relation.rs`):

- `test_folded_instance_per_table_vec_extension_binding` — k=2 single
  fold from outer base; asserts `FoldedInstance::{comm_L, comm_ts,
  comm_inv_w, comm_inv_t}` are populated as `Some(Vec<Commitment<E>>)`
  of length 2; per-`j` outer-base body `comm_X_j_fold = r_b *
  fresh_per_table[j].comm_X`.
- `fold_with_lookup_per_table_no_cross_j_interference` — Falsifier B
  negative test: corrupting `fresh_per_table[1].comm_X` for each
  `X ∈ {L, ts, inv_w, inv_t}` leaves `folded.comm_X_per_table[0]`
  unchanged (Corrigendum microsoft#6 primitive 3). Includes counter-check that
  `comm_X_per_table[1]` DOES change (homomorphic propagation of the
  corrupted fresh input).
- `fold_with_lookup_per_table_differential_against_reference_1000_iter`
  — 1000-iter ChaCha20Rng-seeded differential against the off-circuit
  reference formula per Gadget contract (`.claude/rules/cryptography.md`
  ≥ 1000-iter behavioural earned-trust threshold). Single fixed seed
  `0xC1BE_5BAD_C0DE_700A` for US-05 reviewer-reproducibility. Runs
  ~7.6s in release mode.

Pre-existing `fold_with_lookup_threads_per_table_running_scalars_k2`
test (GH-microsoft#2 M.2 regression) updated to pass per-table payload slice
through the new signature; semantics unchanged.

## Verification

- `cargo check -p nova-snark --features lookup-fold --all-targets
  --release`: clean.
- `cargo check -p nova-snark --all-targets --release` (without
  lookup-fold): clean — the widening is fully `#[cfg(feature =
  "lookup-fold")]` gated; non-lookup-fold builds unaffected.
- `cargo test -p nova-snark --features lookup-fold --release`: 156
  passed, 7 ignored, 0 failures. Includes all 5 tests in
  `neutron::relation::tests`.
- `cargo test --workspace --release`: clean except 4 pre-existing
  `arity_envelope_acceptance.rs` RED scaffold `unimplemented!()` panics
  (verified pre-existing via `git stash` cross-check; unrelated to
  M.GH7.0a; downstream feature work).
- `cargo test -p inumbra-spend-harness --release --test
  m11_fold_of_four_absent_table_differential`: 2 passed (1000-iter
  fold-of-four 21min).
- `cargo test -p inumbra-spend-harness --release --test
  m_gh5_6_running_scalar_reference_differential`: 1 passed (1000-iter
  running-scalar 6min).
- `cargo test -p inumbra-spend-harness --release --test
  m_gh5_7_negative_test_triple`: 4 passed.
- `cargo test -p inumbra-spend-harness --release --test
  multi_table_lookup_fold_differential --test
  lookup_fold_differential`: 5 passed.

Step-ID: 01-01

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
marcus-sa added a commit to marcus-sa/Nova that referenced this pull request May 15, 2026
…ld negative-test triple per Corrigendum microsoft#15 + sub-ratification (Constraint hygiene §70-78 discharge; AO 19 FULL)

Three #[test] functions in vendor/nova/src/neutron/mod.rs #[cfg(test)] mod tests,
sibling to the M.GH7.3a positive-path acceptance test at mod.rs:1466-1900.
Discharges .claude/rules/cryptography.md §70-78 ("Constraint hygiene; a missing
constraint is a forgery vector") for the M.GH7.3a public prover API surface:
RecursiveSNARK::prove_step_with_lookup_fold sibling + LookupStepCircuit<E>
trait + RecursiveSNARK::running_lws field + PublicParams::setup lookup_shape
parameter, landed at e3fe75d.

Per Corrigendum microsoft#15 + 2026-05-12 late-evening Sub-ratification in
docs/research/cryptography/microsoftgh-7-stage-k-compressed-snark-design-pin-2026-05-11.md
§0 entry 15 + §3.1.1 + §5.5 row M.GH7.3b:

  test (i)   `prove_step_with_lookup_fold_corrupt_t_at_index_in_consumed_u1
              _rejects_at_r1cs_c_binding`
              Corrupt verifier-input U1.T += Scalar::ONE; reject at R1CS-side
              (C)-binding (nifs.rs:2000-2001, Step 7).
              Anchored on Nova 2021/370 v3 §3 R1CS sumcheck soundness.
              (RE-TARGETED per Sub-ratification — Corrigendum microsoft#15's original
              bundles[j].running_lw corruption was structurally inadequate at
              the (C)-binding rejection mechanism because
              LookupSumcheckInstance::prove_step at lookup_sumcheck.rs:278-285
              sets evals[1] := t_lookup_running − evals[0] BY CONSTRUCTION,
              making poly_lookup_j(0)+poly_lookup_j(1) = t_lookup_running_j a
              TAUTOLOGY for any honestly-produced polynomial. Fifth
              Halpert-class verify-don't-assume failure named.)

  test (ii)  `prove_step_with_lookup_fold_post_prove_mutate_poly_lookup
              _via_nifs_rebuild_rejects_at_lookup_c_binding`
              Mutate nifs.poly_lookup[0].coeffs[0] += Scalar::ONE (ADDITIVE,
              per Sub-ratification — replaces Corrigendum microsoft#15's original
              multiplicative × 2 which preserved the (C)-binding tautology
              under all-zero T_lookup_running = 0); reject at lookup-side
              (C)-binding (nifs.rs:2025-2028, Step 8).
              Anchored on FS-NIZK soundness (Fischlin-Fischlin 2005).

  test (iii) `prove_step_with_lookup_fold_corrupt_t_lookup_at_index_in
              _consumed_u1_rejects_at_lookup_c_binding`
              Corrupt verifier-input U1.T_lookup[0] += Scalar::ONE; reject at
              lookup-side (C)-binding (nifs.rs:2025-2028, Step 8).
              Anchored on Nova 2021/370 v3 §4 binding-by-hash composition.
              (STANDS verbatim from Corrigendum microsoft#15.)

All three reject with NovaError::InvalidSumcheckProof, asserted via the M.14
typed-variant pattern at nifs.rs:6140. Implementation pattern follows the M.14
precedent at nifs.rs:5719-6149 (direct NIFS::prove_with_multi_table_lookup at
vendor-test scope) — per Sub-ratification Gap-2 clarification, RecursiveSNARK
has no `nifs` field at vendor HEAD e3fe75d so extraction is structurally
impossible; the direct-NIFS pattern routes through the same (C)-binding
algebra that prove_step_with_lookup_fold invokes internally.

Fixture: AbsentTableStepCircuit-style — K=1, TABLE_SIZE=4, TABLE_LOG2=2,
Bn256EngineKZG + GrumpkinEngine, single empty-columns multi_column_table —
inherited from the M.GH7.3a positive-path acceptance test. No fixture
amendment required (NEITHER Option A NOR Option B from the original dispatch
brief). The three algebraic preconditions (BN254 scalar field characteristic
odd; rho ≠ ONE w.o.p. under RO-squeeze; prime field of size ≥ 2) hold under
this fixture. Deterministic seeds 0xC1BE_5BAD_C0DE_703{1,2,3} per US-05.

Test verdicts (release-mode, scoped, by name):
  test (i)   1 passed (0.19s)
  test (ii)  1 passed (0.19s)
  test (iii) 1 passed (0.18s)
  M.GH7.3a positive-path regression  1 passed (0.70s)

Empirical-close of STOP-AND-ASK triggers:
  microsoft#1 accessor-surface gap         verified absent (NIFS::poly_lookup and
                                   UniPoly::coeffs are pub; FoldedInstance.T
                                   and .T_lookup pub(crate) accessible from
                                   sibling test module).
  microsoft#2 rejection-variant divergence verified absent (all three reject with
                                   InvalidSumcheckProof).
  microsoft#3 loop-iteration-order         verified absent (K=1, single iteration).
  microsoft#4 positive-baseline regression verified absent (M.GH7.3a positive-path
                                   still GREEN).
  microsoft#5 (NEW) test (i) rejection-locus
                                  empirically confirmed at R1CS-side
                                   (C)-binding (nifs.rs:2001:7) via
                                   distinguishing-panic probe (probe removed
                                   before commit; vendor working tree clean
                                   at nifs.rs).
  microsoft#6 (NEW) test (ii) additive-mutation effectiveness
                                  empirically confirmed (in-test assert that
                                   mutated_sum = honest_sum + 2·ONE in BN254
                                   scalar field of odd characteristic).

AO 19 FULL discharge: completes the partial discharge from M.GH7.3a's
positive-path round-trip per Corrigendum microsoft#15 §6.3 AO 19 boundary refinement
(unchanged by Sub-ratification). No new Pass Criterion at M.GH7.0.5; no new
auditor obligation; no algebra change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
marcus-sa added a commit to marcus-sa/Nova that referenced this pull request May 15, 2026
…rewires for per-table FS-transcript per Corrigendum microsoft#16 sub-ratification (D-i); AO 22 discharge with IVC↔Spartan binding-deferral routed to M.GH7.5 path (b)

Authors the M.GH7.4c envelope-side wire-up of the M.GH7.4a/4b Spartan-
sibling-with-logup path through `CompressedSNARK<E, EE>` per the
Corrigendum microsoft#16 sub-ratification (D-i)/(P1) at superproject `ce032d9`.

Scope (per sub-ratification §5.5 row M.GH7.4c crisp scope):

  1. Lift M.GH7.4b synthetic-data helpers `build_honest_logup_witnesses`
     and `build_per_table_commitments` from `#[cfg(test)] mod tests` at
     `vendor/nova/src/spartan/snark.rs:2780-2907` to `pub(crate)`
     envelope scope at `vendor/nova/src/neutron/compressed_snark.rs`.
     Gate microsoft#10 preservation: helpers preserved VERBATIM — LogUp-
     consistency-by-construction (the (A)+(B)+(C) Haböck §3 identities
     hold pointwise at the helper's internal `r_logup_j` via
     `batch_invert_plus_r`).

  2. Extend `CompressedSNARK<E, EE>` envelope struct (NOT
     `BridgedNeutronInstance` per (D-i) gate microsoft#7 resolution) with six new
     fields carrying per-table commitments + `T_lookup` data + per-table
     `r_logup` scalars. Fields are empty `Vec::new()` at k=0 (STAGE-0
     path) and populated by the prover at k>0.

  3. Rewire `prove_from_parts` to invoke
     `prove_with_T_claim_split_error_with_logup` (M.GH7.4a/4b sibling)
     with empty per-table slices at k=0. The `_with_logup` sibling at k=0
     is byte-functionally equivalent to the β' sibling (no per-table
     absorption; empty per-table residue body; identical
     batch_eval_reduce u_vec/w_vec at `3 + 4·0 = 3` entries). M.GH7.0.2
     envelope regression tests pass under unconditional rewiring because
     verify-side is also rewired uniformly.

  4. Author `pub(crate) fn prove_from_parts_with_logup(...)` — the
     M.GH7.4c acceptance-test entry point at k>0. Performs the envelope-
     side per-table FS-transcript discipline per dispatch authoring
     steps (6) + (7):
       - Absorb `comm_L_j`, `comm_ts_j`, `comm_inv_w_j`, `comm_inv_t_j`,
         `T_lookup_j` in `table_id`-canonical order at envelope-side
         transcript `b"NeutronCompressedSNARK_envelope"`.
       - Squeeze per-table `r_logup_j` sequentially (mirroring
         `nifs.rs:1492-1495` vendor NIFS-side discipline).
       - Invoke `_with_logup` sibling with prover-threaded per-table
         polynomial slices, per-table commitments, and (under (D-i)/(P1)
         binding-deferral disposition) the witness-construction
         `r_logup_per_table` (NOT the FS-squeezed value).

  5. Rewire `CompressedSNARK::verify` to mirror the prover's per-table
     absorption + FS-squeeze sequence, then invoke
     `verify_with_T_claim_split_error_with_logup` (M.GH7.4b sibling)
     with the proof-envelope-carried `per_table_r_logup` scalars.

  6. Add the M.GH7.4c end-to-end positive-path acceptance test at
     `vendor/nova/src/neutron/compressed_snark.rs::tests::m_gh7_4c_compressed_snark_end_to_end_with_per_table_lookup_envelope_transcript`
     — exercises `CompressedSNARK::prove_from_parts_with_logup →
     CompressedSNARK::verify` round-trip at `num_cons = 8, k = 2,
     table_size = 4` with envelope-built synthetic per-table data.
     Deterministic seed `0xC1BE_5BAD_C0DE_704Cu64`.

IVC↔Spartan binding-deferral disposition under (D-i)/(P1) — Gate microsoft#11
narration discipline:

  Per-table commitments are envelope-fresh at length `num_cons` (R1CS-
  row domain), STRUCTURALLY DISTINCT from any IVC-layer `r_U.comm_L[j]`
  (witness-address domain, length `n_w`). The envelope FS squeeze of
  `r_logup_j` is STRUCTURALLY EXERCISED for transcript discipline (the
  audit-firm engagement can inspect the FS log) but the squeezed value
  is DISCARDED at M.GH7.4c — the sibling's LogUp-identity algebra
  closes against the prover-threaded `r_logup_per_table` (built INSIDE
  `build_honest_logup_witnesses`). The IVC↔Spartan binding between the
  envelope-fresh per-table commitments and the IVC-trace data lives at
  M.GH7.5 path (b) — to be crisped by Halpert authoring of the binding-
  discharge mechanism specification. Candidate mechanisms:
    (i)  Two-round FS discipline: re-derive `r_logup_j` from FS during
         witness construction, with separate absorptions of `(L, ts, T)`
         and `(inv_w, inv_t)` either side of the per-`j` squeeze.
    (ii) Σ-protocol equality-of-opening between IVC-layer `r_U.comm_L[j]`
         and envelope-side `per_table_comm_L[j]` (analogous to
         Corrigendum microsoft#11's `SigmaE2EqualityProof`).
    (iii) Augmented-circuit-final-step absorption of envelope-side
          per-table commitments into the IVC public input.
  If M.GH7.5 path (b) cannot implement any of (i)/(ii)/(iii), the (D-i)
  ratification flips to (P3) per the sub-ratification's STOP-AND-ASK
  trigger #M.GH7.5.2.

Regression invariants verified at vendor HEAD:

  - test_pp_digest (neutron + nova): 2/2 pass.
  - test_ivc_trivial (neutron + nova): 2/2 pass.
  - test_ivc_nontrivial_neutron: 1/1 pass.
  - test_ivc_base (neutron + nova): 2/2 pass.
  - test_setup (neutron + nova): 2/2 pass.
  - m_gh7_0_0b (3 tests including 1000-iter differential): 3/3 pass.
  - m_gh7_0_2 envelope regression (acceptance + negative + 1000-iter):
    3/3 pass; the 1000-iter Pedersen-additive binding differential
    confirms the unconditional rewiring of `prove_from_parts` through
    `_with_logup` at k=0 is byte-functionally identical to the prior β'
    path.
  - m_gh7_3a positive-path: 1/1 pass.
  - m_gh7_4a/4a-degree-3-empirical/4b PCS-opening: 4/4 pass.
  - m_gh7_4c acceptance test: 1/1 pass — END-TO-END envelope-transcript
    round-trip at `num_cons = 8, k = 2, table_size = 4` closes.

STOP-AND-ASK gate dispositions:

  Gate microsoft#6 FS-transcript independence: PRESERVED. Envelope-side absorbs
    happen on `b"NeutronCompressedSNARK_envelope"`; Spartan-side
    initialises FRESH `b"RelaxedR1CSSNARK"` per `snark.rs:1395`. The
    two transcripts NEVER share bytes — M.GH7.4a's note at
    `snark.rs:1755-1762` re-verified at vendor HEAD.
  Gate microsoft#7 STRUCT EXTENSION VS SIBLING: RESOLVED by sub-ratification —
    `BridgedNeutronInstance<E>` UNCHANGED; `CompressedSNARK<E, EE>`
    extended with six per-table Vec fields.
  Gate microsoft#8 KPI microsoft#2 proof-size: at fixture (num_cons=8, k=2), envelope
    grows by ~832 bytes (commitments + `r_logup` + length-8 T_lookup
    polys). Production-scale `T_lookup` carriage in `CompressedSNARK<E,
    EE>` is a future-milestone optimisation (move to `VerifierKey<E,
    EE>` or `pp`-level once binding-discharge mechanism is crisped at
    M.GH7.5). At M.GH7.4c fixture scale, growth is well under 12 KB.
  Gate microsoft#9 M.GH7.5 reachability: documented in acceptance test code
    comment + verify body comment + struct field doc-comments. M.GH7.5
    path (b) is load-bearing for (D-i) soundness composition.
  Gate microsoft#10 LogUp-consistency of lifted helpers: PRESERVED VERBATIM
    from M.GH7.4b — helpers byte-equivalent at the algorithmic level.
  Gate microsoft#11 IVC↔Spartan binding-deferral narration: DOCUMENTED
    EXPLICITLY in acceptance test code comment (lines documenting
    "M.GH7.4c (D-i)/(P1) IVC↔Spartan binding-deferral disposition") +
    in verify body (binding-deferral discipline + three candidate
    mechanisms enumerated) + in struct field doc-comments.

Per `.claude/rules/cryptography.md` (Gadget contract): the lifted
helpers `build_honest_logup_witnesses` + `build_per_table_commitments`
are the off-circuit reference for the M.GH7.4c synthetic-data
construction; they live in production-shipped envelope code (NOT test
scaffolding) so the wallet can derive the synthetic per-table data at
envelope-build time under the (D-i)/(P1) disposition. The in-circuit
half is the M.GH7.4a `prove_with_T_claim_split_error_with_logup`
sibling (which the wallet's prover invokes via this M.GH7.4c envelope
wire-up). The 1000-iter differential discipline is preserved by the
M.GH7.0.2 envelope-binding differential (3/3 pass at 1000 iters under
unconditional `_with_logup` rewiring).

Sequencing: M.GH7.4c closes Phase 02. Phase 03 opens with M.GH7.5
(IVC↔Spartan binding-discharge mechanism specification + negative-test
triple) — requires Halpert authoring per the sub-ratification's
STOP-AND-ASK trigger #M.GH7.5.1 BEFORE crafter dispatch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
marcus-sa added a commit to marcus-sa/Nova that referenced this pull request May 15, 2026
…er-table fold propagation per Corrigendum microsoft#19

Wires post-fold per-table running commitments (comm_L, comm_ts) through the
augmented circuit's verify_with_multi_table_lookup as untrusted hints, mirroring
the existing comm_W_fold / comm_E_fold discipline at circuit/nifs.rs:46-55
/ :649-664. Closes the IVC↔envelope binding by making the in-circuit
Unew.comm_L_per_table[j] (absorbed at the final-step hash via M.GH7.5.0a's
absorb_in_ro extension) byte-equal to the off-circuit r_U.comm_L[j] (absorbed
at off-circuit RecursiveSNARK::verify via M.GH7.5.0a's absorb_in_ro2 extension).

Changes:

- Off-circuit NIFS<E> struct (src/neutron/nifs.rs): adds two new lookup-fold-
  gated hint fields, comm_L_fold_per_table and comm_ts_fold_per_table, each
  Option<Vec<Commitment<E>>>. Populated in prove_with_multi_table_lookup_inner
  by cloning U.comm_L / U.comm_ts from the off-circuit U1.fold_with_lookup
  output (the per-`j` independent fold body at relation.rs:862-884 per
  Corrigendum microsoft#6 primitive 3). Set to None on the non-multi-table prover paths
  (prove, prove_with_lookup, prove_with_multi_column_lookup) — those paths do
  not feed the augmented-circuit multi-table verifier.

- In-circuit AllocatedLookupNIFSMultiTable (src/neutron/circuit/lookup.rs):
  adds Vec<AllocatedNonnativePoint<E>> fields comm_L_fold_per_table and
  comm_ts_fold_per_table; AllocatedLookupNIFSMultiTable::alloc allocates them
  from the per-step NIFS message's prover-supplied Vec hints (point-at-infinity
  default under shape-derivation `nifs == None`).

- LookupVerifyOutputMultiTable (src/neutron/circuit/nifs.rs): widened to carry
  the per-table post-fold commitment hints. verify_with_multi_table_lookup
  clones the allocated hints from the AllocatedLookupNIFSMultiTable into the
  output — NO new constraint emission at this layer (pure passthrough);
  soundness via IVC hash-chain binding + envelope-side off-FS commitment-
  equality per Corrigendum microsoft#17 path (b).

- from_lookup_fold_output (src/neutron/circuit/relation.rs): widened signature
  accepts comm_L_fold_per_table / comm_ts_fold_per_table Vec hints and
  OVERRIDES the M.GH7.5.0a passthrough of u_fold.comm_L_per_table /
  comm_ts_per_table. This is the load-bearing path α.2 wiring — without the
  override, Unew.comm_L_per_table[j] would carry PRE-fold (passthrough) values
  while r_U.comm_L[j] carries POST-fold values, and the IVC hash-chain check
  would reject the honest fold.

- synthesize_non_base_case_lookup_fold (src/neutron/circuit/mod.rs): passes
  the new hint Vecs from lookup_output through to the widened
  from_lookup_fold_output. No prover-side wiring changes required — the
  data-flow is already in place via the NIFS message threading at
  prove_step_with_lookup_fold:976-988.

- Acceptance tests (src/neutron/circuit/relation.rs stage0_byte_equivalence_tests):
  (a) Extends m_gh5_0_stage0_absorb_in_ro_byte_equivalence_at_fold_depth_1_and_2
      with M.GH7.5.5 work-item 8 assertions: nifs1.comm_L_fold_per_table[j]
      and nifs2.comm_L_fold_per_table[j] MUST byte-equal folded_U_s1.comm_L[j]
      and folded_U_s2.comm_L[j] respectively (the hint-attachment locus
      sources from the POST-fold off-circuit U), plus the comm_ts variants
      plus a non-vacuity assertion that at least one entry is non-default
      (the k=2 multi-table fixture queries non-trivial table entries).
  (b) Adds m_gh7_5_0b_path_alpha2_from_lookup_fold_output_overrides_with_hint_vecs:
      directly synthesises from_lookup_fold_output with synthetic non-trivial
      hint Vecs distinct from the u_fold passthrough, then asserts (i) the
      result's comm_L_per_table x-limb-0 witness values match the HINTS
      (NOT the passthrough — falsifies a regression to M.GH7.5.0a behaviour);
      (ii) the in-circuit absorb_in_ro squeeze byte-equals the off-circuit
      absorb_in_ro2 squeeze on the same hint values.

pp_digest baseline: test_pp_digest at lookup_fold_k = 0 is byte-identical
(Vec-emptiness skip preserves the legacy R1CS-only shape); no expect refresh
required since no production test_pp_digest fixture exercises lookup_fold_k > 0.

Soundness anchors: parallel reasoning to vendor HEAD's existing comm_W_fold
discipline (circuit/nifs.rs:46-55 / :649-664). The rejection mechanism for
malicious post-fold per-table hints composes (i) IVC hash-chain divergence at
the next step's Phase-1 hash check OR at the off-circuit RecursiveSNARK::verify
hash reconstruction (M.GH7.5.0a-landed absorb extension + RO2 collision
resistance per Nova 2021/370 v3 §4); (ii) envelope-side off-FS commitment-
equality at CompressedSNARK::verify per Corrigendum microsoft#17 path (b), composed
with Spartan-close Pedersen+LogUp identity binding (Haböck 2022/1530 §3 +
Spartan 2019/550 §5). No new in-circuit constraint family is introduced; the
Gadget contract from .claude/rules/cryptography.md is satisfied by parallel
reasoning to the existing comm_W_fold discipline at vendor HEAD.

Test scope: cargo test --release -p nova-snark --features lookup-fold (scoped):
  - test_pp_digest                                              1 passed
  - neutron::circuit::tests                                     1 passed
  - neutron::circuit::relation::stage0_byte_equivalence_tests   6 passed
    (5 pre-existing M.GH7.5.0a + 1 new M.GH7.5.0b consumer test)
  - neutron::circuit::nifs                                      1 passed
  - neutron::circuit::lookup::tests                             4 passed
  - neutron::nifs::tests                                       17 passed
  - neutron::compressed_snark                                  10 passed
  - neutron::tests (incl. prove_step_with_lookup_fold IVC)      9 passed

Refs: docs/research/cryptography/microsoftgh-7-stage-k-compressed-snark-design-pin-2026-05-11.md
Corrigendum microsoft#19 §"Revised implementation roadmap under path α.2" (work-items
1-9 discharged; STOP-AND-ASK trigger #M.GH7.5.5 byte-equivalence test
explicitly authored at work-item 8).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
marcus-sa added a commit to marcus-sa/Nova that referenced this pull request May 15, 2026
…riment D-prime — diagnostic fixture for Obstruction 2 (n=1 hash-chain divergence locus); NOT a fix; PASSES as documented Obstruction 2 record

Appends `m_gh7_5_obstruction2_experiment_d_prime_n1_hash_chain_divergence_locus`
to `vendor/nova/src/neutron/compressed_snark.rs` at the test-module tail
sibling to Experiments A and B. Mirrors the verifier-side hash-chain
re-derive at `mod.rs:774-789` byte-for-byte AND a prover-equivalent
reconstruction of the augmented circuit's base-case output-hash absorb
sequence (`circuit/mod.rs:939-955` composed with `circuit/relation.rs:
502-570` on `default_with_lookup_k(cs, 1, k=1)` shape). Captures partial
squeezes after each absorb step on both sides via fresh `PoseidonRO`
re-absorb per prefix (since `PoseidonRO` is not `Clone`). Identifies the
first divergence index and routes to D1/D2/D3/D4 per Corrigendum microsoft#24.

Empirical close at vendor HEAD `b233301`: **D1 — (iv.γ) constant-shape-
vs-Option-shape absorb-sequence drift at base case.** First divergence
at prefix index 7, exactly at the `T_lookup` block: native verifier
side skips (`r_U.T_lookup = None`), prover-equivalent side absorbs one
zero scalar (mirroring `Unew_base.T_lookup_per_table = Some(vec![0; 1])`
per `default_with_lookup_k`). Partials 0..=6 byte-equal across native
and prover sides (`pp.digest`, `num_steps`/`i_new`, `z0`, `zi`/`z_next`,
`comm_W`, `comm_E`, `T`). From index 7 onward streams diverge by one
absorb-shift; final hash mismatch byte-for-byte matches the n=1
`Err(NovaError::ProofVerifyError { reason: "Invalid output hash in
R1CS instance" })` reported by Experiment A.

(iv.α) ruled OUT empirically: comm_W (idx=4) and comm_E (idx=5) partials
byte-equal across native and prover sides — the `comm_E = comm_W.clone()`
shared-variable circuit pattern does not produce an RO2-byte divergence
against the native `Commitment::default()`-distinct-handle pattern at
equal values.

(iv.β) ruled OUT empirically: ri / r_next absorb (last position) is
DOWNSTREAM of the (iv.γ) divergence — the divergence reaches that step
already-corrupted by the (iv.γ) one-absorb-shift, so ri-allocation
parity cannot be the root cause.

Routes Corrigendum microsoft#25 to Fix-α: extend `FoldedInstance::default` to
mirror constant-shape allocation under `lookup_fold_k > 0` (the
strictly-more-general fix that closes the asymmetry without breaking
Corrigendum microsoft#6's constant-shape FS-schedule discipline).

Test asserts the divergence EXISTS (preferred pattern from Corrigendum
microsoft#24 dispatch): PASSES at vendor HEAD `b233301` as a documented record
of Obstruction 2's n=1 hash-chain divergence locus. If a future
Corrigendum microsoft#25 fix-α lands and the divergence disappears, this fixture
FAILS to signal that the obstruction has been resolved — at which point
the fixture should be promoted into a positive single-step n=1
acceptance test (Corrigendum microsoft#23 layer 5b deferred deliverable).

Read-only against vendor body: no modification to `RecursiveSNARK::
verify`, `FoldedInstance::default`, `AllocatedFoldedInstance::default`,
`absorb_in_ro2`, or `absorb_in_ro`. Pure additive test fixture.

Test invocation (release-mode mandatory per `.claude/rules/testing.md`):

  cargo test --release -p nova-snark --features lookup-fold --lib \
    neutron::compressed_snark::tests::\
    m_gh7_5_obstruction2_experiment_d_prime_n1_hash_chain_divergence_locus \
    -- --nocapture

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants