Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
2690c66
Retire the main LDE after the Round 1 commit
jotabulacios Sep 15, 2026
89616b7
Trigger the LDE retire on memory pressure
jotabulacios Sep 15, 2026
a0d182e
Sort the dedup'd ops so trace builds repeat
jotabulacios Sep 15, 2026
9cbca77
Split the trace build into route and per-table
jotabulacios Sep 15, 2026
f8daaa5
Retire the traces too, rebuilding them per chunk
jotabulacios Sep 15, 2026
2ce57c4
Count what the memory modes actually cost
jotabulacios Sep 15, 2026
0745369
Let the executor resume from a saved VM state
jotabulacios Sep 15, 2026
b0066bc
Take HALT's presence from the AIR set
jotabulacios Sep 15, 2026
3db72b7
Walk the execution instead of being handed it
jotabulacios Sep 16, 2026
b9476c9
Close and commit a table while the run continues
jotabulacios Sep 16, 2026
47e1338
Share one derivation for the CPU-driven tables
jotabulacios Sep 16, 2026
0fef095
Stop closing SHIFT early: CPU32 still feeds it
jotabulacios Sep 16, 2026
237b688
Derive MUL and DVRM in the shared derivation
jotabulacios Sep 16, 2026
8d8cc5b
Keep one intermediate for the collected ops
jotabulacios Sep 16, 2026
8482352
Run the Commit phase: commit and drop as you walk
jotabulacios Sep 16, 2026
b5419ea
Pad and commit what the walk could not close
jotabulacios Sep 16, 2026
56b0b9d
Keep a retired chunk's BITWISE lookups
jotabulacios Sep 16, 2026
567eeae
Build the tables written once from the whole run
jotabulacios Sep 16, 2026
4ce864d
Count DECODE lookups by pc, not per cycle
jotabulacios Sep 16, 2026
f53347e
Finish the end-of-run tables: HALT, REGISTER, PAGE
jotabulacios Sep 16, 2026
dcdc2e3
Split a preprocessed table's Round 1 roots
jotabulacios Sep 16, 2026
ead5b0d
Carry what the statement needs out of the walk
jotabulacios Sep 16, 2026
d1e025d
Run the Commit phase to the end in one call
jotabulacios Sep 16, 2026
352dd0a
Sample one challenge for the whole execution
jotabulacios Sep 16, 2026
3efd331
Measure each trace-build path on its own
jotabulacios Sep 16, 2026
a6ab177
Walk the execution once per pass, not per phase
jotabulacios Sep 16, 2026
26281d6
Build the LogUp columns against the one challenge
jotabulacios Sep 16, 2026
e11422a
Measure Approach 1 one pass at a time
jotabulacios Sep 16, 2026
3b7a2c5
Fold rounds 2 and 3 into the LogUp pass
jotabulacios Sep 16, 2026
80c436c
Stop building a Merkle tree the pass throws away
jotabulacios Sep 16, 2026
2f9f93a
Work on several tables at a time in each pass
jotabulacios Sep 16, 2026
7f7a5d0
Prove each table through round 4, and verify it
jotabulacios Sep 16, 2026
11607ce
Report what one FRI per table costs
jotabulacios Sep 16, 2026
05d1257
Fold a group of tables into one FRI
jotabulacios Sep 16, 2026
dc1971e
Show where the proving pass spends its time
jotabulacios Sep 16, 2026
6917f6b
Bring the mixed-height MMCS and the batched FRI
jotabulacios Sep 17, 2026
3494ef7
Take the batched format back out for now
jotabulacios Sep 17, 2026
02e7681
Draw the batch coefficient from every table
jotabulacios Sep 17, 2026
57b5f12
Fold every table into one FRI per domain
jotabulacios Sep 17, 2026
bbe6aa6
Give each group the queries its members open at
jotabulacios Sep 17, 2026
053660a
Open a table at the indices its group decided
jotabulacios Sep 17, 2026
bf8adb8
Walk once more and open every table
jotabulacios Sep 17, 2026
e50a46e
Assemble what the five passes produce
jotabulacios Sep 17, 2026
91e177a
Weigh the batched proof against the per-table one
jotabulacios Sep 17, 2026
3527f51
Stop rebuilding roots the Commit phase already has
jotabulacios Sep 17, 2026
228ac2d
Open against the trees, instead of hashing them twice
jotabulacios Sep 17, 2026
2f505b9
Take the kept trees back out: measured, bad trade
jotabulacios Sep 17, 2026
43c2cbb
Fold each codeword as it appears, and drop it
jotabulacios Sep 17, 2026
26631c1
Derive the batched proof's challenges from the proof
jotabulacios Sep 17, 2026
c462b4f
Keep walking while the batch is proved
jotabulacios Sep 17, 2026
4147862
Measure the walk on its own
jotabulacios Sep 17, 2026
9abbdae
Keep large buffers warm in jemalloc
jotabulacios Sep 18, 2026
fdffe8d
Build the AIRs once for all the passes
jotabulacios Sep 18, 2026
cf2d51e
Take the resident commits off the critical path
jotabulacios Sep 18, 2026
fbba10a
Give LT and BITWISE what retired chunks owe them
jotabulacios Sep 18, 2026
1f6bae7
Keep the composition parts for the Open pass
jotabulacios Sep 18, 2026
43700b1
Batch the resident tables after the walk
jotabulacios Sep 18, 2026
ed06112
Verify the batched proof
jotabulacios Sep 18, 2026
b6ce5d3
Document the prove-and-retire prover
jotabulacios Sep 18, 2026
f800e4b
Name the trace-build flag after what it does
jotabulacios Sep 18, 2026
0d5abe2
Review fixes for prove-and-retire (#994) (#995)
MauroToscano Sep 18, 2026
f9d29f2
Merge main and adopt its empty-table rule
jotabulacios Sep 21, 2026
f960262
Verify a batched proof inside the VM
jotabulacios Sep 19, 2026
95bd62b
Reject a lying width when the proof is bytes
jotabulacios Sep 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 24 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,15 @@ RECURSION_VERIFIER_PRESETS := min blowup2 blowup4 blowup8
# `continuation` feature: verify a multi-epoch ContinuationProof bundle instead
# of a monolithic VmProof. Only the presets the benchmarks actually measure.
RECURSION_CONT_PRESETS := min blowup2 blowup4
# `batched` feature: verify a BatchedProof (one FRI per domain height) instead
# of a monolithic VmProof.
RECURSION_BATCHED_PRESETS := min blowup2
# `owned` feature: the monolithic proof, deserialized instead of read in place.
RECURSION_OWNED_PRESETS := blowup2
RECURSION_VERIFIER_ARTIFACTS := $(addprefix $(RECURSION_ARTIFACTS_DIR)/recursion-, $(addsuffix .elf, $(RECURSION_VERIFIER_PRESETS))) \
$(addprefix $(RECURSION_ARTIFACTS_DIR)/recursion-cont-, $(addsuffix .elf, $(RECURSION_CONT_PRESETS)))
$(addprefix $(RECURSION_ARTIFACTS_DIR)/recursion-cont-, $(addsuffix .elf, $(RECURSION_CONT_PRESETS))) \
$(addprefix $(RECURSION_ARTIFACTS_DIR)/recursion-batched-, $(addsuffix .elf, $(RECURSION_BATCHED_PRESETS))) \
$(addprefix $(RECURSION_ARTIFACTS_DIR)/recursion-owned-, $(addsuffix .elf, $(RECURSION_OWNED_PRESETS)))

# Override with: make ... SYSROOT_DIR=$HOME/.lambda-vm-sysroot
# to install the sysroot in a user-writable location and avoid sudo.
Expand Down Expand Up @@ -248,6 +255,22 @@ $(RECURSION_ARTIFACTS_DIR)/recursion-cont-$(1).elf: FORCE | prepare-sysroot $(RE
endef
$(foreach preset,$(RECURSION_CONT_PRESETS),$(eval $(call recursion_cont_verifier_rule,$(preset))))

# Batched variants: same crate, `batched` feature on top of the preset feature
# -> recursion-batched-<preset>-bench -> recursion-batched-<preset>.elf.
define recursion_batched_verifier_rule
$(RECURSION_ARTIFACTS_DIR)/recursion-batched-$(1).elf: FORCE | prepare-sysroot $(RECURSION_ARTIFACTS_DIR)
$$(call build_guest_elf,$$(RECURSION_GUESTS_DIR)/recursion,recursion-batched-$(1)-bench,--features "batched $(1)")
endef
$(foreach preset,$(RECURSION_BATCHED_PRESETS),$(eval $(call recursion_batched_verifier_rule,$(preset))))

# Owned variants: same crate and the same blob as the default guest, read via
# one rkyv deserialize instead of in place.
define recursion_owned_verifier_rule
$(RECURSION_ARTIFACTS_DIR)/recursion-owned-$(1).elf: FORCE | prepare-sysroot $(RECURSION_ARTIFACTS_DIR)
$$(call build_guest_elf,$$(RECURSION_GUESTS_DIR)/recursion,recursion-owned-$(1)-bench,--features "owned $(1)")
endef
$(foreach preset,$(RECURSION_OWNED_PRESETS),$(eval $(call recursion_owned_verifier_rule,$(preset))))

clean-asm:
-rm -rf $(ASM_ARTIFACTS_DIR)

Expand Down
24 changes: 24 additions & 0 deletions bench_vs/lambda/recursion/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ blowup8 = []
# memory-bounded inner prove) instead of a monolithic VmProof. Selects the
# `recursion-cont-<preset>-bench` bins below.
continuation = []
# Orthogonal to the presets: verify a BatchedProof (one FRI per domain height
# instead of one per table) instead of a monolithic VmProof. Selects the
# `recursion-batched-<preset>-bench` bins below. Mutually exclusive with
# `continuation` — main.rs guards it.
batched = []
# Orthogonal to the presets: verify the same monolithic VmProof as the default
# guest, but deserialized into owned values instead of read in place. Exists to
# price zero-copy against an owned copy in guest cycles.
owned = []

# One distinctly named binary per preset (selected by its feature) so a parallel
# `make -j` builds them to different filenames — structurally race-free, no cp
Expand Down Expand Up @@ -60,6 +69,21 @@ name = "recursion-cont-blowup4-bench"
path = "src/main.rs"
required-features = ["continuation", "blowup4"]

[[bin]]
name = "recursion-batched-min-bench"
path = "src/main.rs"
required-features = ["batched", "min"]

[[bin]]
name = "recursion-batched-blowup2-bench"
path = "src/main.rs"
required-features = ["batched", "blowup2"]

[[bin]]
name = "recursion-owned-blowup2-bench"
path = "src/main.rs"
required-features = ["owned", "blowup2"]

[dependencies]
lambda-vm-prover = { path = "../../../prover", default-features = false, features = [
"profile-markers",
Expand Down
18 changes: 17 additions & 1 deletion bench_vs/lambda/recursion/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ compile_error!("select exactly one of the `min`/`blowup2`/`blowup4`/`blowup8` fe
all(feature = "blowup4", feature = "blowup8"),
))]
compile_error!("select exactly one of the `min`/`blowup2`/`blowup4`/`blowup8` features");
#[cfg(any(
all(feature = "continuation", feature = "batched"),
all(feature = "continuation", feature = "owned"),
all(feature = "batched", feature = "owned"),
))]
compile_error!("`continuation`, `batched` and `owned` are mutually exclusive proof layouts");

