Skip to content

Feat/fri early termination#729

Open
diegokingston wants to merge 17 commits into
mainfrom
feat/fri-early-termination
Open

Feat/fri early termination#729
diegokingston wants to merge 17 commits into
mainfrom
feat/fri-early-termination

Conversation

@diegokingston

@diegokingston diegokingston commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

FRI early termination — send final-polynomial coefficients

Motivation

The FRI commit phase folded the deep-composition codeword down to a single
constant (fri_last_value), so every fold cost a Merkle root plus one symmetric
opening per query. This PR terminates folding early instead: once the codeword
encodes a polynomial of degree < 2^k, the prover sends those 2^k
coefficients directly (the standard Plonky3 / ethSTARK approach). k is a new
option fri_final_poly_log_degree (default 7).

Trade-off: we add 2^k field elements to the proof once, but drop the last k
fold layers — k Merkle roots and k openings per query. For production
query counts this shrinks the proof and cuts commit-phase work, and clamps to a
no-op for traces too small to fold that far.

Description

  • Option: new ProofOptions.fri_final_poly_log_degree: u8 (default 7).
  • Proof format (breaking): StarkProof.fri_last_value
    fri_final_poly_coeffs: Vec<FieldElement<E>>.
  • New module fri/terminal.rs: pure helpers converting between a terminal
    codeword and its polynomial coefficients (coeffs_from_terminal_codeword
    prover-side, terminal_codeword_from_coeffs verifier-side).
  • Prover (fri/mod.rs, gpu_lde.rs): commit_phase_from_evaluations and
    the GPU mirror commit total_folds - 1 layers, do one final fold to the
    terminal codeword, and append its coefficients to the transcript.
  • Verifier (verifier.rs): a fri_termination_params helper centralizes the
    fold schedule (used by the Fiat-Shamir replay and step_3_verify_fri); the
    prover derives the identical schedule inline. step_3_verify_fri reconstructs the terminal
    codeword and, before the query loop, checks (1) committed-layer count,
    (2) fri_final_poly_coeffs.len() == 2^expected_k, (3) every per-query
    decommitment has exactly num_committed layers. verify_query_and_sym_openings
    compares each folded query against the reconstructed terminal codeword
    (no-fold / single-fold / general cases).
  • Fiat-Shamir: fri_final_poly_log_degree is absorbed into the statement and
    the coefficients replace the final value in the transcript; tags bumped
    STATEMENT_V2→V3, CONTINUATION_EPOCH_V1→V2.
  • Robustness: overflow-safe clamp so an out-of-range k can't
    divide-by-zero the prover (degrades to no early termination).
  • Cleanup: removed the dead number_layers param and unused
    Domain.root_order field.

Soundness

  • The verifier reconstructs the terminal codeword by FFT from exactly
    2^expected_k coefficients, so it is low-degree by construction and the
    blowup (rate) is preserved — per-query soundness is unchanged from folding to
    a constant.
  • The per-query decommitment vectors are untrusted and not bound in the
    transcript; check (3) pins their length before the fold loop, blocking empty
    (vacuous accept) or padded (skip terminal check) decommitments.

Bench results

Metric main PR Δ
Verify time (per-side) 4.977s 4.531s -8.96%
Proof size 256.86 MiB 228.31 MiB -11.12%

@diegokingston

Copy link
Copy Markdown
Collaborator Author

/bench

@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown

Benchmark — ethrex 20 transfers (median of 3)

Table parallelism: auto (cores / 3)

Metric main PR Δ
Peak heap 73337 MB 72428 MB -909 MB (-1.2%) ⚪
Prove time 40.250s 40.274s +0.024s (+0.1%) ⚪

✅ No significant change.

✅ Low variance (time: 1.1%, heap: 0.4%)

Commit: d07779f · Baseline: cached · Runner: self-hosted bench

@diegokingston

Copy link
Copy Markdown
Collaborator Author

/bench

@diegokingston diegokingston marked this pull request as ready for review June 29, 2026 15:28
FRI stops folding when the polynomial reaches degree < 2^k (k =
ProofOptions.fri_final_poly_log_degree, default 7) instead of folding to a
single constant. The prover sends the 2^k final-polynomial coefficients
(fri_final_poly_coeffs); the verifier reconstructs the terminal codeword via a
coset FFT (base-field twiddles) and checks each query against it, with a
structural degree-bound check and a clamp for tiny traces. k is bound into the
Fiat-Shamir statement (DOMAIN_TAG _V3).

