From e9affb5d8cc6fd25c2faca6de9020cde50a4910c Mon Sep 17 00:00:00 2001 From: Danil Lugovskoi Date: Wed, 6 May 2026 20:55:01 +0200 Subject: [PATCH 1/3] feat(host): GPU prover setup cache wired into GpuProverBuilder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add `GpuProverBuilder::with_setup_cache_path`. On first run the prover computes the per-level setups as before and dumps them to the given path; on later runs the cache is loaded with the upstream `UnrolledProver::new_with_cache` constructor and the multi-minute setup compute is skipped. The caller is responsible for keying the cache path on a binary fingerprint — stale caches surface as proving-time failures, not load errors. Bumps the pinned zksync-airbender rev to f1e26aa5 to pick up the new constructor. --- Cargo.lock | 89 ++++++------- Cargo.toml | 14 +-- crates/airbender-host/Cargo.toml | 1 + .../airbender-host/src/prover/gpu_prover.rs | 117 ++++++++++++++++-- 4 files changed, 161 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2162b8126..483e3fc3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,7 +5,7 @@ version = 4 [[package]] name = "add_sub_lui_auipc_mop" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "common_constants", "prover", @@ -17,7 +17,7 @@ dependencies = [ [[package]] name = "add_sub_lui_auipc_mop_verifier" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "field", "unroll", @@ -149,6 +149,7 @@ version = "0.1.0" dependencies = [ "airbender-codec", "airbender-core", + "bincode 2.0.1", "execution_utils", "gpu_prover", "riscv_transpiler", @@ -590,7 +591,7 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "bigint_with_control" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "common_constants", "prover", @@ -602,7 +603,7 @@ dependencies = [ [[package]] name = "bigint_with_control_verifier" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "field", "unroll", @@ -658,7 +659,7 @@ dependencies = [ [[package]] name = "blake2_with_compression" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "common_constants", "prover", @@ -670,7 +671,7 @@ dependencies = [ [[package]] name = "blake2_with_compression_verifier" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "field", "unroll", @@ -680,7 +681,7 @@ dependencies = [ [[package]] name = "blake2s_u32" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "common_constants", "unroll", @@ -877,7 +878,7 @@ checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "common_constants" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "seq-macro", ] @@ -1037,7 +1038,7 @@ dependencies = [ [[package]] name = "cs" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "bincode 1.3.3", "blake2s_u32", @@ -1285,7 +1286,7 @@ dependencies = [ [[package]] name = "execution_utils" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "clap", "full_statement_verifier", @@ -1350,7 +1351,7 @@ dependencies = [ [[package]] name = "fft" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "field", "seq-macro", @@ -1363,7 +1364,7 @@ dependencies = [ [[package]] name = "field" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "rand 0.9.4", "seq-macro", @@ -1413,7 +1414,7 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "full_statement_verifier" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "add_sub_lui_auipc_mop_verifier", "bigint_with_control_verifier", @@ -1521,7 +1522,7 @@ dependencies = [ [[package]] name = "gpu_prover" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "blake2s_u32", "cmake", @@ -1691,7 +1692,7 @@ dependencies = [ [[package]] name = "inits_and_teardowns" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "common_constants", "prover", @@ -1703,7 +1704,7 @@ dependencies = [ [[package]] name = "inits_and_teardowns_verifier" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "field", "unroll", @@ -1764,7 +1765,7 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jump_branch_slt" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "common_constants", "prover", @@ -1776,7 +1777,7 @@ dependencies = [ [[package]] name = "jump_branch_slt_verifier" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "field", "unroll", @@ -1807,7 +1808,7 @@ dependencies = [ [[package]] name = "keccak_special5" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "common_constants", "prover", @@ -1819,7 +1820,7 @@ dependencies = [ [[package]] name = "keccak_special5_verifier" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "field", "unroll", @@ -1874,7 +1875,7 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "load_store_subword_only" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "common_constants", "prover", @@ -1886,7 +1887,7 @@ dependencies = [ [[package]] name = "load_store_subword_only_verifier" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "field", "unroll", @@ -1896,7 +1897,7 @@ dependencies = [ [[package]] name = "load_store_word_only" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "common_constants", "prover", @@ -1908,7 +1909,7 @@ dependencies = [ [[package]] name = "load_store_word_only_verifier" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "field", "unroll", @@ -1967,7 +1968,7 @@ dependencies = [ [[package]] name = "mul_div" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "common_constants", "prover", @@ -1979,7 +1980,7 @@ dependencies = [ [[package]] name = "mul_div_unsigned" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "common_constants", "prover", @@ -1991,7 +1992,7 @@ dependencies = [ [[package]] name = "mul_div_unsigned_verifier" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "field", "unroll", @@ -2001,7 +2002,7 @@ dependencies = [ [[package]] name = "mul_div_verifier" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "field", "unroll", @@ -2011,7 +2012,7 @@ dependencies = [ [[package]] name = "non_determinism_source" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" [[package]] name = "ntapi" @@ -2317,7 +2318,7 @@ dependencies = [ [[package]] name = "prover" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "blake2s_u32", "common_constants", @@ -2342,7 +2343,7 @@ dependencies = [ [[package]] name = "prover_examples" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "bincode 1.3.3", "common_constants", @@ -2561,7 +2562,7 @@ checksum = "68b59d645e392e041ad18f5e529ed13242d8405c66bb192f59703ea2137017d0" [[package]] name = "riscv_common" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "common_constants", "seq-macro", @@ -2570,7 +2571,7 @@ dependencies = [ [[package]] name = "riscv_transpiler" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "addr2line", "blake2s_u32", @@ -2824,7 +2825,7 @@ dependencies = [ [[package]] name = "setups" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "add_sub_lui_auipc_mop", "bigint_with_control", @@ -2889,7 +2890,7 @@ checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77" [[package]] name = "shift_binary_csr" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "common_constants", "prover", @@ -2901,7 +2902,7 @@ dependencies = [ [[package]] name = "shift_binary_csr_verifier" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "field", "unroll", @@ -3183,7 +3184,7 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "trace_and_split" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "common_constants", "prover", @@ -3197,7 +3198,7 @@ dependencies = [ [[package]] name = "trace_holder" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "field", "worker", @@ -3267,7 +3268,7 @@ dependencies = [ [[package]] name = "transcript" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "blake2s_u32", "unroll", @@ -3362,7 +3363,7 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "unified_reduced_machine" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "common_constants", "prover", @@ -3374,7 +3375,7 @@ dependencies = [ [[package]] name = "unified_reduced_machine_verifier" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "field", "unroll", @@ -3412,7 +3413,7 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "verifier_common" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "blake2s_u32", "cs", @@ -3427,7 +3428,7 @@ dependencies = [ [[package]] name = "verifier_generator" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "proc-macro2", "prover", @@ -3798,7 +3799,7 @@ dependencies = [ [[package]] name = "worker" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-airbender?rev=73d69b5#73d69b5346b3c2350fa104a56ec4df78840cea99" +source = "git+https://github.com/matter-labs/zksync-airbender?rev=f1e26aa5#f1e26aa586430151c74d76cddc3599ed3a7a6212" dependencies = [ "num_cpus", "rayon", diff --git a/Cargo.toml b/Cargo.toml index e2a955b97..16330e0b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,8 +62,8 @@ insta = "1" tera = { version = "1", default-features = false } # Dependencies for airbender-crypto -common_constants = { git = "https://github.com/matter-labs/zksync-airbender", rev = "73d69b5", default-features = false } -blake2s_u32 = { git = "https://github.com/matter-labs/zksync-airbender", rev = "73d69b5", default-features = false } +common_constants = { git = "https://github.com/matter-labs/zksync-airbender", rev = "f1e26aa5", default-features = false } +blake2s_u32 = { git = "https://github.com/matter-labs/zksync-airbender", rev = "f1e26aa5", default-features = false } blake2 = { version = "0.10", default-features = false } sha2 = { version = "0.10", default-features = false } k256 = { version = "0.13", default-features = false } @@ -91,11 +91,11 @@ ark-test-curves = { version = "0.5", default-features = false } rand_core = { version = "0.6.4", default-features = false } # Airbender dependencies -riscv_common = { git = "https://github.com/matter-labs/zksync-airbender", rev = "73d69b5" } -execution_utils = { git = "https://github.com/matter-labs/zksync-airbender", rev = "73d69b5" } -gpu_prover = { git = "https://github.com/matter-labs/zksync-airbender", rev = "73d69b5" } -riscv_transpiler = { git = "https://github.com/matter-labs/zksync-airbender", rev = "73d69b5" } -verifier_common = { git = "https://github.com/matter-labs/zksync-airbender", rev = "73d69b5" } +riscv_common = { git = "https://github.com/matter-labs/zksync-airbender", rev = "f1e26aa5" } +execution_utils = { git = "https://github.com/matter-labs/zksync-airbender", rev = "f1e26aa5" } +gpu_prover = { git = "https://github.com/matter-labs/zksync-airbender", rev = "f1e26aa5" } +riscv_transpiler = { git = "https://github.com/matter-labs/zksync-airbender", rev = "f1e26aa5" } +verifier_common = { git = "https://github.com/matter-labs/zksync-airbender", rev = "f1e26aa5" } # These packages can require too much stack space to compile, # which can be increased with `RUST_MIN_STACK=16777216` environment variable. diff --git a/crates/airbender-host/Cargo.toml b/crates/airbender-host/Cargo.toml index f5b47884d..b5ce9a9cf 100644 --- a/crates/airbender-host/Cargo.toml +++ b/crates/airbender-host/Cargo.toml @@ -19,6 +19,7 @@ gpu-prover = ["dep:gpu_prover", "execution_utils/gpu_prover"] airbender-core = { path = "../airbender-core" } airbender-codec = { path = "../airbender-codec" } serde = { workspace = true, features = ["derive"] } +bincode = { workspace = true, features = ["serde", "std"] } execution_utils = { workspace = true } gpu_prover = { workspace = true, optional = true } riscv_transpiler = { workspace = true, features = ["jit", "flamegraph"] } diff --git a/crates/airbender-host/src/prover/gpu_prover.rs b/crates/airbender-host/src/prover/gpu_prover.rs index a01fb0f82..396eb54b1 100644 --- a/crates/airbender-host/src/prover/gpu_prover.rs +++ b/crates/airbender-host/src/prover/gpu_prover.rs @@ -4,7 +4,7 @@ use super::{ use crate::error::{HostError, Result}; use crate::proof::{Proof, RealProof}; use crate::security::SecurityLevel; -use execution_utils::unrolled_gpu::UnrolledProver; +use execution_utils::unrolled_gpu::{UnrolledProver, UnrolledProverCache}; use gpu_prover::execution::prover::ExecutionProverConfiguration; use riscv_transpiler::abstractions::non_determinism::QuasiUARTSource; use std::any::Any; @@ -12,6 +12,7 @@ use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{mpsc, Mutex}; use std::thread::JoinHandle; +use tracing::info; /// Builder for creating a configured cached GPU prover. pub struct GpuProverBuilder { @@ -19,6 +20,7 @@ pub struct GpuProverBuilder { worker_threads: Option, security: SecurityLevel, level: ProverLevel, + setup_cache_path: Option, } impl GpuProverBuilder { @@ -28,6 +30,7 @@ impl GpuProverBuilder { worker_threads: None, security: SecurityLevel::default(), level: ProverLevel::RecursionUnified, + setup_cache_path: None, } } @@ -53,12 +56,32 @@ impl GpuProverBuilder { self } + /// Cache the per-level setup data at `path`. On the first call the file is + /// missing, the prover computes the setup as usual and writes it; on later + /// calls the file is loaded and the compute is skipped. + /// + /// The caller is responsible for keying the path on whatever distinguishes + /// the underlying binaries (e.g. an app-binary hash). Stale cache files + /// produce proving-time failures, not load-time errors. + pub fn with_setup_cache_path(mut self, path: impl AsRef) -> Self { + self.setup_cache_path = Some(path.as_ref().to_path_buf()); + self + } + + pub fn maybe_setup_cache_path(self, path: Option) -> Self { + match path { + Some(p) => self.with_setup_cache_path(p), + None => self, + } + } + pub fn build(self) -> Result { GpuProver::new( &self.app_bin_path, self.worker_threads, self.security, self.level, + self.setup_cache_path, ) } } @@ -94,6 +117,7 @@ impl GpuProver { worker_threads: Option, security: SecurityLevel, level: ProverLevel, + setup_cache_path: Option, ) -> Result { if matches!(worker_threads, Some(0)) { return Err(HostError::Prover( @@ -102,8 +126,13 @@ impl GpuProver { } let app_bin_path = resolve_app_bin_path(app_bin_path)?; - let (command_tx, worker_handle) = - spawn_worker(app_bin_path, worker_threads, security, level)?; + let (command_tx, worker_handle) = spawn_worker( + app_bin_path, + worker_threads, + security, + level, + setup_cache_path, + )?; Ok(Self { command_tx, @@ -189,6 +218,7 @@ fn spawn_worker( worker_threads: Option, security: SecurityLevel, level: ProverLevel, + setup_cache_path: Option, ) -> Result<(mpsc::Sender, JoinHandle<()>)> { let (command_tx, command_rx) = mpsc::channel(); let (init_tx, init_rx) = mpsc::channel(); @@ -203,6 +233,7 @@ fn spawn_worker( worker_threads, security, level, + setup_cache_path, ) }) .map_err(|err| { @@ -235,6 +266,7 @@ fn gpu_worker_loop( worker_threads: Option, security: SecurityLevel, level: ProverLevel, + setup_cache_path: Option, ) { // Keep all prover state inside this dedicated thread so a panic does not unwind // through host-call boundaries or require `AssertUnwindSafe`. @@ -243,6 +275,7 @@ fn gpu_worker_loop( worker_threads, security, level.as_unrolled_level(), + setup_cache_path.as_deref(), ) { Ok(prover) => prover, Err(err) => { @@ -294,6 +327,7 @@ fn create_unrolled_prover( worker_threads: Option, security: SecurityLevel, level: execution_utils::unrolled_gpu::UnrolledProverLevel, + setup_cache_path: Option<&Path>, ) -> Result { let base_path = base_path(app_bin_path)?; let mut configuration = ExecutionProverConfiguration::default(); @@ -301,10 +335,75 @@ fn create_unrolled_prover( configuration.max_thread_pool_threads = Some(threads); configuration.replay_worker_threads_count = threads; } - Ok(UnrolledProver::new( - security.into(), - &base_path, - configuration, - level, - )) + + let Some(cache_path) = setup_cache_path else { + return Ok(UnrolledProver::new( + security.into(), + &base_path, + configuration, + level, + )); + }; + + if cache_path.exists() { + info!(path = %cache_path.display(), "Loading GPU prover setup cache"); + let bytes = std::fs::read(cache_path).map_err(|err| { + HostError::Prover(format!( + "failed to read setup cache {}: {err}", + cache_path.display() + )) + })?; + let (cache, decoded_len): (UnrolledProverCache, usize) = + bincode::serde::decode_from_slice(&bytes, bincode::config::standard()).map_err( + |err| { + HostError::Prover(format!( + "failed to decode setup cache {}: {err}", + cache_path.display() + )) + }, + )?; + if decoded_len != bytes.len() { + return Err(HostError::Prover(format!( + "setup cache {} has trailing bytes", + cache_path.display() + ))); + } + return UnrolledProver::new_with_cache( + security.into(), + &base_path, + configuration, + level, + &cache, + ) + .map_err(|err| { + HostError::Prover(format!( + "setup cache {} is incompatible: {err}", + cache_path.display() + )) + }); + } + + info!(path = %cache_path.display(), "Setup cache missing, computing and saving"); + let prover = UnrolledProver::new(security.into(), &base_path, configuration, level); + let cache = prover.dump_cache(); + let encoded = bincode::serde::encode_to_vec(&cache, bincode::config::standard()) + .map_err(|err| HostError::Prover(format!("failed to encode setup cache: {err}")))?; + if let Some(parent) = cache_path.parent() { + if !parent.as_os_str().is_empty() { + std::fs::create_dir_all(parent).map_err(|err| { + HostError::Prover(format!( + "failed to create setup cache directory {}: {err}", + parent.display() + )) + })?; + } + } + std::fs::write(cache_path, &encoded).map_err(|err| { + HostError::Prover(format!( + "failed to write setup cache {}: {err}", + cache_path.display() + )) + })?; + info!(path = %cache_path.display(), bytes = encoded.len(), "Wrote GPU prover setup cache"); + Ok(prover) } From 8e979511df8d1fa384bfefc269731e1addadf77c Mon Sep 17 00:00:00 2001 From: Danil Lugovskoi Date: Thu, 7 May 2026 09:39:55 +0200 Subject: [PATCH 2/3] Error for load cache Signed-off-by: Danil Lugovskoi --- crates/airbender-host/src/error.rs | 4 + .../airbender-host/src/prover/gpu_prover.rs | 97 ++++++++----------- 2 files changed, 43 insertions(+), 58 deletions(-) diff --git a/crates/airbender-host/src/error.rs b/crates/airbender-host/src/error.rs index 8114f793b..03fd30d37 100644 --- a/crates/airbender-host/src/error.rs +++ b/crates/airbender-host/src/error.rs @@ -16,6 +16,10 @@ pub enum HostError { Prover(String), #[error("verification error: {0}")] Verification(String), + #[error("setup cache not found in path: {0}")] + SetupCacheNotFound(String), + + } pub type Result = std::result::Result; diff --git a/crates/airbender-host/src/prover/gpu_prover.rs b/crates/airbender-host/src/prover/gpu_prover.rs index 396eb54b1..e55cb3e42 100644 --- a/crates/airbender-host/src/prover/gpu_prover.rs +++ b/crates/airbender-host/src/prover/gpu_prover.rs @@ -8,11 +8,10 @@ use execution_utils::unrolled_gpu::{UnrolledProver, UnrolledProverCache}; use gpu_prover::execution::prover::ExecutionProverConfiguration; use riscv_transpiler::abstractions::non_determinism::QuasiUARTSource; use std::any::Any; -use std::path::{Path, PathBuf}; +use std::path::{self, Path, PathBuf}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{mpsc, Mutex}; use std::thread::JoinHandle; -use tracing::info; /// Builder for creating a configured cached GPU prover. pub struct GpuProverBuilder { @@ -345,65 +344,47 @@ fn create_unrolled_prover( )); }; - if cache_path.exists() { - info!(path = %cache_path.display(), "Loading GPU prover setup cache"); - let bytes = std::fs::read(cache_path).map_err(|err| { - HostError::Prover(format!( - "failed to read setup cache {}: {err}", - cache_path.display() - )) - })?; - let (cache, decoded_len): (UnrolledProverCache, usize) = - bincode::serde::decode_from_slice(&bytes, bincode::config::standard()).map_err( - |err| { - HostError::Prover(format!( - "failed to decode setup cache {}: {err}", - cache_path.display() - )) - }, - )?; - if decoded_len != bytes.len() { - return Err(HostError::Prover(format!( - "setup cache {} has trailing bytes", - cache_path.display() - ))); - } - return UnrolledProver::new_with_cache( - security.into(), - &base_path, - configuration, - level, - &cache, - ) - .map_err(|err| { - HostError::Prover(format!( - "setup cache {} is incompatible: {err}", - cache_path.display() - )) - }); - } + let cache = load_cache(cache_path)?; + return UnrolledProver::new_with_cache( + security.into(), + &base_path, + configuration, + level, + &cache, + ) + .map_err(|err| { + HostError::Prover(format!( + "setup cache {} is incompatible: {err}", + cache_path.display() + )) + }); +} - info!(path = %cache_path.display(), "Setup cache missing, computing and saving"); - let prover = UnrolledProver::new(security.into(), &base_path, configuration, level); - let cache = prover.dump_cache(); - let encoded = bincode::serde::encode_to_vec(&cache, bincode::config::standard()) - .map_err(|err| HostError::Prover(format!("failed to encode setup cache: {err}")))?; - if let Some(parent) = cache_path.parent() { - if !parent.as_os_str().is_empty() { - std::fs::create_dir_all(parent).map_err(|err| { - HostError::Prover(format!( - "failed to create setup cache directory {}: {err}", - parent.display() - )) - })?; - } +fn load_cache(path: &Path) -> Result { + if !path.exists() { + return Err(HostError::SetupCacheNotFound(path.display().to_string())); } - std::fs::write(cache_path, &encoded).map_err(|err| { + + let bytes = std::fs::read(path).map_err(|err| { HostError::Prover(format!( - "failed to write setup cache {}: {err}", - cache_path.display() + "failed to read setup cache {}: {err}", + path.display() )) })?; - info!(path = %cache_path.display(), bytes = encoded.len(), "Wrote GPU prover setup cache"); - Ok(prover) + + let (cache, decoded_len): (UnrolledProverCache, usize) = + bincode::serde::decode_from_slice(&bytes, bincode::config::standard()).map_err(|err| { + HostError::Prover(format!( + "failed to decode setup cache {}: {err}", + path.display() + )) + })?; + if decoded_len != bytes.len() { + return Err(HostError::Prover(format!( + "setup cache {} has trailing bytes", + path.display() + ))); + } + + Ok(cache) } From 8d6866a0cff27c9ad6fcad1096036c1bc35e540b Mon Sep 17 00:00:00 2001 From: Danil Lugovskoi Date: Thu, 7 May 2026 11:20:50 +0200 Subject: [PATCH 3/3] feat(host): VK caching for the real verifier + setup-cache-only loading - Add `RealVerifierBuilder::with_vk_cache_path` so callers can persist the multi-minute VK derivation across process restarts (mirrors the GPU prover's setup cache). - Add `RealVerifierBuilder::with_setup_cache_path` plus `vk::unified_vk_from_setup_cache` so a verifier can derive its `RecursionUnified` VK directly from a pre-existing prover setup cache, no compute. Cuts the verifier startup to a file read. - Switch `create_unrolled_prover` to a load-only semantic: when a setup cache path is provided, the file MUST exist; missing-file is now surfaced as `HostError::SetupCacheNotFound` instead of being silently re-computed and written. Server deployments expect caches to be pre-generated. - Add the inverse `From for SecurityLevel` conversion that the new VK derivation needs. Updates the docs on `with_setup_cache_path` to reflect the new contract. --- crates/airbender-host/src/error.rs | 2 - .../airbender-host/src/prover/gpu_prover.rs | 11 +- crates/airbender-host/src/security.rs | 9 + crates/airbender-host/src/verifier.rs | 176 +++++++++++++++++- crates/airbender-host/src/vk.rs | 25 +++ 5 files changed, 212 insertions(+), 11 deletions(-) diff --git a/crates/airbender-host/src/error.rs b/crates/airbender-host/src/error.rs index 03fd30d37..2e18a85cd 100644 --- a/crates/airbender-host/src/error.rs +++ b/crates/airbender-host/src/error.rs @@ -18,8 +18,6 @@ pub enum HostError { Verification(String), #[error("setup cache not found in path: {0}")] SetupCacheNotFound(String), - - } pub type Result = std::result::Result; diff --git a/crates/airbender-host/src/prover/gpu_prover.rs b/crates/airbender-host/src/prover/gpu_prover.rs index e55cb3e42..2190c8789 100644 --- a/crates/airbender-host/src/prover/gpu_prover.rs +++ b/crates/airbender-host/src/prover/gpu_prover.rs @@ -55,9 +55,10 @@ impl GpuProverBuilder { self } - /// Cache the per-level setup data at `path`. On the first call the file is - /// missing, the prover computes the setup as usual and writes it; on later - /// calls the file is loaded and the compute is skipped. + /// Load pre-computed per-level setup data from `path` instead of running + /// the multi-minute compute. The file must exist (built earlier with a + /// dedicated generator that calls `UnrolledProver::dump_cache`); a missing + /// file at `build` time produces `HostError::SetupCacheNotFound`. /// /// The caller is responsible for keying the path on whatever distinguishes /// the underlying binaries (e.g. an app-binary hash). Stale cache files @@ -345,7 +346,7 @@ fn create_unrolled_prover( }; let cache = load_cache(cache_path)?; - return UnrolledProver::new_with_cache( + UnrolledProver::new_with_cache( security.into(), &base_path, configuration, @@ -357,7 +358,7 @@ fn create_unrolled_prover( "setup cache {} is incompatible: {err}", cache_path.display() )) - }); + }) } fn load_cache(path: &Path) -> Result { diff --git a/crates/airbender-host/src/security.rs b/crates/airbender-host/src/security.rs index 5a80bb19f..0c45604e7 100644 --- a/crates/airbender-host/src/security.rs +++ b/crates/airbender-host/src/security.rs @@ -33,6 +33,15 @@ impl From for verifier_common::SecurityModel { } } +impl From for SecurityLevel { + fn from(model: verifier_common::SecurityModel) -> Self { + match model { + verifier_common::SecurityModel::Security80 => SecurityLevel::Bits80, + verifier_common::SecurityModel::Security100 => SecurityLevel::Bits100, + } + } +} + impl std::fmt::Display for SecurityLevel { fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(formatter, "{}", self.bits()) diff --git a/crates/airbender-host/src/verifier.rs b/crates/airbender-host/src/verifier.rs index 926630718..e133b9881 100644 --- a/crates/airbender-host/src/verifier.rs +++ b/crates/airbender-host/src/verifier.rs @@ -3,11 +3,13 @@ use crate::proof::{hash_app_bin, hash_input_words, Proof, RealProof}; use crate::prover::ProverLevel; use crate::security::SecurityLevel; use crate::vk::{ - compute_unified_vk, compute_unrolled_vk, verify_proof, verify_unrolled_proof, UnifiedVk, - UnrolledVk, + compute_unified_vk, compute_unrolled_vk, unified_vk_from_setup_cache, verify_proof, + verify_unrolled_proof, UnifiedVk, UnrolledVk, }; use airbender_core::guest::Commit; +use execution_utils::unrolled_gpu::UnrolledProverCache; use std::path::{Path, PathBuf}; +use tracing::info; /// Wrapper around all verification-key flavors. #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] @@ -121,6 +123,8 @@ impl DevVerifierBuilder { pub struct RealVerifierBuilder { app_bin_path: PathBuf, level: ProverLevel, + vk_cache_path: Option, + setup_cache_path: Option, } impl RealVerifierBuilder { @@ -128,11 +132,50 @@ impl RealVerifierBuilder { Self { app_bin_path: app_bin_path.as_ref().to_path_buf(), level, + vk_cache_path: None, + setup_cache_path: None, + } + } + + /// Cache the verification key at `path`. The first `generate_vk` call writes + /// the freshly-computed VK; later calls load it back, skipping the + /// multi-minute compute. The caller is responsible for keying the path on + /// the binary fingerprint — stale caches are not detected. + pub fn with_vk_cache_path(mut self, path: impl AsRef) -> Self { + self.vk_cache_path = Some(path.as_ref().to_path_buf()); + self + } + + pub fn maybe_vk_cache_path(self, path: Option) -> Self { + match path { + Some(p) => self.with_vk_cache_path(p), + None => self, + } + } + + /// Reuse the GPU prover's setup cache (the same file written by + /// `GpuProverBuilder::with_setup_cache_path`) to derive the VK without + /// running compute. Only meaningful for `ProverLevel::RecursionUnified`; + /// other levels fall back to either a VK cache (if set) or a fresh compute. + pub fn with_setup_cache_path(mut self, path: impl AsRef) -> Self { + self.setup_cache_path = Some(path.as_ref().to_path_buf()); + self + } + + pub fn maybe_setup_cache_path(self, path: Option) -> Self { + match path { + Some(p) => self.with_setup_cache_path(p), + None => self, } } pub fn build(self) -> Result { - RealVerifier::new(&self.app_bin_path, self.level) + RealVerifier::new( + &self.app_bin_path, + self.level, + self.vk_cache_path, + self.setup_cache_path, + ) } } @@ -228,20 +271,81 @@ pub struct RealVerifier { app_bin_path: PathBuf, app_bin_hash: [u8; 32], level: ProverLevel, + vk_cache_path: Option, + setup_cache_path: Option, } impl RealVerifier { - fn new(app_bin_path: &Path, level: ProverLevel) -> Result { + fn new( + app_bin_path: &Path, + level: ProverLevel, + vk_cache_path: Option, + setup_cache_path: Option, + ) -> Result { let app_bin_path = resolve_app_bin_path(app_bin_path)?; let app_bin_hash = hash_app_bin(&app_bin_path)?; Ok(Self { app_bin_path, app_bin_hash, level, + vk_cache_path, + setup_cache_path, }) } pub fn generate_vk(&self, security: SecurityLevel) -> Result { + // 1. If a VK cache file exists, use it. + if let Some(path) = self.vk_cache_path.as_deref() { + if path.exists() { + let vk = load_vk_from_cache(path)?; + if vk.security() != security { + return Err(HostError::Verification(format!( + "verification key cache {} was built for {} bits, requested {} bits", + path.display(), + vk.security(), + security + ))); + } + info!(path = %path.display(), "Loaded verification key from cache"); + return Ok(vk); + } + } + + // 2. If a setup cache exists and we're at the unified level, derive + // the VK from it for free instead of recomputing from scratch. + if self.level == ProverLevel::RecursionUnified { + if let Some(path) = self.setup_cache_path.as_deref() { + if path.exists() { + let cache = load_setup_cache(path)?; + let unified = unified_vk_from_setup_cache(&cache, self.app_bin_hash, security) + .ok_or_else(|| { + HostError::Verification(format!( + "setup cache {} does not contain the unified-recursion level needed to derive a VK", + path.display() + )) + })?; + let vk = + VerificationKey::RealUnified(RealUnifiedVerificationKey { vk: unified }); + info!(path = %path.display(), "Derived verification key from prover setup cache"); + if let Some(vk_path) = self.vk_cache_path.as_deref() { + save_vk_to_cache(&vk, vk_path)?; + info!(path = %vk_path.display(), "Wrote derived VK to verification key cache"); + } + return Ok(vk); + } + } + } + + // 3. Fallback: compute from scratch. + let vk = self.compute_vk(security)?; + if let Some(path) = self.vk_cache_path.as_deref() { + save_vk_to_cache(&vk, path)?; + info!(path = %path.display(), "Wrote freshly-computed VK to verification key cache"); + } + Ok(vk) + } + + fn compute_vk(&self, security: SecurityLevel) -> Result { match self.level { ProverLevel::RecursionUnified => { let vk = compute_unified_vk(&self.app_bin_path, security)?; @@ -260,6 +364,70 @@ impl RealVerifier { } } +fn load_vk_from_cache(path: &Path) -> Result { + let bytes = std::fs::read(path).map_err(|err| { + HostError::Verification(format!("failed to read VK cache {}: {err}", path.display())) + })?; + let (vk, decoded_len): (VerificationKey, usize) = + bincode::serde::decode_from_slice(&bytes, bincode::config::standard()).map_err(|err| { + HostError::Verification(format!( + "failed to decode VK cache {}: {err}", + path.display() + )) + })?; + if decoded_len != bytes.len() { + return Err(HostError::Verification(format!( + "VK cache {} has trailing bytes", + path.display() + ))); + } + Ok(vk) +} + +fn save_vk_to_cache(vk: &VerificationKey, path: &Path) -> Result<()> { + let encoded = bincode::serde::encode_to_vec(vk, bincode::config::standard()) + .map_err(|err| HostError::Verification(format!("failed to encode VK: {err}")))?; + if let Some(parent) = path.parent() { + if !parent.as_os_str().is_empty() { + std::fs::create_dir_all(parent).map_err(|err| { + HostError::Verification(format!( + "failed to create VK cache directory {}: {err}", + parent.display() + )) + })?; + } + } + std::fs::write(path, &encoded).map_err(|err| { + HostError::Verification(format!( + "failed to write VK cache {}: {err}", + path.display() + )) + }) +} + +fn load_setup_cache(path: &Path) -> Result { + let bytes = std::fs::read(path).map_err(|err| { + HostError::Verification(format!( + "failed to read setup cache {}: {err}", + path.display() + )) + })?; + let (cache, decoded_len): (UnrolledProverCache, usize) = + bincode::serde::decode_from_slice(&bytes, bincode::config::standard()).map_err(|err| { + HostError::Verification(format!( + "failed to decode setup cache {}: {err}", + path.display() + )) + })?; + if decoded_len != bytes.len() { + return Err(HostError::Verification(format!( + "setup cache {} has trailing bytes", + path.display() + ))); + } + Ok(cache) +} + impl Verifier for RealVerifier { fn generate_vk(&self, security: SecurityLevel) -> Result { RealVerifier::generate_vk(self, security) diff --git a/crates/airbender-host/src/vk.rs b/crates/airbender-host/src/vk.rs index eab5306b0..655838167 100644 --- a/crates/airbender-host/src/vk.rs +++ b/crates/airbender-host/src/vk.rs @@ -8,6 +8,7 @@ use execution_utils::unrolled::{ compute_setup_for_machine_configuration, get_unrolled_circuits_artifacts_for_machine_type, verify_unrolled_layer_proof, UnrolledProgramProof, UnrolledProgramSetup, }; +use execution_utils::unrolled_gpu::{UnrolledProverCache, UnrolledProverLevel}; use riscv_transpiler::cycle::{ IMStandardIsaConfigWithUnsignedMulDiv, IWithoutByteAccessIsaConfigWithDelegation, }; @@ -33,6 +34,30 @@ pub struct UnrolledVk { pub compiled_layouts: setups::CompiledCircuitsSet, } +/// Build a `UnifiedVk` directly from a prover setup cache, no compute. +/// +/// Returns `None` when the cache does not contain the unified-recursion level +/// (e.g. the cache was produced for a `Base`-only or `RecursionUnrolled`-only +/// prover). The caller supplies `app_bin_hash` separately because the setup +/// cache does not carry it. +pub fn unified_vk_from_setup_cache( + cache: &UnrolledProverCache, + app_bin_hash: [u8; 32], + security: SecurityLevel, +) -> Option { + let level_setup = cache.levels.get(&UnrolledProverLevel::RecursionUnified)?; + let cached_security: SecurityLevel = cache.security.into(); + if cached_security != security { + return None; + } + Some(UnifiedVk { + security, + app_bin_hash, + unified_setup: level_setup.setup.clone(), + unified_layouts: level_setup.compiled_layouts.clone(), + }) +} + pub fn compute_unified_vk(app_bin_path: &Path, security: SecurityLevel) -> Result { #[cfg(not(feature = "gpu-prover"))] {