/// The build preset fixing the inner `ProofOptions` (see the module docs).
#[cfg(feature = "min")]
Expand Down Expand Up @@ -88,7 +94,7 @@ pub fn main() -> ! {
// not self-enforcing here.
let options = PRESET.options();

#[cfg(not(feature = "continuation"))]
#[cfg(not(any(feature = "continuation", feature = "batched", feature = "owned")))]
let attestation = lambda_vm_prover::recursion::verify_and_attest_blob(blob, &options)
.expect("verify errored")
.expect("inner proof failed verification");
Expand All @@ -98,6 +104,16 @@ pub fn main() -> ! {
.expect("verify errored")
.expect("inner continuation proof failed verification");

#[cfg(feature = "batched")]
let attestation = lambda_vm_prover::recursion::verify_batched_and_attest(blob, &options)
.expect("verify errored")
.expect("inner batched proof failed verification");

#[cfg(feature = "owned")]
let attestation = lambda_vm_prover::recursion::verify_owned_and_attest(blob, &options)
.expect("verify errored")
.expect("inner proof failed verification");

lambda_vm_syscalls::syscalls::commit(&attestation);
lambda_vm_syscalls::syscalls::sys_halt();
}
13 changes: 11 additions & 2 deletions bin/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ edition = "2024"
license.workspace = true

[dependencies]
serde_cbor = "0.11"
executor = { path = "../../executor" }
prover = { path = "../../prover", package = "lambda-vm-prover" }
stark = { path = "../../crypto/stark" }
Expand All @@ -13,11 +14,19 @@ clap = { version = "4.3.10", features = ["derive"] }
rkyv = { version = "0.8.10", default-features = false, features = ["alloc", "bytecheck", "aligned", "pointer_width_64"] }
tempfile = "3"
tikv-jemallocator = "0.6"
tikv-jemalloc-ctl = { version = "0.6", features = ["stats"], optional = true }
# No `stats` by default: that feature propagates to tikv-jemalloc-sys and builds
# jemalloc with `--enable-stats`, i.e. counters on the malloc fast path, for every
# binary. `keep_large_buffers_warm` needs only `raw`/`mallctl`; the heap tracker is
# what needs the counters, and it is behind `jemalloc-stats`.
tikv-jemalloc-ctl = { version = "0.6" }
tikv-jemalloc-sys = "0.6"
env_logger = "0.11"
# Used by `keep_large_buffers_warm`, whose body is Linux-only — so a macOS build
# will not catch its absence.
log = "0.4"

[features]
jemalloc-stats = ["dep:tikv-jemalloc-ctl"]
jemalloc-stats = ["tikv-jemalloc-ctl/stats"]
disk-spill = ["prover/disk-spill"]
instruments = ["prover/instruments", "stark/instruments"]
# GPU profiling build (Nsight): CUDA prover + instruments spans + NVTX ranges.
Expand Down
102 changes: 102 additions & 0 deletions bin/cli/examples/cmp_proofs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//! Compare two `VmProof` files table by table: `cmp_proofs A.proof B.proof`.
//!
//! A diagnostic for the streaming prover: which table, and which part of it,
//! first departs from the monolithic prover's proof of the same execution.

use std::os::unix::fs::FileExt;

use prover::VmProof;

fn read(path: &str) -> VmProof {
let file = std::fs::File::open(path).expect("open");
let len = file.metadata().expect("metadata").len() as usize;
let mut buf = rkyv::util::AlignedVec::<16>::with_capacity(len);
buf.resize(len, 0);
file.read_exact_at(&mut buf, 0).expect("read");
rkyv::from_bytes::<VmProof, rkyv::rancor::Error>(&buf).expect("deserialize")
}

fn main() {
let args: Vec<String> = std::env::args().collect();
let (a, b) = (read(&args[1]), read(&args[2]));
println!(
"tables: {} vs {}",
a.proof.proofs.len(),
b.proof.proofs.len()
);
println!(
"table_counts equal: {}",
format!("{:?}", a.table_counts) == format!("{:?}", b.table_counts)
);
println!("counts A: {:?}", a.table_counts);
println!("counts B: {:?}", b.table_counts);
println!(
"runtime_page_ranges equal: {}",
format!("{:?}", a.runtime_page_ranges) == format!("{:?}", b.runtime_page_ranges)
);
println!(
"num_private_input_pages: {} vs {}",
a.num_private_input_pages, b.num_private_input_pages
);
println!(
"public_output equal: {}",
a.public_output == b.public_output
);
let mut shown = 0;
for (i, (x, y)) in a.proof.proofs.iter().zip(b.proof.proofs.iter()).enumerate() {
let mut diffs = Vec::new();
if x.trace_length != y.trace_length {
diffs.push(format!(
"trace_length {} vs {}",
x.trace_length, y.trace_length
));
}
if x.lde_trace_main_merkle_root != y.lde_trace_main_merkle_root {
diffs.push("main root".into());
}
if x.lde_trace_precomputed_merkle_root != y.lde_trace_precomputed_merkle_root {
diffs.push("precomputed root".into());
}
if x.lde_trace_aux_merkle_root != y.lde_trace_aux_merkle_root {
diffs.push("aux root".into());
}
if format!("{:?}", x.trace_ood_evaluations) != format!("{:?}", y.trace_ood_evaluations) {
diffs.push("ood".into());
}
if x.composition_poly_root != y.composition_poly_root {
diffs.push("composition root".into());
}
if x.fri_layers_merkle_roots != y.fri_layers_merkle_roots {
diffs.push("fri roots".into());
}
if x.fri_final_poly_coeffs != y.fri_final_poly_coeffs {
diffs.push("fri final".into());
}
if x.query_list.len() != y.query_list.len() {
diffs.push(format!(
"queries {} vs {}",
x.query_list.len(),
y.query_list.len()
));
}
if !diffs.is_empty() && shown < 40 {
println!("table {i}: {}", diffs.join(", "));
shown += 1;
}
}
println!("(showing at most 40 differing tables)");
let main_diff: Vec<usize> = a
.proof
.proofs
.iter()
.zip(b.proof.proofs.iter())
.enumerate()
.filter(|(_, (x, y))| x.lde_trace_main_merkle_root != y.lde_trace_main_merkle_root)
.map(|(i, _)| i)
.collect();
println!(
"tables whose MAIN root differs ({}): {:?}",
main_diff.len(),
main_diff
);
}
Loading
Loading