Impact (blowup 2, 219 queries): ~225 KB smaller proof, constant across trace
size (~16% at 2^20, 25% at 2^16); verifier does ~6,132 fewer Keccak
compressions and ~1,840 fewer Fp3 muls per proof.

- Prover interpolates the terminal poly on the minimal 2^k sub-coset (no
  oversized iFFT, no zero-trim).
- GPU FRI commit (cuda) supports early termination, mirroring the CPU path;
  validated on a CUDA server (proof verifies, gpu_fri_calls fires).
- Soundness: tampered / over-length / under-length coeffs and cross-k all
  reject; terminal codeword<->coeffs roundtrip; single-fold + clamp cases.
@diegokingston diegokingston force-pushed the feat/fri-early-termination branch from c1627b1 to be46afb Compare June 29, 2026 15:31
diegokingston and others added 3 commits June 29, 2026 16:57
Resolve conflicts where main's continuations feature meets this branch's
FRI early-termination statement binding. Both sides changed
absorb_statement's signature, so the merged function takes both
kind: StatementKind (continuations) and fri_final_poly_log_degree
(this branch); all callers thread both.

- continuation.rs: epoch_transcript now binds opts.fri_final_poly_log_degree
  per epoch, matching the value the epoch's FRI actually folds to (read from
  air.options() by both prover and verifier).
- statement_tests.rs: thread fri_final_poly_log_degree through the test
  helpers and main's new continuation-epoch tests.
- cuda_path_integration.rs: keep both newly added GPU tests
  (gpu_fri_commit_produces_verifiable_proof and
  gpu_proof_verifies_row_pair_commitment).
- statement.rs: allow clippy::too_many_arguments on absorb_statement, now at
  8 args after both features each added one (CI runs clippy -D warnings).
@diegokingston

Copy link
Copy Markdown
Collaborator Author

/bench

@jotabulacios

Copy link
Copy Markdown
Collaborator

/bench-verify

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown

Verifier benchmark started on the bench server (~4 min). The bench server is occupied until it finishes.

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown

Verifier benchmark — b67f007594 vs main (20 pairs)

❌ Run failed. Last log lines:

   Compiling reqwest v0.12.28
   Compiling ark-bn254 v0.5.0
   Compiling ethrex-crypto v13.0.0 (https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6)
   Compiling ethrex-trie v13.0.0 (https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6)
   Compiling ethrex-common v13.0.0 (https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6)
   Compiling axum-extra v0.10.3
   Compiling ethrex-levm v13.0.0 (https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6)
   Compiling ethrex-l2-common v13.0.0 (https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6)
   Compiling ethrex-metrics v13.0.0 (https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6)
   Compiling ethrex-storage v13.0.0 (https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6)
   Compiling ethrex-storage-rollup v13.0.0 (https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6)
   Compiling ethrex-vm v13.0.0 (https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6)
   Compiling ethrex-blockchain v13.0.0 (https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6)
   Compiling ethrex-p2p v13.0.0 (https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6)
   Compiling ethrex-rpc v13.0.0 (https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6)
   Compiling ethrex-l2-rpc v13.0.0 (https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6)
   Compiling ethrex-fixtures v0.1.0 (/home/app/actions-runner/_work/lambda_vm/lambda_vm/tooling/ethrex-fixtures)
    Finished `release` profile [optimized] target(s) in 34.63s
wrote executor/tests/ethrex_bench_20.bin (32766 bytes): block #1 with 20/20 transfer(s) [N senders -> N recipients]
==> Cached binaries are for different refs/features; rebuilding.
==> Building both cli binaries in isolated worktree /tmp/verify_wt
Preparing worktree (detached HEAD 71a99f17)
==> Building cli @ 71a99f17cc -> cli_B  (features: jemalloc-stats)
==> Building cli @ b67f007594 -> cli_A  (features: jemalloc-stats)
==> Proving once with the baseline binary (both sides verify this same proof)
==> Running 20 interleaved pairs  (improvement: + = PR faster)
ERROR: could not parse 'Verification time' from cli output:
Reading ELF file...
Reading proof...
Failed to deserialize proof: io error: unexpected end of file

…poly_log_degree can't divide-by-zero the prover
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.

4 participants