From 95487e20992b77c59c01e15f5e7d1ccbb5362ce4 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Wed, 8 Jul 2026 17:03:23 -0300 Subject: [PATCH 01/16] perf(verifier): verify STARK proofs in place via rkyv crypto/stark: StarkProof, MultiProof, FriDecommitment, BusPublicInputs, Table, PolynomialOpenings, and DeepPolynomialOpening dual-derive rkyv alongside serde. The verifier operates over a StarkProofView (owned or an rkyv-archived buffer read in place); multi_verify and multi_verify_archived both build the matching view and share one verification implementation, so neither the owned nor the archived path pays a serialization cost. prover: the recursion guest verifies its inner proof straight from the mmapped private-input region via a 12-byte aligning magic/version prefix, with no deserialization pass. Continuation and CLI verification go through the same proof-view path. executor/syscalls: get_private_input_slice() borrows the private input in place instead of copying it into a Vec. bin/cli: proof file persistence uses rkyv instead of bincode. tooling/ethrex-tests: ethrex's guest tests move into their own detached workspace, since ethrex pulls in rkyv's "unaligned" feature which cannot coexist with the root workspace's "aligned" feature in one resolved dependency graph. --- Cargo.lock | 1588 +---------- bench_vs/lambda/recursion/Cargo.lock | 266 +- bench_vs/lambda/recursion/Cargo.toml | 2 +- bench_vs/lambda/recursion/src/main.rs | 46 +- bin/cli/Cargo.toml | 2 +- bin/cli/src/main.rs | 12 +- crypto/crypto/Cargo.toml | 8 +- crypto/crypto/src/merkle_tree/proof.rs | 48 +- crypto/math/Cargo.toml | 9 + crypto/math/src/field/element.rs | 145 + crypto/stark/Cargo.toml | 4 +- .../src/examples/fibonacci_2_cols_shifted.rs | 2 +- .../src/examples/fibonacci_multi_column.rs | 2 +- crypto/stark/src/examples/fibonacci_rap.rs | 2 +- crypto/stark/src/examples/quadratic_air.rs | 2 +- crypto/stark/src/examples/read_only_memory.rs | 2 +- .../src/examples/read_only_memory_logup.rs | 2 +- crypto/stark/src/examples/simple_addition.rs | 2 +- crypto/stark/src/examples/simple_fibonacci.rs | 2 +- crypto/stark/src/fri/fri_decommit.rs | 4 +- crypto/stark/src/lookup.rs | 31 +- crypto/stark/src/proof/mod.rs | 1 + crypto/stark/src/proof/options.rs | 4 +- crypto/stark/src/proof/stark.rs | 16 +- crypto/stark/src/proof/view.rs | 436 +++ crypto/stark/src/table.rs | 172 +- crypto/stark/src/verifier.rs | 465 ++-- executor/Cargo.toml | 7 - executor/programs/rust/ef_io_demo/Cargo.lock | 331 +++ executor/tests/README.md | 4 + executor/tests/rust.rs | 66 - prover/Cargo.toml | 5 +- prover/src/continuation.rs | 22 +- prover/src/lib.rs | 347 ++- prover/src/recursion.rs | 68 +- prover/src/tests/recursion_smoke_test.rs | 54 +- syscalls/src/syscalls.rs | 21 + tooling/ethrex-tests/Cargo.lock | 2415 +++++++++++++++++ tooling/ethrex-tests/Cargo.toml | 24 + tooling/ethrex-tests/tests/ethrex.rs | 88 + 40 files changed, 4609 insertions(+), 2118 deletions(-) create mode 100644 crypto/stark/src/proof/view.rs create mode 100644 executor/programs/rust/ef_io_demo/Cargo.lock create mode 100644 tooling/ethrex-tests/Cargo.lock create mode 100644 tooling/ethrex-tests/Cargo.toml create mode 100644 tooling/ethrex-tests/tests/ethrex.rs diff --git a/Cargo.lock b/Cargo.lock index ced6a78b9..2250369ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,18 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "ahash" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - [[package]] name = "aho-corasick" version = "1.1.4" @@ -23,21 +11,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "anes" version = "0.1.6" @@ -94,151 +67,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "anyhow" -version = "1.0.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" - -[[package]] -name = "ark-bn254" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-std", -] - -[[package]] -name = "ark-ec" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" -dependencies = [ - "ahash", - "ark-ff", - "ark-poly", - "ark-serialize", - "ark-std", - "educe", - "fnv", - "hashbrown 0.15.5", - "itertools 0.13.0", - "num-bigint", - "num-integer", - "num-traits", - "zeroize", -] - -[[package]] -name = "ark-ff" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" -dependencies = [ - "ark-ff-asm", - "ark-ff-macros", - "ark-serialize", - "ark-std", - "arrayvec", - "digest", - "educe", - "itertools 0.13.0", - "num-bigint", - "num-traits", - "paste", - "zeroize", -] - -[[package]] -name = "ark-ff-asm" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" -dependencies = [ - "quote", - "syn 2.0.111", -] - -[[package]] -name = "ark-ff-macros" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" -dependencies = [ - "num-bigint", - "num-traits", - "proc-macro2", - "quote", - "syn 2.0.111", -] - -[[package]] -name = "ark-poly" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" -dependencies = [ - "ahash", - "ark-ff", - "ark-serialize", - "ark-std", - "educe", - "fnv", - "hashbrown 0.15.5", -] - -[[package]] -name = "ark-serialize" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" -dependencies = [ - "ark-serialize-derive", - "ark-std", - "arrayvec", - "digest", - "num-bigint", -] - -[[package]] -name = "ark-serialize-derive" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - -[[package]] -name = "ark-std" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" -dependencies = [ - "num-traits", - "rand 0.8.5", -] - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - -[[package]] -name = "atomic-polyfill" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" -dependencies = [ - "critical-section", -] - [[package]] name = "atty" version = "0.2.14" @@ -268,18 +96,6 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64ct" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" - [[package]] name = "bincode" version = "1.3.3" @@ -304,22 +120,6 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" -[[package]] -name = "bitcoin-io" -version = "0.1.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11301df0b06f22dea7bb1916403fdd88a371031e495c49b8f96931b28189e175" - -[[package]] -name = "bitcoin_hashes" -version = "0.14.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9901a56e133a1fc86eeb1113e2591f45f4682451ca893bff494d2f88918e3f" -dependencies = [ - "bitcoin-io", - "hex-conservative", -] - [[package]] name = "bitflags" version = "1.3.2" @@ -332,18 +132,6 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - [[package]] name = "block-buffer" version = "0.10.4" @@ -353,31 +141,12 @@ dependencies = [ "generic-array", ] -[[package]] -name = "bls12_381" -version = "0.8.0" -source = "git+https://github.com/lambdaclass/bls12_381?branch=expose-affine-constructors#78cad0378b17fc3157b83f514be192bf46edf9a1" -dependencies = [ - "digest", - "ff", - "group", - "pairing", - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "bumpalo" version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" -[[package]] -name = "byte-slice-cast" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" - [[package]] name = "bytecheck" version = "0.8.2" @@ -401,27 +170,6 @@ dependencies = [ "syn 2.0.111", ] -[[package]] -name = "bytemuck" -version = "1.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" -dependencies = [ - "serde", -] - [[package]] name = "cast" version = "0.3.0" @@ -444,18 +192,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" -[[package]] -name = "chrono" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" -dependencies = [ - "iana-time-zone", - "num-traits", - "serde", - "windows-link", -] - [[package]] name = "ciborium" version = "0.2.2" @@ -491,7 +227,7 @@ checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "bitflags 1.3.2", "clap_lex 0.2.4", - "indexmap 1.9.3", + "indexmap", "textwrap", ] @@ -548,26 +284,17 @@ checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" name = "cli" version = "0.1.0" dependencies = [ - "bincode", "clap 4.5.53", "env_logger", "executor", "lambda-vm-prover", + "rkyv", "stark", "tempfile", "tikv-jemalloc-ctl", "tikv-jemallocator", ] -[[package]] -name = "cobs" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" -dependencies = [ - "thiserror 2.0.17", -] - [[package]] name = "colorchoice" version = "1.0.4" @@ -586,35 +313,6 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "const_format" -version = "0.2.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" -dependencies = [ - "const_format_proc_macros", -] - -[[package]] -name = "const_format_proc_macros" -version = "0.2.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -630,15 +328,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - [[package]] name = "criterion" version = "0.4.0" @@ -705,28 +394,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" -[[package]] -name = "crossbeam" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "crossbeam-deque" version = "0.8.6" @@ -746,15 +413,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "crossbeam-queue" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -780,6 +438,7 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "rayon", + "rkyv", "serde", "sha2", "sha3", @@ -818,140 +477,39 @@ dependencies = [ ] [[package]] -name = "darling" -version = "0.21.3" +name = "der" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ - "darling_core", - "darling_macro", + "const-oid", + "zeroize", ] [[package]] -name = "darling_core" -version = "0.21.3" +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.111", + "block-buffer", + "crypto-common", ] [[package]] -name = "darling_macro" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +name = "ecsm" +version = "0.1.0" dependencies = [ - "darling_core", - "quote", - "syn 2.0.111", + "k256", + "num-bigint", + "num-traits", ] [[package]] -name = "der" -version = "0.7.10" +name = "either" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "deranged" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" -dependencies = [ - "powerfmt", - "serde_core", -] - -[[package]] -name = "derive_more" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" -dependencies = [ - "derive_more-impl", -] - -[[package]] -name = "derive_more-impl" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "syn 2.0.111", - "unicode-xid", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "subtle", -] - -[[package]] -name = "dyn-clone" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" - -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", - "spki", -] - -[[package]] -name = "ecsm" -version = "0.1.0" -dependencies = [ - "k256", - "num-bigint", - "num-traits", -] - -[[package]] -name = "educe" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" -dependencies = [ - "enum-ordinalize", - "proc-macro2", - "quote", - "syn 2.0.111", -] - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "elliptic-curve" @@ -961,11 +519,9 @@ checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", - "digest", "ff", "generic-array", "group", - "pkcs8", "rand_core 0.6.4", "sec1", "subtle", @@ -990,38 +546,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" -[[package]] -name = "embedded-io" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" - -[[package]] -name = "embedded-io" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" - -[[package]] -name = "enum-ordinalize" -version = "4.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1091a7bb1f8f2c4b28f1fe2cef4980ca2d410a3d727d67ecc3178c9b0800f0" -dependencies = [ - "enum-ordinalize-derive", -] - -[[package]] -name = "enum-ordinalize-derive" -version = "4.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "env_filter" version = "0.1.4" @@ -1045,12 +569,6 @@ dependencies = [ "log", ] -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - [[package]] name = "errno" version = "0.3.14" @@ -1061,201 +579,15 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "ethbloom" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c321610643004cf908ec0f5f2aa0d8f1f8e14b540562a2887a1111ff1ecbf7b" -dependencies = [ - "crunchy", - "fixed-hash", - "impl-rlp", - "impl-serde", - "tiny-keccak", -] - -[[package]] -name = "ethereum-types" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ab15ed80916029f878e0267c3a9f92b67df55e79af370bf66199059ae2b4ee3" -dependencies = [ - "ethbloom", - "fixed-hash", - "impl-rlp", - "impl-serde", - "primitive-types", - "uint", -] - -[[package]] -name = "ethrex-common" -version = "13.0.0" -source = "git+https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6a3974f411d71622eecd1b249ee37ff1c" -dependencies = [ - "bytes", - "crc32fast", - "ethereum-types", - "ethrex-crypto", - "ethrex-rlp", - "ethrex-trie", - "hex", - "hex-literal", - "hex-simd", - "indexmap 2.12.1", - "lazy_static", - "libc", - "lru", - "once_cell", - "rayon", - "rkyv", - "rustc-hash", - "secp256k1", - "serde", - "serde_json", - "sha2", - "thiserror 2.0.17", - "tracing", -] - -[[package]] -name = "ethrex-crypto" -version = "13.0.0" -source = "git+https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6a3974f411d71622eecd1b249ee37ff1c" -dependencies = [ - "ark-bn254", - "ark-ec", - "ark-ff", - "bls12_381", - "ethereum-types", - "ff", - "hex-literal", - "k256", - "malachite", - "num-bigint", - "p256", - "ripemd", - "secp256k1", - "sha2", - "thiserror 2.0.17", - "tiny-keccak", -] - -[[package]] -name = "ethrex-guest-program" -version = "13.0.0" -source = "git+https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6a3974f411d71622eecd1b249ee37ff1c" -dependencies = [ - "bytes", - "ethereum-types", - "ethrex-common", - "ethrex-crypto", - "ethrex-l2-common", - "ethrex-rlp", - "ethrex-vm", - "hex", - "rkyv", - "serde", - "serde_with", - "thiserror 2.0.17", -] - -[[package]] -name = "ethrex-l2-common" -version = "13.0.0" -source = "git+https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6a3974f411d71622eecd1b249ee37ff1c" -dependencies = [ - "bytes", - "ethereum-types", - "ethrex-common", - "ethrex-crypto", - "k256", - "lambdaworks-crypto", - "rkyv", - "secp256k1", - "serde", - "serde_with", - "thiserror 2.0.17", - "tracing", -] - -[[package]] -name = "ethrex-levm" -version = "13.0.0" -source = "git+https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6a3974f411d71622eecd1b249ee37ff1c" -dependencies = [ - "bytes", - "derive_more", - "ethrex-common", - "ethrex-crypto", - "ethrex-rlp", - "malachite", - "rayon", - "rustc-hash", - "serde", - "strum", - "thiserror 2.0.17", -] - -[[package]] -name = "ethrex-rlp" -version = "13.0.0" -source = "git+https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6a3974f411d71622eecd1b249ee37ff1c" -dependencies = [ - "bytes", - "ethereum-types", - "thiserror 2.0.17", -] - -[[package]] -name = "ethrex-trie" -version = "13.0.0" -source = "git+https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6a3974f411d71622eecd1b249ee37ff1c" -dependencies = [ - "anyhow", - "bytes", - "crossbeam", - "ethereum-types", - "ethrex-crypto", - "ethrex-rlp", - "lazy_static", - "rayon", - "rkyv", - "rustc-hash", - "serde", - "thiserror 2.0.17", -] - -[[package]] -name = "ethrex-vm" -version = "13.0.0" -source = "git+https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6a3974f411d71622eecd1b249ee37ff1c" -dependencies = [ - "bytes", - "derive_more", - "dyn-clone", - "ethrex-common", - "ethrex-crypto", - "ethrex-levm", - "ethrex-rlp", - "rayon", - "rustc-hash", - "serde", - "thiserror 2.0.17", - "tracing", -] - [[package]] name = "executor" version = "0.1.0" dependencies = [ "ecsm", - "ethrex-guest-program", - "rkyv", "rustc-demangle", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror", "tiny-keccak", ] @@ -1271,7 +603,6 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ - "bitvec", "rand_core 0.6.4", "subtle", ] @@ -1282,42 +613,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" -[[package]] -name = "fixed-hash" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" -dependencies = [ - "byteorder", - "rand 0.8.5", - "rustc-hex", - "static_assertions", -] - [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "foldhash" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - [[package]] name = "generic-array" version = "0.14.7" @@ -1382,62 +683,18 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "hash32" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" -dependencies = [ - "byteorder", -] - [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "allocator-api2", - "foldhash 0.1.5", -] - -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash 0.2.0", -] - [[package]] name = "hashbrown" version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" -[[package]] -name = "heapless" -version = "0.7.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" -dependencies = [ - "atomic-polyfill", - "hash32", - "rustc_version", - "serde", - "spin", - "stable_deref_trait", -] - [[package]] name = "heck" version = "0.5.0" @@ -1459,114 +716,6 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hex-conservative" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" -dependencies = [ - "arrayvec", -] - -[[package]] -name = "hex-literal" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" - -[[package]] -name = "hex-simd" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f7685beb53fc20efc2605f32f5d51e9ba18b8ef237961d1760169d2290d3bee" -dependencies = [ - "outref", - "vsimd", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "impl-codec" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d40b9d5e17727407e55028eafc22b2dc68781786e6d7eb8a21103f5058e3a14" -dependencies = [ - "parity-scale-codec", -] - -[[package]] -name = "impl-rlp" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54ed8ad1f3877f7e775b8cbf30ed1bd3209a95401817f19a0eb4402d13f8cf90" -dependencies = [ - "rlp", -] - -[[package]] -name = "impl-serde" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a143eada6a1ec4aefa5049037a26a6d597bfd64f8c026d07b77133e02b7dd0b" -dependencies = [ - "serde", -] - -[[package]] -name = "impl-trait-for-tuples" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "indexmap" version = "1.9.3" @@ -1575,19 +724,6 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "indexmap" -version = "2.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" -dependencies = [ - "equivalent", - "hashbrown 0.16.1", - "serde", - "serde_core", ] [[package]] @@ -1625,24 +761,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.16" @@ -1690,11 +808,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if", - "ecdsa", "elliptic-curve", - "once_cell", - "sha2", - "signature", ] [[package]] @@ -1720,9 +834,8 @@ dependencies = [ "log", "math", "math-cuda", - "postcard", "rayon", - "serde", + "rkyv", "stark", "sysinfo", "tikv-jemalloc-ctl", @@ -1740,35 +853,7 @@ dependencies = [ "lazy_static", "rand 0.9.2", "riscv", - "thiserror 1.0.69", -] - -[[package]] -name = "lambdaworks-crypto" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58b1a1c1102a5a7fbbda117b79fb3a01e033459c738a3c1642269603484fd1c1" -dependencies = [ - "lambdaworks-math", - "rand 0.8.5", - "rand_chacha 0.3.1", - "serde", - "sha2", - "sha3", -] - -[[package]] -name = "lambdaworks-math" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "018a95aa873eb49896a858dee0d925c33f3978d073c64b08dd4f2c9b35a017c6" -dependencies = [ - "getrandom 0.2.16", - "num-bigint", - "num-traits", - "rand 0.8.5", - "serde", - "serde_json", + "thiserror", ] [[package]] @@ -1793,12 +878,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "libm" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" - [[package]] name = "linked_list_allocator" version = "0.10.6" @@ -1811,76 +890,12 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - [[package]] name = "log" version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -[[package]] -name = "lru" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" -dependencies = [ - "hashbrown 0.16.1", -] - -[[package]] -name = "malachite" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec410515e231332b14cd986a475d1c3323bcfa4c7efc038bfa1d5b410b1c57e4" -dependencies = [ - "malachite-base", - "malachite-nz", - "malachite-q", -] - -[[package]] -name = "malachite-base" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c738d3789301e957a8f7519318fcbb1b92bb95863b28f6938ae5a05be6259f34" -dependencies = [ - "hashbrown 0.15.5", - "itertools 0.14.0", - "libm", - "ryu", -] - -[[package]] -name = "malachite-nz" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1707c9a1fa36ce21749b35972bfad17bbf34cf5a7c96897c0491da321e387d3b" -dependencies = [ - "itertools 0.14.0", - "libm", - "malachite-base", - "wide", -] - -[[package]] -name = "malachite-q" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d764801aa4e96bbb69b389dcd03b50075345131cd63ca2e380bca71cc37a3675" -dependencies = [ - "itertools 0.14.0", - "malachite-base", - "malachite-nz", -] - [[package]] name = "matchers" version = "0.2.0" @@ -1902,6 +917,7 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "rayon", + "rkyv", "serde", "serde_json", ] @@ -1983,12 +999,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - [[package]] name = "num-integer" version = "0.1.46" @@ -2031,61 +1041,6 @@ version = "6.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" -[[package]] -name = "outref" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" - -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "pairing" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" -dependencies = [ - "group", -] - -[[package]] -name = "parity-scale-codec" -version = "3.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" -dependencies = [ - "arrayvec", - "bitvec", - "byte-slice-cast", - "const_format", - "impl-trait-for-tuples", - "parity-scale-codec-derive", - "rustversion", - "serde", -] - -[[package]] -name = "parity-scale-codec-derive" -version = "3.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "paste" version = "1.0.15" @@ -2098,16 +1053,6 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - [[package]] name = "plotters" version = "0.3.7" @@ -2151,63 +1096,13 @@ dependencies = [ "portable-atomic", ] -[[package]] -name = "postcard" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" -dependencies = [ - "cobs", - "embedded-io 0.4.0", - "embedded-io 0.6.1", - "heapless", - "serde", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve", -] - -[[package]] -name = "primitive-types" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d15600a7d856470b7d278b3fe0e311fe28c2526348549f8ef2ff7db3299c87f5" -dependencies = [ - "fixed-hash", - "impl-codec", - "impl-rlp", - "impl-serde", - "uint", -] - -[[package]] -name = "proc-macro-crate" -version = "3.4.0" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "toml_edit", + "zerocopy", ] [[package]] @@ -2279,12 +1174,6 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - [[package]] name = "rancor" version = "0.1.1" @@ -2382,26 +1271,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "ref-cast" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "regex" version = "1.12.2" @@ -2440,25 +1309,6 @@ dependencies = [ "bytecheck", ] -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - -[[package]] -name = "ripemd" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" -dependencies = [ - "digest", -] - [[package]] name = "riscv" version = "0.15.0" @@ -2496,16 +1346,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73389e0c99e664f919275ab5b5b0471391fe9a8de61e1dff9b1eaf56a90f16e3" dependencies = [ "bytecheck", - "bytes", "hashbrown 0.17.1", - "indexmap 2.12.1", "munge", "ptr_meta", "rancor", "rend", "rkyv_derive", "tinyvec", - "uuid", ] [[package]] @@ -2519,16 +1366,6 @@ dependencies = [ "syn 2.0.111", ] -[[package]] -name = "rlp" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa24e92bb2a83198bb76d661a71df9f7076b8c420b8696e4d3d97d50d94479e3" -dependencies = [ - "bytes", - "rustc-hex", -] - [[package]] name = "rlsf" version = "0.2.2" @@ -2548,27 +1385,6 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - [[package]] name = "rustix" version = "1.1.3" @@ -2606,15 +1422,6 @@ version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62049b2877bf12821e8f9ad256ee38fdc31db7387ec2d3b3f403024de2034aea" -[[package]] -name = "safe_arch" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" -dependencies = [ - "bytemuck", -] - [[package]] name = "same-file" version = "1.0.6" @@ -2624,36 +1431,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "schemars" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" -dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", -] - -[[package]] -name = "schemars" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" -dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "sec1" version = "0.7.3" @@ -2663,37 +1440,10 @@ dependencies = [ "base16ct", "der", "generic-array", - "pkcs8", "subtle", "zeroize", ] -[[package]] -name = "secp256k1" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" -dependencies = [ - "bitcoin_hashes", - "rand 0.8.5", - "secp256k1-sys", -] - -[[package]] -name = "secp256k1-sys" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" -dependencies = [ - "cc", -] - -[[package]] -name = "semver" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" - [[package]] name = "serde" version = "1.0.228" @@ -2758,37 +1508,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "serde_with" -version = "3.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" -dependencies = [ - "base64 0.22.1", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.12.1", - "schemars 0.9.0", - "schemars 1.2.0", - "serde_core", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "sha2" version = "0.10.9" @@ -2825,47 +1544,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest", - "rand_core 0.6.4", -] - [[package]] name = "simdutf8" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - [[package]] name = "stark" version = "0.1.0" @@ -2884,49 +1568,23 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "rayon", + "rkyv", "serde", "serde-wasm-bindgen", "serde_cbor", "tempfile", "test-log", - "thiserror 1.0.69", + "thiserror", "wasm-bindgen", "web-sys", ] -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "strum" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "subtle" version = "2.6.1" @@ -2939,7 +1597,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2c04b93fc15d79b39c63218f15e3fdffaa4c227830686e3b7c5f41244eb3e50" dependencies = [ - "base64 0.13.1", + "base64", "proc-macro2", "quote", "syn 1.0.109", @@ -2981,12 +1639,6 @@ dependencies = [ "windows", ] -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - [[package]] name = "tempfile" version = "3.23.0" @@ -3034,16 +1686,7 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" -dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl", ] [[package]] @@ -3057,17 +1700,6 @@ dependencies = [ "syn 2.0.111", ] -[[package]] -name = "thiserror-impl" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "thread_local" version = "1.1.9" @@ -3108,37 +1740,6 @@ dependencies = [ "tikv-jemalloc-sys", ] -[[package]] -name = "time" -version = "0.3.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca" - -[[package]] -name = "time-macros" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e552d1249bf61ac2a52db88179fd0673def1e1ad8243a00d9ec9ed71fee3dd" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "tiny-keccak" version = "2.0.2" @@ -3173,59 +1774,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" -[[package]] -name = "toml_datetime" -version = "0.7.5+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_edit" -version = "0.23.10+spec-1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" -dependencies = [ - "indexmap 2.12.1", - "toml_datetime", - "toml_parser", - "winnow", -] - -[[package]] -name = "toml_parser" -version = "1.0.6+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" -dependencies = [ - "winnow", -] - [[package]] name = "tracing" version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ - "log", "pin-project-lite", - "tracing-attributes", "tracing-core", ] -[[package]] -name = "tracing-attributes" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "tracing-core" version = "0.1.36" @@ -3270,18 +1828,6 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" -[[package]] -name = "uint" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unarray" version = "0.1.4" @@ -3294,40 +1840,18 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - [[package]] name = "unicode-width" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "uuid" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "valuable" version = "0.1.1" @@ -3340,12 +1864,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "vsimd" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" - [[package]] name = "wait-timeout" version = "0.2.1" @@ -3435,16 +1953,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "wide" -version = "0.7.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03" -dependencies = [ - "bytemuck", - "safe_arch", -] - [[package]] name = "winapi" version = "0.3.9" @@ -3608,30 +2116,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "winnow" -version = "0.7.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" -dependencies = [ - "memchr", -] - [[package]] name = "wit-bindgen" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - [[package]] name = "zerocopy" version = "0.8.31" @@ -3657,17 +2147,3 @@ name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] diff --git a/bench_vs/lambda/recursion/Cargo.lock b/bench_vs/lambda/recursion/Cargo.lock index 3af079191..bf31738e2 100644 --- a/bench_vs/lambda/recursion/Cargo.lock +++ b/bench_vs/lambda/recursion/Cargo.lock @@ -2,15 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "atomic-polyfill" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" -dependencies = [ - "critical-section", -] - [[package]] name = "autocfg" version = "1.5.1" @@ -45,25 +36,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] -name = "byteorder" -version = "1.5.0" +name = "bytecheck" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +checksum = "0caa33a2c0edca0419d15ac723dff03f1956f7978329b1e3b5fdaaaed9d3ca8b" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "rancor", + "simdutf8", +] [[package]] -name = "cfg-if" -version = "1.0.4" +name = "bytecheck_derive" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +checksum = "89385e82b5d1821d2219e0b095efa2cc1f246cbf99080f3be46a1a85c0d392d9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] [[package]] -name = "cobs" -version = "0.3.0" +name = "cfg-if" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" -dependencies = [ - "thiserror 2.0.18", -] +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "const-default" @@ -132,6 +131,7 @@ dependencies = [ "math", "rand 0.8.6", "rand_chacha 0.3.1", + "rkyv", "serde", "sha3", ] @@ -228,25 +228,13 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" -[[package]] -name = "embedded-io" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" - -[[package]] -name = "embedded-io" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" - [[package]] name = "executor" version = "0.1.0" dependencies = [ "ecsm", "rustc-demangle", - "thiserror 1.0.69", + "thiserror", ] [[package]] @@ -337,27 +325,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" [[package]] -name = "hash32" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" -dependencies = [ - "byteorder", -] - -[[package]] -name = "heapless" -version = "0.7.17" +name = "hashbrown" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" -dependencies = [ - "atomic-polyfill", - "hash32", - "rustc_version", - "serde", - "spin", - "stable_deref_trait", -] +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "itertools" @@ -414,8 +385,7 @@ dependencies = [ "executor", "log", "math", - "postcard", - "serde", + "rkyv", "stark", "sysinfo", ] @@ -430,7 +400,7 @@ dependencies = [ "lazy_static", "rand 0.9.4", "riscv", - "thiserror 1.0.69", + "thiserror", ] [[package]] @@ -451,15 +421,6 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b23ac50abb8261cb38c6e2a7192d3302e0836dac1628f6a93b82b4fad185897" -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - [[package]] name = "log" version = "0.4.33" @@ -475,6 +436,7 @@ dependencies = [ "num-traits", "rand 0.8.6", "rayon", + "rkyv", "serde", "serde_json", ] @@ -485,6 +447,26 @@ version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" +[[package]] +name = "munge" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e17401f259eba956ca16491461b6e8f72913a0a114e39736ce404410f915a0c" +dependencies = [ + "munge_macro", +] + +[[package]] +name = "munge_macro" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4568f25ccbd45ab5d5603dc34318c1ec56b117531781260002151b8530a9f931" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "ntapi" version = "0.4.3" @@ -540,19 +522,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" -[[package]] -name = "postcard" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" -dependencies = [ - "cobs", - "embedded-io 0.4.0", - "embedded-io 0.6.1", - "heapless", - "serde", -] - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -571,6 +540,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "ptr_meta" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9a0cf95a1196af61d4f1cbdab967179516d9a4a4312af1f31948f8f6224a79" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7347867d0a7e1208d93b46767be83e2b8f978c3dad35f775ac8d8847551d6fe1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "quote" version = "1.0.46" @@ -586,6 +575,15 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "rancor" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daff8b7b3ccf5f7ba270b3e7a0a4d4c701c5797e38dec27c7e2c3dbb830fed1c" +dependencies = [ + "ptr_meta", +] + [[package]] name = "rand" version = "0.8.6" @@ -666,7 +664,16 @@ version = "0.1.0" dependencies = [ "lambda-vm-prover", "lambda-vm-syscalls", - "postcard", + "rkyv", +] + +[[package]] +name = "rend" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "663ba70707f96e871406fe10d68128412e619b06d1d47cb91c3a4c6501176240" +dependencies = [ + "bytecheck", ] [[package]] @@ -699,6 +706,33 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436" +[[package]] +name = "rkyv" +version = "0.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "815cc8a37159a463064825246cadb07961e25cd9885908606f6d08a98d8f8874" +dependencies = [ + "bytecheck", + "hashbrown", + "munge", + "ptr_meta", + "rancor", + "rend", + "rkyv_derive", + "tinyvec", +] + +[[package]] +name = "rkyv_derive" +version = "0.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ed1a78a1b19d184b0daa629dd9a024573173ec7d485b287cb369fb3607cc1c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "rlsf" version = "0.2.2" @@ -718,27 +752,12 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - [[package]] name = "rustversion" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "sec1" version = "0.7.3" @@ -752,12 +771,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "semver" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" - [[package]] name = "serde" version = "1.0.228" @@ -822,25 +835,16 @@ dependencies = [ ] [[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - -[[package]] -name = "spin" -version = "0.9.8" +name = "simdutf8" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] -name = "stable_deref_trait" -version = "1.2.1" +name = "slab" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "stark" @@ -851,9 +855,10 @@ dependencies = [ "itertools", "log", "math", + "rkyv", "serde", "serde_cbor", - "thiserror 1.0.69", + "thiserror", ] [[package]] @@ -916,16 +921,7 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl 2.0.18", + "thiserror-impl", ] [[package]] @@ -940,16 +936,20 @@ dependencies = [ ] [[package]] -name = "thiserror-impl" -version = "2.0.18" +name = "tinyvec" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.118", + "tinyvec_macros", ] +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "typenum" version = "1.20.1" diff --git a/bench_vs/lambda/recursion/Cargo.toml b/bench_vs/lambda/recursion/Cargo.toml index 4b5e1970b..473e3107c 100644 --- a/bench_vs/lambda/recursion/Cargo.toml +++ b/bench_vs/lambda/recursion/Cargo.toml @@ -34,7 +34,7 @@ lambda-vm-prover = { path = "../../../prover", default-features = false, feature "profile-markers", ] } lambda-vm-syscalls = { path = "../../../syscalls" } -postcard = { version = "1.0", features = ["alloc"] } +rkyv = { version = "0.8.10", default-features = false, features = ["alloc", "bytecheck", "aligned"] } [profile.release] debug = 2 diff --git a/bench_vs/lambda/recursion/src/main.rs b/bench_vs/lambda/recursion/src/main.rs index b91f2b841..33a061364 100644 --- a/bench_vs/lambda/recursion/src/main.rs +++ b/bench_vs/lambda/recursion/src/main.rs @@ -1,26 +1,35 @@ //! Naive recursion guest: verifies an inner lambda-vm proof inside the VM. //! -//! Private input (postcard): `lambda_vm_prover::recursion::GuestInput` — the -//! inner proof, the inner program's ELF bytes, and its precomputed DECODE and -//! ELF-data-page commitments, supplied instead of recomputed in-VM. +//! Private input layout: a 12-byte `"LVMR" + version + reserved` prefix +//! followed by an rkyv archive of `lambda_vm_prover::recursion::GuestInput` +//! `{ vm_proof, inner_elf, decode_commitment, page_commitments }` (built +//! host-side by `recursion::encode_guest_input`) — the inner program's ELF +//! bytes plus its precomputed DECODE and ELF-data-page commitments, supplied +//! instead of recomputed in-VM. The prefix 16-aligns the archive in guest +//! memory (the executor maps the payload at `PRIVATE_INPUT_START + 4`, which +//! is only 4-aligned) and tags the format so the guest rejects a wrong-format +//! blob before the unsafe access. The proof is verified **in place** via +//! `recursion::verify_and_attest_blob` — no deserialization pass, no owned +//! `VmProof`. //! //! `ProofOptions` is fixed by the `min`/`blowup8` Cargo feature (a `Preset`), //! not private input — an attacker could otherwise pick trivially weak options //! and have the guest accept as if a real proof had been checked. //! //! On success commits `program_id || inner_public_output` via -//! `recursion::verify_and_attest` (a single ELF parse and a single full-ELF -//! Keccak, shared between the statement absorb and the `program_id` fold). The -//! attestation is not self-enforcing: the binding is established by the -//! consumer via `recursion::check_attestation` (a host-side recompute+compare), -//! never in-guest. +//! `recursion::verify_and_attest_blob` (a single ELF parse and a single +//! full-ELF Keccak, shared between the statement absorb and the `program_id` +//! fold). The id fold is what the consumer rebinds to a trusted ELF +//! (`check_attestation`); it is not self-enforcing here — the binding is +//! established by the consumer via `recursion::check_attestation` (a +//! host-side recompute+compare), never in-guest. //! //! std (not `no_std`): `build-std` provides it, prove-side code is DCE'd. //! `#![no_main]`; inits the syscalls global allocator first thing in `main`. #![no_main] -use lambda_vm_prover::recursion::{GuestInput, Preset}; +use lambda_vm_prover::recursion::Preset; #[cfg(not(any(feature = "min", feature = "blowup8")))] compile_error!("select exactly one of the `min`/`blowup8` features"); @@ -43,9 +52,10 @@ pub fn main() -> ! { lambda_vm_syscalls::syscalls::sys_panic(PANIC_MSG.as_ptr(), PANIC_MSG.len()) })); - let blob = lambda_vm_syscalls::syscalls::get_private_input(); - let (vm_proof, inner_elf, decode_commitment, page_commitments): GuestInput = - postcard::from_bytes(&blob).expect("failed to deserialize recursion input"); + // Zero-copy: borrow the blob straight from the mapped private-input region. + // The 12-byte prefix puts the archive at a 16-aligned guest address, so the + // verifier's in-place doubleword loads don't trap. + let blob = lambda_vm_syscalls::syscalls::get_private_input_slice(); lambda_vm_prover::profile_markers::step_marker::< { lambda_vm_prover::profile_markers::STEP_DECODE_DONE }, >(); @@ -55,15 +65,9 @@ pub fn main() -> ! { // is what the consumer rebinds to a trusted ELF (`check_attestation`); it is // not self-enforcing here. let options = PRESET.options(); - let attestation = lambda_vm_prover::recursion::verify_and_attest( - &vm_proof, - &inner_elf, - &options, - decode_commitment, - &page_commitments, - ) - .expect("verify errored") - .expect("inner proof failed verification"); + let attestation = lambda_vm_prover::recursion::verify_and_attest_blob(blob, &options) + .expect("verify errored") + .expect("inner proof failed verification"); lambda_vm_syscalls::syscalls::commit(&attestation); lambda_vm_syscalls::syscalls::sys_halt(); } diff --git a/bin/cli/Cargo.toml b/bin/cli/Cargo.toml index a7850885f..e4fcdb7fd 100644 --- a/bin/cli/Cargo.toml +++ b/bin/cli/Cargo.toml @@ -9,7 +9,7 @@ executor = { path = "../../executor" } prover = { path = "../../prover", package = "lambda-vm-prover" } stark = { path = "../../crypto/stark" } clap = { version = "4.3.10", features = ["derive"] } -bincode = "1" +rkyv = { version = "0.8.10", default-features = false, features = ["alloc", "bytecheck", "aligned"] } tempfile = "3" tikv-jemallocator = "0.6" tikv-jemalloc-ctl = { version = "0.6", features = ["stats"], optional = true } diff --git a/bin/cli/src/main.rs b/bin/cli/src/main.rs index 95c3050b7..e3e60032c 100644 --- a/bin/cli/src/main.rs +++ b/bin/cli/src/main.rs @@ -627,7 +627,7 @@ fn cmd_prove( }; let mut writer = BufWriter::new(file); - let bytes = match bincode::serialize(&proof) { + let bytes = match rkyv::to_bytes::(&proof) { Ok(b) => b, Err(e) => { eprintln!("Failed to serialize proof: {}", e); @@ -678,7 +678,7 @@ fn cmd_verify(proof_path: PathBuf, elf_path: PathBuf, blowup: u8, time: bool) -> } }; - let proof: VmProof = match bincode::deserialize(&proof_bytes) { + let proof: VmProof = match rkyv::from_bytes::(&proof_bytes) { Ok(p) => p, Err(e) => { eprintln!("Failed to deserialize proof: {}", e); @@ -799,7 +799,7 @@ fn cmd_prove_continuation( } }; let mut writer = BufWriter::new(file); - let bytes = match bincode::serialize(&bundle) { + let bytes = match rkyv::to_bytes::(&bundle) { Ok(b) => b, Err(e) => { eprintln!("Failed to serialize proof: {}", e); @@ -845,7 +845,11 @@ fn cmd_verify_continuation( return ExitCode::FAILURE; } }; - let bundle: prover::continuation::ContinuationProof = match bincode::deserialize(&proof_bytes) { + let bundle: prover::continuation::ContinuationProof = match rkyv::from_bytes::< + prover::continuation::ContinuationProof, + rkyv::rancor::Error, + >(&proof_bytes) + { Ok(p) => p, Err(e) => { eprintln!("Failed to deserialize proof: {}", e); diff --git a/crypto/crypto/Cargo.toml b/crypto/crypto/Cargo.toml index fc8754e1d..6b78f81e7 100644 --- a/crypto/crypto/Cargo.toml +++ b/crypto/crypto/Cargo.toml @@ -22,6 +22,11 @@ rand_chacha = { version = "0.3.1", default-features = false } memmap2 = { version = "0.9", optional = true } tempfile = { version = "3", optional = true } libc = { version = "0.2", optional = true } +rkyv = { version = "0.8.10", default-features = false, features = [ + "alloc", + "bytecheck", + "aligned", +], optional = true } [target.'cfg(target_arch = "riscv64")'.dependencies] lambda-vm-syscalls = { path = "../../syscalls" } @@ -40,4 +45,5 @@ std = ["math/std", "sha3/std", "serde?/std"] serde = ["dep:serde"] parallel = ["dep:rayon"] disk-spill = ["std", "dep:memmap2", "dep:tempfile", "dep:libc"] -alloc = [] \ No newline at end of file +alloc = [] +rkyv = ["dep:rkyv", "math/rkyv"] \ No newline at end of file diff --git a/crypto/crypto/src/merkle_tree/proof.rs b/crypto/crypto/src/merkle_tree/proof.rs index 20d5452a2..2bbcfb3c5 100644 --- a/crypto/crypto/src/merkle_tree/proof.rs +++ b/crypto/crypto/src/merkle_tree/proof.rs @@ -15,29 +15,49 @@ use super::{ /// when verifying. #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr( + feature = "rkyv", + derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize) +)] pub struct Proof { pub merkle_path: Vec, } +/// Verifies a Merkle inclusion proof given the authentication path as a borrowed +/// slice. Shared by [`Proof::verify`] (owned) and the zero-copy verifier (which +/// reads the path straight from an rkyv-archived proof buffer) so both compute +/// the identical root. +pub fn verify_merkle_path( + merkle_path: &[B::Node], + root_hash: &B::Node, + mut index: usize, + value: &B::Data, +) -> bool +where + B: IsMerkleTreeBackend, +{ + let mut hashed_value = B::hash_data(value); + + for sibling_node in merkle_path.iter() { + if index.is_multiple_of(2) { + hashed_value = B::hash_new_parent(&hashed_value, sibling_node); + } else { + hashed_value = B::hash_new_parent(sibling_node, &hashed_value); + } + + index >>= 1; + } + + root_hash == &hashed_value +} + impl Proof { /// Verifies a Merkle inclusion proof for the value contained at leaf index. - pub fn verify(&self, root_hash: &B::Node, mut index: usize, value: &B::Data) -> bool + pub fn verify(&self, root_hash: &B::Node, index: usize, value: &B::Data) -> bool where B: IsMerkleTreeBackend, { - let mut hashed_value = B::hash_data(value); - - for sibling_node in self.merkle_path.iter() { - if index.is_multiple_of(2) { - hashed_value = B::hash_new_parent(&hashed_value, sibling_node); - } else { - hashed_value = B::hash_new_parent(sibling_node, &hashed_value); - } - - index >>= 1; - } - - root_hash == &hashed_value + verify_merkle_path::(&self.merkle_path, root_hash, index, value) } } diff --git a/crypto/math/Cargo.toml b/crypto/math/Cargo.toml index 85979a7c4..df43ea975 100644 --- a/crypto/math/Cargo.toml +++ b/crypto/math/Cargo.toml @@ -23,6 +23,14 @@ rayon = { version = "1.7", optional = true } num-bigint = { version = "0.4.6", default-features = false } num-traits = { version = "0.2.19", default-features = false } +# rkyv zero-copy (de)serialization. Optional; used by the recursion verifier to +# read a proof straight from its byte buffer with no deserialization pass. +rkyv = { version = "0.8.10", default-features = false, features = [ + "alloc", + "bytecheck", + "aligned", +], optional = true } + [dev-dependencies] rand_chacha = "0.3.1" criterion = "0.5.1" @@ -39,6 +47,7 @@ lambdaworks-serde-string = ["dep:serde", "dep:serde_json", "alloc"] proptest = ["dep:proptest"] instruments = [] test-utils = [] +rkyv = ["dep:rkyv"] [target.wasm32-unknown-unknown.dependencies] getrandom = { version = "0.2.15", features = ["js"] } diff --git a/crypto/math/src/field/element.rs b/crypto/math/src/field/element.rs index 0eb0aef96..9b1a9badf 100644 --- a/crypto/math/src/field/element.rs +++ b/crypto/math/src/field/element.rs @@ -850,3 +850,148 @@ impl<'de, F: IsPrimeField> Deserialize<'de> for FieldElement { deserializer.deserialize_struct("FieldElement", FIELDS, FieldElementVisitor(PhantomData)) } } + +// ============================================================================ +// rkyv zero-copy (de)serialization +// ============================================================================ +// +// `FieldElement` is `#[repr(transparent)]` over `F::BaseType`. Its archived +// form is a local `#[repr(transparent)]` newtype wrapping the archived form of +// `F::BaseType` (e.g. archived `u64` for Goldilocks, `[ArchivedFieldElement; 3]` +// for the cubic extension). Keeping it a LOCAL type (rather than reusing +// `::Archived` directly) is what lets us implement +// `Deserialize` without colliding with rkyv's blanket impls — while the +// transparent repr keeps the archived bytes identical to the base type, so the +// recursion verifier still reads field elements straight from the proof buffer. + +/// Archived form of [`FieldElement`]; see the module note above. +#[cfg(feature = "rkyv")] +#[repr(transparent)] +pub struct ArchivedFieldElement +where + F::BaseType: rkyv::Archive, +{ + value: ::Archived, +} + +#[cfg(feature = "rkyv")] +const _: () = { + use rkyv::{Archive, Deserialize, Place, Portable, Serialize}; + + // SAFETY: `ArchivedFieldElement` is `#[repr(transparent)]` over the base + // type's archived form, which is itself `Portable` (required by `Archive`). + // A transparent wrapper over a `Portable` type is position-independent and + // valid for the same byte patterns, so it is `Portable` too. + unsafe impl Portable for ArchivedFieldElement + where + F: IsField, + F::BaseType: Archive, + ::Archived: Portable, + { + } + + impl Archive for FieldElement + where + F: IsField, + F::BaseType: Archive, + { + type Archived = ArchivedFieldElement; + type Resolver = ::Resolver; + + #[inline] + fn resolve(&self, resolver: Self::Resolver, out: Place) { + // `ArchivedFieldElement` is `#[repr(transparent)]` over the base + // type's archived form, so resolving into the inner field resolves + // the whole newtype. + let inner = unsafe { out.cast_unchecked::<::Archived>() }; + self.value.resolve(resolver, inner); + } + } + + impl Serialize for FieldElement + where + F: IsField, + F::BaseType: Serialize, + S: rkyv::rancor::Fallible + ?Sized, + { + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + self.value.serialize(serializer) + } + } + + impl Deserialize, D> for ArchivedFieldElement + where + F: IsField, + F::BaseType: Archive, + ::Archived: Deserialize, + D: rkyv::rancor::Fallible + ?Sized, + { + #[inline] + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + Ok(FieldElement { + value: self.value.deserialize(deserializer)?, + }) + } + } + + // SAFETY: `#[repr(transparent)]` over the inner archived value, so checking + // the inner type's bytes checks the whole newtype. + unsafe impl rkyv::bytecheck::CheckBytes for ArchivedFieldElement + where + F: IsField, + F::BaseType: Archive, + ::Archived: rkyv::bytecheck::CheckBytes, + C: rkyv::rancor::Fallible + ?Sized, + { + unsafe fn check_bytes(value: *const Self, context: &mut C) -> Result<(), C::Error> { + unsafe { + <::Archived as rkyv::bytecheck::CheckBytes>::check_bytes( + value as *const ::Archived, + context, + ) + } + } + } +}; + +// ---------------------------------------------------------------------------- +// Zero-copy native views (little-endian only) +// ---------------------------------------------------------------------------- +// +// rkyv archives integers as `rend::*_le` types, which are `#[repr(C, align(N))]` +// and bit-identical to the native little-endian primitive. `FieldElement` is +// `#[repr(transparent)]` over `F::BaseType` and `ArchivedFieldElement` is +// `#[repr(transparent)]` over `::Archived`. So on a +// little-endian target the two types share size, alignment, and bit layout — +// an archived field element *is* a native field element. These views let the +// verifier read field elements straight out of the proof buffer with no copy +// and no allocation. +// +// Restricted to `target_endian = "little"` (the lambda-vm guest target). On a +// big-endian host these would be wrong, so they simply don't exist there. +#[cfg(all(feature = "rkyv", target_endian = "little"))] +impl ArchivedFieldElement +where + F::BaseType: rkyv::Archive, +{ + /// Reinterpret this archived element as a native [`FieldElement`] (no copy). + /// + /// Sound on little-endian: see the module note above. + #[inline] + pub fn as_native(&self) -> &FieldElement { + // SAFETY: identical size/align/bit-layout on little-endian. + unsafe { &*(self as *const Self as *const FieldElement) } + } + + /// Reinterpret a slice of archived elements as a slice of native + /// [`FieldElement`]s (no copy, no allocation). + #[inline] + pub fn slice_as_native(slice: &[Self]) -> &[FieldElement] { + // SAFETY: element-wise identical layout on little-endian, so the slice + // (same length, same element stride) reinterprets directly. + unsafe { + core::slice::from_raw_parts(slice.as_ptr() as *const FieldElement, slice.len()) + } + } +} diff --git a/crypto/stark/Cargo.toml b/crypto/stark/Cargo.toml index 89483bbdf..78d95be67 100644 --- a/crypto/stark/Cargo.toml +++ b/crypto/stark/Cargo.toml @@ -12,13 +12,15 @@ crate-type = ["cdylib", "rlib"] math = { path = "../math", features = [ "std", "lambdaworks-serde-binary", + "rkyv", ] } -crypto = { path = "../crypto", features = ["std", "serde"] } +crypto = { path = "../crypto", features = ["std", "serde", "rkyv"] } thiserror = "1.0.38" log = "0.4.17" digest = "0.10.7" serde = { version = "1.0", features = ["derive"] } itertools = "0.11.0" +rkyv = { version = "0.8.10", default-features = false, features = ["alloc", "bytecheck", "aligned"] } # Parallelization crates rayon = { version = "1.8.0", optional = true } diff --git a/crypto/stark/src/examples/fibonacci_2_cols_shifted.rs b/crypto/stark/src/examples/fibonacci_2_cols_shifted.rs index 855469e4b..823c2cef5 100644 --- a/crypto/stark/src/examples/fibonacci_2_cols_shifted.rs +++ b/crypto/stark/src/examples/fibonacci_2_cols_shifted.rs @@ -16,7 +16,7 @@ use math::{ traits::AsBytes, }; use std::marker::PhantomData; -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] #[serde(bound = "FieldElement: serde::Serialize + serde::de::DeserializeOwned")] pub struct PublicInputs where diff --git a/crypto/stark/src/examples/fibonacci_multi_column.rs b/crypto/stark/src/examples/fibonacci_multi_column.rs index ae6e61527..127836ca9 100644 --- a/crypto/stark/src/examples/fibonacci_multi_column.rs +++ b/crypto/stark/src/examples/fibonacci_multi_column.rs @@ -20,7 +20,7 @@ use math::field::{ /// Public inputs for the multi-column Fibonacci AIR. /// Contains the initial values (first two elements) for each column. -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] #[serde(bound = "FieldElement: serde::Serialize + serde::de::DeserializeOwned")] pub struct FibonacciMultiColumnPublicInputs { /// Initial values for each column: (a0, a1) pairs diff --git a/crypto/stark/src/examples/fibonacci_rap.rs b/crypto/stark/src/examples/fibonacci_rap.rs index 22003952d..d715f9a9d 100644 --- a/crypto/stark/src/examples/fibonacci_rap.rs +++ b/crypto/stark/src/examples/fibonacci_rap.rs @@ -71,7 +71,7 @@ where phantom: PhantomData, } -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] #[serde(bound = "FieldElement: serde::Serialize + serde::de::DeserializeOwned")] pub struct FibonacciRAPPublicInputs where diff --git a/crypto/stark/src/examples/quadratic_air.rs b/crypto/stark/src/examples/quadratic_air.rs index aedaf1d72..bf0b3374e 100644 --- a/crypto/stark/src/examples/quadratic_air.rs +++ b/crypto/stark/src/examples/quadratic_air.rs @@ -50,7 +50,7 @@ where phantom: PhantomData, } -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] #[serde(bound = "FieldElement: serde::Serialize + serde::de::DeserializeOwned")] pub struct QuadraticPublicInputs where diff --git a/crypto/stark/src/examples/read_only_memory.rs b/crypto/stark/src/examples/read_only_memory.rs index 521bd7ca9..935eb3e59 100644 --- a/crypto/stark/src/examples/read_only_memory.rs +++ b/crypto/stark/src/examples/read_only_memory.rs @@ -82,7 +82,7 @@ where phantom: PhantomData, } -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] #[serde(bound = "FieldElement: serde::Serialize + serde::de::DeserializeOwned")] pub struct ReadOnlyPublicInputs where diff --git a/crypto/stark/src/examples/read_only_memory_logup.rs b/crypto/stark/src/examples/read_only_memory_logup.rs index 5090098bd..a1fe964af 100644 --- a/crypto/stark/src/examples/read_only_memory_logup.rs +++ b/crypto/stark/src/examples/read_only_memory_logup.rs @@ -99,7 +99,7 @@ where phantom: PhantomData<(F, E)>, } -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] #[serde(bound = "FieldElement: serde::Serialize + serde::de::DeserializeOwned")] pub struct LogReadOnlyPublicInputs where diff --git a/crypto/stark/src/examples/simple_addition.rs b/crypto/stark/src/examples/simple_addition.rs index d064acd55..433496422 100644 --- a/crypto/stark/src/examples/simple_addition.rs +++ b/crypto/stark/src/examples/simple_addition.rs @@ -54,7 +54,7 @@ where phantom: PhantomData, } -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] #[serde(bound = "FieldElement: serde::Serialize + serde::de::DeserializeOwned")] pub struct SimpleAdditionPublicInputs where diff --git a/crypto/stark/src/examples/simple_fibonacci.rs b/crypto/stark/src/examples/simple_fibonacci.rs index db84ab439..917d0be55 100644 --- a/crypto/stark/src/examples/simple_fibonacci.rs +++ b/crypto/stark/src/examples/simple_fibonacci.rs @@ -50,7 +50,7 @@ where phantom: PhantomData, } -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] #[serde(bound = "FieldElement: serde::Serialize + serde::de::DeserializeOwned")] pub struct FibonacciPublicInputs where diff --git a/crypto/stark/src/fri/fri_decommit.rs b/crypto/stark/src/fri/fri_decommit.rs index f398096d5..252483c1a 100644 --- a/crypto/stark/src/fri/fri_decommit.rs +++ b/crypto/stark/src/fri/fri_decommit.rs @@ -4,7 +4,9 @@ use math::field::traits::IsField; use crate::config::Commitment; -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize +)] #[serde(bound = "")] pub struct FriDecommitment { pub layers_auth_paths: Vec>, diff --git a/crypto/stark/src/lookup.rs b/crypto/stark/src/lookup.rs index 4273f29a7..2f017404e 100644 --- a/crypto/stark/src/lookup.rs +++ b/crypto/stark/src/lookup.rs @@ -1498,7 +1498,9 @@ impl BusInteraction { /// /// For the circular constraint, `table_contribution / N` is the per-row offset /// that makes the accumulated column wrap to zero at row N-1. -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize +)] #[serde(bound = "")] pub struct BusPublicInputs where @@ -1507,20 +1509,45 @@ where /// Total sum of all LogUp terms across all rows (L). /// Used for bus balance check and to derive the per-row offset L/N. pub table_contribution: FieldElement, - /// Per-bus sums for this table (bus_id → sum) - for debug aggregation + /// Per-bus sums for this table (bus_id → sum) - for debug aggregation. + /// Debug-only aggregation state; not part of the archived proof (`Skip`). #[cfg(feature = "debug-checks")] + #[rkyv(with = rkyv::with::Skip)] pub per_bus_sums: HashMap>, /// Per-bus sender sums (bus_id → sum) - positive contributions #[cfg(feature = "debug-checks")] + #[rkyv(with = rkyv::with::Skip)] pub per_bus_sender_sums: HashMap>, /// Per-bus receiver sums (bus_id → sum) - absolute value (before negation) #[cfg(feature = "debug-checks")] + #[rkyv(with = rkyv::with::Skip)] pub per_bus_receiver_sums: HashMap>, /// Table name for debug output #[cfg(feature = "debug-checks")] + #[rkyv(with = rkyv::with::Skip)] pub table_name: String, } +impl BusPublicInputs { + /// Build a `BusPublicInputs` carrying just the table contribution `L`. + /// The debug-only per-bus aggregation fields are defaulted (empty). Used by + /// the zero-copy verifier, which reads only `table_contribution` from the + /// archived proof. + pub fn from_contribution(table_contribution: FieldElement) -> Self { + Self { + table_contribution, + #[cfg(feature = "debug-checks")] + per_bus_sums: HashMap::new(), + #[cfg(feature = "debug-checks")] + per_bus_sender_sums: HashMap::new(), + #[cfg(feature = "debug-checks")] + per_bus_receiver_sums: HashMap::new(), + #[cfg(feature = "debug-checks")] + table_name: String::new(), + } + } +} + /// Trait representing boundary constraint building behaviour. /// Should be defined when creating an `AirWithBuses` if the AIR requires its own boundary constraints aside from the lookup ones pub trait BoundaryConstraintBuilder< diff --git a/crypto/stark/src/proof/mod.rs b/crypto/stark/src/proof/mod.rs index bd12710f2..e02dab654 100644 --- a/crypto/stark/src/proof/mod.rs +++ b/crypto/stark/src/proof/mod.rs @@ -1,2 +1,3 @@ pub mod options; pub mod stark; +pub mod view; diff --git a/crypto/stark/src/proof/options.rs b/crypto/stark/src/proof/options.rs index 4624943e8..277f5d9f3 100644 --- a/crypto/stark/src/proof/options.rs +++ b/crypto/stark/src/proof/options.rs @@ -40,7 +40,9 @@ impl fmt::Display for ProofOptionsError { /// - `grinding_factor`: the number of leading zeros that we want for the Hash(hash || nonce) /// - `fri_final_poly_log_degree`: log2 degree bound at which FRI terminates folding #[cfg_attr(feature = "wasm", wasm_bindgen)] -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +#[derive( + Clone, Debug, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize +)] pub struct ProofOptions { pub blowup_factor: u8, pub fri_number_of_queries: usize, diff --git a/crypto/stark/src/proof/stark.rs b/crypto/stark/src/proof/stark.rs index 675160837..471c9f74d 100644 --- a/crypto/stark/src/proof/stark.rs +++ b/crypto/stark/src/proof/stark.rs @@ -8,7 +8,9 @@ use crate::{ config::Commitment, fri::fri_decommit::FriDecommitment, lookup::BusPublicInputs, table::Table, }; -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize +)] #[serde(bound = "")] /// Opening of a bit-reversed, row-paired commitment at one FRI query. /// @@ -22,7 +24,9 @@ pub struct PolynomialOpenings { pub evaluations_sym: Vec>, } -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize +)] #[serde(bound = "")] pub struct DeepPolynomialOpening, E: IsField> { pub composition_poly: PolynomialOpenings, @@ -35,7 +39,9 @@ pub struct DeepPolynomialOpening, E: IsField> { pub type DeepPolynomialOpenings = Vec>; -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize +)] #[serde(bound = "PI: serde::Serialize + serde::de::DeserializeOwned")] pub struct StarkProof, E: IsField, PI> { // Length of the execution trace @@ -78,7 +84,9 @@ pub struct StarkProof, E: IsField, PI> { /// A collection of STARK proofs for multiple AIRs. /// Used for multi-table proving where tables are linked via bus (LogUp). /// Returned by `Prover::multi_prove` and verified by `Verifier::multi_verify`. -#[derive(Debug, serde::Serialize, serde::Deserialize)] +#[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize +)] #[serde(bound = "PI: serde::Serialize + serde::de::DeserializeOwned")] pub struct MultiProof, E: IsField, PI> { pub proofs: Vec>, diff --git a/crypto/stark/src/proof/view.rs b/crypto/stark/src/proof/view.rs new file mode 100644 index 000000000..ba39a8afc --- /dev/null +++ b/crypto/stark/src/proof/view.rs @@ -0,0 +1,436 @@ +//! Borrowed views over a STARK proof that work identically whether the proof +//! is a real owned object or an rkyv-archived buffer. +//! +//! Each view is `Owned(&T)` or `Archived(&Archived)`; scalar fields are +//! copied out (`to_native()` vs. a plain copy), field-element/commitment +//! arrays stay borrowed (`slice_as_native` vs. the `Vec`'s slice directly). +//! This lets the verifier be written once and run over either representation +//! with no serialization and no logic duplication. + +use crate::config::Commitment; +use crate::fri::fri_decommit::{ArchivedFriDecommitment, FriDecommitment}; +use crate::frame::Frame; +use crate::proof::stark::{ + ArchivedDeepPolynomialOpening, ArchivedPolynomialOpenings, ArchivedStarkProof, + DeepPolynomialOpening, PolynomialOpenings, StarkProof, +}; +use crate::table::{ArchivedTable, Table}; +use math::field::element::{ArchivedFieldElement, FieldElement}; +use math::field::traits::{IsField, IsSubFieldOf}; + +/// Deserializer used to materialize the (tiny) per-proof `PI` public inputs. +pub type PiDeserializer = rkyv::api::high::HighDeserializer; + +/// `&[FieldElement]` view over an archived field-element vector (no copy). +#[inline] +pub(crate) fn evals( + v: &rkyv::vec::ArchivedVec>, +) -> &[FieldElement] +where + G::BaseType: rkyv::Archive, +{ + ArchivedFieldElement::slice_as_native(v.as_slice()) +} + +pub enum PolynomialOpeningsView<'a, F: IsField> +where + F::BaseType: rkyv::Archive, +{ + Owned(&'a PolynomialOpenings), + Archived(&'a ArchivedPolynomialOpenings), +} + +// Manual Clone/Copy: the variants are plain references, so this holds for +// every `F`, regardless of whether `F` itself is `Clone`/`Copy`. A derive +// would add a spurious `F: Clone`/`F: Copy` bound. +impl<'a, F: IsField> Clone for PolynomialOpeningsView<'a, F> +where + F::BaseType: rkyv::Archive, +{ + fn clone(&self) -> Self { + *self + } +} +impl<'a, F: IsField> Copy for PolynomialOpeningsView<'a, F> where F::BaseType: rkyv::Archive {} + +impl<'a, F: IsField> PolynomialOpeningsView<'a, F> +where + F::BaseType: rkyv::Archive, +{ + pub fn merkle_path(&self) -> &'a [Commitment] { + match self { + Self::Owned(p) => &p.proof.merkle_path, + Self::Archived(p) => p.proof.merkle_path.as_slice(), + } + } + + pub fn evaluations(&self) -> &'a [FieldElement] { + match self { + Self::Owned(p) => &p.evaluations, + Self::Archived(p) => evals(&p.evaluations), + } + } + + pub fn evaluations_sym(&self) -> &'a [FieldElement] { + match self { + Self::Owned(p) => &p.evaluations_sym, + Self::Archived(p) => evals(&p.evaluations_sym), + } + } +} + +pub enum DeepPolynomialOpeningView<'a, F: IsSubFieldOf, E: IsField> +where + F::BaseType: rkyv::Archive, + E::BaseType: rkyv::Archive, +{ + Owned(&'a DeepPolynomialOpening), + Archived(&'a ArchivedDeepPolynomialOpening), +} + +impl<'a, F: IsSubFieldOf, E: IsField> Clone for DeepPolynomialOpeningView<'a, F, E> +where + F::BaseType: rkyv::Archive, + E::BaseType: rkyv::Archive, +{ + fn clone(&self) -> Self { + *self + } +} +impl<'a, F: IsSubFieldOf, E: IsField> Copy for DeepPolynomialOpeningView<'a, F, E> +where + F::BaseType: rkyv::Archive, + E::BaseType: rkyv::Archive, +{ +} + +impl<'a, F: IsSubFieldOf, E: IsField> DeepPolynomialOpeningView<'a, F, E> +where + F::BaseType: rkyv::Archive, + E::BaseType: rkyv::Archive, +{ + pub fn composition_poly(&self) -> PolynomialOpeningsView<'a, E> { + match self { + Self::Owned(p) => PolynomialOpeningsView::Owned(&p.composition_poly), + Self::Archived(p) => PolynomialOpeningsView::Archived(&p.composition_poly), + } + } + + pub fn main_trace_polys(&self) -> PolynomialOpeningsView<'a, F> { + match self { + Self::Owned(p) => PolynomialOpeningsView::Owned(&p.main_trace_polys), + Self::Archived(p) => PolynomialOpeningsView::Archived(&p.main_trace_polys), + } + } + + pub fn precomputed_trace_polys(&self) -> Option> { + match self { + Self::Owned(p) => p + .precomputed_trace_polys + .as_ref() + .map(PolynomialOpeningsView::Owned), + Self::Archived(p) => p + .precomputed_trace_polys + .as_ref() + .map(PolynomialOpeningsView::Archived), + } + } + + pub fn aux_trace_polys(&self) -> Option> { + match self { + Self::Owned(p) => p.aux_trace_polys.as_ref().map(PolynomialOpeningsView::Owned), + Self::Archived(p) => p + .aux_trace_polys + .as_ref() + .map(PolynomialOpeningsView::Archived), + } + } +} + +pub enum FriDecommitmentView<'a, E: IsField> +where + E::BaseType: rkyv::Archive, +{ + Owned(&'a FriDecommitment), + Archived(&'a ArchivedFriDecommitment), +} + +impl<'a, E: IsField> Clone for FriDecommitmentView<'a, E> +where + E::BaseType: rkyv::Archive, +{ + fn clone(&self) -> Self { + *self + } +} +impl<'a, E: IsField> Copy for FriDecommitmentView<'a, E> where E::BaseType: rkyv::Archive {} + +impl<'a, E: IsField> FriDecommitmentView<'a, E> +where + E::BaseType: rkyv::Archive, +{ + pub fn layers_auth_paths_len(&self) -> usize { + match self { + Self::Owned(p) => p.layers_auth_paths.len(), + Self::Archived(p) => p.layers_auth_paths.len(), + } + } + + pub fn layer_auth_path(&self, i: usize) -> &'a [Commitment] { + match self { + Self::Owned(p) => &p.layers_auth_paths[i].merkle_path, + Self::Archived(p) => p.layers_auth_paths[i].merkle_path.as_slice(), + } + } + + pub fn layers_evaluations_sym(&self) -> &'a [FieldElement] { + match self { + Self::Owned(p) => &p.layers_evaluations_sym, + Self::Archived(p) => evals(&p.layers_evaluations_sym), + } + } +} + +pub enum StarkTableView<'a, F: IsField> +where + F::BaseType: rkyv::Archive, +{ + Owned(&'a Table), + Archived(&'a ArchivedTable), +} + +impl<'a, F: IsField> Clone for StarkTableView<'a, F> +where + F::BaseType: rkyv::Archive, +{ + fn clone(&self) -> Self { + *self + } +} +impl<'a, F: IsField> Copy for StarkTableView<'a, F> where F::BaseType: rkyv::Archive {} + +impl<'a, F: IsField> StarkTableView<'a, F> +where + F::BaseType: rkyv::Archive, +{ + pub fn width(&self) -> usize { + match self { + Self::Owned(t) => t.width, + Self::Archived(t) => t.width(), + } + } + + pub fn height(&self) -> usize { + match self { + Self::Owned(t) => t.height, + Self::Archived(t) => t.height(), + } + } + + pub fn get_row(&self, row_idx: usize) -> &'a [FieldElement] { + match self { + Self::Owned(t) => t.get_row(row_idx), + Self::Archived(t) => t.get_row(row_idx), + } + } + + pub fn row_major_data(&self) -> &'a [FieldElement] { + match self { + Self::Owned(t) => t.row_major_data(), + Self::Archived(t) => t.row_major_data(), + } + } + + /// `true` iff `width * height` matches the backing data length — the + /// invariant `get_row` indexing relies on. + pub fn dimensions_consistent(&self) -> bool { + match self { + Self::Owned(t) => t + .width + .checked_mul(t.height) + .is_some_and(|n| n == t.row_major_data().len()), + Self::Archived(t) => t.dimensions_consistent(), + } + } + + pub fn into_frame(&self, main_trace_columns: usize, step_size: usize) -> Frame + where + F: IsSubFieldOf, + { + match self { + Self::Owned(t) => t.into_frame(main_trace_columns, step_size), + Self::Archived(t) => t.into_frame(main_trace_columns, step_size), + } + } +} + +pub enum StarkProofView<'a, F: IsSubFieldOf, E: IsField, PI> +where + F::BaseType: rkyv::Archive, + E::BaseType: rkyv::Archive, + PI: rkyv::Archive, + ::Archived: rkyv::Deserialize, +{ + Owned(&'a StarkProof), + Archived(&'a ArchivedStarkProof), +} + +impl<'a, F: IsSubFieldOf, E: IsField, PI> Clone for StarkProofView<'a, F, E, PI> +where + F::BaseType: rkyv::Archive, + E::BaseType: rkyv::Archive, + PI: rkyv::Archive, + ::Archived: rkyv::Deserialize, +{ + fn clone(&self) -> Self { + *self + } +} +impl<'a, F: IsSubFieldOf, E: IsField, PI> Copy for StarkProofView<'a, F, E, PI> +where + F::BaseType: rkyv::Archive, + E::BaseType: rkyv::Archive, + PI: rkyv::Archive, + ::Archived: rkyv::Deserialize, +{ +} + +impl<'a, F: IsSubFieldOf, E: IsField, PI> StarkProofView<'a, F, E, PI> +where + F::BaseType: rkyv::Archive, + E::BaseType: rkyv::Archive, + PI: rkyv::Archive, + ::Archived: rkyv::Deserialize, +{ + pub fn trace_length(&self) -> usize { + match self { + Self::Owned(p) => p.trace_length, + Self::Archived(p) => p.trace_length.to_native() as usize, + } + } + + pub fn lde_trace_main_merkle_root(&self) -> &'a Commitment { + match self { + Self::Owned(p) => &p.lde_trace_main_merkle_root, + Self::Archived(p) => &p.lde_trace_main_merkle_root, + } + } + + pub fn lde_trace_aux_merkle_root(&self) -> Option<&'a Commitment> { + match self { + Self::Owned(p) => p.lde_trace_aux_merkle_root.as_ref(), + Self::Archived(p) => p.lde_trace_aux_merkle_root.as_ref(), + } + } + + pub fn lde_trace_precomputed_merkle_root(&self) -> Option<&'a Commitment> { + match self { + Self::Owned(p) => p.lde_trace_precomputed_merkle_root.as_ref(), + Self::Archived(p) => p.lde_trace_precomputed_merkle_root.as_ref(), + } + } + + pub fn trace_ood_evaluations(&self) -> StarkTableView<'a, E> { + match self { + Self::Owned(p) => StarkTableView::Owned(&p.trace_ood_evaluations), + Self::Archived(p) => StarkTableView::Archived(&p.trace_ood_evaluations), + } + } + + pub fn composition_poly_root(&self) -> &'a Commitment { + match self { + Self::Owned(p) => &p.composition_poly_root, + Self::Archived(p) => &p.composition_poly_root, + } + } + + pub fn composition_poly_parts_ood_evaluation(&self) -> &'a [FieldElement] { + match self { + Self::Owned(p) => &p.composition_poly_parts_ood_evaluation, + Self::Archived(p) => evals(&p.composition_poly_parts_ood_evaluation), + } + } + + pub fn fri_layers_merkle_roots(&self) -> &'a [Commitment] { + match self { + Self::Owned(p) => &p.fri_layers_merkle_roots, + Self::Archived(p) => p.fri_layers_merkle_roots.as_slice(), + } + } + + pub fn fri_final_poly_coeffs(&self) -> &'a [FieldElement] { + match self { + Self::Owned(p) => &p.fri_final_poly_coeffs, + Self::Archived(p) => evals(&p.fri_final_poly_coeffs), + } + } + + pub fn query_list_len(&self) -> usize { + match self { + Self::Owned(p) => p.query_list.len(), + Self::Archived(p) => p.query_list.len(), + } + } + + pub fn query(&self, i: usize) -> FriDecommitmentView<'a, E> { + match self { + Self::Owned(p) => FriDecommitmentView::Owned(&p.query_list[i]), + Self::Archived(p) => FriDecommitmentView::Archived(&p.query_list.as_slice()[i]), + } + } + + pub fn deep_poly_openings_len(&self) -> usize { + match self { + Self::Owned(p) => p.deep_poly_openings.len(), + Self::Archived(p) => p.deep_poly_openings.len(), + } + } + + pub fn deep_poly_opening(&self, i: usize) -> DeepPolynomialOpeningView<'a, F, E> { + match self { + Self::Owned(p) => DeepPolynomialOpeningView::Owned(&p.deep_poly_openings[i]), + Self::Archived(p) => { + DeepPolynomialOpeningView::Archived(&p.deep_poly_openings.as_slice()[i]) + } + } + } + + pub fn nonce(&self) -> Option { + match self { + Self::Owned(p) => p.nonce, + Self::Archived(p) => p.nonce.as_ref().map(|n| n.to_native()), + } + } + + /// The bus interaction's table contribution (L), if present. This is the + /// only field of `BusPublicInputs` the verifier reads; both sides copy it + /// out (it's a single field element, not worth a dedicated view type). + pub fn bus_table_contribution(&self) -> Option> { + match self { + Self::Owned(p) => p.bus_public_inputs.as_ref().map(|b| b.table_contribution.clone()), + Self::Archived(p) => p + .bus_public_inputs + .as_ref() + .map(|b| b.table_contribution.as_native().clone()), + } + } + + pub fn has_bus_public_inputs(&self) -> bool { + match self { + Self::Owned(p) => p.bus_public_inputs.is_some(), + Self::Archived(p) => p.bus_public_inputs.is_some(), + } + } + + /// Materializes the (tiny) `PI` public inputs: a clone on the owned side, + /// an rkyv deserialize on the archived side. + pub fn public_inputs(&self) -> Option + where + PI: Clone, + { + match self { + Self::Owned(p) => Some(p.public_inputs.clone()), + Self::Archived(p) => rkyv::deserialize::(&p.public_inputs).ok(), + } + } +} diff --git a/crypto/stark/src/table.rs b/crypto/stark/src/table.rs index dc188d690..0dae77c1a 100644 --- a/crypto/stark/src/table.rs +++ b/crypto/stark/src/table.rs @@ -44,7 +44,15 @@ impl std::fmt::Debug for TableMmapBacking { #[derive(Default, Debug, serde::Deserialize)] #[cfg_attr( not(feature = "disk-spill"), - derive(serde::Serialize, Clone, PartialEq, Eq) + derive( + Clone, + PartialEq, + Eq, + serde::Serialize, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize + ) )] #[serde(bound = "")] pub struct Table { @@ -99,6 +107,167 @@ where } } +// Manual rkyv impl under disk-spill: the derive can't handle `mmap_backing`, +// and serialization must read through `row_major_data()` so a spilled table +// archives its mmap contents (deserializing always yields an unspilled table). +// The archived layout matches what the derive generates without disk-spill, so +// both configurations produce byte-identical archives. +#[cfg(feature = "disk-spill")] +mod archived_table { + use super::{FieldElement, IsField, Table}; + use math::field::element::ArchivedFieldElement; + use rkyv::rancor::Fallible; + use rkyv::ser::{Allocator, Writer}; + use rkyv::vec::{ArchivedVec, VecResolver}; + use rkyv::{Archive, Deserialize, Place, Portable, Serialize}; + + #[derive(Portable, rkyv::bytecheck::CheckBytes)] + #[bytecheck(crate = rkyv::bytecheck)] + #[repr(C)] + pub struct ArchivedTable + where + F::BaseType: Archive, + { + pub data: ArchivedVec>, + pub width: rkyv::primitive::ArchivedUsize, + pub height: rkyv::primitive::ArchivedUsize, + } + + pub struct TableResolver { + data: VecResolver, + } + + impl Archive for Table + where + F::BaseType: Archive, + { + type Archived = ArchivedTable; + type Resolver = TableResolver; + + fn resolve(&self, resolver: Self::Resolver, out: Place) { + rkyv::munge::munge!(let ArchivedTable { data, width, height } = out); + ArchivedVec::resolve_from_len(self.width * self.height, resolver.data, data); + self.width.resolve((), width); + self.height.resolve((), height); + } + } + + impl Serialize for Table + where + F::BaseType: Archive, + FieldElement: Serialize, + S: Fallible + Allocator + Writer + ?Sized, + { + fn serialize(&self, serializer: &mut S) -> Result { + Ok(TableResolver { + data: ArchivedVec::serialize_from_slice(self.row_major_data(), serializer)?, + }) + } + } + + impl Deserialize, D> for ArchivedTable + where + F::BaseType: Archive, + ArchivedFieldElement: Deserialize, D>, + D: Fallible + ?Sized, + { + fn deserialize(&self, deserializer: &mut D) -> Result, D::Error> { + // Element-by-element rather than `self.data.deserialize(...)`: + // `ArchivedVec`'s blanket `Deserialize` impl needs a + // `DeserializeUnsized` bound this crate doesn't otherwise use, + // while the per-element bound below is already satisfied. + let data = self + .data + .iter() + .map(|elem| elem.deserialize(deserializer)) + .collect::, _>>()?; + Ok(Table { + data, + width: self.width.to_native() as usize, + height: self.height.to_native() as usize, + mmap_backing: None, + }) + } + } +} + +#[cfg(feature = "disk-spill")] +pub use archived_table::ArchivedTable; + +/// Read API over an rkyv-archived [`Table`], used by the verifier to consume +/// the out-of-domain evaluations straight from the proof buffer. On +/// little-endian targets the element data is viewed in place with no copy. +#[cfg(target_endian = "little")] +impl ArchivedTable +where + F::BaseType: rkyv::Archive, +{ + #[inline] + pub fn width(&self) -> usize { + self.width.to_native() as usize + } + + #[inline] + pub fn height(&self) -> usize { + self.height.to_native() as usize + } + + /// Full row-major element data, viewed in place. + #[inline] + pub fn row_major_data(&self) -> &[FieldElement] { + math::field::element::ArchivedFieldElement::slice_as_native(self.data.as_slice()) + } + + /// `true` iff the backing data holds exactly `width × height` elements — + /// the invariant `get_row` indexing relies on. A malformed archive can + /// advertise dimensions that disagree with the data length; callers must + /// reject such tables before row access. + #[inline] + pub fn dimensions_consistent(&self) -> bool { + self.width() + .checked_mul(self.height()) + .is_some_and(|n| n == self.data.len()) + } + + /// Row `row_idx` as a native field-element slice (no copy). + #[inline] + pub fn get_row(&self, row_idx: usize) -> &[FieldElement] { + let width = self.width(); + let start = row_idx * width; + &self.row_major_data()[start..start + width] + } + + /// Build a [`Frame`] over this table, identical to [`Table::into_frame`]. + /// Only the small OOD frame is materialized (bounded by `step_size × width`), + /// never the whole proof. + pub fn into_frame(&self, main_trace_columns: usize, step_size: usize) -> Frame + where + F: IsSubFieldOf, + { + let height = self.height(); + debug_assert!(height.is_multiple_of(step_size)); + let steps = (0..height) + .step_by(step_size) + .map(|initial_row_idx| { + let end_row_idx = initial_row_idx + step_size; + + let mut step_main_data: Vec>> = Vec::new(); + let mut step_aux_data: Vec>> = Vec::new(); + + (initial_row_idx..end_row_idx).for_each(|row_idx| { + let row = self.get_row(row_idx); + step_main_data.push(row[..main_trace_columns].to_vec()); + step_aux_data.push(row[main_trace_columns..].to_vec()); + }); + + TableView::new(step_main_data, step_aux_data) + }) + .collect(); + + Frame::new(steps) + } +} + /// Cloning a spilled table copies its mmap bytes into a fresh heap `Vec` /// and returns an unspilled clone. #[cfg(feature = "disk-spill")] @@ -359,7 +528,6 @@ impl Table { pub fn advise_drop_cache(&self) {} /// Given a step size, converts the given table into a `Frame`. - /// Clones row data into owned Vecs (only used by verifier on small OOD tables). pub fn into_frame(&self, main_trace_columns: usize, step_size: usize) -> Frame { debug_assert!(self.height.is_multiple_of(step_size)); let steps = (0..self.height) diff --git a/crypto/stark/src/verifier.rs b/crypto/stark/src/verifier.rs index fdee3d2ff..510325738 100644 --- a/crypto/stark/src/verifier.rs +++ b/crypto/stark/src/verifier.rs @@ -1,7 +1,6 @@ use super::{ config::BatchedMerkleTreeBackend, domain::VerifierDomain, - fri::fri_decommit::FriDecommitment, grinding, proof::stark::StarkProof, traits::{AIR, TransitionEvaluationContext}, @@ -9,10 +8,17 @@ use super::{ use crate::{ config::Commitment, domain::new_verifier_domain, - lookup::{LOGUP_CHALLENGE_ALPHA, LOGUP_NUM_CHALLENGES, compute_alpha_powers}, - proof::stark::{DeepPolynomialOpening, MultiProof, PolynomialOpenings}, + lookup::{ + BusPublicInputs, LOGUP_CHALLENGE_ALPHA, LOGUP_NUM_CHALLENGES, compute_alpha_powers, + }, + proof::stark::{ArchivedStarkProof, MultiProof}, + proof::view::{ + DeepPolynomialOpeningView, FriDecommitmentView, PolynomialOpeningsView, StarkProofView, + }, }; -use crypto::{fiat_shamir::is_transcript::IsStarkTranscript, merkle_tree::proof::Proof}; +pub use crate::proof::view::PiDeserializer; +use crypto::fiat_shamir::is_transcript::IsStarkTranscript; +use crypto::merkle_tree::proof::verify_merkle_path; #[cfg(not(feature = "test_fiat_shamir"))] use log::error; #[cfg(feature = "debug-checks")] @@ -25,10 +31,10 @@ use math::{ }, traits::AsBytes, }; -use std::collections::HashMap; use std::marker::PhantomData; #[cfg(feature = "instruments")] use std::time::Instant; +use std::collections::HashMap; /// A default STARK verifier implementing `IsStarkVerifier`. pub struct Verifier< @@ -44,6 +50,11 @@ impl< FieldExtension: IsField + Send + Sync, PI, > IsStarkVerifier for Verifier +where + Field::BaseType: rkyv::Archive, + FieldExtension::BaseType: rkyv::Archive, + PI: rkyv::Archive + Clone, + ::Archived: rkyv::Deserialize, { } @@ -75,13 +86,30 @@ where pub type DeepPolynomialEvaluations = (Vec>, Vec>); +// The verifier reads proofs in place from their rkyv archive; archived field +// elements are viewed as native ones, which is only valid on little-endian. +#[cfg(not(target_endian = "little"))] +compile_error!("the zero-copy STARK verifier requires a little-endian target"); + /// The functionality of a STARK verifier providing methods to run the STARK Verify protocol /// https://lambdaclass.github.io/lambdaworks/starks/protocol.html +/// +/// Every method below takes proof data through a [`StarkProofView`] (and its +/// nested `*View` types), a borrowed view implemented once for a real owned +/// [`StarkProof`] and once for an rkyv-archived proof read in place. This is +/// the single verification implementation: [`Self::multi_verify`] (owned) and +/// [`Self::multi_verify_archived`] (archived, used by the recursion guest) +/// are thin entry points that build the matching view and share every +/// downstream check — no serialization, no duplicated logic. pub trait IsStarkVerifier< Field: IsSubFieldOf + IsFFTField + Send + Sync, FieldExtension: Send + Sync + IsField, PI, -> +> where + Field::BaseType: rkyv::Archive, + FieldExtension::BaseType: rkyv::Archive, + PI: rkyv::Archive + Clone, + ::Archived: rkyv::Deserialize, { fn sample_query_indexes( number_of_queries: usize, @@ -99,18 +127,22 @@ pub trait IsStarkVerifier< /// See https://lambdaclass.github.io/lambdaworks/starks/protocol.html#step-2-verify-claimed-composition-polynomial fn step_2_verify_claimed_composition_polynomial( air: &dyn AIR, - proof: &StarkProof, + proof: StarkProofView<'_, Field, FieldExtension, PI>, + public_inputs: &PI, domain: &VerifierDomain, challenges: &Challenges, ) -> bool { crate::profile_markers::step_marker::< { crate::profile_markers::STEP_VERIFY_CLAIMED_COMPOSITION_POLYNOMIAL }, >(); - let trace_length = proof.trace_length; + let trace_length = proof.trace_length(); + // Owned `BusPublicInputs` (just the table contribution L — one field + // element) reconstructed for the AIR boundary call. + let bus_public_inputs = proof.bus_table_contribution().map(BusPublicInputs::from_contribution); let boundary_constraints = air.boundary_constraints( - &proof.public_inputs, + public_inputs, &challenges.rap_challenges, - proof.bus_public_inputs.as_ref(), + bus_public_inputs.as_ref(), trace_length, ); // Precompute g^step once per distinct step to avoid the prior O(B^2) @@ -129,7 +161,8 @@ pub trait IsStarkVerifier< .collect(); let main_trace_width = air.trace_layout().0; - let ood_row = proof.trace_ood_evaluations.get_row(0); + let trace_ood_evaluations = proof.trace_ood_evaluations(); + let ood_row = trace_ood_evaluations.get_row(0); let (boundary_c_i_evaluations_num, mut boundary_c_i_evaluations_den): ( Vec>, @@ -167,8 +200,15 @@ pub trait IsStarkVerifier< .map(|((num, den), beta)| num * den * beta) .fold(FieldElement::::zero(), |acc, x| acc + x); - let num_main_trace_columns = - proof.trace_ood_evaluations.width - air.num_auxiliary_rap_columns(); + // A malformed archive can advertise fewer OOD columns than the AIR's + // aux count; reject instead of underflowing. + let num_main_trace_columns = match trace_ood_evaluations + .width() + .checked_sub(air.num_auxiliary_rap_columns()) + { + Some(n) => n, + None => return false, + }; let logup_alpha_powers: Vec> = if challenges.rap_challenges.len() > LOGUP_CHALLENGE_ALPHA { @@ -180,19 +220,18 @@ pub trait IsStarkVerifier< Vec::new() }; - let logup_table_offset = match &proof.bus_public_inputs { - Some(bpi) => { + let logup_table_offset = match proof.bus_table_contribution() { + Some(contribution) => { let n = FieldElement::::from(trace_length as u64); match n.inv() { - Ok(n_inv) => n_inv * &bpi.table_contribution, + Ok(n_inv) => n_inv * &contribution, Err(_) => return false, // trace_length == 0 is invalid } } None => FieldElement::zero(), }; - let ood_frame = - (proof.trace_ood_evaluations).into_frame(num_main_trace_columns, air.step_size()); + let ood_frame = trace_ood_evaluations.into_frame(num_main_trace_columns, air.step_size()); let transition_evaluation_context = TransitionEvaluationContext::new_verifier( &ood_frame, &challenges.rap_challenges, @@ -226,7 +265,7 @@ pub trait IsStarkVerifier< &boundary_quotient_ood_evaluation + transition_c_i_evaluations_sum; let composition_poly_claimed_ood_evaluation = proof - .composition_poly_parts_ood_evaluation + .composition_poly_parts_ood_evaluation() .iter() .rev() .fold(FieldElement::zero(), |acc, coeff| { @@ -260,7 +299,7 @@ pub trait IsStarkVerifier< /// FRI decommitments are valid and correspond to the Deep composition polynomial. fn step_3_verify_fri( air: &dyn AIR, - proof: &StarkProof, + proof: StarkProofView<'_, Field, FieldExtension, PI>, domain: &VerifierDomain, challenges: &Challenges, ) -> bool @@ -286,12 +325,12 @@ pub trait IsStarkVerifier< // Structural check: number of committed FRI layers must equal // `num_committed` (zero when no fold or a single final fold happened). - if proof.fri_layers_merkle_roots.len() != num_committed { + if proof.fri_layers_merkle_roots().len() != num_committed { return false; } // Structural check: the final polynomial must have exactly `2^effective_k` // coefficients; otherwise the reconstruction below is ill-defined. - if proof.fri_final_poly_coeffs.len() != (1usize << layout.effective_k) { + if proof.fri_final_poly_coeffs().len() != (1usize << layout.effective_k) { return false; } // Structural check: every per-query FRI decommitment must carry exactly @@ -302,9 +341,10 @@ pub trait IsStarkVerifier< // iterations and accept the query vacuously) or padded (making the loop // skip the terminal low-degree check), bypassing FRI entirely. This length // check is the only thing that pins them, so it must run before the loop. - if proof.query_list.iter().any(|decommitment| { - decommitment.layers_auth_paths.len() != num_committed - || decommitment.layers_evaluations_sym.len() != num_committed + if (0..proof.query_list_len()).any(|i| { + let decommitment = proof.query(i); + decommitment.layers_auth_paths_len() != num_committed + || decommitment.layers_evaluations_sym().len() != num_committed }) { return false; } @@ -312,7 +352,7 @@ pub trait IsStarkVerifier< let terminal_offset = domain.coset_offset.pow(1u64 << layout.total_folds); let terminal_codeword = crate::fri::terminal::terminal_codeword_from_coeffs::( - &proof.fri_final_poly_coeffs, + proof.fri_final_poly_coeffs(), &terminal_offset, layout.terminal_len, ); @@ -328,18 +368,14 @@ pub trait IsStarkVerifier< return false; } - proof - .query_list - .iter() - .zip(&challenges.iotas) + (0..challenges.iotas.len()) .zip(evaluation_point_inverse) - .enumerate() - .all(|(i, ((proof_s, iota_s), eval))| { + .all(|(i, eval)| { Self::verify_query_and_sym_openings( proof, &challenges.zetas, - *iota_s, - proof_s, + challenges.iotas[i], + proof.query(i), eval, &deep_poly_evaluations[i], &deep_poly_evaluations_sym[i], @@ -363,11 +399,10 @@ pub trait IsStarkVerifier< /// Verify a row-paired `PolynomialOpenings` against `root`. The row pair /// (`2·iota`, `2·iota+1`) is committed as the single leaf at position `iota`, - /// so one Merkle path authenticates both rows: reconstruct the leaf from - /// `evaluations ‖ evaluations_sym` and verify once. (Same as the composition - /// opening check.) + /// so one Merkle path authenticates both `evaluations` (the row) and + /// `evaluations_sym` (its symmetric). Same layout used for trace and composition. fn verify_opening_pair( - opening: &PolynomialOpenings, + opening: PolynomialOpeningsView<'_, E>, root: &Commitment, iota: usize, ) -> bool @@ -375,20 +410,19 @@ pub trait IsStarkVerifier< FieldElement: AsBytes + Sync + Send, FieldElement: AsBytes + Sync + Send, E: IsField, + E::BaseType: rkyv::Archive, Field: IsSubFieldOf, { - let mut value = opening.evaluations.clone(); - value.extend_from_slice(&opening.evaluations_sym); - opening - .proof - .verify::>(root, iota, &value) + let mut value = opening.evaluations().to_vec(); + value.extend_from_slice(opening.evaluations_sym()); + verify_merkle_path::>(opening.merkle_path(), root, iota, &value) } /// Verify opening Open(tⱼ(D_LDE), 𝜐) and Open(tⱼ(D_LDE), -𝜐) for all trace polynomials tⱼ, /// where 𝜐 and -𝜐 are the elements corresponding to the index challenge `iota`. fn verify_trace_openings( - proof: &StarkProof, - deep_poly_openings: &DeepPolynomialOpening, + proof: StarkProofView<'_, Field, FieldExtension, PI>, + deep_poly_openings: DeepPolynomialOpeningView<'_, Field, FieldExtension>, iota: usize, ) -> bool where @@ -397,8 +431,8 @@ pub trait IsStarkVerifier< { // Main trace (multiplicities for preprocessed, full trace for normal). let mut ok = Self::verify_opening_pair::( - &deep_poly_openings.main_trace_polys, - &proof.lde_trace_main_merkle_root, + deep_poly_openings.main_trace_polys(), + proof.lde_trace_main_merkle_root(), iota, ); @@ -406,8 +440,8 @@ pub trait IsStarkVerifier< // unreachable in practice (multi_verify rejects such proofs upstream), // but a defensive check keeps this function self-contained. ok &= match ( - &proof.lde_trace_precomputed_merkle_root, - &deep_poly_openings.precomputed_trace_polys, + proof.lde_trace_precomputed_merkle_root(), + deep_poly_openings.precomputed_trace_polys(), ) { (Some(root), Some(opening)) => Self::verify_opening_pair::(opening, root, iota), (None, None) => true, @@ -416,11 +450,11 @@ pub trait IsStarkVerifier< // Auxiliary trace. ok &= match ( - proof.lde_trace_aux_merkle_root, - &deep_poly_openings.aux_trace_polys, + proof.lde_trace_aux_merkle_root(), + deep_poly_openings.aux_trace_polys(), ) { (Some(root), Some(opening)) => { - Self::verify_opening_pair::(opening, &root, iota) + Self::verify_opening_pair::(opening, root, iota) } (None, None) => true, _ => false, @@ -432,7 +466,7 @@ pub trait IsStarkVerifier< /// Verify opening Open(Hᵢ(D_LDE), 𝜐) and Open(Hᵢ(D_LDE), -𝜐) for all parts Hᵢof the composition /// polynomial, where 𝜐 and -𝜐 are the elements corresponding to the index challenge `iota`. fn verify_composition_poly_opening( - deep_poly_openings: &DeepPolynomialOpening, + deep_poly_openings: DeepPolynomialOpeningView<'_, Field, FieldExtension>, composition_poly_merkle_root: &Commitment, iota: &usize, ) -> bool @@ -440,24 +474,23 @@ pub trait IsStarkVerifier< FieldElement: AsBytes + Sync + Send, FieldElement: AsBytes + Sync + Send, { - let mut value = deep_poly_openings.composition_poly.evaluations.clone(); - value.extend_from_slice(&deep_poly_openings.composition_poly.evaluations_sym); - - deep_poly_openings - .composition_poly - .proof - .verify::>( - composition_poly_merkle_root, - *iota, - &value, - ) + let composition_poly = deep_poly_openings.composition_poly(); + let mut value = composition_poly.evaluations().to_vec(); + value.extend_from_slice(composition_poly.evaluations_sym()); + + verify_merkle_path::>( + composition_poly.merkle_path(), + composition_poly_merkle_root, + *iota, + &value, + ) } /// Verifies the validity of the purported values of the trace polynomials and the composition polynomial /// parts at the domain elements and their symmetric counterparts corresponding to all the FRI query /// index challenges. fn step_4_verify_trace_and_composition_openings( - proof: &StarkProof, + proof: StarkProofView<'_, Field, FieldExtension, PI>, challenges: &Challenges, ) -> bool where @@ -467,23 +500,22 @@ pub trait IsStarkVerifier< crate::profile_markers::step_marker::< { crate::profile_markers::STEP_VERIFY_TRACE_AND_COMPOSITION_OPENINGS }, >(); - challenges - .iotas - .iter() - .zip(&proof.deep_poly_openings) - .all(|(iota_n, deep_poly_opening)| { - Self::verify_composition_poly_opening( - deep_poly_opening, - &proof.composition_poly_root, - iota_n, - ) && Self::verify_trace_openings(proof, deep_poly_opening, *iota_n) - }) + // `step_3_verify_fri` (which runs before this) already rejects proofs + // whose `deep_poly_openings` is shorter than `challenges.iotas`. + challenges.iotas.iter().enumerate().all(|(i, iota_n)| { + let deep_poly_opening = proof.deep_poly_opening(i); + Self::verify_composition_poly_opening( + deep_poly_opening, + proof.composition_poly_root(), + iota_n, + ) && Self::verify_trace_openings(proof, deep_poly_opening, *iota_n) + }) } /// Verifies the openings of a fold polynomial of an inner layer of FRI. fn verify_fri_layer_openings( merkle_root: &Commitment, - auth_path_sym: &Proof, + auth_path_sym: &[Commitment], evaluation: &FieldElement, evaluation_sym: &FieldElement, iota: usize, @@ -498,7 +530,8 @@ pub trait IsStarkVerifier< vec![evaluation.clone(), evaluation_sym.clone()] }; - auth_path_sym.verify::>( + verify_merkle_path::>( + auth_path_sym, merkle_root, iota >> 1, &evaluations, @@ -515,10 +548,10 @@ pub trait IsStarkVerifier< /// `deep_composition_evaluation_sym`: precomputed value of p₀(-𝜐), where p₀ is the deep composition polynomial. #[allow(clippy::too_many_arguments)] fn verify_query_and_sym_openings( - proof: &StarkProof, + proof: StarkProofView<'_, Field, FieldExtension, PI>, zetas: &[FieldElement], iota: usize, - fri_decommitment: &FriDecommitment, + fri_decommitment: FriDecommitmentView<'_, FieldExtension>, evaluation_point_inv: FieldElement, deep_composition_evaluation: &FieldElement, deep_composition_evaluation_sym: &FieldElement, @@ -528,7 +561,7 @@ pub trait IsStarkVerifier< FieldElement: AsBytes + Sync + Send, FieldElement: AsBytes + Sync + Send, { - let fri_layers_merkle_roots = &proof.fri_layers_merkle_roots; + let fri_layers_merkle_roots = proof.fri_layers_merkle_roots(); let p0_eval = deep_composition_evaluation; let p0_eval_sym = deep_composition_evaluation_sym; @@ -563,43 +596,37 @@ pub trait IsStarkVerifier< // previous iteration), then obtain pᵢ₊₁(𝜐^(2ⁱ⁺¹)). When there are no // committed layers (`total_folds == 1`, a single final fold) this fold is // empty and `v`/`index` already hold the terminal-layer value/position. - let openings_ok = - fri_layers_merkle_roots - .iter() - .enumerate() - .zip(&fri_decommitment.layers_auth_paths) - .zip(&fri_decommitment.layers_evaluations_sym) - .zip(evaluation_point_vec) - .fold( - true, - |result, - ( - (((i, merkle_root), auth_path_sym), evaluation_sym), - evaluation_point_inv, - )| { - // Verify opening Open(pᵢ(Dₖ), −𝜐^(2ⁱ)) and Open(pᵢ(Dₖ), 𝜐^(2ⁱ)). - // `v` is pᵢ(𝜐^(2ⁱ)). - // `evaluation_sym` is pᵢ(−𝜐^(2ⁱ)). - let openings_ok = Self::verify_fri_layer_openings( - merkle_root, - auth_path_sym, - &v, - evaluation_sym, - index, - ); - - // Update `v` with next value pᵢ₊₁(𝜐^(2ⁱ⁺¹)). - v = (&v + evaluation_sym) - + evaluation_point_inv * &zetas[i + 1] * (&v - evaluation_sym); - - // Update index for next iteration. The index of the squares in the next layer - // is obtained by halving the current index. This is due to the bit-reverse - // ordering of the elements in the Merkle tree. - index >>= 1; - - result & openings_ok - }, - ); + let openings_ok = fri_layers_merkle_roots + .iter() + .zip(fri_decommitment.layers_evaluations_sym()) + .zip(evaluation_point_vec) + .enumerate() + .fold( + true, + |result, (i, ((merkle_root, evaluation_sym), evaluation_point_inv))| { + // Verify opening Open(pᵢ(Dₖ), −𝜐^(2ⁱ)) and Open(pᵢ(Dₖ), 𝜐^(2ⁱ)). + // `v` is pᵢ(𝜐^(2ⁱ)). + // `evaluation_sym` is pᵢ(−𝜐^(2ⁱ)). + let openings_ok = Self::verify_fri_layer_openings( + merkle_root, + fri_decommitment.layer_auth_path(i), + &v, + evaluation_sym, + index, + ); + + // Update `v` with next value pᵢ₊₁(𝜐^(2ⁱ⁺¹)). + v = (&v + evaluation_sym) + + evaluation_point_inv * &zetas[i + 1] * (&v - evaluation_sym); + + // Update index for next iteration. The index of the squares in the next layer + // is obtained by halving the current index. This is due to the bit-reverse + // ordering of the elements in the Merkle tree. + index >>= 1; + + result & openings_ok + }, + ); // After folding through all committed layers, `v` is the query's value at // the terminal layer and `index` its FRI-order position there. Check it @@ -613,7 +640,7 @@ pub trait IsStarkVerifier< fn reconstruct_deep_composition_poly_evaluations_for_all_queries( challenges: &Challenges, domain: &VerifierDomain, - proof: &StarkProof, + proof: StarkProofView<'_, Field, FieldExtension, PI>, ) -> Option> { let num_queries = challenges.iotas.len(); @@ -624,7 +651,7 @@ pub trait IsStarkVerifier< // verifier with an out-of-bounds index on a malicious proof. Reject // instead. (Extra entries are harmless — they are never indexed — // matching the `<` convention of the `query_list` guard.) - if proof.deep_poly_openings.len() < num_queries { + if proof.deep_poly_openings_len() < num_queries { return None; } @@ -638,19 +665,18 @@ pub trait IsStarkVerifier< .expect("verifier domain root_order is a valid power of two"); for (i, iota) in challenges.iotas.iter().enumerate() { - let opening = &proof.deep_poly_openings[i]; + let opening = proof.deep_poly_opening(i); // Base-field portion: precomputed columns FIRST, then main trace columns. let mut lde_base: Vec> = Vec::new(); - if let Some(p) = &opening.precomputed_trace_polys { - lde_base.extend_from_slice(&p.evaluations); + if let Some(p) = opening.precomputed_trace_polys() { + lde_base.extend_from_slice(p.evaluations()); } - lde_base.extend_from_slice(&opening.main_trace_polys.evaluations); + lde_base.extend_from_slice(opening.main_trace_polys().evaluations()); let lde_aux: &[FieldElement] = opening - .aux_trace_polys - .as_ref() - .map(|a| a.evaluations.as_slice()) + .aux_trace_polys() + .map(|a| a.evaluations()) .unwrap_or(&[]); let evaluation_point = Self::query_challenge_to_evaluation_point(*iota, false, domain); @@ -661,20 +687,19 @@ pub trait IsStarkVerifier< challenges, &lde_base, lde_aux, - &opening.composition_poly.evaluations, + opening.composition_poly().evaluations(), )?); // Mirror for the symmetric query point. let mut lde_base_sym: Vec> = Vec::new(); - if let Some(p) = &opening.precomputed_trace_polys { - lde_base_sym.extend_from_slice(&p.evaluations_sym); + if let Some(p) = opening.precomputed_trace_polys() { + lde_base_sym.extend_from_slice(p.evaluations_sym()); } - lde_base_sym.extend_from_slice(&opening.main_trace_polys.evaluations_sym); + lde_base_sym.extend_from_slice(opening.main_trace_polys().evaluations_sym()); let lde_aux_sym: &[FieldElement] = opening - .aux_trace_polys - .as_ref() - .map(|a| a.evaluations_sym.as_slice()) + .aux_trace_polys() + .map(|a| a.evaluations_sym()) .unwrap_or(&[]); let evaluation_point = Self::query_challenge_to_evaluation_point(*iota, true, domain); @@ -685,14 +710,14 @@ pub trait IsStarkVerifier< challenges, &lde_base_sym, lde_aux_sym, - &opening.composition_poly.evaluations_sym, + opening.composition_poly().evaluations_sym(), )?); } Some((deep_poly_evaluations, deep_poly_evaluations_sym)) } fn reconstruct_deep_composition_poly_evaluation( - proof: &StarkProof, + proof: StarkProofView<'_, Field, FieldExtension, PI>, evaluation_point: &FieldElement, primitive_root: &FieldElement, challenges: &Challenges, @@ -700,8 +725,12 @@ pub trait IsStarkVerifier< lde_trace_aux_evaluations: &[FieldElement], lde_composition_poly_parts_evaluation: &[FieldElement], ) -> Option> { - let ood_evaluations_table_height = proof.trace_ood_evaluations.height; - let ood_evaluations_table_width = proof.trace_ood_evaluations.width; + let trace_ood_evaluations = proof.trace_ood_evaluations(); + let ood_evaluations_table_height = trace_ood_evaluations.height(); + let ood_evaluations_table_width = trace_ood_evaluations.width(); + // Hot loop below: resolve the OOD data to one flat slice once instead + // of re-deriving a row slice per element. + let ood_data = trace_ood_evaluations.row_major_data(); let trace_term_coeffs = &challenges.trace_term_coeffs; // Runtime guard: a malformed proof may supply opening evaluations whose @@ -736,7 +765,7 @@ pub trait IsStarkVerifier< let trace_i = (0..ood_evaluations_table_height).zip(coeff_row).fold( FieldElement::zero(), |trace_t, (row_idx, coeff)| { - let ood_val = &proof.trace_ood_evaluations.get_row(row_idx)[col_idx]; + let ood_val = &ood_data[row_idx * ood_evaluations_table_width + col_idx]; // Stay in base when we can: F: IsSubFieldOf gives F - E -> E. let diff: FieldElement = if col_idx < num_base { &lde_trace_base_evaluations[col_idx] - ood_val @@ -750,6 +779,7 @@ pub trait IsStarkVerifier< trace_terms + trace_i }); + let composition_parts_ood = proof.composition_poly_parts_ood_evaluation(); let number_of_parts = lde_composition_poly_parts_evaluation.len(); let z_pow = &challenges.z.pow(number_of_parts); @@ -759,7 +789,7 @@ pub trait IsStarkVerifier< for (j, h_i_upsilon) in lde_composition_poly_parts_evaluation.iter().enumerate() { // Bounds-check via `.get(j)?`: a malformed opening may have more // parts than the proof header advertises. - let h_i_zpower = proof.composition_poly_parts_ood_evaluation.get(j)?; + let h_i_zpower = composition_parts_ood.get(j)?; let gamma = challenges.gammas.get(j)?; let h_i_term = (h_i_upsilon - h_i_zpower) * gamma; h_terms += h_i_term; @@ -798,11 +828,46 @@ pub trait IsStarkVerifier< FieldElement: AsBytes + Sync + Send, FieldElement: AsBytes + Sync + Send, { - if airs.len() != multi_proof.proofs.len() { + let views: Vec> = + multi_proof.proofs.iter().map(StarkProofView::Owned).collect(); + Self::multi_verify_views(airs, &views, transcript, expected_bus_balance) + } + + /// Verifies one or more rkyv-archived STARK proofs read **in place** from + /// their archive buffer — no proof deserialization, no per-field allocation. + fn multi_verify_archived( + airs: &[&dyn AIR], + proofs: &[ArchivedStarkProof], + transcript: &mut (impl IsStarkTranscript + Clone), + expected_bus_balance: &FieldElement, + ) -> bool + where + FieldElement: AsBytes + Sync + Send, + FieldElement: AsBytes + Sync + Send, + { + let views: Vec> = + proofs.iter().map(StarkProofView::Archived).collect(); + Self::multi_verify_views(airs, &views, transcript, expected_bus_balance) + } + + /// The single verification implementation, shared by [`Self::multi_verify`] + /// (owned) and [`Self::multi_verify_archived`] (archived), operating on + /// proof views rather than either's concrete type. + fn multi_verify_views( + airs: &[&dyn AIR], + proofs: &[StarkProofView], + transcript: &mut (impl IsStarkTranscript + Clone), + expected_bus_balance: &FieldElement, + ) -> bool + where + FieldElement: AsBytes + Sync + Send, + FieldElement: AsBytes + Sync + Send, + { + if airs.len() != proofs.len() { error!( "AIR count ({}) does not match proof count ({})", airs.len(), - multi_proof.proofs.len() + proofs.len() ); return false; } @@ -816,15 +881,25 @@ pub trait IsStarkVerifier< // For preprocessed tables, use the hardcoded commitment (verifier cannot // trust the prover). For normal tables, use the commitment from the proof. - for (idx, (air, proof)) in airs.iter().zip(&multi_proof.proofs).enumerate() { + for (idx, (air, proof)) in airs.iter().zip(proofs).enumerate() { + let proof = *proof; // Soundness: the number of composition-poly parts is fixed by the AIR's // degree bound, NOT chosen by the prover. Deriving it from the proof would // let a malicious prover inflate the part count, widening the composition // polynomial's degree space and weakening the low-degree test. Reject any // proof whose advertised part count disagrees with the AIR. - if proof.trace_length == 0 - || proof.composition_poly_parts_ood_evaluation.len() - != air.composition_poly_degree_bound(proof.trace_length) / proof.trace_length + let trace_length = proof.trace_length(); + if trace_length == 0 + || proof.composition_poly_parts_ood_evaluation().len() + != air.composition_poly_degree_bound(trace_length) / trace_length + { + return false; + } + // The archive is read in place without validation; reject an OOD + // table whose advertised dimensions disagree with its data length + // (or that has no rows) before any row access indexes into it. + let trace_ood_evaluations = proof.trace_ood_evaluations(); + if !trace_ood_evaluations.dimensions_consistent() || trace_ood_evaluations.height() == 0 { return false; } @@ -832,7 +907,7 @@ pub trait IsStarkVerifier< // Preprocessed table: VERIFY precomputed commitment matches hardcoded. // This is the critical soundness check - ensures prover used correct precomputed values. let expected_precomputed = air.precomputed_commitment(); - match &proof.lde_trace_precomputed_merkle_root { + match proof.lde_trace_precomputed_merkle_root() { Some(actual) if *actual == expected_precomputed => { // OK - commitment matches hardcoded } @@ -853,10 +928,10 @@ pub trait IsStarkVerifier< // Precomputed commitment binds challenges to correct precomputed values. // Multiplicities commitment binds challenges to actual lookups made. transcript.append_bytes(&expected_precomputed); - transcript.append_bytes(&proof.lde_trace_main_merkle_root); + transcript.append_bytes(proof.lde_trace_main_merkle_root()); } else { // Normal table: use commitment from proof - transcript.append_bytes(&proof.lde_trace_main_merkle_root); + transcript.append_bytes(proof.lde_trace_main_merkle_root()); } } @@ -881,14 +956,15 @@ pub trait IsStarkVerifier< // boundary constraints on LogUp columns, so the bus balance check is // the only cross-table validation. - for (idx, (air, proof)) in airs.iter().zip(&multi_proof.proofs).enumerate() { - if air.has_trace_interaction() && proof.bus_public_inputs.is_none() { + for (idx, (air, proof)) in airs.iter().zip(proofs).enumerate() { + let proof = *proof; + if air.has_trace_interaction() && !proof.has_bus_public_inputs() { error!( "Table {idx}: AIR has LogUp interactions but proof is missing bus_public_inputs" ); return false; } - if !air.has_trace_interaction() && proof.bus_public_inputs.is_some() { + if !air.has_trace_interaction() && proof.has_bus_public_inputs() { error!( "Table {idx}: AIR has no LogUp interactions but proof contains bus_public_inputs" ); @@ -903,7 +979,8 @@ pub trait IsStarkVerifier< // state after Phase B, domain-separated by table index). This matches // the prover's forking and makes per-table verification independent. - for (idx, (air, proof)) in airs.iter().zip(&multi_proof.proofs).enumerate() { + for (idx, (air, proof)) in airs.iter().zip(proofs).enumerate() { + let proof = *proof; // Must match prover: fork with domain separator for multi-table, // use original transcript directly for single-table. let num_tables = airs.len(); @@ -913,19 +990,27 @@ pub trait IsStarkVerifier< } // Phase C: replay aux commitment - if let Some(root) = proof.lde_trace_aux_merkle_root { - table_transcript.append_bytes(&root); + if let Some(root) = proof.lde_trace_aux_merkle_root() { + table_transcript.append_bytes(root); } // Bind table_contribution (L) to transcript, matching prover. - if let Some(ref bpi) = proof.bus_public_inputs { - table_transcript.append_field_element(&bpi.table_contribution); + if let Some(contribution) = proof.bus_table_contribution() { + table_transcript.append_field_element(&contribution); } + // The AIR API takes owned public inputs; materialize the (tiny) PI. + // For the VM verifier `PI = ()` and this is a no-op. + let public_inputs: PI = match proof.public_inputs() { + Some(pi) => pi, + None => return false, + }; + // Rounds 2-4: verify if !Self::verify_rounds_2_to_4( *air, proof, + &public_inputs, &mut table_transcript, lookup_challenges.clone(), ) { @@ -951,11 +1036,11 @@ pub trait IsStarkVerifier< if needs_lookup_challenges { let mut total = FieldElement::::zero(); - for (air, proof) in airs.iter().zip(&multi_proof.proofs) { + for (air, proof) in airs.iter().zip(proofs) { if air.has_trace_interaction() - && let Some(interaction) = &proof.bus_public_inputs + && let Some(contribution) = proof.bus_table_contribution() { - total = total + &interaction.table_contribution; + total += contribution; } } @@ -984,7 +1069,6 @@ pub trait IsStarkVerifier< where FieldElement: AsBytes + Sync + Send, FieldElement: AsBytes + Sync + Send, - PI: Clone, { let multi_proof = MultiProof { proofs: vec![proof.clone()], @@ -996,7 +1080,8 @@ pub trait IsStarkVerifier< /// already been replayed and the RAP challenges are known. fn replay_rounds_after_round_1( air: &dyn AIR, - proof: &StarkProof, + proof: StarkProofView<'_, Field, FieldExtension, PI>, + public_inputs: &PI, domain: &VerifierDomain, transcript: &mut impl IsStarkTranscript, rap_challenges: Vec>, @@ -1014,12 +1099,13 @@ pub trait IsStarkVerifier< // <<<< Receive challenge: 𝛽 let beta = transcript.sample_field_element(); - let trace_length = proof.trace_length; + let trace_length = proof.trace_length(); + let bus_public_inputs = proof.bus_table_contribution().map(BusPublicInputs::from_contribution); let num_boundary_constraints = air .boundary_constraints( - &proof.public_inputs, + public_inputs, &rap_challenges, - proof.bus_public_inputs.as_ref(), + bus_public_inputs.as_ref(), trace_length, ) .constraints @@ -1034,7 +1120,7 @@ pub trait IsStarkVerifier< let boundary_coeffs = coefficients; // <<<< Receive commitments: [H₁], [H₂] - transcript.append_bytes(&proof.composition_poly_root); + transcript.append_bytes(proof.composition_poly_root()); // =================================== // ==========| Round 3 |========== @@ -1048,14 +1134,16 @@ pub trait IsStarkVerifier< ); // <<<< Receive values: tⱼ(zgᵏ) - let trace_ood_evaluations_columns = proof.trace_ood_evaluations.columns(); - for col in trace_ood_evaluations_columns.iter() { - for elem in col.iter() { - transcript.append_field_element(elem); + // Column-major append (matches `Table::columns()` order) reading the + // rows in place, without materializing transposed columns. + let ood = proof.trace_ood_evaluations(); + for col_idx in 0..ood.width() { + for row_idx in 0..ood.height() { + transcript.append_field_element(&ood.get_row(row_idx)[col_idx]); } } // <<<< Receive value: Hᵢ(z^N) - for element in proof.composition_poly_parts_ood_evaluation.iter() { + for element in proof.composition_poly_parts_ood_evaluation().iter() { transcript.append_field_element(element); } @@ -1063,7 +1151,7 @@ pub trait IsStarkVerifier< // ==========| Round 4 |========== // =================================== - let num_terms_composition_poly = proof.composition_poly_parts_ood_evaluation.len(); + let num_terms_composition_poly = proof.composition_poly_parts_ood_evaluation().len(); let num_terms_trace = air.context().transition_offsets.len() * air.step_size() * air.context().trace_columns; let gamma = transcript.sample_field_element(); @@ -1085,7 +1173,7 @@ pub trait IsStarkVerifier< let gammas = deep_composition_coefficients; // FRI commit phase - let merkle_roots = &proof.fri_layers_merkle_roots; + let merkle_roots = proof.fri_layers_merkle_roots(); let mut zetas = merkle_roots .iter() .map(|root| { @@ -1110,7 +1198,7 @@ pub trait IsStarkVerifier< // <<<< Receive the FRI final-polynomial coefficients (same Vec, same // order the prover appended them in `commit_phase_from_evaluations`). - for c in &proof.fri_final_poly_coeffs { + for c in proof.fri_final_poly_coeffs() { transcript.append_field_element(c); } @@ -1118,7 +1206,7 @@ pub trait IsStarkVerifier< let security_bits = air.context().proof_options.grinding_factor; let mut grinding_seed = [0u8; 32]; if security_bits > 0 - && let Some(nonce_value) = proof.nonce + && let Some(nonce_value) = proof.nonce() { grinding_seed = transcript.state(); transcript.append_bytes(&nonce_value.to_be_bytes()); @@ -1145,7 +1233,8 @@ pub trait IsStarkVerifier< /// Verifies a single table after round 1 has been replayed. fn verify_rounds_2_to_4( air: &dyn AIR, - proof: &StarkProof, + proof: StarkProofView<'_, Field, FieldExtension, PI>, + public_inputs: &PI, transcript: &mut impl IsStarkTranscript, rap_challenges: Vec>, ) -> bool @@ -1153,10 +1242,10 @@ pub trait IsStarkVerifier< FieldElement: AsBytes + Sync + Send, FieldElement: AsBytes + Sync + Send, { - let domain = new_verifier_domain(air, proof.trace_length); + let domain = new_verifier_domain(air, proof.trace_length()); // Verify there are enough queries - if proof.query_list.len() < air.options().fri_number_of_queries { + if proof.query_list_len() < air.options().fri_number_of_queries { return false; } @@ -1165,13 +1254,19 @@ pub trait IsStarkVerifier< #[cfg(feature = "instruments")] let timer1 = Instant::now(); - let challenges = - Self::replay_rounds_after_round_1(air, proof, &domain, transcript, rap_challenges); + let challenges = Self::replay_rounds_after_round_1( + air, + proof, + public_inputs, + &domain, + transcript, + rap_challenges, + ); // verify grinding let security_bits = air.context().proof_options.grinding_factor; if security_bits > 0 { - let nonce_is_valid = proof.nonce.is_some_and(|nonce_value| { + let nonce_is_valid = proof.nonce().is_some_and(|nonce_value| { grinding::is_valid_nonce(&challenges.grinding_seed, nonce_value, security_bits) }); @@ -1192,7 +1287,13 @@ pub trait IsStarkVerifier< #[cfg(feature = "instruments")] let timer2 = Instant::now(); - if !Self::step_2_verify_claimed_composition_polynomial(air, proof, &domain, &challenges) { + if !Self::step_2_verify_claimed_composition_polynomial( + air, + proof, + public_inputs, + &domain, + &challenges, + ) { #[cfg(not(feature = "test_fiat_shamir"))] error!("Composition Polynomial verification failed"); return false; diff --git a/executor/Cargo.toml b/executor/Cargo.toml index 5d1e4ae49..3f278e1c6 100644 --- a/executor/Cargo.toml +++ b/executor/Cargo.toml @@ -13,10 +13,3 @@ ecsm = { path = "../crypto/ecsm" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tiny-keccak = { version = "2.0", features = ["keccak"] } -# Exact pin: must match the fixture writer + guest so the rkyv ProgramInput -# layout the executor tests read stays consistent (see tooling/ethrex-fixtures). -rkyv = { version = "=0.8.16", features = ["std", "unaligned"] } -# Native reference for the ethrex tests (host-side `execution_program` with -# `NativeCrypto`). Pinned to the same ethrex rev as the guest ELF -# (executor/programs/rust/ethrex) — the open LambdaVM-backend PR branch. -ethrex-guest-program = { git = "https://github.com/lambdaclass/ethrex.git", rev = "156cb8d6a3974f411d71622eecd1b249ee37ff1c", package = "ethrex-guest-program" } diff --git a/executor/programs/rust/ef_io_demo/Cargo.lock b/executor/programs/rust/ef_io_demo/Cargo.lock new file mode 100644 index 000000000..84ea36965 --- /dev/null +++ b/executor/programs/rust/ef_io_demo/Cargo.lock @@ -0,0 +1,331 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "const-default" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b396d1f76d455557e1218ec8066ae14bba60b4b36ecd55577ba979f5db7ecaa" + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "ef_io_demo" +version = "0.1.0" +dependencies = [ + "lambda-vm-syscalls", +] + +[[package]] +name = "embedded-alloc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f2de9133f68db0d4627ad69db767726c99ff8585272716708227008d3f1bddd" +dependencies = [ + "const-default", + "critical-section", + "linked_list_allocator", + "rlsf", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "lambda-vm-syscalls" +version = "0.1.0" +dependencies = [ + "embedded-alloc", + "getrandom 0.2.17", + "getrandom 0.3.4", + "lazy_static", + "rand", + "riscv", + "thiserror", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "linked_list_allocator" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b23ac50abb8261cb38c6e2a7192d3302e0836dac1628f6a93b82b4fad185897" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "riscv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05cfa3f7b30c84536a9025150d44d26b8e1cc20ddf436448d74cd9591eefb25" +dependencies = [ + "critical-section", + "embedded-hal", + "paste", + "riscv-macros", + "riscv-pac", +] + +[[package]] +name = "riscv-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d323d13972c1b104aa036bc692cd08b822c8bbf23d79a27c526095856499799" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "riscv-pac" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436" + +[[package]] +name = "rlsf" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1646a59a9734b8b7a0ac51689388a60fe1625d4b956348e9de07591a1478457a" +dependencies = [ + "cfg-if", + "const-default", + "libc", + "rustversion", + "svgbobdoc", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "svgbobdoc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2c04b93fc15d79b39c63218f15e3fdffaa4c227830686e3b7c5f41244eb3e50" +dependencies = [ + "base64", + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-width", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "zerocopy" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75726053136156d419e285b9b7eddaaea9e3fea6ce32eed44a89901f0bd98de1" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4714fd92cf900833d49538023a9b3915155210801d1c1169eba513b2addefd71" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] diff --git a/executor/tests/README.md b/executor/tests/README.md index eddd3b525..39fd13813 100644 --- a/executor/tests/README.md +++ b/executor/tests/README.md @@ -5,6 +5,10 @@ The `ethrex_*.bin` files are rkyv-serialized `ethrex_guest_program::l1::ProgramInput` values consumed by the ethrex guest (`executor/programs/rust/ethrex`). +The native-reference tests live in `tooling/ethrex-tests` (a detached +workspace: ethrex pins rkyv `unaligned`, which must not feature-unify with the +main workspace's aligned proof format). + The ethrex guest, the native test reference, and the fixture generator are all pinned to the same ethrex revision (the open LambdaVM-backend PR branch, until it merges to `main`): diff --git a/executor/tests/rust.rs b/executor/tests/rust.rs index a62c481af..1c13ad1a5 100644 --- a/executor/tests/rust.rs +++ b/executor/tests/rust.rs @@ -343,72 +343,6 @@ fn test_args_panics() { } } -/// Larger-block smoke test: a synthetic ethrex block with 10 ETH transfers. -/// (Replaces the old `ethrex_hoodi.bin` real-block fixture, which was in the -/// pre-Crypto-trait ethrex format and no longer deserializes.) Fixture is -/// generated by `tooling/ethrex-fixtures`; see `tests/README.md`. -#[ignore = "heavier synthetic block (10 txs); run in the dedicated --ignored CI step"] -#[test] -fn test_ethrex() { - use ethrex_guest_program::crypto::NativeCrypto; - use ethrex_guest_program::l1::{ProgramInput, execution_program}; - use rkyv::rancor::Error; - use std::fs; - use std::sync::Arc; - let inputs = fs::read("tests/ethrex_10_transfers.bin").unwrap(); - let input = rkyv::from_bytes::(&inputs).unwrap(); - let output = execution_program(input, Arc::new(NativeCrypto)).unwrap(); - run_program_and_check_public_output( - "./program_artifacts/rust/ethrex.elf", - output.encode(), - inputs, - ); -} - -/// Executes a stateless ethrex block containing a single (plain ETH transfer) -/// transaction. Execution only — no proving — against the ethrex guest ELF -/// built from the same pinned ethrex revision as the native reference. The -/// fixture is a serialized `ProgramInput`; see `tests/README.md` for provenance. -/// -/// The fixture is generated by `tooling/ethrex-fixtures` at the same ethrex rev -/// as the guest (see `tests/README.md`). -#[test] -fn test_ethrex_simple_tx() { - use ethrex_guest_program::crypto::NativeCrypto; - use ethrex_guest_program::l1::{ProgramInput, execution_program}; - use rkyv::rancor::Error; - use std::sync::Arc; - let inputs = std::fs::read("tests/ethrex_simple_tx.bin").unwrap(); - let input = rkyv::from_bytes::(&inputs).unwrap(); - let output = execution_program(input, Arc::new(NativeCrypto)).unwrap(); - run_program_and_check_public_output( - "./program_artifacts/rust/ethrex.elf", - output.encode(), - inputs, - ); -} - -/// Executes a stateless ethrex block with NO transactions (empty block). -/// Execution only — no proving. Pins the committed `ethrex_empty_block.bin` -/// fixture into the default suite so its rkyv `ProgramInput` layout (the 0-tx -/// edge case) is exercised and stays consistent with the guest across ethrex -/// rev bumps. Mirrors `test_ethrex_simple_tx`; see `tests/README.md`. -#[test] -fn test_ethrex_empty_block() { - use ethrex_guest_program::crypto::NativeCrypto; - use ethrex_guest_program::l1::{ProgramInput, execution_program}; - use rkyv::rancor::Error; - use std::sync::Arc; - let inputs = std::fs::read("tests/ethrex_empty_block.bin").unwrap(); - let input = rkyv::from_bytes::(&inputs).unwrap(); - let output = execution_program(input, Arc::new(NativeCrypto)).unwrap(); - run_program_and_check_public_output( - "./program_artifacts/rust/ethrex.elf", - output.encode(), - inputs, - ); -} - #[ignore = "Ignored until the vm is fast enough to run this test"] #[test] fn test_ckzg() { diff --git a/prover/Cargo.toml b/prover/Cargo.toml index 186bd18ce..656d35b03 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -20,14 +20,11 @@ crypto = { path = "../crypto/crypto" } math = { path = "../crypto/math" } executor = { path = "../executor" } ecsm = { path = "../crypto/ecsm" } -serde = { version = "1.0", features = ["derive"] } -# The recursion guest-input blob codec (see `recursion::encode_guest_input`); -# no_std+alloc, so the in-VM guest build (default-features = false) is fine. -postcard = { version = "1.0", features = ["alloc"] } rayon = { version = "1.8.0", optional = true } sysinfo = { version = "0.31", default-features = false, features = ["system"] } log = "0.4" digest = "0.10.7" +rkyv = { version = "0.8.10", default-features = false, features = ["alloc", "bytecheck", "aligned"] } [dev-dependencies] env_logger = "*" diff --git a/prover/src/continuation.rs b/prover/src/continuation.rs index 54a3ee583..2e5c56a8b 100644 --- a/prover/src/continuation.rs +++ b/prover/src/continuation.rs @@ -341,7 +341,7 @@ struct EpochStart<'a> { /// Note: continuation epochs use the L2G memory bookend, so PAGE is skipped and the /// per-epoch page config set is empty — the verifier builds the AIRs with no PAGE /// tables rather than trusting any prover-supplied page config. -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] struct EpochProof { /// The epoch's STARK proof (its tables + the epoch-local L2G sub-table last). proof: MultiProof, @@ -377,8 +377,8 @@ struct EpochProof { /// AIR-count checks, so a wrong value is rejected; the count is also bound-checked up front. /// /// `verify_continuation` checks this using only the bundle and the ELF. It derives -/// serde, so it round-trips through `bincode` exactly like a monolithic `VmProof`. -#[derive(serde::Serialize, serde::Deserialize)] +/// rkyv, so it round-trips exactly like a monolithic `VmProof`. +#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] pub struct ContinuationProof { epochs: Vec, global: MultiProof, @@ -1202,17 +1202,18 @@ mod tests { assert_eq!(out.as_deref(), Some(&[0xAA, 0xBB, 0xCC, 0xDD][..])); } - // A bundle survives a bincode round-trip and still verifies to the same output — + // A bundle survives an rkyv round-trip and still verifies to the same output — // the serialization path the CLI's `prove`/`verify --continuations` relies on. #[test] - fn test_continuation_bincode_roundtrip() { + fn test_continuation_rkyv_roundtrip() { let _ = env_logger::builder().is_test(true).try_init(); let elf_bytes = asm_elf_bytes("test_commit_split"); let bundle = prove_continuation(&elf_bytes, &[], 4, &ProofOptions::default_test_options()).unwrap(); - let bytes = bincode::serialize(&bundle).unwrap(); - let restored: ContinuationProof = bincode::deserialize(&bytes).unwrap(); + let bytes = rkyv::to_bytes::(&bundle).unwrap(); + let restored: ContinuationProof = + rkyv::from_bytes::<_, rkyv::rancor::Error>(&bytes).unwrap(); let out = verify_continuation(&elf_bytes, &restored, &ProofOptions::default_test_options()) .unwrap(); @@ -1339,11 +1340,12 @@ mod tests { "a program that reads private input must have a private-input page in the global proof" ); - // The serialized bundle must carry no raw private bytes: it survives a bincode + // The serialized bundle must carry no raw private bytes: it survives an rkyv // round-trip and still verifies using ONLY the bundle + ELF (no private input // is passed to `verify_continuation`). - let bytes = bincode::serialize(&bundle).unwrap(); - let restored: ContinuationProof = bincode::deserialize(&bytes).unwrap(); + let bytes = rkyv::to_bytes::(&bundle).unwrap(); + let restored: ContinuationProof = + rkyv::from_bytes::<_, rkyv::rancor::Error>(&bytes).unwrap(); let out = verify_continuation(&elf_bytes, &restored, &ProofOptions::default_test_options()) .unwrap(); assert_eq!( diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 49fd0bd30..0bed63eb2 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -61,17 +61,18 @@ use crate::test_utils::{ // Re-exported for downstream hosts and verifier guests (e.g. the in-VM // recursion guest): `Commitment` is carried in the guest's private input -// (see `recursion::GuestInput`); the proof-options types name the parameters +// (see `GuestInput`); the proof-options types name the parameters // fixed at guest build time (`recursion::Preset`). pub use stark::config::Commitment; pub use stark::proof::options::{GoldilocksCubicProofOptions, ProofOptions}; use stark::proof::stark::MultiProof; +use stark::proof::view::StarkProofView; /// A run-length encoded range of contiguous zero-initialized 4KB pages. /// /// Represents `count` contiguous pages starting at `base`, used for /// runtime-allocated memory (stack, heap) not covered by ELF segments. -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[derive(Debug, Clone, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] pub struct RuntimePageRange { /// Base address of the first page (4KB-aligned). pub base: u64, @@ -86,7 +87,7 @@ pub const FIXED_TABLE_COUNT: usize = 10; /// Number of chunks for each split table. /// The verifier needs this to reconstruct matching AIRs. -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[derive(Debug, Clone, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] pub struct TableCounts { pub cpu: usize, pub lt: usize, @@ -158,7 +159,7 @@ impl TableCounts { /// A complete VM proof bundle containing the STARK proof and metadata /// needed by the verifier to reconstruct the AIR configuration. -#[derive(Debug, serde::Serialize, serde::Deserialize)] +#[derive(Debug, Clone, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] pub struct VmProof { /// The multi-table STARK proof. pub proof: MultiProof, @@ -177,6 +178,232 @@ pub struct VmProof { pub num_private_input_pages: usize, } +/// The private-input bundle the recursion verifier guest consumes: an inner +/// proof plus the DECODE/ELF-data-page commitments supplied instead of +/// recomputed in-VM (see `bench_vs/lambda/recursion`), and the inner ELF bytes +/// needed to reconstruct the AIRs and (via `statement::program_id_from_elf`) +/// bind the supplied roots to a program identity. +/// +/// Archived as one rkyv blob so the guest reads every field straight from the +/// input buffer with no deserialization pass. +#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] +pub struct GuestInput { + pub vm_proof: VmProof, + pub inner_elf: Vec, + pub decode_commitment: Commitment, + pub page_commitments: Vec<(u64, Commitment)>, +} + +// ============================================================================ +// Recursion-input wire format: aligning magic prefix + rkyv archive +// ============================================================================ +// +// The guest reads the archive in place with naturally-aligned loads (archived +// field elements are 8-aligned; we require 16 for headroom), and the executor +// traps unaligned doubleword loads. The executor maps the private input as +// `[u32 len][payload...]` with the payload at `PRIVATE_INPUT_START + 4`, which +// is only 4-aligned. A fixed prefix pads the payload so the archive that +// follows lands 16-aligned, and doubles as a magic + version tag the guest +// validates before the unsafe access. + +/// 4-byte magic identifying a lambda-vm recursion input blob ("LVMR"). +pub const RECURSION_INPUT_MAGIC: [u8; 4] = *b"LVMR"; + +/// Wire-format version of the recursion input blob. +pub const RECURSION_INPUT_VERSION: u32 = 1; + +/// Required alignment (bytes) of the archive's first byte in guest memory. +pub const RECURSION_INPUT_ALIGN: usize = 16; + +/// Aligning prefix length: `magic(4) + version(4) + reserved(4) = 12` bytes, +/// chosen so the archive starts 16-aligned given the executor's +/// `PRIVATE_INPUT_START + 4` payload base. Asserted below. +pub const RECURSION_INPUT_PREFIX_LEN: usize = 12; + +const _: () = { + let payload_base = (executor::vm::memory::PRIVATE_INPUT_START_INDEX as usize) + 4; + let pad = + (RECURSION_INPUT_ALIGN - (payload_base % RECURSION_INPUT_ALIGN)) % RECURSION_INPUT_ALIGN; + assert!( + RECURSION_INPUT_PREFIX_LEN == pad, + "prefix length must align the archive to RECURSION_INPUT_ALIGN given the private-input payload base", + ); + assert!( + (payload_base + RECURSION_INPUT_PREFIX_LEN).is_multiple_of(RECURSION_INPUT_ALIGN), + "archive must start at a RECURSION_INPUT_ALIGN-aligned guest address", + ); +}; + +/// Encode a [`GuestInput`] into the on-wire blob: a 12-byte +/// `magic + version + reserved` prefix followed by the rkyv archive. The prefix +/// both aligns the archive in guest memory (so in-place reads don't trap) and +/// tags the format/version so the guest can validate before the unsafe access. +pub fn encode_recursion_input(input: &GuestInput) -> Result, Error> { + let archive = rkyv::to_bytes::(input) + .map_err(|e| Error::Execution(format!("rkyv encode failed: {e}")))?; + let mut blob = Vec::with_capacity(RECURSION_INPUT_PREFIX_LEN + archive.len()); + blob.extend_from_slice(&RECURSION_INPUT_MAGIC); + blob.extend_from_slice(&RECURSION_INPUT_VERSION.to_le_bytes()); + blob.extend_from_slice(&[0u8; 4]); // reserved + debug_assert_eq!(blob.len(), RECURSION_INPUT_PREFIX_LEN); + blob.extend_from_slice(&archive); + Ok(blob) +} + +/// Validate the wire prefix and return the archive bytes (zero-copy slice). +/// Returns `None` if the magic or version doesn't match — the caller should +/// halt cleanly rather than proceed into an `access_unchecked`. +pub fn recursion_archive_bytes(blob: &[u8]) -> Option<&[u8]> { + if blob.len() < RECURSION_INPUT_PREFIX_LEN { + return None; + } + if blob[0..4] != RECURSION_INPUT_MAGIC { + return None; + } + let version = u32::from_le_bytes([blob[4], blob[5], blob[6], blob[7]]); + if version != RECURSION_INPUT_VERSION { + return None; + } + Some(&blob[RECURSION_INPUT_PREFIX_LEN..]) +} + +/// Result of a recursion-blob verification: the verdict plus the inner +/// proof's committed public output (zero-copy from the blob), which the +/// recursion guest folds into `program_id(...) ‖ public_output`. +pub struct RecursionVerification<'a> { + /// Whether the inner proof verified. + pub ok: bool, + /// The inner proof's committed public output (zero-copy from the blob). + pub public_output: &'a [u8], + /// The inner ELF bytes (zero-copy from the blob). + pub inner_elf: &'a [u8], + /// The DECODE commitment supplied for the inner proof (zero-copy from the blob). + pub decode_commitment: Commitment, + /// The ELF-data-page commitments supplied for the inner proof (materialized — + /// small, bounded by the ELF's data-page count). + pub page_commitments: Vec<(u64, Commitment)>, + /// Full-ELF Keccak digest of `inner_elf`, computed once here — callers + /// folding a `program_id` (e.g. `recursion::verify_and_attest_blob`) reuse + /// it instead of re-hashing the whole ELF. + pub elf_digest: [u8; 32], + /// `inner_elf`'s entry point, from the same [`Elf::load`] used to verify — + /// callers folding a `program_id` reuse it instead of re-parsing the ELF. + pub entry_point: u64, +} + +/// Verify a recursion-input blob produced by [`encode_recursion_input`]. +/// +/// `proof_options` is caller-supplied (never taken from the blob — an attacker +/// could otherwise pick trivially weak options and have the guest accept as if +/// a real proof had been checked; see `bench_vs/lambda/recursion`). +/// +/// The archive is read **in place**: the STARK proof is verified straight from +/// the blob with no deserialization and no per-field allocation. Only tiny +/// metadata (table counts, page ranges, page commitments) is materialized. +pub fn verify_recursion_blob<'a>( + blob: &'a [u8], + proof_options: &ProofOptions, +) -> Result, Error> { + use rkyv::rancor::Error as RkyvError; + + // Validate + strip the aligning magic/version prefix. In the guest the + // returned slice starts at the 16-aligned archive base (the prefix exists + // precisely so the archive lands aligned at + // `PRIVATE_INPUT_START + 4 + PREFIX_LEN`), so the in-place doubleword + // loads do not trap. + let archive_bytes = recursion_archive_bytes(blob).ok_or_else(|| { + Error::Execution(String::from("recursion blob: bad magic or version")) + })?; + + // A host caller's buffer carries no alignment guarantee (`Vec` is + // align-1) — in-place access there would be UB. Fall back to one aligned + // copy when the base is misaligned; the guest path is aligned by + // construction and stays zero-copy. + let mut aligned_fallback = rkyv::util::AlignedVec::<{ RECURSION_INPUT_ALIGN }>::new(); + let archive: &[u8] = if (archive_bytes.as_ptr() as usize).is_multiple_of(RECURSION_INPUT_ALIGN) + { + archive_bytes + } else { + aligned_fallback.extend_from_slice(archive_bytes); + &aligned_fallback + }; + + // SAFETY: `archive` is RECURSION_INPUT_ALIGN-aligned (checked above). No + // structural validation is performed. On the host, callers must pass blobs + // produced by our own `encode_recursion_input`. In the recursion guest the + // blob is PROVER-SUPPLIED: a malformed archive makes the in-place reads + // hit arbitrary guest addresses, which the VM memory model defines (reads + // of unmapped cells return 0), so the outcome is garbage field elements + // that fail the cryptographic checks or an in-guest panic — verification + // failure either way, never a false accept. + let archived = unsafe { rkyv::access_unchecked::(archive) }; + + // Materialize only the small metadata; the proof stays in the buffer. + let table_counts: TableCounts = + rkyv::deserialize::(&archived.vm_proof.table_counts) + .map_err(|e| Error::Execution(format!("rkyv deserialize table_counts failed: {e}")))?; + let runtime_page_ranges: Vec = rkyv::deserialize::< + Vec, + RkyvError, + >(&archived.vm_proof.runtime_page_ranges) + .map_err(|e| Error::Execution(format!("rkyv deserialize page ranges failed: {e}")))?; + let page_commitments: Vec<(u64, Commitment)> = + rkyv::deserialize::, RkyvError>(&archived.page_commitments) + .map_err(|e| Error::Execution(format!("rkyv deserialize page commitments failed: {e}")))?; + let num_private_input_pages = archived.vm_proof.num_private_input_pages.to_native() as usize; + // Bytes read straight from the archived buffer (zero-copy). + let inner_elf: &[u8] = archived.inner_elf.as_slice(); + let public_output: &[u8] = archived.vm_proof.public_output.as_slice(); + let decode_commitment: Commitment = archived.decode_commitment; + + // Rebase the returned slices onto the caller's buffer: `archive` may be + // the aligned fallback copy, whose lifetime ends with this call. Same + // bytes at the same offsets in both buffers. + let rebase = |s: &[u8]| -> &'a [u8] { + let offset = s.as_ptr() as usize - archive.as_ptr() as usize; + &archive_bytes[offset..offset + s.len()] + }; + let inner_elf_rebased = rebase(inner_elf); + let public_output_rebased = rebase(public_output); + + // Single `Elf::load` and single full-ELF Keccak, shared between the + // statement absorb below and any `program_id` fold the caller does + // (`recursion::verify_and_attest_blob`) — see `RecursionVerification`. + let program = Elf::load(inner_elf).map_err(|e| Error::ElfLoad(format!("{e}")))?; + let elf_digest = statement::elf_digest(inner_elf); + + let views: Vec> = archived + .vm_proof + .proof + .proofs + .as_slice() + .iter() + .map(StarkProofView::Archived) + .collect(); + let ok = verify_proof_parts( + &views, + &table_counts, + &runtime_page_ranges, + num_private_input_pages, + public_output, + &program, + &elf_digest, + proof_options, + Some(decode_commitment), + Some(&page_commitments), + )?; + + Ok(RecursionVerification { + ok, + public_output: public_output_rebased, + inner_elf: inner_elf_rebased, + decode_commitment, + page_commitments, + elf_digest, + entry_point: program.entry_point, + }) +} + /// Error type for the prover crate. #[derive(Debug)] pub enum Error { @@ -730,6 +957,38 @@ pub(crate) fn compute_expected_commit_bus_balance( compute_commit_bus_offset(public_output_bytes, start_index, &z, &alpha) } +/// View counterpart of [`replay_transcript_phase_a`]: replays Phase A over a +/// proof view (owned or archived-in-place), with no `MultiProof` +/// deserialization required either way. +pub(crate) fn replay_transcript_phase_a_view( + airs: &[&dyn AIR], + proofs: &[StarkProofView], + transcript: &mut DefaultTranscript, +) -> (FieldElement, FieldElement) { + for (air, proof) in airs.iter().zip(proofs) { + if air.is_preprocessed() { + transcript.append_bytes(&air.precomputed_commitment()); + } + transcript.append_bytes(proof.lde_trace_main_merkle_root()); + } + let z: FieldElement = transcript.sample_field_element(); + let alpha: FieldElement = transcript.sample_field_element(); + (z, alpha) +} + +/// View counterpart of [`compute_expected_commit_bus_balance`]: operates on a +/// proof view slice (owned or archived-in-place). +pub(crate) fn compute_expected_commit_bus_balance_view( + airs: &[&dyn AIR], + proofs: &[StarkProofView], + public_output_bytes: &[u8], + start_index: u64, + transcript: &mut DefaultTranscript, +) -> Option> { + let (z, alpha) = replay_transcript_phase_a_view(airs, proofs, transcript); + compute_commit_bus_offset(public_output_bytes, start_index, &z, &alpha) +} + /// Bind the final cross-epoch GlobalMemory proof to the per-epoch proofs. /// /// The final proof commits one local-to-global sub-table per epoch as its first @@ -1030,7 +1289,7 @@ pub fn verify_with_options( /// [`verify_with_options`] with the ELF already parsed and digested. Callers /// that need the parsed ELF or the digest for other purposes reuse them — the -/// recursion attestation (`recursion::verify_and_attest`) shares one +/// recursion attestation (`recursion::verify_and_attest_blob`) shares one /// `Elf::load` and one full-ELF Keccak between verification and the /// `program_id` fold, which matters in-guest where both are expensive. pub(crate) fn verify_prepared( @@ -1040,40 +1299,74 @@ pub(crate) fn verify_prepared( proof_options: &ProofOptions, decode_commitment: Option, page_commitments: Option<&[(u64, Commitment)]>, +) -> Result { + let views: Vec> = + vm_proof.proof.proofs.iter().map(StarkProofView::Owned).collect(); + + verify_proof_parts( + &views, + &vm_proof.table_counts, + &vm_proof.runtime_page_ranges, + vm_proof.num_private_input_pages, + &vm_proof.public_output, + program, + elf_digest, + proof_options, + decode_commitment, + page_commitments, + ) +} + +/// The single VM-proof verification implementation, given the proof's +/// metadata fields plus an already-parsed ELF and its digest. Both +/// [`verify_prepared`] (owned proof) and [`verify_recursion_blob`] (guest +/// blob, zero-copy) funnel here, passing a [`StarkProofView`] slice over +/// their respective (owned or archived) proof data — no serialization, no +/// duplicated verification logic, and no repeated `Elf::load`/digest. +#[allow(clippy::too_many_arguments)] +fn verify_proof_parts( + proofs: &[StarkProofView], + table_counts: &TableCounts, + runtime_page_ranges: &[RuntimePageRange], + num_private_input_pages: usize, + public_output: &[u8], + program: &Elf, + elf_digest: &[u8; 32], + proof_options: &ProofOptions, + decode_commitment: Option, + page_commitments: Option<&[(u64, Commitment)]>, ) -> Result { // Validate table_counts before constructing AIRs. // A malicious prover could set counts to 0, removing entire constraint sets. - vm_proof.table_counts.validate()?; + table_counts.validate()?; // Bound num_private_input_pages before allocating PageConfigs — the tight honest // max, shared with the continuation verifier (see `page::max_private_input_pages`). { let max_pages = crate::tables::page::max_private_input_pages(); - if vm_proof.num_private_input_pages > max_pages { + if num_private_input_pages > max_pages { return Err(Error::InvalidTableCounts(format!( - "num_private_input_pages ({}) exceeds max ({max_pages})", - vm_proof.num_private_input_pages, + "num_private_input_pages ({num_private_input_pages}) exceeds max ({max_pages})", ))); } } let page_configs = Traces::page_configs_from_elf_and_runtime( program, - &vm_proof.runtime_page_ranges, - vm_proof.num_private_input_pages, + runtime_page_ranges, + num_private_input_pages, ); // Cross-check: table_counts must match the number of sub-proofs. // FIXED_TABLE_COUNT always-present tables, plus page tables. - let expected_proof_count = - vm_proof.table_counts.total() + FIXED_TABLE_COUNT + page_configs.len(); - if expected_proof_count != vm_proof.proof.proofs.len() { + let expected_proof_count = table_counts.total() + FIXED_TABLE_COUNT + page_configs.len(); + if expected_proof_count != proofs.len() { return Err(Error::InvalidTableCounts(format!( "table_counts total ({}) + {FIXED_TABLE_COUNT} fixed + {} pages = {}, but proof contains {} sub-proofs", - vm_proof.table_counts.total(), + table_counts.total(), page_configs.len(), expected_proof_count, - vm_proof.proof.proofs.len(), + proofs.len(), ))); } @@ -1082,7 +1375,7 @@ pub(crate) fn verify_prepared( proof_options, false, &page_configs, - &vm_proof.table_counts, + table_counts, decode_commitment, true, None, @@ -1090,7 +1383,7 @@ pub(crate) fn verify_prepared( None, ); - // Recompute the COMMIT output bus offset from VmProof.public_output. + // Recompute the COMMIT output bus offset from the public output. // If public_output was tampered, the recomputed offset won't match the // actual bus total in the proof, and multi_verify will reject. let air_refs = airs.air_refs(); @@ -1103,10 +1396,10 @@ pub(crate) fn verify_prepared( &mut transcript, StatementKind::Monolithic, elf_digest, - &vm_proof.public_output, - &vm_proof.table_counts, - vm_proof.num_private_input_pages, - &vm_proof.runtime_page_ranges, + public_output, + table_counts, + num_private_input_pages, + runtime_page_ranges, proof_options.fri_final_poly_log_degree, ); @@ -1114,10 +1407,10 @@ pub(crate) fn verify_prepared( // independently of the multi_verify transcript, but both must start from // the same statement-bound state. let mut transcript_for_replay = transcript.clone(); - let expected_bus_balance = match compute_expected_commit_bus_balance( + let expected_bus_balance = match compute_expected_commit_bus_balance_view( &air_refs, - &vm_proof.proof, - &vm_proof.public_output, + proofs, + public_output, // Monolithic proof: commits are indexed from 0. 0, &mut transcript_for_replay, @@ -1129,9 +1422,9 @@ pub(crate) fn verify_prepared( stark::profile_markers::step_marker::<{ stark::profile_markers::STEP_AIRS_AND_BUS_BALANCE_DONE }>( ); - Ok(Verifier::multi_verify( + Ok(Verifier::multi_verify_views( &air_refs, - &vm_proof.proof, + proofs, &mut transcript, &expected_bus_balance, )) diff --git a/prover/src/recursion.rs b/prover/src/recursion.rs index 3ec22130a..6bb3b1247 100644 --- a/prover/src/recursion.rs +++ b/prover/src/recursion.rs @@ -1,13 +1,13 @@ //! Host and guest API for the naive (single-step) recursion pipeline. //! //! The recursion verifier guest (`bench_vs/lambda/recursion`) verifies an -//! inner lambda-vm proof in-VM. Its private input ([`GuestInput`], built -//! host-side by [`encode_guest_input`]) carries the inner program's +//! inner lambda-vm proof in-VM. Its private input (a [`crate::GuestInput`], +//! built host-side by [`encode_guest_input`]) carries the inner program's //! precomputed DECODE/ELF-data-page roots so the guest skips the in-VM //! FFT + Merkle rebuild. `verify_with_options` uses supplied roots verbatim — //! it does NOT bind them to the inner ELF — so on success the guest commits //! an attestation that folds them into the identity instead: -//! `program_id || inner_public_output` (see [`verify_and_attest`]). +//! `program_id || inner_public_output` (see [`verify_and_attest_blob`]). //! //! Trust model: the attestation is NOT self-enforcing. A consumer of the //! outer proof MUST recompute the id from the inner ELF it trusts and @@ -84,11 +84,6 @@ impl Preset { } } -/// The guest's private-input layout, postcard-encoded by -/// [`encode_guest_input`] and decoded verbatim by the guest: -/// `(inner proof, inner ELF bytes, DECODE root, ELF-data-page roots)`. -pub type GuestInput = (VmProof, Vec, Commitment, Vec<(u64, Commitment)>); - /// Precompute the DECODE and ELF-data-page preprocessed roots for `elf_bytes` /// under `opts` — the values the guest receives via private input instead of /// recomputing in-VM, and the values [`expected_program_id`] recomputes @@ -116,20 +111,20 @@ pub fn precomputed_commitments( } /// Build the guest's private-input blob for `inner_proof` of `inner_elf`: -/// precomputes the roots and postcard-encodes the [`GuestInput`] tuple. +/// precomputes the roots and rkyv-encodes a [`crate::GuestInput`] (see +/// [`crate::encode_recursion_input`]). pub fn encode_guest_input( inner_proof: &VmProof, inner_elf: &[u8], opts: &ProofOptions, ) -> Result, Error> { let (decode_commitment, page_commitments) = precomputed_commitments(inner_elf, opts)?; - postcard::to_allocvec(&( - inner_proof, - inner_elf, - &decode_commitment, - &page_commitments, - )) - .map_err(|e| Error::Recursion(format!("postcard encode: {e}"))) + crate::encode_recursion_input(&crate::GuestInput { + vm_proof: inner_proof.clone(), + inner_elf: inner_elf.to_vec(), + decode_commitment, + page_commitments, + }) } /// Domain tag for [`program_id`]. @@ -137,7 +132,7 @@ const PROGRAM_ID_TAG: &[u8] = b"LAMBDAVM_PROGRAM_ID_V1"; /// [`program_id`] from a precomputed ELF digest and entry point — the guest /// path, sharing one full-ELF Keccak pass with the verify-side statement -/// absorb (see [`verify_and_attest`]). +/// absorb (see [`verify_and_attest_blob`]). pub fn program_id_from_digest( elf_digest: &[u8; 32], pc_start: u64, @@ -199,43 +194,32 @@ pub fn program_id_from_elf( )) } -/// Verify an inner proof against supplied roots and, on success, produce the -/// attestation bytes the recursion guest commits: +/// Verify the guest's private-input blob ([`encode_guest_input`]) in place and, +/// on success, produce the attestation bytes the recursion guest commits: /// `program_id(elf, roots) || inner_public_output`. `Ok(None)` means the /// proof did not verify. This is the guest's whole job in one call; it does a -/// single `Elf::load` and a single full-ELF Keccak, shared between the -/// statement absorb and the `program_id` fold. +/// single `Elf::load` and a single full-ELF Keccak (inside +/// [`crate::verify_recursion_blob`]), shared between the statement absorb and +/// the `program_id` fold — no deserialization pass over the inner proof. /// /// The attestation binds identity only for a consumer that recomputes the id /// from a trusted ELF ([`check_attestation`]) — see the module docs. -pub fn verify_and_attest( - vm_proof: &VmProof, - elf_bytes: &[u8], +pub fn verify_and_attest_blob( + blob: &[u8], proof_options: &ProofOptions, - decode_commitment: Commitment, - page_commitments: &[(u64, Commitment)], ) -> Result>, Error> { - let program = Elf::load(elf_bytes).map_err(|e| Error::ElfLoad(format!("{e}")))?; - let digest = elf_digest(elf_bytes); - let ok = crate::verify_prepared( - vm_proof, - &program, - &digest, - proof_options, - Some(decode_commitment), - Some(page_commitments), - )?; - if !ok { + let verification = crate::verify_recursion_blob(blob, proof_options)?; + if !verification.ok { return Ok(None); } let id = program_id_from_digest( - &digest, - program.entry_point, - &decode_commitment, - page_commitments, + &verification.elf_digest, + verification.entry_point, + &verification.decode_commitment, + &verification.page_commitments, ); let mut attestation = id.to_vec(); - attestation.extend_from_slice(&vm_proof.public_output); + attestation.extend_from_slice(verification.public_output); Ok(Some(attestation)) } diff --git a/prover/src/tests/recursion_smoke_test.rs b/prover/src/tests/recursion_smoke_test.rs index 61659a372..15817df3f 100644 --- a/prover/src/tests/recursion_smoke_test.rs +++ b/prover/src/tests/recursion_smoke_test.rs @@ -33,8 +33,8 @@ fn read_guest_elf(root: &std::path::Path, name: &str) -> Vec { /// Prove `inner_elf` under `opts` and build the guest's private-input blob via /// [`recursion::encode_guest_input`] (which precomputes the DECODE/page roots -/// and postcard-encodes the [`recursion::GuestInput`] tuple). Returns the proof -/// and the blob. +/// and rkyv-encodes the [`crate::GuestInput`]). Returns the proof and the +/// blob. fn prove_inner_and_encode_blob( tag: &str, inner_elf: &[u8], @@ -55,7 +55,7 @@ fn prove_inner_and_encode_blob( let blob = recursion::encode_guest_input(&inner_proof, inner_elf, opts) .expect("recursion::encode_guest_input failed"); - eprintln!("[{tag}] postcard blob: {} bytes", blob.len()); + eprintln!("[{tag}] rkyv blob: {} bytes", blob.len()); (inner_proof, blob) } @@ -200,7 +200,7 @@ fn resolve_pc(symbols: &executor::elf::SymbolTable, pc: u64) -> String { /// so `multi_verify`'s per-table `3,4,5,6` repetition re-attributes cycles to /// the correct step on each table's `6->3` transition instead of latching at 6. const STEP_LABELS: [&str; 7] = [ - "0. setup (alloc init + postcard decode)", + "0. setup (alloc init + blob prefix check)", "1. airs_and_bus_balance (Elf::load/VmAirs::new preprocessed FFT+Merkle/bus balance)", "2. multi_verify setup (transcript replay phase A/B, per-table fork)", "3. step 1: replay_rounds_after_round_1", @@ -493,48 +493,42 @@ fn run_recursion_pipeline( fn test_recursion_blob_decodes_and_verifies_on_host() { let root = workspace_root(); let empty_elf_bytes = read_guest_elf(&root, "empty"); - let (_inner, blob) = + let (inner, blob) = prove_inner_and_encode_blob("roundtrip", &empty_elf_bytes, &[], &MIN_PROOF_OPTIONS); - // Decode exactly as the guest does (built with the `min` feature). - let decoded: Result = postcard::from_bytes(&blob); - let (vm_proof, inner_elf, decode_commitment, page_commitments) = match decoded { - Ok(t) => t, - Err(e) => panic!("[roundtrip] postcard DECODE failed (this is the guest panic): {e}"), - }; - eprintln!( - "[roundtrip] decode ok: elf {} bytes, {} page commitments", - inner_elf.len(), - page_commitments.len(), - ); - - // Mirror the guest exactly: verify_and_attest over the supplied roots. - let attestation = match recursion::verify_and_attest( - &vm_proof, - &inner_elf, - &MIN_PROOF_OPTIONS, - decode_commitment, - &page_commitments, - ) { + // Mirror the guest exactly: decode + verify_and_attest_blob over the blob. + let attestation = match recursion::verify_and_attest_blob(&blob, &MIN_PROOF_OPTIONS) { Ok(Some(a)) => { - eprintln!("[roundtrip] verify_and_attest accepted — guest path is sound"); + eprintln!("[roundtrip] verify_and_attest_blob accepted — guest path is sound"); a } Ok(None) => panic!( - "[roundtrip] verify_and_attest returned None (guest hits the failed-verification expect) — proof did not survive the postcard round-trip" + "[roundtrip] verify_and_attest_blob returned None (guest hits the failed-verification expect) — proof did not survive the rkyv round-trip" ), - Err(e) => panic!("[roundtrip] verify_and_attest ERRORED (guest hits .expect): {e:?}"), + Err(e) => panic!("[roundtrip] verify_and_attest_blob ERRORED (guest hits .expect): {e:?}"), }; // Consumer check: the committed attestation must bind to the trusted inner // ELF and carry the inner proof's public output. - let output = recursion::check_attestation(&attestation, &inner_elf, &MIN_PROOF_OPTIONS) + let output = recursion::check_attestation(&attestation, &empty_elf_bytes, &MIN_PROOF_OPTIONS) .expect("check_attestation errored") .expect("attestation must match the trusted inner ELF (program_id recompute+compare)"); assert_eq!( - output, vm_proof.public_output, + output, inner.public_output, "attested public output must equal the inner proof's public output" ); + + // Host buffers carry no alignment guarantee, so `verify_recursion_blob` + // must accept the blob at any base alignment (falling back to an aligned + // copy when needed). The plain call above already exercises the common + // misaligned case (`Vec` base + 12-byte prefix → 4-aligned archive); + // shifting the base by 4 covers another residue class. + let mut padded: Vec = Vec::with_capacity(blob.len() + 4); + padded.extend_from_slice(&[0u8; 4]); + padded.extend_from_slice(&blob); + let v = crate::verify_recursion_blob(&padded[4..], &MIN_PROOF_OPTIONS) + .expect("verify_recursion_blob errored on misaligned buffer"); + assert!(v.ok, "misaligned-buffer verify must also succeed"); } /// Corrupting a private-input commitment on an *honest* proof makes diff --git a/syscalls/src/syscalls.rs b/syscalls/src/syscalls.rs index 491315ecb..8f6ee31c1 100644 --- a/syscalls/src/syscalls.rs +++ b/syscalls/src/syscalls.rs @@ -97,6 +97,27 @@ pub fn get_private_input() -> Vec { unimplemented!("syscalls are only implemented for riscv64 targets"); } +/// Borrow the private input bytes in place from the memory-mapped region — +/// no copy, no allocation. Same layout as [`get_private_input`]; the returned +/// slice starts at `PRIVATE_INPUT_START + 4` (a 4-aligned address) and lives +/// for the whole execution (the host never remaps the region). +#[cfg(target_arch = "riscv64")] +pub fn get_private_input_slice() -> &'static [u8] { + // SAFETY: The host pre-loads private input at PRIVATE_INPUT_START before + // execution and never remaps it afterward, so the returned slice is valid + // for the `'static` lifetime of the guest's single-threaded execution + // region, which stays mapped and unmodified for the whole execution. + let len_ptr = PRIVATE_INPUT_START as *const u32; + let len = unsafe { core::ptr::read_volatile(len_ptr) } as usize; + let data_ptr = (PRIVATE_INPUT_START + 4) as *const u8; + unsafe { core::slice::from_raw_parts(data_ptr, len) } +} + +#[cfg(not(target_arch = "riscv64"))] +pub fn get_private_input_slice() -> &'static [u8] { + unimplemented!("syscalls are only implemented for riscv64 targets"); +} + #[cfg(target_arch = "riscv64")] pub fn sys_halt() -> ! { // NOTE: no print_string here — the Print ecall is unmatched on the Ecall bus diff --git a/tooling/ethrex-tests/Cargo.lock b/tooling/ethrex-tests/Cargo.lock new file mode 100644 index 000000000..26f991c7a --- /dev/null +++ b/tooling/ethrex-tests/Cargo.lock @@ -0,0 +1,2415 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" + +[[package]] +name = "ark-bn254" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" +dependencies = [ + "ahash", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "educe", + "fnv", + "hashbrown 0.15.5", + "itertools 0.13.0", + "num-bigint", + "num-integer", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "arrayvec", + "digest", + "educe", + "itertools 0.13.0", + "num-bigint", + "num-traits", + "paste", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "ark-ff-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ark-poly" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" +dependencies = [ + "ahash", + "ark-ff", + "ark-serialize", + "ark-std", + "educe", + "fnv", + "hashbrown 0.15.5", +] + +[[package]] +name = "ark-serialize" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "arrayvec", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ark-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bitcoin-consensus-encoding" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2d6094e2a1ba3c93b5a596fe5a10d1a10c3c6e06785cde89f693a044c01aa40" +dependencies = [ + "bitcoin-internals", +] + +[[package]] +name = "bitcoin-internals" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a30a22d1f112dde8e16be7b45c63645dc165cef254f835b3e1e9553e485cfa64" +dependencies = [ + "hex-conservative 0.3.2", +] + +[[package]] +name = "bitcoin-io" +version = "0.1.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb5de036369d1ac59d3c1819ebc4d850f89466f5401c571a285b6ed564a4cb78" +dependencies = [ + "bitcoin-consensus-encoding", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.14.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca4c7abb40c8817d77403c880988cfd484f23ab2365726afb2f798363e2c4a2" +dependencies = [ + "bitcoin-io", + "hex-conservative 0.2.2", +] + +[[package]] +name = "bitvec" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bls12_381" +version = "0.8.0" +source = "git+https://github.com/lambdaclass/bls12_381?branch=expose-affine-constructors#78cad0378b17fc3157b83f514be192bf46edf9a1" +dependencies = [ + "digest", + "ff", + "group", + "pairing", + "rand_core", + "subtle", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "byte-slice-cast" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" + +[[package]] +name = "bytecheck" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0caa33a2c0edca0419d15ac723dff03f1956f7978329b1e3b5fdaaaed9d3ca8b" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "rancor", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89385e82b5d1821d2219e0b095efa2cc1f246cbf99080f3be46a1a85c0d392d9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" +dependencies = [ + "serde", +] + +[[package]] +name = "cc" +version = "1.2.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const_format" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4481a617ad9a412be3b97c5d403fef8ed023103368908b9c50af598ff467cc1e" +dependencies = [ + "const_format_proc_macros", + "konst", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "803d13fb3b09d88be9f4dbc29062c66b19bf7170867ceb746d2a8689bf6c7a26" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "serde_core", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ecsm" +version = "0.1.0" +dependencies = [ + "k256", + "num-bigint", + "num-traits", +] + +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "enum-ordinalize" +version = "4.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07f808d588c10e464ea6f7d3eaed500049eff30aaac103460f61828c2d65b3eb" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e528e2d34ba8a67a1a650b86beae8ef69fc5fdb638016f386b973226590432" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "ethbloom" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c321610643004cf908ec0f5f2aa0d8f1f8e14b540562a2887a1111ff1ecbf7b" +dependencies = [ + "crunchy", + "fixed-hash", + "impl-rlp", + "impl-serde", + "tiny-keccak", +] + +[[package]] +name = "ethereum-types" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ab15ed80916029f878e0267c3a9f92b67df55e79af370bf66199059ae2b4ee3" +dependencies = [ + "ethbloom", + "fixed-hash", + "impl-rlp", + "impl-serde", + "primitive-types", + "uint", +] + +[[package]] +name = "ethrex-common" +version = "13.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6a3974f411d71622eecd1b249ee37ff1c" +dependencies = [ + "bytes", + "crc32fast", + "ethereum-types", + "ethrex-crypto", + "ethrex-rlp", + "ethrex-trie", + "hex", + "hex-literal", + "hex-simd", + "indexmap 2.14.0", + "lazy_static", + "libc", + "lru", + "once_cell", + "rayon", + "rkyv", + "rustc-hash", + "secp256k1", + "serde", + "serde_json", + "sha2", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "ethrex-crypto" +version = "13.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6a3974f411d71622eecd1b249ee37ff1c" +dependencies = [ + "ark-bn254", + "ark-ec", + "ark-ff", + "bls12_381", + "ethereum-types", + "ff", + "hex-literal", + "k256", + "malachite", + "num-bigint", + "p256", + "ripemd", + "secp256k1", + "sha2", + "thiserror 2.0.18", + "tiny-keccak", +] + +[[package]] +name = "ethrex-guest-program" +version = "13.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6a3974f411d71622eecd1b249ee37ff1c" +dependencies = [ + "bytes", + "ethereum-types", + "ethrex-common", + "ethrex-crypto", + "ethrex-l2-common", + "ethrex-rlp", + "ethrex-vm", + "hex", + "rkyv", + "serde", + "serde_with", + "thiserror 2.0.18", +] + +[[package]] +name = "ethrex-l2-common" +version = "13.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6a3974f411d71622eecd1b249ee37ff1c" +dependencies = [ + "bytes", + "ethereum-types", + "ethrex-common", + "ethrex-crypto", + "k256", + "lambdaworks-crypto", + "rkyv", + "secp256k1", + "serde", + "serde_with", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "ethrex-levm" +version = "13.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6a3974f411d71622eecd1b249ee37ff1c" +dependencies = [ + "bytes", + "derive_more", + "ethrex-common", + "ethrex-crypto", + "ethrex-rlp", + "malachite", + "rayon", + "rustc-hash", + "serde", + "strum", + "thiserror 2.0.18", +] + +[[package]] +name = "ethrex-rlp" +version = "13.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6a3974f411d71622eecd1b249ee37ff1c" +dependencies = [ + "bytes", + "ethereum-types", + "thiserror 2.0.18", +] + +[[package]] +name = "ethrex-tests" +version = "0.1.0" +dependencies = [ + "ethrex-guest-program", + "executor", + "rkyv", +] + +[[package]] +name = "ethrex-trie" +version = "13.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6a3974f411d71622eecd1b249ee37ff1c" +dependencies = [ + "anyhow", + "bytes", + "crossbeam", + "ethereum-types", + "ethrex-crypto", + "ethrex-rlp", + "lazy_static", + "rayon", + "rkyv", + "rustc-hash", + "serde", + "thiserror 2.0.18", +] + +[[package]] +name = "ethrex-vm" +version = "13.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?rev=156cb8d6a3974f411d71622eecd1b249ee37ff1c#156cb8d6a3974f411d71622eecd1b249ee37ff1c" +dependencies = [ + "bytes", + "derive_more", + "dyn-clone", + "ethrex-common", + "ethrex-crypto", + "ethrex-levm", + "ethrex-rlp", + "rayon", + "rustc-hash", + "serde", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "executor" +version = "0.1.0" +dependencies = [ + "ecsm", + "rustc-demangle", + "thiserror 1.0.69", +] + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "bitvec", + "rand_core", + "subtle", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-conservative" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "hex-conservative" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830e599c2904b08f0834ee6337d8fe8f0ed4a63b5d9e7a7f49c0ffa06d08d360" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hex-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f7685beb53fc20efc2605f32f5d51e9ba18b8ef237961d1760169d2290d3bee" +dependencies = [ + "outref", + "vsimd", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "impl-codec" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d40b9d5e17727407e55028eafc22b2dc68781786e6d7eb8a21103f5058e3a14" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-rlp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54ed8ad1f3877f7e775b8cbf30ed1bd3209a95401817f19a0eb4402d13f8cf90" +dependencies = [ + "rlp", +] + +[[package]] +name = "impl-serde" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a143eada6a1ec4aefa5049037a26a6d597bfd64f8c026d07b77133e02b7dd0b" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", + "signature", +] + +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "konst" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128133ed7824fcd73d6e7b17957c5eb7bacb885649bd8c69708b2331a10bcefb" +dependencies = [ + "konst_macro_rules", +] + +[[package]] +name = "konst_macro_rules" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" + +[[package]] +name = "lambdaworks-crypto" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58b1a1c1102a5a7fbbda117b79fb3a01e033459c738a3c1642269603484fd1c1" +dependencies = [ + "lambdaworks-math", + "rand", + "rand_chacha", + "serde", + "sha2", + "sha3", +] + +[[package]] +name = "lambdaworks-math" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "018a95aa873eb49896a858dee0d925c33f3978d073c64b08dd4f2c9b35a017c6" +dependencies = [ + "getrandom", + "num-bigint", + "num-traits", + "rand", + "serde", + "serde_json", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "lru" +version = "0.16.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" +dependencies = [ + "hashbrown 0.16.1", +] + +[[package]] +name = "malachite" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec410515e231332b14cd986a475d1c3323bcfa4c7efc038bfa1d5b410b1c57e4" +dependencies = [ + "malachite-base", + "malachite-nz", + "malachite-q", +] + +[[package]] +name = "malachite-base" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c738d3789301e957a8f7519318fcbb1b92bb95863b28f6938ae5a05be6259f34" +dependencies = [ + "hashbrown 0.15.5", + "itertools 0.14.0", + "libm", + "ryu", +] + +[[package]] +name = "malachite-nz" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1707c9a1fa36ce21749b35972bfad17bbf34cf5a7c96897c0491da321e387d3b" +dependencies = [ + "itertools 0.14.0", + "libm", + "malachite-base", + "wide", +] + +[[package]] +name = "malachite-q" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d764801aa4e96bbb69b389dcd03b50075345131cd63ca2e380bca71cc37a3675" +dependencies = [ + "itertools 0.14.0", + "malachite-base", + "malachite-nz", +] + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "munge" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e17401f259eba956ca16491461b6e8f72913a0a114e39736ce404410f915a0c" +dependencies = [ + "munge_macro", +] + +[[package]] +name = "munge_macro" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4568f25ccbd45ab5d5603dc34318c1ec56b117531781260002151b8530a9f931" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-bigint" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "outref" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "pairing" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" +dependencies = [ + "group", +] + +[[package]] +name = "parity-scale-codec" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "const_format", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "rustversion", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "primitive-types" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d15600a7d856470b7d278b3fe0e311fe28c2526348549f8ef2ff7db3299c87f5" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "ptr_meta" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9a0cf95a1196af61d4f1cbdab967179516d9a4a4312af1f31948f8f6224a79" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7347867d0a7e1208d93b46767be83e2b8f978c3dad35f775ac8d8847551d6fe1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rancor" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daff8b7b3ccf5f7ba270b3e7a0a4d4c701c5797e38dec27c7e2c3dbb830fed1c" +dependencies = [ + "ptr_meta", +] + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "rend" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "663ba70707f96e871406fe10d68128412e619b06d1d47cb91c3a4c6501176240" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest", +] + +[[package]] +name = "rkyv" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73389e0c99e664f919275ab5b5b0471391fe9a8de61e1dff9b1eaf56a90f16e3" +dependencies = [ + "bytecheck", + "bytes", + "hashbrown 0.17.1", + "indexmap 2.14.0", + "munge", + "ptr_meta", + "rancor", + "rend", + "rkyv_derive", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d2ed0b54125315fb36bd021e82d314d1c126548f871634b483f46b31d13cac6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "rlp" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa24e92bb2a83198bb76d661a71df9f7076b8c420b8696e4d3d97d50d94479e3" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b74b56ffa8bb2830709a538c2cbcae9aa062db0d2a42563bfb09bdaae44020eb" + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "safe_arch" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" +dependencies = [ + "bitcoin_hashes", + "rand", + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +dependencies = [ + "cc", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_with" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" +dependencies = [ + "base64", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.0", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +dependencies = [ + "indexmap 2.14.0", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "uint" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "uuid" +version = "1.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wide" +version = "0.7.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03" +dependencies = [ + "bytemuck", + "safe_arch", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zerocopy" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75726053136156d419e285b9b7eddaaea9e3fea6ce32eed44a89901f0bd98de1" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4714fd92cf900833d49538023a9b3915155210801d1c1169eba513b2addefd71" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/tooling/ethrex-tests/Cargo.toml b/tooling/ethrex-tests/Cargo.toml new file mode 100644 index 000000000..f4774278d --- /dev/null +++ b/tooling/ethrex-tests/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "ethrex-tests" +version = "0.1.0" +edition = "2024" + +# Detached workspace: ethrex pins rkyv with the `unaligned` feature (a global +# archived-layout switch), which must never feature-unify with the main +# workspace's aligned rkyv proof format. See tests/ethrex.rs. +[workspace] + +[dev-dependencies] +executor = { path = "../../executor" } +# Pinned to the SAME ethrex rev as the guest (open LambdaVM-backend PR branch) +# so the native reference reads the same ProgramInput rkyv layout. +ethrex-guest-program = { git = "https://github.com/lambdaclass/ethrex.git", rev = "156cb8d6a3974f411d71622eecd1b249ee37ff1c", package = "ethrex-guest-program" } +# Exact pin: the fixture writer and the guest/executor readers must agree on the +# rkyv layout. Keep this in sync with tooling/ethrex-fixtures and +# executor/programs/rust/ethrex/Cargo.toml. +rkyv = { version = "=0.8.16", features = ["std", "unaligned"] } + +# Match the root workspace's optimized dev profile: these tests execute the VM. +[profile.dev] +opt-level = 3 +debug = true diff --git a/tooling/ethrex-tests/tests/ethrex.rs b/tooling/ethrex-tests/tests/ethrex.rs new file mode 100644 index 000000000..c87ccceba --- /dev/null +++ b/tooling/ethrex-tests/tests/ethrex.rs @@ -0,0 +1,88 @@ +//! Host-reference execution tests for the ethrex guest, relocated out of the +//! `executor` test suite: `ethrex-guest-program` pins rkyv with the +//! `unaligned` feature (a global archived-layout switch), which would +//! feature-unify with the main workspace's aligned rkyv and silently change +//! the proof wire format. This crate is a detached workspace so the two rkyv +//! configurations never meet. +//! +//! Fixtures are generated by `tooling/ethrex-fixtures`; the guest ELF comes +//! from `make compile-programs`. See `executor/tests/README.md`. + +use executor::elf::Elf; +use executor::vm::execution::Executor; +use executor::vm::execution::ReturnValues; + +fn run_program_without_expect( + elf_path: &str, + private_inputs: Vec, +) -> Result { + println!("Testing {}", elf_path); + let elf_data = std::fs::read(elf_path).unwrap(); + let program = Elf::load(&elf_data).unwrap(); + println!("Program entry: 0x{:016x}", program.entry_point); + let mut executor = Executor::new(&program, private_inputs)?; + while let Some(_logs) = executor.resume()? {} + executor.finish() +} + +fn run_program_and_check_public_output( + elf_path: &str, + expected_output: Vec, + private_inputs: Vec, +) { + let result = + run_program_without_expect(elf_path, private_inputs).expect("Failed to run program"); + + assert_eq!(result.memory_values, expected_output); +} + +const ELF_PATH: &str = "../../executor/program_artifacts/rust/ethrex.elf"; +const FIXTURES_DIR: &str = "../../executor/tests"; + +/// Larger-block smoke test: a synthetic ethrex block with 10 ETH transfers. +/// (Replaces the old `ethrex_hoodi.bin` real-block fixture, which was in the +/// pre-Crypto-trait ethrex format and no longer deserializes.) +#[ignore = "heavier synthetic block (10 txs); run in the dedicated --ignored CI step"] +#[test] +fn test_ethrex() { + use ethrex_guest_program::crypto::NativeCrypto; + use ethrex_guest_program::l1::{ProgramInput, execution_program}; + use rkyv::rancor::Error; + use std::fs; + use std::sync::Arc; + let inputs = fs::read(format!("{FIXTURES_DIR}/ethrex_10_transfers.bin")).unwrap(); + let input = rkyv::from_bytes::(&inputs).unwrap(); + let output = execution_program(input, Arc::new(NativeCrypto)).unwrap(); + run_program_and_check_public_output(ELF_PATH, output.encode(), inputs); +} + +/// Executes a stateless ethrex block containing a single (plain ETH transfer) +/// transaction. Execution only — no proving — against the ethrex guest ELF +/// built from the same pinned ethrex revision as the native reference. +#[test] +fn test_ethrex_simple_tx() { + use ethrex_guest_program::crypto::NativeCrypto; + use ethrex_guest_program::l1::{ProgramInput, execution_program}; + use rkyv::rancor::Error; + use std::sync::Arc; + let inputs = std::fs::read(format!("{FIXTURES_DIR}/ethrex_simple_tx.bin")).unwrap(); + let input = rkyv::from_bytes::(&inputs).unwrap(); + let output = execution_program(input, Arc::new(NativeCrypto)).unwrap(); + run_program_and_check_public_output(ELF_PATH, output.encode(), inputs); +} + +/// Executes a stateless ethrex block with NO transactions (empty block). +/// Execution only — no proving. Pins the committed `ethrex_empty_block.bin` +/// fixture so its rkyv `ProgramInput` layout (the 0-tx edge case) is +/// exercised and stays consistent with the guest across ethrex rev bumps. +#[test] +fn test_ethrex_empty_block() { + use ethrex_guest_program::crypto::NativeCrypto; + use ethrex_guest_program::l1::{ProgramInput, execution_program}; + use rkyv::rancor::Error; + use std::sync::Arc; + let inputs = std::fs::read(format!("{FIXTURES_DIR}/ethrex_empty_block.bin")).unwrap(); + let input = rkyv::from_bytes::(&inputs).unwrap(); + let output = execution_program(input, Arc::new(NativeCrypto)).unwrap(); + run_program_and_check_public_output(ELF_PATH, output.encode(), inputs); +} From 6e75155851ab8ee962490e8601671bc1e2921356 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Wed, 8 Jul 2026 17:52:20 -0300 Subject: [PATCH 02/16] fmt --- bin/cli/src/main.rs | 21 ++++++------ .../src/examples/fibonacci_2_cols_shifted.rs | 10 +++++- .../src/examples/fibonacci_multi_column.rs | 10 +++++- crypto/stark/src/examples/fibonacci_rap.rs | 10 +++++- crypto/stark/src/examples/quadratic_air.rs | 10 +++++- crypto/stark/src/examples/read_only_memory.rs | 10 +++++- .../src/examples/read_only_memory_logup.rs | 10 +++++- crypto/stark/src/examples/simple_addition.rs | 10 +++++- crypto/stark/src/examples/simple_fibonacci.rs | 10 +++++- crypto/stark/src/fri/fri_decommit.rs | 8 ++++- crypto/stark/src/lookup.rs | 8 ++++- crypto/stark/src/proof/options.rs | 8 ++++- crypto/stark/src/proof/stark.rs | 32 ++++++++++++++++--- crypto/stark/src/proof/view.rs | 16 +++++++--- crypto/stark/src/verifier.rs | 23 +++++++------ prover/src/lib.rs | 21 +++++++----- 16 files changed, 170 insertions(+), 47 deletions(-) diff --git a/bin/cli/src/main.rs b/bin/cli/src/main.rs index e3e60032c..95e041908 100644 --- a/bin/cli/src/main.rs +++ b/bin/cli/src/main.rs @@ -845,17 +845,16 @@ fn cmd_verify_continuation( return ExitCode::FAILURE; } }; - let bundle: prover::continuation::ContinuationProof = match rkyv::from_bytes::< - prover::continuation::ContinuationProof, - rkyv::rancor::Error, - >(&proof_bytes) - { - Ok(p) => p, - Err(e) => { - eprintln!("Failed to deserialize proof: {}", e); - return ExitCode::FAILURE; - } - }; + let bundle: prover::continuation::ContinuationProof = + match rkyv::from_bytes::( + &proof_bytes, + ) { + Ok(p) => p, + Err(e) => { + eprintln!("Failed to deserialize proof: {}", e); + return ExitCode::FAILURE; + } + }; let opts = match GoldilocksCubicProofOptions::with_blowup(blowup) { Ok(opts) => opts, diff --git a/crypto/stark/src/examples/fibonacci_2_cols_shifted.rs b/crypto/stark/src/examples/fibonacci_2_cols_shifted.rs index 823c2cef5..a1f90f197 100644 --- a/crypto/stark/src/examples/fibonacci_2_cols_shifted.rs +++ b/crypto/stark/src/examples/fibonacci_2_cols_shifted.rs @@ -16,7 +16,15 @@ use math::{ traits::AsBytes, }; use std::marker::PhantomData; -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] +#[derive( + Clone, + Debug, + serde::Serialize, + serde::Deserialize, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, +)] #[serde(bound = "FieldElement: serde::Serialize + serde::de::DeserializeOwned")] pub struct PublicInputs where diff --git a/crypto/stark/src/examples/fibonacci_multi_column.rs b/crypto/stark/src/examples/fibonacci_multi_column.rs index 127836ca9..64c9f57c2 100644 --- a/crypto/stark/src/examples/fibonacci_multi_column.rs +++ b/crypto/stark/src/examples/fibonacci_multi_column.rs @@ -20,7 +20,15 @@ use math::field::{ /// Public inputs for the multi-column Fibonacci AIR. /// Contains the initial values (first two elements) for each column. -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] +#[derive( + Clone, + Debug, + serde::Serialize, + serde::Deserialize, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, +)] #[serde(bound = "FieldElement: serde::Serialize + serde::de::DeserializeOwned")] pub struct FibonacciMultiColumnPublicInputs { /// Initial values for each column: (a0, a1) pairs diff --git a/crypto/stark/src/examples/fibonacci_rap.rs b/crypto/stark/src/examples/fibonacci_rap.rs index d715f9a9d..c00ffdac8 100644 --- a/crypto/stark/src/examples/fibonacci_rap.rs +++ b/crypto/stark/src/examples/fibonacci_rap.rs @@ -71,7 +71,15 @@ where phantom: PhantomData, } -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] +#[derive( + Clone, + Debug, + serde::Serialize, + serde::Deserialize, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, +)] #[serde(bound = "FieldElement: serde::Serialize + serde::de::DeserializeOwned")] pub struct FibonacciRAPPublicInputs where diff --git a/crypto/stark/src/examples/quadratic_air.rs b/crypto/stark/src/examples/quadratic_air.rs index bf0b3374e..08354ac59 100644 --- a/crypto/stark/src/examples/quadratic_air.rs +++ b/crypto/stark/src/examples/quadratic_air.rs @@ -50,7 +50,15 @@ where phantom: PhantomData, } -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] +#[derive( + Clone, + Debug, + serde::Serialize, + serde::Deserialize, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, +)] #[serde(bound = "FieldElement: serde::Serialize + serde::de::DeserializeOwned")] pub struct QuadraticPublicInputs where diff --git a/crypto/stark/src/examples/read_only_memory.rs b/crypto/stark/src/examples/read_only_memory.rs index 935eb3e59..ee07ee7e7 100644 --- a/crypto/stark/src/examples/read_only_memory.rs +++ b/crypto/stark/src/examples/read_only_memory.rs @@ -82,7 +82,15 @@ where phantom: PhantomData, } -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] +#[derive( + Clone, + Debug, + serde::Serialize, + serde::Deserialize, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, +)] #[serde(bound = "FieldElement: serde::Serialize + serde::de::DeserializeOwned")] pub struct ReadOnlyPublicInputs where diff --git a/crypto/stark/src/examples/read_only_memory_logup.rs b/crypto/stark/src/examples/read_only_memory_logup.rs index a1fe964af..9068e7276 100644 --- a/crypto/stark/src/examples/read_only_memory_logup.rs +++ b/crypto/stark/src/examples/read_only_memory_logup.rs @@ -99,7 +99,15 @@ where phantom: PhantomData<(F, E)>, } -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] +#[derive( + Clone, + Debug, + serde::Serialize, + serde::Deserialize, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, +)] #[serde(bound = "FieldElement: serde::Serialize + serde::de::DeserializeOwned")] pub struct LogReadOnlyPublicInputs where diff --git a/crypto/stark/src/examples/simple_addition.rs b/crypto/stark/src/examples/simple_addition.rs index 433496422..df2e6d8c0 100644 --- a/crypto/stark/src/examples/simple_addition.rs +++ b/crypto/stark/src/examples/simple_addition.rs @@ -54,7 +54,15 @@ where phantom: PhantomData, } -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] +#[derive( + Clone, + Debug, + serde::Serialize, + serde::Deserialize, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, +)] #[serde(bound = "FieldElement: serde::Serialize + serde::de::DeserializeOwned")] pub struct SimpleAdditionPublicInputs where diff --git a/crypto/stark/src/examples/simple_fibonacci.rs b/crypto/stark/src/examples/simple_fibonacci.rs index 917d0be55..4df8bcd28 100644 --- a/crypto/stark/src/examples/simple_fibonacci.rs +++ b/crypto/stark/src/examples/simple_fibonacci.rs @@ -50,7 +50,15 @@ where phantom: PhantomData, } -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] +#[derive( + Clone, + Debug, + serde::Serialize, + serde::Deserialize, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, +)] #[serde(bound = "FieldElement: serde::Serialize + serde::de::DeserializeOwned")] pub struct FibonacciPublicInputs where diff --git a/crypto/stark/src/fri/fri_decommit.rs b/crypto/stark/src/fri/fri_decommit.rs index 252483c1a..0c1c24112 100644 --- a/crypto/stark/src/fri/fri_decommit.rs +++ b/crypto/stark/src/fri/fri_decommit.rs @@ -5,7 +5,13 @@ use math::field::traits::IsField; use crate::config::Commitment; #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize + Debug, + Clone, + serde::Serialize, + serde::Deserialize, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, )] #[serde(bound = "")] pub struct FriDecommitment { diff --git a/crypto/stark/src/lookup.rs b/crypto/stark/src/lookup.rs index 2f017404e..abd3218b8 100644 --- a/crypto/stark/src/lookup.rs +++ b/crypto/stark/src/lookup.rs @@ -1499,7 +1499,13 @@ impl BusInteraction { /// For the circular constraint, `table_contribution / N` is the per-row offset /// that makes the accumulated column wrap to zero at row N-1. #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize + Debug, + Clone, + serde::Serialize, + serde::Deserialize, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, )] #[serde(bound = "")] pub struct BusPublicInputs diff --git a/crypto/stark/src/proof/options.rs b/crypto/stark/src/proof/options.rs index 277f5d9f3..15e2c8909 100644 --- a/crypto/stark/src/proof/options.rs +++ b/crypto/stark/src/proof/options.rs @@ -41,7 +41,13 @@ impl fmt::Display for ProofOptionsError { /// - `fri_final_poly_log_degree`: log2 degree bound at which FRI terminates folding #[cfg_attr(feature = "wasm", wasm_bindgen)] #[derive( - Clone, Debug, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize + Clone, + Debug, + serde::Serialize, + serde::Deserialize, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, )] pub struct ProofOptions { pub blowup_factor: u8, diff --git a/crypto/stark/src/proof/stark.rs b/crypto/stark/src/proof/stark.rs index 471c9f74d..f04370ea5 100644 --- a/crypto/stark/src/proof/stark.rs +++ b/crypto/stark/src/proof/stark.rs @@ -9,7 +9,13 @@ use crate::{ }; #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize + Debug, + Clone, + serde::Serialize, + serde::Deserialize, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, )] #[serde(bound = "")] /// Opening of a bit-reversed, row-paired commitment at one FRI query. @@ -25,7 +31,13 @@ pub struct PolynomialOpenings { } #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize + Debug, + Clone, + serde::Serialize, + serde::Deserialize, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, )] #[serde(bound = "")] pub struct DeepPolynomialOpening, E: IsField> { @@ -40,7 +52,13 @@ pub struct DeepPolynomialOpening, E: IsField> { pub type DeepPolynomialOpenings = Vec>; #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize + Debug, + Clone, + serde::Serialize, + serde::Deserialize, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, )] #[serde(bound = "PI: serde::Serialize + serde::de::DeserializeOwned")] pub struct StarkProof, E: IsField, PI> { @@ -85,7 +103,13 @@ pub struct StarkProof, E: IsField, PI> { /// Used for multi-table proving where tables are linked via bus (LogUp). /// Returned by `Prover::multi_prove` and verified by `Verifier::multi_verify`. #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize + Debug, + Clone, + serde::Serialize, + serde::Deserialize, + rkyv::Archive, + rkyv::Serialize, + rkyv::Deserialize, )] #[serde(bound = "PI: serde::Serialize + serde::de::DeserializeOwned")] pub struct MultiProof, E: IsField, PI> { diff --git a/crypto/stark/src/proof/view.rs b/crypto/stark/src/proof/view.rs index ba39a8afc..84c530e19 100644 --- a/crypto/stark/src/proof/view.rs +++ b/crypto/stark/src/proof/view.rs @@ -8,8 +8,8 @@ //! with no serialization and no logic duplication. use crate::config::Commitment; -use crate::fri::fri_decommit::{ArchivedFriDecommitment, FriDecommitment}; use crate::frame::Frame; +use crate::fri::fri_decommit::{ArchivedFriDecommitment, FriDecommitment}; use crate::proof::stark::{ ArchivedDeepPolynomialOpening, ArchivedPolynomialOpenings, ArchivedStarkProof, DeepPolynomialOpening, PolynomialOpenings, StarkProof, @@ -138,7 +138,10 @@ where pub fn aux_trace_polys(&self) -> Option> { match self { - Self::Owned(p) => p.aux_trace_polys.as_ref().map(PolynomialOpeningsView::Owned), + Self::Owned(p) => p + .aux_trace_polys + .as_ref() + .map(PolynomialOpeningsView::Owned), Self::Archived(p) => p .aux_trace_polys .as_ref() @@ -407,7 +410,10 @@ where /// out (it's a single field element, not worth a dedicated view type). pub fn bus_table_contribution(&self) -> Option> { match self { - Self::Owned(p) => p.bus_public_inputs.as_ref().map(|b| b.table_contribution.clone()), + Self::Owned(p) => p + .bus_public_inputs + .as_ref() + .map(|b| b.table_contribution.clone()), Self::Archived(p) => p .bus_public_inputs .as_ref() @@ -430,7 +436,9 @@ where { match self { Self::Owned(p) => Some(p.public_inputs.clone()), - Self::Archived(p) => rkyv::deserialize::(&p.public_inputs).ok(), + Self::Archived(p) => { + rkyv::deserialize::(&p.public_inputs).ok() + } } } } diff --git a/crypto/stark/src/verifier.rs b/crypto/stark/src/verifier.rs index 510325738..f38bb6d00 100644 --- a/crypto/stark/src/verifier.rs +++ b/crypto/stark/src/verifier.rs @@ -5,18 +5,16 @@ use super::{ proof::stark::StarkProof, traits::{AIR, TransitionEvaluationContext}, }; +pub use crate::proof::view::PiDeserializer; use crate::{ config::Commitment, domain::new_verifier_domain, - lookup::{ - BusPublicInputs, LOGUP_CHALLENGE_ALPHA, LOGUP_NUM_CHALLENGES, compute_alpha_powers, - }, + lookup::{BusPublicInputs, LOGUP_CHALLENGE_ALPHA, LOGUP_NUM_CHALLENGES, compute_alpha_powers}, proof::stark::{ArchivedStarkProof, MultiProof}, proof::view::{ DeepPolynomialOpeningView, FriDecommitmentView, PolynomialOpeningsView, StarkProofView, }, }; -pub use crate::proof::view::PiDeserializer; use crypto::fiat_shamir::is_transcript::IsStarkTranscript; use crypto::merkle_tree::proof::verify_merkle_path; #[cfg(not(feature = "test_fiat_shamir"))] @@ -31,10 +29,10 @@ use math::{ }, traits::AsBytes, }; +use std::collections::HashMap; use std::marker::PhantomData; #[cfg(feature = "instruments")] use std::time::Instant; -use std::collections::HashMap; /// A default STARK verifier implementing `IsStarkVerifier`. pub struct Verifier< @@ -138,7 +136,9 @@ pub trait IsStarkVerifier< let trace_length = proof.trace_length(); // Owned `BusPublicInputs` (just the table contribution L — one field // element) reconstructed for the AIR boundary call. - let bus_public_inputs = proof.bus_table_contribution().map(BusPublicInputs::from_contribution); + let bus_public_inputs = proof + .bus_table_contribution() + .map(BusPublicInputs::from_contribution); let boundary_constraints = air.boundary_constraints( public_inputs, &challenges.rap_challenges, @@ -828,8 +828,11 @@ pub trait IsStarkVerifier< FieldElement: AsBytes + Sync + Send, FieldElement: AsBytes + Sync + Send, { - let views: Vec> = - multi_proof.proofs.iter().map(StarkProofView::Owned).collect(); + let views: Vec> = multi_proof + .proofs + .iter() + .map(StarkProofView::Owned) + .collect(); Self::multi_verify_views(airs, &views, transcript, expected_bus_balance) } @@ -1100,7 +1103,9 @@ pub trait IsStarkVerifier< // <<<< Receive challenge: 𝛽 let beta = transcript.sample_field_element(); let trace_length = proof.trace_length(); - let bus_public_inputs = proof.bus_table_contribution().map(BusPublicInputs::from_contribution); + let bus_public_inputs = proof + .bus_table_contribution() + .map(BusPublicInputs::from_contribution); let num_boundary_constraints = air .boundary_constraints( public_inputs, diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 0bed63eb2..2063b7676 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -311,9 +311,8 @@ pub fn verify_recursion_blob<'a>( // precisely so the archive lands aligned at // `PRIVATE_INPUT_START + 4 + PREFIX_LEN`), so the in-place doubleword // loads do not trap. - let archive_bytes = recursion_archive_bytes(blob).ok_or_else(|| { - Error::Execution(String::from("recursion blob: bad magic or version")) - })?; + let archive_bytes = recursion_archive_bytes(blob) + .ok_or_else(|| Error::Execution(String::from("recursion blob: bad magic or version")))?; // A host caller's buffer carries no alignment guarantee (`Vec` is // align-1) — in-place access there would be UB. Fall back to one aligned @@ -347,9 +346,11 @@ pub fn verify_recursion_blob<'a>( RkyvError, >(&archived.vm_proof.runtime_page_ranges) .map_err(|e| Error::Execution(format!("rkyv deserialize page ranges failed: {e}")))?; - let page_commitments: Vec<(u64, Commitment)> = - rkyv::deserialize::, RkyvError>(&archived.page_commitments) - .map_err(|e| Error::Execution(format!("rkyv deserialize page commitments failed: {e}")))?; + let page_commitments: Vec<(u64, Commitment)> = rkyv::deserialize::< + Vec<(u64, Commitment)>, + RkyvError, + >(&archived.page_commitments) + .map_err(|e| Error::Execution(format!("rkyv deserialize page commitments failed: {e}")))?; let num_private_input_pages = archived.vm_proof.num_private_input_pages.to_native() as usize; // Bytes read straight from the archived buffer (zero-copy). let inner_elf: &[u8] = archived.inner_elf.as_slice(); @@ -1300,8 +1301,12 @@ pub(crate) fn verify_prepared( decode_commitment: Option, page_commitments: Option<&[(u64, Commitment)]>, ) -> Result { - let views: Vec> = - vm_proof.proof.proofs.iter().map(StarkProofView::Owned).collect(); + let views: Vec> = vm_proof + .proof + .proofs + .iter() + .map(StarkProofView::Owned) + .collect(); verify_proof_parts( &views, From e57d9381ed63fae6b0b603ef4d1d7a30eea408ae Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Mon, 13 Jul 2026 15:36:45 -0300 Subject: [PATCH 03/16] fix(prover): use rkyv in disk-spill tests instead of stale bincode calls CI failed to compile lambda-vm-prover under the disk-spill feature since these tests were never updated during the bincode->rkyv proof migration. --- prover/src/tests/disk_spill_tests.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/prover/src/tests/disk_spill_tests.rs b/prover/src/tests/disk_spill_tests.rs index a03575ba7..93945bfff 100644 --- a/prover/src/tests/disk_spill_tests.rs +++ b/prover/src/tests/disk_spill_tests.rs @@ -29,8 +29,9 @@ fn test_disk_spill_prove_verify_and_roundtrip_small() { "verification returned false" ); - let bytes = bincode::serialize(&proof).expect("serialize failed"); - let proof2: VmProof = bincode::deserialize(&bytes).expect("deserialize failed"); + let bytes = rkyv::to_bytes::(&proof).expect("serialize failed"); + let proof2: VmProof = + rkyv::from_bytes::(&bytes).expect("deserialize failed"); assert!( crate::verify_with_options(&proof2, &elf_bytes, &opts, None, None).expect("verify failed"), "verification failed after serialization roundtrip" @@ -49,8 +50,9 @@ fn test_disk_spill_prove_verify_and_roundtrip_chunked() { "verification returned false" ); - let bytes = bincode::serialize(&proof).expect("serialize failed"); - let proof2: VmProof = bincode::deserialize(&bytes).expect("deserialize failed"); + let bytes = rkyv::to_bytes::(&proof).expect("serialize failed"); + let proof2: VmProof = + rkyv::from_bytes::(&bytes).expect("deserialize failed"); assert!( crate::verify_with_options(&proof2, &elf_bytes, &opts, None, None).expect("verify failed"), "verification failed after serialization roundtrip (chunked)" From 0c3ea8459a535efac57f4167f10d774765a2ea6b Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Mon, 13 Jul 2026 17:08:13 -0300 Subject: [PATCH 04/16] fix(prover): bytecheck-validate recursion blob, guard OOD step_size, align CLI proof reads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit verify_recursion_blob trusted access_unchecked on a prover-supplied archive (real UB on any non-guest target); switch to checked rkyv::access, which measured at ~0.26% of guest cycles on the multiquery profile — not worth the risk for that saving. multi_verify_views only rejected inconsistent/empty OOD dimensions, not a height that isn't a multiple of the AIR's step_size, letting a malformed archive panic in into_frame instead of failing verification cleanly. The CLI verify commands read proof files into a plain Vec before handing them to rkyv, which requires alignment the allocator only happens to provide; read directly into an AlignedVec instead. --- bin/cli/src/main.rs | 23 +++++++++++++++++++++-- crypto/stark/src/verifier.rs | 12 +++++++++--- prover/src/lib.rs | 17 ++++++++--------- 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/bin/cli/src/main.rs b/bin/cli/src/main.rs index 95e041908..29fb0dfca 100644 --- a/bin/cli/src/main.rs +++ b/bin/cli/src/main.rs @@ -19,6 +19,25 @@ use stark::proof::options::GoldilocksCubicProofOptions; const DEFAULT_CONTINUATION_EPOCH_SIZE_LOG2: u32 = 20; const MIN_CONTINUATION_EPOCH_SIZE_LOG2: u32 = 18; +/// Read a file into a buffer aligned for `rkyv::from_bytes`. A plain +/// `Vec` from `std::fs::read` is align-1 by the type system even though +/// the allocator happens to return well-aligned memory in practice — read +/// straight into an `AlignedVec` instead of relying on that. +fn read_aligned_file(path: &Path) -> std::io::Result> { + use std::os::unix::fs::FileExt; + + let file = std::fs::File::open(path)?; + let len = file.metadata()?.len() as usize; + let mut aligned = rkyv::util::AlignedVec::<16>::with_capacity(len); + // SAFETY: `with_capacity(len)` guarantees at least `len` allocated bytes + // at `as_mut_ptr()`; `read_exact_at` below fully initializes them before + // `set_len` exposes them as valid `u8`s. + let buf = unsafe { std::slice::from_raw_parts_mut(aligned.as_mut_ptr(), len) }; + file.read_exact_at(buf, 0)?; + unsafe { aligned.set_len(len) }; + Ok(aligned) +} + /// Polls jemalloc `stats.allocated` every 10ms from a background thread, /// tracking the high-water mark. Near-zero overhead because jemalloc uses /// thread-local caches — `epoch::advance()` just merges cached counters. @@ -670,7 +689,7 @@ fn cmd_verify(proof_path: PathBuf, elf_path: PathBuf, blowup: u8, time: bool) -> }; eprintln!("Reading proof..."); - let proof_bytes = match std::fs::read(&proof_path) { + let proof_bytes = match read_aligned_file(&proof_path) { Ok(b) => b, Err(e) => { eprintln!("Failed to read proof file: {}", e); @@ -838,7 +857,7 @@ fn cmd_verify_continuation( }; eprintln!("Reading proof..."); - let proof_bytes = match std::fs::read(&proof_path) { + let proof_bytes = match read_aligned_file(&proof_path) { Ok(b) => b, Err(e) => { eprintln!("Failed to read proof file: {}", e); diff --git a/crypto/stark/src/verifier.rs b/crypto/stark/src/verifier.rs index f38bb6d00..e163cfe7c 100644 --- a/crypto/stark/src/verifier.rs +++ b/crypto/stark/src/verifier.rs @@ -899,10 +899,16 @@ pub trait IsStarkVerifier< return false; } // The archive is read in place without validation; reject an OOD - // table whose advertised dimensions disagree with its data length - // (or that has no rows) before any row access indexes into it. + // table whose advertised dimensions disagree with its data length, + // has no rows, or whose height isn't a whole number of AIR steps + // (which `into_frame` below only `debug_assert!`s, not checks) — + // all before any row access indexes into it. let trace_ood_evaluations = proof.trace_ood_evaluations(); - if !trace_ood_evaluations.dimensions_consistent() || trace_ood_evaluations.height() == 0 + if !trace_ood_evaluations.dimensions_consistent() + || trace_ood_evaluations.height() == 0 + || !trace_ood_evaluations + .height() + .is_multiple_of(air.step_size()) { return false; } diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 2063b7676..77c534d48 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -300,6 +300,10 @@ pub struct RecursionVerification<'a> { /// The archive is read **in place**: the STARK proof is verified straight from /// the blob with no deserialization and no per-field allocation. Only tiny /// metadata (table counts, page ranges, page commitments) is materialized. +/// +/// `blob` is untrusted (prover-supplied) input, so the archive is +/// bytecheck-validated (`rkyv::access`) before the zero-copy access — measured +/// at ~0.26% of total guest cycles, cheap enough to not be worth skipping. pub fn verify_recursion_blob<'a>( blob: &'a [u8], proof_options: &ProofOptions, @@ -327,15 +331,10 @@ pub fn verify_recursion_blob<'a>( &aligned_fallback }; - // SAFETY: `archive` is RECURSION_INPUT_ALIGN-aligned (checked above). No - // structural validation is performed. On the host, callers must pass blobs - // produced by our own `encode_recursion_input`. In the recursion guest the - // blob is PROVER-SUPPLIED: a malformed archive makes the in-place reads - // hit arbitrary guest addresses, which the VM memory model defines (reads - // of unmapped cells return 0), so the outcome is garbage field elements - // that fail the cryptographic checks or an in-guest panic — verification - // failure either way, never a false accept. - let archived = unsafe { rkyv::access_unchecked::(archive) }; + // `blob` is untrusted; validate before the zero-copy access. + let archived = rkyv::access::(archive).map_err(|e| { + Error::Execution(format!("recursion blob: bytecheck validation failed: {e}")) + })?; // Materialize only the small metadata; the proof stays in the buffer. let table_counts: TableCounts = From 3f86a6c48f8094063e530b0fd44a7be0caef1043 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Tue, 14 Jul 2026 15:34:26 -0300 Subject: [PATCH 05/16] fix(cli): avoid UB from reading into uninitialized AlignedVec read_aligned_file created a &mut [u8] over uninitialized memory before the OS write, which is UB regardless of pread's actual behavior. Zero the buffer via resize instead of forming the reference unsafely. --- bin/cli/src/main.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/bin/cli/src/main.rs b/bin/cli/src/main.rs index 29fb0dfca..1de36220b 100644 --- a/bin/cli/src/main.rs +++ b/bin/cli/src/main.rs @@ -29,12 +29,8 @@ fn read_aligned_file(path: &Path) -> std::io::Result> let file = std::fs::File::open(path)?; let len = file.metadata()?.len() as usize; let mut aligned = rkyv::util::AlignedVec::<16>::with_capacity(len); - // SAFETY: `with_capacity(len)` guarantees at least `len` allocated bytes - // at `as_mut_ptr()`; `read_exact_at` below fully initializes them before - // `set_len` exposes them as valid `u8`s. - let buf = unsafe { std::slice::from_raw_parts_mut(aligned.as_mut_ptr(), len) }; - file.read_exact_at(buf, 0)?; - unsafe { aligned.set_len(len) }; + aligned.resize(len, 0); + file.read_exact_at(&mut aligned, 0)?; Ok(aligned) } From 39abfadde988013ea00deb9d11a3177d3e62dd6c Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Tue, 14 Jul 2026 15:34:38 -0300 Subject: [PATCH 06/16] fix(math): gate zero-copy archived field-element casts by sealed marker as_native/slice_as_native reinterpreted an archived base type as its native form based only on F::BaseType: Archive, which arbitrary IsField impls can satisfy without matching size/align/layout. Restrict the cast to a sealed NativeArchived trait implemented for u32, u64, and types built from them (FieldElement, [T; N]), and propagate the bound through the stark crate's zero-copy proof views. --- crypto/math/src/field/element.rs | 36 ++++++++++++++++- crypto/stark/src/proof/view.rs | 67 ++++++++++++++++++-------------- crypto/stark/src/table.rs | 2 +- crypto/stark/src/verifier.rs | 10 ++--- 4 files changed, 79 insertions(+), 36 deletions(-) diff --git a/crypto/math/src/field/element.rs b/crypto/math/src/field/element.rs index 9b1a9badf..23f660487 100644 --- a/crypto/math/src/field/element.rs +++ b/crypto/math/src/field/element.rs @@ -970,10 +970,44 @@ const _: () = { // // Restricted to `target_endian = "little"` (the lambda-vm guest target). On a // big-endian host these would be wrong, so they simply don't exist there. +// `IsField` is a public trait, so an arbitrary `F::BaseType: Archive` gives no +// guarantee that `Archived` shares size/align/layout with the base type — +// only rkyv's own primitive archived forms (and types built from them) do. +// `NativeArchived` is sealed to just those, so the views below are only +// callable for base types this crate has vetted. +#[cfg(all(feature = "rkyv", target_endian = "little"))] +mod sealed { + pub trait Sealed {} + impl Sealed for u32 {} + impl Sealed for u64 {} + impl Sealed for super::FieldElement where F::BaseType: super::NativeArchived {} + impl Sealed for [T; N] {} +} + +/// See the module note above: implemented only for base types whose rkyv +/// `Archived` form is bit-identical to the native type on little-endian +/// targets (same size, same alignment, same byte layout). +/// +/// # Safety +/// Implementors must guarantee `Self` and `Self::Archived` have identical +/// size and layout, and `Self`'s alignment is at least `Self::Archived`'s, +/// under `target_endian = "little"`. +#[cfg(all(feature = "rkyv", target_endian = "little"))] +pub unsafe trait NativeArchived: rkyv::Archive + sealed::Sealed {} + +#[cfg(all(feature = "rkyv", target_endian = "little"))] +unsafe impl NativeArchived for u32 {} +#[cfg(all(feature = "rkyv", target_endian = "little"))] +unsafe impl NativeArchived for u64 {} +#[cfg(all(feature = "rkyv", target_endian = "little"))] +unsafe impl NativeArchived for FieldElement where F::BaseType: NativeArchived {} +#[cfg(all(feature = "rkyv", target_endian = "little"))] +unsafe impl NativeArchived for [T; N] {} + #[cfg(all(feature = "rkyv", target_endian = "little"))] impl ArchivedFieldElement where - F::BaseType: rkyv::Archive, + F::BaseType: NativeArchived, { /// Reinterpret this archived element as a native [`FieldElement`] (no copy). /// diff --git a/crypto/stark/src/proof/view.rs b/crypto/stark/src/proof/view.rs index 84c530e19..1adb5caf7 100644 --- a/crypto/stark/src/proof/view.rs +++ b/crypto/stark/src/proof/view.rs @@ -27,14 +27,14 @@ pub(crate) fn evals( v: &rkyv::vec::ArchivedVec>, ) -> &[FieldElement] where - G::BaseType: rkyv::Archive, + G::BaseType: math::field::element::NativeArchived, { ArchivedFieldElement::slice_as_native(v.as_slice()) } pub enum PolynomialOpeningsView<'a, F: IsField> where - F::BaseType: rkyv::Archive, + F::BaseType: math::field::element::NativeArchived, { Owned(&'a PolynomialOpenings), Archived(&'a ArchivedPolynomialOpenings), @@ -45,17 +45,20 @@ where // would add a spurious `F: Clone`/`F: Copy` bound. impl<'a, F: IsField> Clone for PolynomialOpeningsView<'a, F> where - F::BaseType: rkyv::Archive, + F::BaseType: math::field::element::NativeArchived, { fn clone(&self) -> Self { *self } } -impl<'a, F: IsField> Copy for PolynomialOpeningsView<'a, F> where F::BaseType: rkyv::Archive {} +impl<'a, F: IsField> Copy for PolynomialOpeningsView<'a, F> where + F::BaseType: math::field::element::NativeArchived +{ +} impl<'a, F: IsField> PolynomialOpeningsView<'a, F> where - F::BaseType: rkyv::Archive, + F::BaseType: math::field::element::NativeArchived, { pub fn merkle_path(&self) -> &'a [Commitment] { match self { @@ -81,8 +84,8 @@ where pub enum DeepPolynomialOpeningView<'a, F: IsSubFieldOf, E: IsField> where - F::BaseType: rkyv::Archive, - E::BaseType: rkyv::Archive, + F::BaseType: math::field::element::NativeArchived, + E::BaseType: math::field::element::NativeArchived, { Owned(&'a DeepPolynomialOpening), Archived(&'a ArchivedDeepPolynomialOpening), @@ -90,8 +93,8 @@ where impl<'a, F: IsSubFieldOf, E: IsField> Clone for DeepPolynomialOpeningView<'a, F, E> where - F::BaseType: rkyv::Archive, - E::BaseType: rkyv::Archive, + F::BaseType: math::field::element::NativeArchived, + E::BaseType: math::field::element::NativeArchived, { fn clone(&self) -> Self { *self @@ -99,15 +102,15 @@ where } impl<'a, F: IsSubFieldOf, E: IsField> Copy for DeepPolynomialOpeningView<'a, F, E> where - F::BaseType: rkyv::Archive, - E::BaseType: rkyv::Archive, + F::BaseType: math::field::element::NativeArchived, + E::BaseType: math::field::element::NativeArchived, { } impl<'a, F: IsSubFieldOf, E: IsField> DeepPolynomialOpeningView<'a, F, E> where - F::BaseType: rkyv::Archive, - E::BaseType: rkyv::Archive, + F::BaseType: math::field::element::NativeArchived, + E::BaseType: math::field::element::NativeArchived, { pub fn composition_poly(&self) -> PolynomialOpeningsView<'a, E> { match self { @@ -152,7 +155,7 @@ where pub enum FriDecommitmentView<'a, E: IsField> where - E::BaseType: rkyv::Archive, + E::BaseType: math::field::element::NativeArchived, { Owned(&'a FriDecommitment), Archived(&'a ArchivedFriDecommitment), @@ -160,17 +163,20 @@ where impl<'a, E: IsField> Clone for FriDecommitmentView<'a, E> where - E::BaseType: rkyv::Archive, + E::BaseType: math::field::element::NativeArchived, { fn clone(&self) -> Self { *self } } -impl<'a, E: IsField> Copy for FriDecommitmentView<'a, E> where E::BaseType: rkyv::Archive {} +impl<'a, E: IsField> Copy for FriDecommitmentView<'a, E> where + E::BaseType: math::field::element::NativeArchived +{ +} impl<'a, E: IsField> FriDecommitmentView<'a, E> where - E::BaseType: rkyv::Archive, + E::BaseType: math::field::element::NativeArchived, { pub fn layers_auth_paths_len(&self) -> usize { match self { @@ -196,7 +202,7 @@ where pub enum StarkTableView<'a, F: IsField> where - F::BaseType: rkyv::Archive, + F::BaseType: math::field::element::NativeArchived, { Owned(&'a Table), Archived(&'a ArchivedTable), @@ -204,17 +210,20 @@ where impl<'a, F: IsField> Clone for StarkTableView<'a, F> where - F::BaseType: rkyv::Archive, + F::BaseType: math::field::element::NativeArchived, { fn clone(&self) -> Self { *self } } -impl<'a, F: IsField> Copy for StarkTableView<'a, F> where F::BaseType: rkyv::Archive {} +impl<'a, F: IsField> Copy for StarkTableView<'a, F> where + F::BaseType: math::field::element::NativeArchived +{ +} impl<'a, F: IsField> StarkTableView<'a, F> where - F::BaseType: rkyv::Archive, + F::BaseType: math::field::element::NativeArchived, { pub fn width(&self) -> usize { match self { @@ -269,8 +278,8 @@ where pub enum StarkProofView<'a, F: IsSubFieldOf, E: IsField, PI> where - F::BaseType: rkyv::Archive, - E::BaseType: rkyv::Archive, + F::BaseType: math::field::element::NativeArchived, + E::BaseType: math::field::element::NativeArchived, PI: rkyv::Archive, ::Archived: rkyv::Deserialize, { @@ -280,8 +289,8 @@ where impl<'a, F: IsSubFieldOf, E: IsField, PI> Clone for StarkProofView<'a, F, E, PI> where - F::BaseType: rkyv::Archive, - E::BaseType: rkyv::Archive, + F::BaseType: math::field::element::NativeArchived, + E::BaseType: math::field::element::NativeArchived, PI: rkyv::Archive, ::Archived: rkyv::Deserialize, { @@ -291,8 +300,8 @@ where } impl<'a, F: IsSubFieldOf, E: IsField, PI> Copy for StarkProofView<'a, F, E, PI> where - F::BaseType: rkyv::Archive, - E::BaseType: rkyv::Archive, + F::BaseType: math::field::element::NativeArchived, + E::BaseType: math::field::element::NativeArchived, PI: rkyv::Archive, ::Archived: rkyv::Deserialize, { @@ -300,8 +309,8 @@ where impl<'a, F: IsSubFieldOf, E: IsField, PI> StarkProofView<'a, F, E, PI> where - F::BaseType: rkyv::Archive, - E::BaseType: rkyv::Archive, + F::BaseType: math::field::element::NativeArchived, + E::BaseType: math::field::element::NativeArchived, PI: rkyv::Archive, ::Archived: rkyv::Deserialize, { diff --git a/crypto/stark/src/table.rs b/crypto/stark/src/table.rs index 0dae77c1a..22ec1d3db 100644 --- a/crypto/stark/src/table.rs +++ b/crypto/stark/src/table.rs @@ -200,7 +200,7 @@ pub use archived_table::ArchivedTable; #[cfg(target_endian = "little")] impl ArchivedTable where - F::BaseType: rkyv::Archive, + F::BaseType: math::field::element::NativeArchived, { #[inline] pub fn width(&self) -> usize { diff --git a/crypto/stark/src/verifier.rs b/crypto/stark/src/verifier.rs index e163cfe7c..30584632b 100644 --- a/crypto/stark/src/verifier.rs +++ b/crypto/stark/src/verifier.rs @@ -49,8 +49,8 @@ impl< PI, > IsStarkVerifier for Verifier where - Field::BaseType: rkyv::Archive, - FieldExtension::BaseType: rkyv::Archive, + Field::BaseType: math::field::element::NativeArchived, + FieldExtension::BaseType: math::field::element::NativeArchived, PI: rkyv::Archive + Clone, ::Archived: rkyv::Deserialize, { @@ -104,8 +104,8 @@ pub trait IsStarkVerifier< FieldExtension: Send + Sync + IsField, PI, > where - Field::BaseType: rkyv::Archive, - FieldExtension::BaseType: rkyv::Archive, + Field::BaseType: math::field::element::NativeArchived, + FieldExtension::BaseType: math::field::element::NativeArchived, PI: rkyv::Archive + Clone, ::Archived: rkyv::Deserialize, { @@ -410,7 +410,7 @@ pub trait IsStarkVerifier< FieldElement: AsBytes + Sync + Send, FieldElement: AsBytes + Sync + Send, E: IsField, - E::BaseType: rkyv::Archive, + E::BaseType: math::field::element::NativeArchived, Field: IsSubFieldOf, { let mut value = opening.evaluations().to_vec(); From f0304cb1eb413972de4efc767d5cea58aef720a8 Mon Sep 17 00:00:00 2001 From: MauroFab Date: Tue, 14 Jul 2026 18:26:31 -0300 Subject: [PATCH 07/16] chore(prover): drop unused bincode dev-dependency After the rkyv migration nothing under prover/ references bincode; the dev-dependency was dead. Other crates that still use bincode keep their own declarations. --- Cargo.lock | 1 - prover/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2250369ed..74986dcc9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -824,7 +824,6 @@ dependencies = [ name = "lambda-vm-prover" version = "0.1.0" dependencies = [ - "bincode", "criterion 0.5.1", "crypto", "digest", diff --git a/prover/Cargo.toml b/prover/Cargo.toml index 656d35b03..821b2771d 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -29,7 +29,6 @@ rkyv = { version = "0.8.10", default-features = false, features = ["alloc", "byt [dev-dependencies] env_logger = "*" criterion = { version = "0.5", default-features = false } -bincode = "1" tikv-jemallocator = "0.6" tikv-jemalloc-ctl = { version = "0.6", features = ["stats"] } tiny-keccak = { version = "2.0", features = ["keccak"] } From 5e10bb7a332d8830086a076f7a51935b0e26a643 Mon Sep 17 00:00:00 2001 From: MauroFab Date: Tue, 14 Jul 2026 18:26:31 -0300 Subject: [PATCH 08/16] docs: fix stale bincode/serde references after rkyv migration The continuation bundle derives rkyv (not serde) and round-trips through rkyv; bin/cli's VM-proof format is now rkyv, so examples_cli no longer mirrors it. Correct both statements. --- crypto/stark/examples/examples_cli.rs | 3 ++- docs/continuations_design.md | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/crypto/stark/examples/examples_cli.rs b/crypto/stark/examples/examples_cli.rs index 58afa0d5f..d8d20528b 100644 --- a/crypto/stark/examples/examples_cli.rs +++ b/crypto/stark/examples/examples_cli.rs @@ -6,7 +6,8 @@ //! examples_cli prove -o //! examples_cli verify //! -//! Proofs are bincode-serialized, mirroring `bin/cli`'s VM-proof format. +//! Proofs are bincode-serialized (this example's own format); `bin/cli`'s +//! VM-proof format is now rkyv, so this no longer mirrors it. //! Trace sizes and public inputs mirror the existing stark tests //! (`src/tests/air_tests.rs`, `src/tests/small_trace_tests.rs`, //! `src/tests/bus_tests/completeness_tests.rs`) so a proof produced by one diff --git a/docs/continuations_design.md b/docs/continuations_design.md index f788d7f71..71bb3577a 100644 --- a/docs/continuations_design.md +++ b/docs/continuations_design.md @@ -577,7 +577,7 @@ fresh agents) of the register/x254 chain, the L2G root binding, and completeness-by-enumeration found no false-accept: each forgery is caught by a Merkle/hash collision, a bus imbalance, or a Fiat-Shamir divergence. -The bundle derives serde and round-trips through `bincode` (exactly like a +The bundle derives rkyv and round-trips through `rkyv` (exactly like a monolithic `VmProof`); the CLI drives it via `prove --continuations` (writes the bundle) and `verify --continuations` (checks bundle + ELF only). `prove` picks the epoch size from `--epoch-size-log2 N` (`N=20` means 1,048,576 cycles), defaulting @@ -600,7 +600,7 @@ recursion/aggregation layer (deferred). (§3.5), **private-input genesis not bundled/recomputed** (§3.6), **cross-epoch registers** (§6), the **commit index x254** across epochs (§6), the **Fiat-Shamir statement binding** (§7), and the **standalone split prover/verifier** (§8) — bundle serialized - with `bincode` and driven from the CLI (`prove`/`verify --continuations`). + with `rkyv` and driven from the CLI (`prove`/`verify --continuations`). - **The committed code implements Design X** (`MU` gates every L2G interaction), which is the sound design. Design Y was implemented briefly, then found unsound (§4, the chain-truncation attack) and **reverted**. Do not re-introduce the From 310d824c6cd565622f5fc2e464f504f14e0b72b4 Mon Sep 17 00:00:00 2001 From: MauroFab Date: Tue, 14 Jul 2026 18:26:31 -0300 Subject: [PATCH 09/16] docs(stark): note proof serde derives are kept only for examples/tests rkyv is the authoritative wire format; the serde derives survive solely for examples/examples_cli.rs and the serde_cbor round-trip tests. Record that so nobody adds a production serde dependency on these types. --- crypto/stark/src/proof/stark.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crypto/stark/src/proof/stark.rs b/crypto/stark/src/proof/stark.rs index f04370ea5..960594866 100644 --- a/crypto/stark/src/proof/stark.rs +++ b/crypto/stark/src/proof/stark.rs @@ -8,6 +8,14 @@ use crate::{ config::Commitment, fri::fri_decommit::FriDecommitment, lookup::BusPublicInputs, table::Table, }; +// The proof types below intentionally derive both serde and rkyv. rkyv is the +// authoritative wire format (prover, CLI, recursion guest all use it); no +// production path relies on serde. The serde derives are kept only for +// `examples/examples_cli.rs` (bincode cross-version reference tool) and the +// `serde_cbor` round-trip tests in `tests/prove_verify_roundtrip_tests.rs` and +// `tests/bus_tests/completeness_tests.rs`. Do not add a production serde +// dependency on these types. + #[derive( Debug, Clone, From 410eb549d53603969c1e0f2893906303e2810288 Mon Sep 17 00:00:00 2001 From: MauroFab Date: Tue, 14 Jul 2026 18:26:32 -0300 Subject: [PATCH 10/16] refactor(syscalls): dedupe get_private_input via get_private_input_slice Both functions had byte-identical volatile-length + from_raw_parts logic. Delegate the owned-Vec path to the borrowing one so the memory layout and its single unsafe block live in exactly one place. --- syscalls/src/syscalls.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/syscalls/src/syscalls.rs b/syscalls/src/syscalls.rs index 8f6ee31c1..71d15ba40 100644 --- a/syscalls/src/syscalls.rs +++ b/syscalls/src/syscalls.rs @@ -82,14 +82,10 @@ pub fn commit(slice: &[u8]) { /// No ecall is performed — it's a plain memory read (ZisK-style). #[cfg(target_arch = "riscv64")] pub fn get_private_input() -> Vec { - // SAFETY: The host pre-loads private input at PRIVATE_INPUT_START before - // execution. The 4-byte LE length prefix is always valid (written by the - // executor). The data pointer and length are within the memory-mapped region. - let len_ptr = PRIVATE_INPUT_START as *const u32; - let len = unsafe { core::ptr::read_volatile(len_ptr) } as usize; - let data_ptr = (PRIVATE_INPUT_START + 4) as *const u8; - let slice = unsafe { core::slice::from_raw_parts(data_ptr, len) }; - slice.to_vec() + // Copy the borrowed private-input bytes into an owned `Vec`. The raw-pointer + // read (length prefix + data slice) and its single `unsafe` block live in + // `get_private_input_slice`, so the memory layout is defined in one place. + get_private_input_slice().to_vec() } #[cfg(not(target_arch = "riscv64"))] From a25904ae378659e8f1b6b69786ff2181af1d9f45 Mon Sep 17 00:00:00 2001 From: MauroFab Date: Tue, 14 Jul 2026 18:26:32 -0300 Subject: [PATCH 11/16] refactor(stark): avoid cloning the proof in Verifier::verify multi_verify only wraps owned proofs into StarkProofView::Owned and delegates to multi_verify_views. Call multi_verify_views directly with a borrowed Owned view instead of deep-cloning the proof into a throwaway single-element MultiProof. --- crypto/stark/src/verifier.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crypto/stark/src/verifier.rs b/crypto/stark/src/verifier.rs index 30584632b..d62981eb9 100644 --- a/crypto/stark/src/verifier.rs +++ b/crypto/stark/src/verifier.rs @@ -1079,10 +1079,12 @@ pub trait IsStarkVerifier< FieldElement: AsBytes + Sync + Send, FieldElement: AsBytes + Sync + Send, { - let multi_proof = MultiProof { - proofs: vec![proof.clone()], - }; - Self::multi_verify(&[air], &multi_proof, transcript, &FieldElement::zero()) + Self::multi_verify_views( + &[air], + &[StarkProofView::Owned(proof)], + transcript, + &FieldElement::zero(), + ) } /// Replays rounds 2, 3 and 4 of the protocol for a given proof, assuming round 1 has From b7e64fac0540499a4acb502bd406c6c4a7afd978 Mon Sep 17 00:00:00 2001 From: MauroFab Date: Tue, 14 Jul 2026 18:26:32 -0300 Subject: [PATCH 12/16] refactor(stark): unify owned/archived Table dimensions_consistent and into_frame Give owned Table a dimensions_consistent method (reading length through row_major_data() so it stays correct under disk-spill) and have both StarkTableView arms delegate to it. Collapse the two verbatim into_frame copies into a single StarkTableView::into_frame written over the uniform get_row/height accessors, deleting the duplicated owned and archived bodies. No behavior change. --- crypto/stark/src/proof/view.rs | 36 +++++++++++++----- crypto/stark/src/table.rs | 67 ++++++---------------------------- 2 files changed, 39 insertions(+), 64 deletions(-) diff --git a/crypto/stark/src/proof/view.rs b/crypto/stark/src/proof/view.rs index 1adb5caf7..80b9356df 100644 --- a/crypto/stark/src/proof/view.rs +++ b/crypto/stark/src/proof/view.rs @@ -14,7 +14,7 @@ use crate::proof::stark::{ ArchivedDeepPolynomialOpening, ArchivedPolynomialOpenings, ArchivedStarkProof, DeepPolynomialOpening, PolynomialOpenings, StarkProof, }; -use crate::table::{ArchivedTable, Table}; +use crate::table::{ArchivedTable, Table, TableView}; use math::field::element::{ArchivedFieldElement, FieldElement}; use math::field::traits::{IsField, IsSubFieldOf}; @@ -257,22 +257,40 @@ where /// invariant `get_row` indexing relies on. pub fn dimensions_consistent(&self) -> bool { match self { - Self::Owned(t) => t - .width - .checked_mul(t.height) - .is_some_and(|n| n == t.row_major_data().len()), + Self::Owned(t) => t.dimensions_consistent(), Self::Archived(t) => t.dimensions_consistent(), } } + /// Build a [`Frame`] over this table. Only the small OOD frame is + /// materialized (bounded by `step_size × width`), never the whole proof. + /// Written once over the uniform `get_row`/`height` accessors so the owned + /// and archived paths cannot diverge. pub fn into_frame(&self, main_trace_columns: usize, step_size: usize) -> Frame where F: IsSubFieldOf, { - match self { - Self::Owned(t) => t.into_frame(main_trace_columns, step_size), - Self::Archived(t) => t.into_frame(main_trace_columns, step_size), - } + let height = self.height(); + debug_assert!(height.is_multiple_of(step_size)); + let steps = (0..height) + .step_by(step_size) + .map(|initial_row_idx| { + let end_row_idx = initial_row_idx + step_size; + + let mut step_main_data: Vec>> = Vec::new(); + let mut step_aux_data: Vec>> = Vec::new(); + + (initial_row_idx..end_row_idx).for_each(|row_idx| { + let row = self.get_row(row_idx); + step_main_data.push(row[..main_trace_columns].to_vec()); + step_aux_data.push(row[main_trace_columns..].to_vec()); + }); + + TableView::new(step_main_data, step_aux_data) + }) + .collect(); + + Frame::new(steps) } } diff --git a/crypto/stark/src/table.rs b/crypto/stark/src/table.rs index 22ec1d3db..238c4fcfb 100644 --- a/crypto/stark/src/table.rs +++ b/crypto/stark/src/table.rs @@ -1,4 +1,3 @@ -use crate::frame::Frame; #[cfg(feature = "disk-spill")] use crypto::mmap_util::spill_slice_to_mmap; use math::field::{ @@ -236,36 +235,6 @@ where let start = row_idx * width; &self.row_major_data()[start..start + width] } - - /// Build a [`Frame`] over this table, identical to [`Table::into_frame`]. - /// Only the small OOD frame is materialized (bounded by `step_size × width`), - /// never the whole proof. - pub fn into_frame(&self, main_trace_columns: usize, step_size: usize) -> Frame - where - F: IsSubFieldOf, - { - let height = self.height(); - debug_assert!(height.is_multiple_of(step_size)); - let steps = (0..height) - .step_by(step_size) - .map(|initial_row_idx| { - let end_row_idx = initial_row_idx + step_size; - - let mut step_main_data: Vec>> = Vec::new(); - let mut step_aux_data: Vec>> = Vec::new(); - - (initial_row_idx..end_row_idx).for_each(|row_idx| { - let row = self.get_row(row_idx); - step_main_data.push(row[..main_trace_columns].to_vec()); - step_aux_data.push(row[main_trace_columns..].to_vec()); - }); - - TableView::new(step_main_data, step_aux_data) - }) - .collect(); - - Frame::new(steps) - } } /// Cloning a spilled table copies its mmap bytes into a fresh heap `Vec` @@ -409,6 +378,18 @@ impl Table { &self.data } + /// `true` iff the backing data holds exactly `width × height` elements — + /// the invariant `get_row` indexing relies on. Owned counterpart to + /// `ArchivedTable::dimensions_consistent`, reading the length through + /// `row_major_data()` so a disk-spilled table (whose `data` Vec is emptied) + /// reports its true mmap-backed length. + #[inline] + pub fn dimensions_consistent(&self) -> bool { + self.width + .checked_mul(self.height) + .is_some_and(|n| n == self.row_major_data().len()) + } + /// Returns a vector of vectors of field elements representing the table /// columns pub fn columns(&self) -> Vec>> { @@ -526,30 +507,6 @@ impl Table { #[cfg(all(feature = "disk-spill", not(unix)))] pub fn advise_drop_cache(&self) {} - - /// Given a step size, converts the given table into a `Frame`. - pub fn into_frame(&self, main_trace_columns: usize, step_size: usize) -> Frame { - debug_assert!(self.height.is_multiple_of(step_size)); - let steps = (0..self.height) - .step_by(step_size) - .map(|initial_row_idx| { - let end_row_idx = initial_row_idx + step_size; - - let mut step_main_data: Vec>> = Vec::new(); - let mut step_aux_data: Vec>> = Vec::new(); - - (initial_row_idx..end_row_idx).for_each(|row_idx| { - let row = self.get_row(row_idx); - step_main_data.push(row[..main_trace_columns].to_vec()); - step_aux_data.push(row[main_trace_columns..].to_vec()); - }); - - TableView::new(step_main_data, step_aux_data) - }) - .collect(); - - Frame::new(steps) - } } /// A view of a contiguous subset of rows of a table. From 3cf0c2b21243d543220c3f2e45c7e30ec0ca4edb Mon Sep 17 00:00:00 2001 From: MauroFab Date: Wed, 15 Jul 2026 12:07:13 -0300 Subject: [PATCH 13/16] ci: actually run the ethrex host-reference tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ethrex tests were relocated to the detached tooling/ethrex-tests workspace, but no CI job or Makefile target ran it, and the old `cargo test -p executor test_ethrex` step matched zero tests and passed vacuously — so the ethrex guest/host rkyv ProgramInput cross-check ran nowhere and the detached crate wasn't even compile-checked. Replace the vacuous step with one that runs the detached workspace (`cd tooling/ethrex-tests && cargo test --release -- --include-ignored`, using its isolated Cargo.lock so the rkyv unaligned/aligned feature conflict stays contained), and add a matching `make test-ethrex` target. --- .github/workflows/pr_main.yaml | 11 ++++++++++- Makefile | 7 ++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr_main.yaml b/.github/workflows/pr_main.yaml index 34cac9dc0..cb10ec72a 100644 --- a/.github/workflows/pr_main.yaml +++ b/.github/workflows/pr_main.yaml @@ -99,9 +99,18 @@ jobs: - name: Run ignored executor tests run: | - cargo test --release -p executor test_ethrex -- --ignored cargo test --release -p executor test_ckzg -- --ignored + # ethrex host-reference tests live in the detached `tooling/ethrex-tests` + # workspace (ethrex pins rkyv's `unaligned` feature, which must not + # feature-unify with the main workspace's aligned proof format), so run + # them from that directory to use its isolated Cargo.lock. The guest ELF + # and committed fixtures are already present from the steps above. + # --include-ignored also runs the heavier synthetic-block test. + - name: Run ethrex host-reference tests (detached workspace) + run: | + cd tooling/ethrex-tests && cargo test --release -- --include-ignored + test-cli: name: CLI tests runs-on: ubuntu-latest diff --git a/Makefile b/Makefile index bf39496b9..110c2d31f 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .PHONY: deps deps-linux deps-macos compile-programs-asm compile-programs-rust compile-bench \ compile-programs compile-recursion-elfs clean-asm clean-rust clean-bench clean-shared \ clean-recursion-elfs clean test test-asm \ -test-rust test-executor test-flamegraph flamegraph-prover test-profile-recursion test-profile-recursion-single test-profile-recursion-multi \ +test-rust test-ethrex test-executor test-flamegraph flamegraph-prover test-profile-recursion test-profile-recursion-single test-profile-recursion-multi \ test-fast test-prover test-prover-all test-prover-debug test-disk-spill test-math-cuda test-cuda-integration test-cuda-fallback \ test-prover-cuda test-prover-comprehensive-cuda \ bench-math-cuda bench-prover bench-prover-cuda build check clippy fmt lint regen-ethrex-fixtures \ @@ -262,6 +262,11 @@ test-asm: compile-programs-asm test-rust: compile-programs-rust cargo test -p executor --test rust +# ethrex host-reference tests live in the detached `tooling/ethrex-tests` +# workspace (ethrex pins rkyv's `unaligned` feature; isolated Cargo.lock). +test-ethrex: compile-programs-rust + cd tooling/ethrex-tests && cargo test --release -- --include-ignored + test-flamegraph: cargo test -p executor --test flamegraph From a1411b0ba3cb53e7c636653fc6cc8747815caa1b Mon Sep 17 00:00:00 2001 From: MauroFab Date: Wed, 15 Jul 2026 12:10:36 -0300 Subject: [PATCH 14/16] fix(stark): compile-guard proof-view field coverage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The verifier reads proof data only through the StarkProofView family, but nothing links a struct field to a view accessor: adding a field to StarkProof (or PolynomialOpenings / DeepPolynomialOpening / FriDecommitment) compiles with no accessor, and the verifier silently ignores it — a soundness gap. Add never-run functions that exhaustively destructure each backing struct without `..`, so a newly-added field becomes a compile error (E0027) pointing at the guard, forcing the author to wire an accessor. Enforces accessor presence, not arm symmetry (a wrong-but-same-typed field in an arm still needs a behavioral test). --- crypto/stark/src/proof/view.rs | 69 ++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/crypto/stark/src/proof/view.rs b/crypto/stark/src/proof/view.rs index 80b9356df..e2f84f711 100644 --- a/crypto/stark/src/proof/view.rs +++ b/crypto/stark/src/proof/view.rs @@ -469,3 +469,72 @@ where } } } + +// --------------------------------------------------------------------------- +// Field-coverage guards. +// +// Each view above mirrors a proof struct field-by-field, but nothing in the +// type system links a struct field to a view accessor: a field added to one of +// these structs would compile with no accessor, and the verifier — which reads +// proof data only through the views — would silently ignore it. That is a +// soundness gap. +// +// These functions never run. They exhaustively destructure each backing struct +// *without* `..`, so adding a field turns the omission into a compile error +// (E0027, "pattern does not mention field ...") pointing right here. When one +// stops compiling, add the matching view accessor above, then bind the new +// field below to acknowledge it is covered. +// +// This enforces accessor *presence*, not arm symmetry: an accessor whose Owned +// and Archived arms read different (same-typed) fields still type-checks and is +// only caught by a behavioral test. +#[allow(dead_code)] +fn assert_stark_proof_view_is_exhaustive, E: IsField, PI>( + p: &StarkProof, +) { + let StarkProof { + trace_length: _, + lde_trace_main_merkle_root: _, + lde_trace_aux_merkle_root: _, + lde_trace_precomputed_merkle_root: _, + trace_ood_evaluations: _, + composition_poly_root: _, + composition_poly_parts_ood_evaluation: _, + fri_layers_merkle_roots: _, + fri_final_poly_coeffs: _, + query_list: _, + deep_poly_openings: _, + nonce: _, + bus_public_inputs: _, + public_inputs: _, + } = p; +} + +#[allow(dead_code)] +fn assert_polynomial_openings_view_is_exhaustive(p: &PolynomialOpenings) { + let PolynomialOpenings { + proof: _, + evaluations: _, + evaluations_sym: _, + } = p; +} + +#[allow(dead_code)] +fn assert_deep_polynomial_opening_view_is_exhaustive, E: IsField>( + p: &DeepPolynomialOpening, +) { + let DeepPolynomialOpening { + composition_poly: _, + main_trace_polys: _, + precomputed_trace_polys: _, + aux_trace_polys: _, + } = p; +} + +#[allow(dead_code)] +fn assert_fri_decommitment_view_is_exhaustive(p: &FriDecommitment) { + let FriDecommitment { + layers_auth_paths: _, + layers_evaluations_sym: _, + } = p; +} From 5adc5d57891af96fafcbe55bbba14f89560dc34a Mon Sep 17 00:00:00 2001 From: MauroFab Date: Wed, 15 Jul 2026 12:14:17 -0300 Subject: [PATCH 15/16] harden(syscalls): clamp private-input length to MAX_PRIVATE_INPUT_SIZE get_private_input_slice read a prover-controlled u32 length prefix and built a slice of that length with no upper bound. Clamp it to 64 MiB (the same cap the host enforces at store time). An honest length is always within bound, so this never changes behavior for real inputs; it only bounds the slice when a malformed/forged prefix claims more. Defense-in-depth only: on 64-bit the u32 length can't overflow the pointer range, and no writable region overlaps the oversized span today, so this is a documented-invariant / robustness guard, not a fix for a reachable bug. --- syscalls/src/syscalls.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/syscalls/src/syscalls.rs b/syscalls/src/syscalls.rs index 71d15ba40..ad9947855 100644 --- a/syscalls/src/syscalls.rs +++ b/syscalls/src/syscalls.rs @@ -8,6 +8,15 @@ use core::arch::asm; #[cfg(target_arch = "riscv64")] pub const PRIVATE_INPUT_START: usize = 0xFF000000; +/// Maximum private-input length the guest will read, in bytes (64 MiB). +/// The host caps stored input at this size in `Memory::store_private_inputs`, +/// so an honest length prefix is always `<=` this bound; a larger value can only +/// come from a malformed or forged prefix. The reader clamps to this cap so a +/// bogus length can never make the guest fabricate an arbitrarily long slice. +/// Must match `executor::vm::memory::MAX_PRIVATE_INPUT_SIZE`. +#[cfg(target_arch = "riscv64")] +const MAX_PRIVATE_INPUT_SIZE: usize = 64 * 1024 * 1024; + #[cfg(target_arch = "riscv64")] pub enum SyscallNumbers { Print = 1, @@ -104,7 +113,12 @@ pub fn get_private_input_slice() -> &'static [u8] { // for the `'static` lifetime of the guest's single-threaded execution // region, which stays mapped and unmodified for the whole execution. let len_ptr = PRIVATE_INPUT_START as *const u32; - let len = unsafe { core::ptr::read_volatile(len_ptr) } as usize; + // Clamp the prover-written length prefix to `MAX_PRIVATE_INPUT_SIZE`. An + // honest prefix (written by the host, which caps stored input at this size) + // is always within bound, so clamping never changes behavior for real + // inputs — it only bounds the slice length when a malformed or forged prefix + // claims more, keeping the read deterministic. + let len = (unsafe { core::ptr::read_volatile(len_ptr) } as usize).min(MAX_PRIVATE_INPUT_SIZE); let data_ptr = (PRIVATE_INPUT_START + 4) as *const u8; unsafe { core::slice::from_raw_parts(data_ptr, len) } } From d906803b8643099d4845511f80d10a1e582a5963 Mon Sep 17 00:00:00 2001 From: MauroFab Date: Wed, 15 Jul 2026 15:06:28 -0300 Subject: [PATCH 16/16] refactor(verifier): collapse to a single archived proof representation The verifier previously read proofs through five Owned/Archived view enums (~64 match arms) plus ten hand-written Clone/Copy impls, so it could run over either an owned StarkProof or an rkyv-archived buffer. Collapse this to one representation: the rkyv archive. - Delete the view enums entirely. Each accessor becomes an inherent method on the corresponding Archived type (ArchivedStarkProof / ArchivedTable / ArchivedFriDecommitment / ArchivedDeepPolynomialOpening / ArchivedPolynomialOpenings). The verifier now operates on &ArchivedStarkProof and its nested Archived* refs; a &Archived_ is Copy for free, so all ten Clone/Copy blocks are gone. view.rs drops from 471 to 168 lines. - Owned public API is preserved via serialize->access shims: Verifier::verify and Verifier::multi_verify (and the prover-level verify / verify_with_options / verify_prepared / continuation::verify_continuation) keep their owned signatures but rkyv::to_bytes the proof into an aligned buffer, rkyv::access it (bytecheck), and run the archived verifier. No test/bench/example changed. - CLI verify is now zero-copy: the proof file is already read into a 16-aligned buffer, so replace the full rkyv::from_bytes:: deserialize with rkyv::access + a new prover::verify_archived_with_options entry point that materializes only the tiny metadata and reads the STARK proof in place. - The recursion guest feeds the already-accessed archived sub-proofs straight into the verifier, dropping the StarkProofView::Archived wrapper Vec (one small allocation removed; still zero-copy, no proof-sized copy/deserialize). - multi_verify_archived is now the single verification core (formerly multi_verify_views); the old thin multi_verify_archived wrapper is removed. No verification logic changed: only how proof fields are accessed (view accessor -> archived accessor / direct field). Every soundness check, transcript absorb, and Merkle/FRI/DEEP computation is byte-identical, and the guest path stays byte-identical and zero-copy. --- bin/cli/src/main.rs | 13 +- crypto/stark/src/proof/view.rs | 434 +++++---------------------------- crypto/stark/src/table.rs | 34 +++ crypto/stark/src/verifier.rs | 133 +++++----- prover/src/lib.rs | 105 +++++--- 5 files changed, 256 insertions(+), 463 deletions(-) diff --git a/bin/cli/src/main.rs b/bin/cli/src/main.rs index 1de36220b..07fbe0ee1 100644 --- a/bin/cli/src/main.rs +++ b/bin/cli/src/main.rs @@ -13,7 +13,6 @@ static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; use executor::vm::instruction::decoding::Instruction; use executor::vm::instruction::execution::{Accelerator, SyscallNumbers}; use executor::{elf::Elf, flamegraph::FlamegraphGenerator, vm::execution::Executor}; -use prover::VmProof; use stark::proof::options::GoldilocksCubicProofOptions; const DEFAULT_CONTINUATION_EPOCH_SIZE_LOG2: u32 = 20; @@ -693,10 +692,14 @@ fn cmd_verify(proof_path: PathBuf, elf_path: PathBuf, blowup: u8, time: bool) -> } }; - let proof: VmProof = match rkyv::from_bytes::(&proof_bytes) { - Ok(p) => p, + // Zero-copy: the proof bytes are already in a 16-aligned buffer, so access + // the archived `VmProof` in place (bytecheck-validated) and verify straight + // from it — no full-proof deserialization. + let archived = match rkyv::access::(&proof_bytes) + { + Ok(a) => a, Err(e) => { - eprintln!("Failed to deserialize proof: {}", e); + eprintln!("Failed to read proof: {}", e); return ExitCode::FAILURE; } }; @@ -710,7 +713,7 @@ fn cmd_verify(proof_path: PathBuf, elf_path: PathBuf, blowup: u8, time: bool) -> return ExitCode::FAILURE; } }; - let result = prover::verify_with_options(&proof, &elf_data, &opts, None, None); + let result = prover::verify_archived_with_options(archived, &elf_data, &opts, None, None); let verify_elapsed = start.elapsed(); let result = match result { Ok(valid) => valid, diff --git a/crypto/stark/src/proof/view.rs b/crypto/stark/src/proof/view.rs index e2f84f711..9a65179ef 100644 --- a/crypto/stark/src/proof/view.rs +++ b/crypto/stark/src/proof/view.rs @@ -1,20 +1,20 @@ -//! Borrowed views over a STARK proof that work identically whether the proof -//! is a real owned object or an rkyv-archived buffer. +//! Read accessors over an rkyv-archived STARK proof. //! -//! Each view is `Owned(&T)` or `Archived(&Archived)`; scalar fields are -//! copied out (`to_native()` vs. a plain copy), field-element/commitment -//! arrays stay borrowed (`slice_as_native` vs. the `Vec`'s slice directly). -//! This lets the verifier be written once and run over either representation -//! with no serialization and no logic duplication. +//! There is a single proof representation: the rkyv archive. The verifier reads +//! it in place (zero-copy); owned callers reach this path by serializing to +//! rkyv first (see `Verifier::verify`/`multi_verify`). These inherent accessors +//! on the `Archived*` proof types localize the archive->native conversions — +//! scalar fields are copied out (`to_native()`), field-element/commitment +//! arrays are viewed in place (`slice_as_native`/`as_slice`) — so the verifier +//! reads archived proof data through plain method calls. use crate::config::Commitment; -use crate::frame::Frame; use crate::fri::fri_decommit::{ArchivedFriDecommitment, FriDecommitment}; use crate::proof::stark::{ ArchivedDeepPolynomialOpening, ArchivedPolynomialOpenings, ArchivedStarkProof, DeepPolynomialOpening, PolynomialOpenings, StarkProof, }; -use crate::table::{ArchivedTable, Table, TableView}; +use crate::table::ArchivedTable; use math::field::element::{ArchivedFieldElement, FieldElement}; use math::field::traits::{IsField, IsSubFieldOf}; @@ -23,309 +23,70 @@ pub type PiDeserializer = rkyv::api::high::HighDeserializer /// `&[FieldElement]` view over an archived field-element vector (no copy). #[inline] -pub(crate) fn evals( - v: &rkyv::vec::ArchivedVec>, -) -> &[FieldElement] +fn evals(v: &rkyv::vec::ArchivedVec>) -> &[FieldElement] where G::BaseType: math::field::element::NativeArchived, { ArchivedFieldElement::slice_as_native(v.as_slice()) } -pub enum PolynomialOpeningsView<'a, F: IsField> +impl ArchivedPolynomialOpenings where F::BaseType: math::field::element::NativeArchived, { - Owned(&'a PolynomialOpenings), - Archived(&'a ArchivedPolynomialOpenings), -} - -// Manual Clone/Copy: the variants are plain references, so this holds for -// every `F`, regardless of whether `F` itself is `Clone`/`Copy`. A derive -// would add a spurious `F: Clone`/`F: Copy` bound. -impl<'a, F: IsField> Clone for PolynomialOpeningsView<'a, F> -where - F::BaseType: math::field::element::NativeArchived, -{ - fn clone(&self) -> Self { - *self - } -} -impl<'a, F: IsField> Copy for PolynomialOpeningsView<'a, F> where - F::BaseType: math::field::element::NativeArchived -{ -} - -impl<'a, F: IsField> PolynomialOpeningsView<'a, F> -where - F::BaseType: math::field::element::NativeArchived, -{ - pub fn merkle_path(&self) -> &'a [Commitment] { - match self { - Self::Owned(p) => &p.proof.merkle_path, - Self::Archived(p) => p.proof.merkle_path.as_slice(), - } + pub fn merkle_path(&self) -> &[Commitment] { + self.proof.merkle_path.as_slice() } - pub fn evaluations(&self) -> &'a [FieldElement] { - match self { - Self::Owned(p) => &p.evaluations, - Self::Archived(p) => evals(&p.evaluations), - } + pub fn evaluations(&self) -> &[FieldElement] { + evals(&self.evaluations) } - pub fn evaluations_sym(&self) -> &'a [FieldElement] { - match self { - Self::Owned(p) => &p.evaluations_sym, - Self::Archived(p) => evals(&p.evaluations_sym), - } + pub fn evaluations_sym(&self) -> &[FieldElement] { + evals(&self.evaluations_sym) } } -pub enum DeepPolynomialOpeningView<'a, F: IsSubFieldOf, E: IsField> +impl, E: IsField> ArchivedDeepPolynomialOpening where F::BaseType: math::field::element::NativeArchived, E::BaseType: math::field::element::NativeArchived, { - Owned(&'a DeepPolynomialOpening), - Archived(&'a ArchivedDeepPolynomialOpening), -} - -impl<'a, F: IsSubFieldOf, E: IsField> Clone for DeepPolynomialOpeningView<'a, F, E> -where - F::BaseType: math::field::element::NativeArchived, - E::BaseType: math::field::element::NativeArchived, -{ - fn clone(&self) -> Self { - *self + pub fn composition_poly(&self) -> &ArchivedPolynomialOpenings { + &self.composition_poly } -} -impl<'a, F: IsSubFieldOf, E: IsField> Copy for DeepPolynomialOpeningView<'a, F, E> -where - F::BaseType: math::field::element::NativeArchived, - E::BaseType: math::field::element::NativeArchived, -{ -} -impl<'a, F: IsSubFieldOf, E: IsField> DeepPolynomialOpeningView<'a, F, E> -where - F::BaseType: math::field::element::NativeArchived, - E::BaseType: math::field::element::NativeArchived, -{ - pub fn composition_poly(&self) -> PolynomialOpeningsView<'a, E> { - match self { - Self::Owned(p) => PolynomialOpeningsView::Owned(&p.composition_poly), - Self::Archived(p) => PolynomialOpeningsView::Archived(&p.composition_poly), - } - } - - pub fn main_trace_polys(&self) -> PolynomialOpeningsView<'a, F> { - match self { - Self::Owned(p) => PolynomialOpeningsView::Owned(&p.main_trace_polys), - Self::Archived(p) => PolynomialOpeningsView::Archived(&p.main_trace_polys), - } + pub fn main_trace_polys(&self) -> &ArchivedPolynomialOpenings { + &self.main_trace_polys } - pub fn precomputed_trace_polys(&self) -> Option> { - match self { - Self::Owned(p) => p - .precomputed_trace_polys - .as_ref() - .map(PolynomialOpeningsView::Owned), - Self::Archived(p) => p - .precomputed_trace_polys - .as_ref() - .map(PolynomialOpeningsView::Archived), - } + pub fn precomputed_trace_polys(&self) -> Option<&ArchivedPolynomialOpenings> { + self.precomputed_trace_polys.as_ref() } - pub fn aux_trace_polys(&self) -> Option> { - match self { - Self::Owned(p) => p - .aux_trace_polys - .as_ref() - .map(PolynomialOpeningsView::Owned), - Self::Archived(p) => p - .aux_trace_polys - .as_ref() - .map(PolynomialOpeningsView::Archived), - } - } -} - -pub enum FriDecommitmentView<'a, E: IsField> -where - E::BaseType: math::field::element::NativeArchived, -{ - Owned(&'a FriDecommitment), - Archived(&'a ArchivedFriDecommitment), -} - -impl<'a, E: IsField> Clone for FriDecommitmentView<'a, E> -where - E::BaseType: math::field::element::NativeArchived, -{ - fn clone(&self) -> Self { - *self + pub fn aux_trace_polys(&self) -> Option<&ArchivedPolynomialOpenings> { + self.aux_trace_polys.as_ref() } } -impl<'a, E: IsField> Copy for FriDecommitmentView<'a, E> where - E::BaseType: math::field::element::NativeArchived -{ -} -impl<'a, E: IsField> FriDecommitmentView<'a, E> +impl ArchivedFriDecommitment where E::BaseType: math::field::element::NativeArchived, { pub fn layers_auth_paths_len(&self) -> usize { - match self { - Self::Owned(p) => p.layers_auth_paths.len(), - Self::Archived(p) => p.layers_auth_paths.len(), - } + self.layers_auth_paths.len() } - pub fn layer_auth_path(&self, i: usize) -> &'a [Commitment] { - match self { - Self::Owned(p) => &p.layers_auth_paths[i].merkle_path, - Self::Archived(p) => p.layers_auth_paths[i].merkle_path.as_slice(), - } + pub fn layer_auth_path(&self, i: usize) -> &[Commitment] { + self.layers_auth_paths[i].merkle_path.as_slice() } - pub fn layers_evaluations_sym(&self) -> &'a [FieldElement] { - match self { - Self::Owned(p) => &p.layers_evaluations_sym, - Self::Archived(p) => evals(&p.layers_evaluations_sym), - } + pub fn layers_evaluations_sym(&self) -> &[FieldElement] { + evals(&self.layers_evaluations_sym) } } -pub enum StarkTableView<'a, F: IsField> -where - F::BaseType: math::field::element::NativeArchived, -{ - Owned(&'a Table), - Archived(&'a ArchivedTable), -} - -impl<'a, F: IsField> Clone for StarkTableView<'a, F> -where - F::BaseType: math::field::element::NativeArchived, -{ - fn clone(&self) -> Self { - *self - } -} -impl<'a, F: IsField> Copy for StarkTableView<'a, F> where - F::BaseType: math::field::element::NativeArchived -{ -} - -impl<'a, F: IsField> StarkTableView<'a, F> -where - F::BaseType: math::field::element::NativeArchived, -{ - pub fn width(&self) -> usize { - match self { - Self::Owned(t) => t.width, - Self::Archived(t) => t.width(), - } - } - - pub fn height(&self) -> usize { - match self { - Self::Owned(t) => t.height, - Self::Archived(t) => t.height(), - } - } - - pub fn get_row(&self, row_idx: usize) -> &'a [FieldElement] { - match self { - Self::Owned(t) => t.get_row(row_idx), - Self::Archived(t) => t.get_row(row_idx), - } - } - - pub fn row_major_data(&self) -> &'a [FieldElement] { - match self { - Self::Owned(t) => t.row_major_data(), - Self::Archived(t) => t.row_major_data(), - } - } - - /// `true` iff `width * height` matches the backing data length — the - /// invariant `get_row` indexing relies on. - pub fn dimensions_consistent(&self) -> bool { - match self { - Self::Owned(t) => t.dimensions_consistent(), - Self::Archived(t) => t.dimensions_consistent(), - } - } - - /// Build a [`Frame`] over this table. Only the small OOD frame is - /// materialized (bounded by `step_size × width`), never the whole proof. - /// Written once over the uniform `get_row`/`height` accessors so the owned - /// and archived paths cannot diverge. - pub fn into_frame(&self, main_trace_columns: usize, step_size: usize) -> Frame - where - F: IsSubFieldOf, - { - let height = self.height(); - debug_assert!(height.is_multiple_of(step_size)); - let steps = (0..height) - .step_by(step_size) - .map(|initial_row_idx| { - let end_row_idx = initial_row_idx + step_size; - - let mut step_main_data: Vec>> = Vec::new(); - let mut step_aux_data: Vec>> = Vec::new(); - - (initial_row_idx..end_row_idx).for_each(|row_idx| { - let row = self.get_row(row_idx); - step_main_data.push(row[..main_trace_columns].to_vec()); - step_aux_data.push(row[main_trace_columns..].to_vec()); - }); - - TableView::new(step_main_data, step_aux_data) - }) - .collect(); - - Frame::new(steps) - } -} - -pub enum StarkProofView<'a, F: IsSubFieldOf, E: IsField, PI> -where - F::BaseType: math::field::element::NativeArchived, - E::BaseType: math::field::element::NativeArchived, - PI: rkyv::Archive, - ::Archived: rkyv::Deserialize, -{ - Owned(&'a StarkProof), - Archived(&'a ArchivedStarkProof), -} - -impl<'a, F: IsSubFieldOf, E: IsField, PI> Clone for StarkProofView<'a, F, E, PI> -where - F::BaseType: math::field::element::NativeArchived, - E::BaseType: math::field::element::NativeArchived, - PI: rkyv::Archive, - ::Archived: rkyv::Deserialize, -{ - fn clone(&self) -> Self { - *self - } -} -impl<'a, F: IsSubFieldOf, E: IsField, PI> Copy for StarkProofView<'a, F, E, PI> -where - F::BaseType: math::field::element::NativeArchived, - E::BaseType: math::field::element::NativeArchived, - PI: rkyv::Archive, - ::Archived: rkyv::Deserialize, -{ -} - -impl<'a, F: IsSubFieldOf, E: IsField, PI> StarkProofView<'a, F, E, PI> +impl, E: IsField, PI> ArchivedStarkProof where F::BaseType: math::field::element::NativeArchived, E::BaseType: math::field::element::NativeArchived, @@ -333,140 +94,77 @@ where ::Archived: rkyv::Deserialize, { pub fn trace_length(&self) -> usize { - match self { - Self::Owned(p) => p.trace_length, - Self::Archived(p) => p.trace_length.to_native() as usize, - } + self.trace_length.to_native() as usize } - pub fn lde_trace_main_merkle_root(&self) -> &'a Commitment { - match self { - Self::Owned(p) => &p.lde_trace_main_merkle_root, - Self::Archived(p) => &p.lde_trace_main_merkle_root, - } + pub fn lde_trace_main_merkle_root(&self) -> &Commitment { + &self.lde_trace_main_merkle_root } - pub fn lde_trace_aux_merkle_root(&self) -> Option<&'a Commitment> { - match self { - Self::Owned(p) => p.lde_trace_aux_merkle_root.as_ref(), - Self::Archived(p) => p.lde_trace_aux_merkle_root.as_ref(), - } + pub fn lde_trace_aux_merkle_root(&self) -> Option<&Commitment> { + self.lde_trace_aux_merkle_root.as_ref() } - pub fn lde_trace_precomputed_merkle_root(&self) -> Option<&'a Commitment> { - match self { - Self::Owned(p) => p.lde_trace_precomputed_merkle_root.as_ref(), - Self::Archived(p) => p.lde_trace_precomputed_merkle_root.as_ref(), - } + pub fn lde_trace_precomputed_merkle_root(&self) -> Option<&Commitment> { + self.lde_trace_precomputed_merkle_root.as_ref() } - pub fn trace_ood_evaluations(&self) -> StarkTableView<'a, E> { - match self { - Self::Owned(p) => StarkTableView::Owned(&p.trace_ood_evaluations), - Self::Archived(p) => StarkTableView::Archived(&p.trace_ood_evaluations), - } + pub fn trace_ood_evaluations(&self) -> &ArchivedTable { + &self.trace_ood_evaluations } - pub fn composition_poly_root(&self) -> &'a Commitment { - match self { - Self::Owned(p) => &p.composition_poly_root, - Self::Archived(p) => &p.composition_poly_root, - } + pub fn composition_poly_root(&self) -> &Commitment { + &self.composition_poly_root } - pub fn composition_poly_parts_ood_evaluation(&self) -> &'a [FieldElement] { - match self { - Self::Owned(p) => &p.composition_poly_parts_ood_evaluation, - Self::Archived(p) => evals(&p.composition_poly_parts_ood_evaluation), - } + pub fn composition_poly_parts_ood_evaluation(&self) -> &[FieldElement] { + evals(&self.composition_poly_parts_ood_evaluation) } - pub fn fri_layers_merkle_roots(&self) -> &'a [Commitment] { - match self { - Self::Owned(p) => &p.fri_layers_merkle_roots, - Self::Archived(p) => p.fri_layers_merkle_roots.as_slice(), - } + pub fn fri_layers_merkle_roots(&self) -> &[Commitment] { + self.fri_layers_merkle_roots.as_slice() } - pub fn fri_final_poly_coeffs(&self) -> &'a [FieldElement] { - match self { - Self::Owned(p) => &p.fri_final_poly_coeffs, - Self::Archived(p) => evals(&p.fri_final_poly_coeffs), - } + pub fn fri_final_poly_coeffs(&self) -> &[FieldElement] { + evals(&self.fri_final_poly_coeffs) } pub fn query_list_len(&self) -> usize { - match self { - Self::Owned(p) => p.query_list.len(), - Self::Archived(p) => p.query_list.len(), - } + self.query_list.len() } - pub fn query(&self, i: usize) -> FriDecommitmentView<'a, E> { - match self { - Self::Owned(p) => FriDecommitmentView::Owned(&p.query_list[i]), - Self::Archived(p) => FriDecommitmentView::Archived(&p.query_list.as_slice()[i]), - } + pub fn query(&self, i: usize) -> &ArchivedFriDecommitment { + &self.query_list.as_slice()[i] } pub fn deep_poly_openings_len(&self) -> usize { - match self { - Self::Owned(p) => p.deep_poly_openings.len(), - Self::Archived(p) => p.deep_poly_openings.len(), - } + self.deep_poly_openings.len() } - pub fn deep_poly_opening(&self, i: usize) -> DeepPolynomialOpeningView<'a, F, E> { - match self { - Self::Owned(p) => DeepPolynomialOpeningView::Owned(&p.deep_poly_openings[i]), - Self::Archived(p) => { - DeepPolynomialOpeningView::Archived(&p.deep_poly_openings.as_slice()[i]) - } - } + pub fn deep_poly_opening(&self, i: usize) -> &ArchivedDeepPolynomialOpening { + &self.deep_poly_openings.as_slice()[i] } pub fn nonce(&self) -> Option { - match self { - Self::Owned(p) => p.nonce, - Self::Archived(p) => p.nonce.as_ref().map(|n| n.to_native()), - } + self.nonce.as_ref().map(|n| n.to_native()) } /// The bus interaction's table contribution (L), if present. This is the - /// only field of `BusPublicInputs` the verifier reads; both sides copy it - /// out (it's a single field element, not worth a dedicated view type). + /// only field of `BusPublicInputs` the verifier reads; copied out (it's a + /// single field element, not worth a dedicated view type). pub fn bus_table_contribution(&self) -> Option> { - match self { - Self::Owned(p) => p - .bus_public_inputs - .as_ref() - .map(|b| b.table_contribution.clone()), - Self::Archived(p) => p - .bus_public_inputs - .as_ref() - .map(|b| b.table_contribution.as_native().clone()), - } + self.bus_public_inputs + .as_ref() + .map(|b| b.table_contribution.as_native().clone()) } pub fn has_bus_public_inputs(&self) -> bool { - match self { - Self::Owned(p) => p.bus_public_inputs.is_some(), - Self::Archived(p) => p.bus_public_inputs.is_some(), - } + self.bus_public_inputs.is_some() } - /// Materializes the (tiny) `PI` public inputs: a clone on the owned side, - /// an rkyv deserialize on the archived side. - pub fn public_inputs(&self) -> Option - where - PI: Clone, - { - match self { - Self::Owned(p) => Some(p.public_inputs.clone()), - Self::Archived(p) => { - rkyv::deserialize::(&p.public_inputs).ok() - } - } + /// Materializes the (tiny) `PI` public inputs via an rkyv deserialize. + pub fn public_inputs(&self) -> Option { + rkyv::deserialize::(&self.public_inputs).ok() } } diff --git a/crypto/stark/src/table.rs b/crypto/stark/src/table.rs index 238c4fcfb..c8852fda2 100644 --- a/crypto/stark/src/table.rs +++ b/crypto/stark/src/table.rs @@ -235,6 +235,40 @@ where let start = row_idx * width; &self.row_major_data()[start..start + width] } + + /// Build a [`Frame`](crate::frame::Frame) over this archived table. Only the + /// small OOD frame is materialized (bounded by `step_size × width`), never + /// the whole proof. Reads through the uniform `get_row`/`height` accessors. + pub fn into_frame( + &self, + main_trace_columns: usize, + step_size: usize, + ) -> crate::frame::Frame + where + F: IsSubFieldOf, + { + let height = self.height(); + debug_assert!(height.is_multiple_of(step_size)); + let steps = (0..height) + .step_by(step_size) + .map(|initial_row_idx| { + let end_row_idx = initial_row_idx + step_size; + + let mut step_main_data: Vec>> = Vec::new(); + let mut step_aux_data: Vec>> = Vec::new(); + + (initial_row_idx..end_row_idx).for_each(|row_idx| { + let row = self.get_row(row_idx); + step_main_data.push(row[..main_trace_columns].to_vec()); + step_aux_data.push(row[main_trace_columns..].to_vec()); + }); + + TableView::new(step_main_data, step_aux_data) + }) + .collect(); + + crate::frame::Frame::new(steps) + } } /// Cloning a spilled table copies its mmap bytes into a fresh heap `Vec` diff --git a/crypto/stark/src/verifier.rs b/crypto/stark/src/verifier.rs index d62981eb9..20219bfc9 100644 --- a/crypto/stark/src/verifier.rs +++ b/crypto/stark/src/verifier.rs @@ -9,10 +9,11 @@ pub use crate::proof::view::PiDeserializer; use crate::{ config::Commitment, domain::new_verifier_domain, + fri::fri_decommit::ArchivedFriDecommitment, lookup::{BusPublicInputs, LOGUP_CHALLENGE_ALPHA, LOGUP_NUM_CHALLENGES, compute_alpha_powers}, - proof::stark::{ArchivedStarkProof, MultiProof}, - proof::view::{ - DeepPolynomialOpeningView, FriDecommitmentView, PolynomialOpeningsView, StarkProofView, + proof::stark::{ + ArchivedDeepPolynomialOpening, ArchivedMultiProof, ArchivedPolynomialOpenings, + ArchivedStarkProof, MultiProof, }, }; use crypto::fiat_shamir::is_transcript::IsStarkTranscript; @@ -29,6 +30,11 @@ use math::{ }, traits::AsBytes, }; +use rkyv::api::high::{HighSerializer, HighValidator}; +use rkyv::bytecheck::CheckBytes; +use rkyv::rancor::Error as RkyvError; +use rkyv::ser::allocator::ArenaHandle; +use rkyv::util::AlignedVec; use std::collections::HashMap; use std::marker::PhantomData; #[cfg(feature = "instruments")] @@ -92,13 +98,12 @@ compile_error!("the zero-copy STARK verifier requires a little-endian target"); /// The functionality of a STARK verifier providing methods to run the STARK Verify protocol /// https://lambdaclass.github.io/lambdaworks/starks/protocol.html /// -/// Every method below takes proof data through a [`StarkProofView`] (and its -/// nested `*View` types), a borrowed view implemented once for a real owned -/// [`StarkProof`] and once for an rkyv-archived proof read in place. This is -/// the single verification implementation: [`Self::multi_verify`] (owned) and -/// [`Self::multi_verify_archived`] (archived, used by the recursion guest) -/// are thin entry points that build the matching view and share every -/// downstream check — no serialization, no duplicated logic. +/// There is a single proof representation: the rkyv archive. Every method below +/// reads proof data straight from `&ArchivedStarkProof` (and its nested +/// `Archived*` types) in place. [`Self::multi_verify_archived`] is the single +/// verification implementation; the owned entry points [`Self::multi_verify`] +/// and [`Self::verify`] serialize the proof to rkyv first and funnel into it, +/// so there is no duplicated verification logic. pub trait IsStarkVerifier< Field: IsSubFieldOf + IsFFTField + Send + Sync, FieldExtension: Send + Sync + IsField, @@ -125,7 +130,7 @@ pub trait IsStarkVerifier< /// See https://lambdaclass.github.io/lambdaworks/starks/protocol.html#step-2-verify-claimed-composition-polynomial fn step_2_verify_claimed_composition_polynomial( air: &dyn AIR, - proof: StarkProofView<'_, Field, FieldExtension, PI>, + proof: &ArchivedStarkProof, public_inputs: &PI, domain: &VerifierDomain, challenges: &Challenges, @@ -299,7 +304,7 @@ pub trait IsStarkVerifier< /// FRI decommitments are valid and correspond to the Deep composition polynomial. fn step_3_verify_fri( air: &dyn AIR, - proof: StarkProofView<'_, Field, FieldExtension, PI>, + proof: &ArchivedStarkProof, domain: &VerifierDomain, challenges: &Challenges, ) -> bool @@ -402,7 +407,7 @@ pub trait IsStarkVerifier< /// so one Merkle path authenticates both `evaluations` (the row) and /// `evaluations_sym` (its symmetric). Same layout used for trace and composition. fn verify_opening_pair( - opening: PolynomialOpeningsView<'_, E>, + opening: &ArchivedPolynomialOpenings, root: &Commitment, iota: usize, ) -> bool @@ -421,8 +426,8 @@ pub trait IsStarkVerifier< /// Verify opening Open(tⱼ(D_LDE), 𝜐) and Open(tⱼ(D_LDE), -𝜐) for all trace polynomials tⱼ, /// where 𝜐 and -𝜐 are the elements corresponding to the index challenge `iota`. fn verify_trace_openings( - proof: StarkProofView<'_, Field, FieldExtension, PI>, - deep_poly_openings: DeepPolynomialOpeningView<'_, Field, FieldExtension>, + proof: &ArchivedStarkProof, + deep_poly_openings: &ArchivedDeepPolynomialOpening, iota: usize, ) -> bool where @@ -466,7 +471,7 @@ pub trait IsStarkVerifier< /// Verify opening Open(Hᵢ(D_LDE), 𝜐) and Open(Hᵢ(D_LDE), -𝜐) for all parts Hᵢof the composition /// polynomial, where 𝜐 and -𝜐 are the elements corresponding to the index challenge `iota`. fn verify_composition_poly_opening( - deep_poly_openings: DeepPolynomialOpeningView<'_, Field, FieldExtension>, + deep_poly_openings: &ArchivedDeepPolynomialOpening, composition_poly_merkle_root: &Commitment, iota: &usize, ) -> bool @@ -490,7 +495,7 @@ pub trait IsStarkVerifier< /// parts at the domain elements and their symmetric counterparts corresponding to all the FRI query /// index challenges. fn step_4_verify_trace_and_composition_openings( - proof: StarkProofView<'_, Field, FieldExtension, PI>, + proof: &ArchivedStarkProof, challenges: &Challenges, ) -> bool where @@ -548,10 +553,10 @@ pub trait IsStarkVerifier< /// `deep_composition_evaluation_sym`: precomputed value of p₀(-𝜐), where p₀ is the deep composition polynomial. #[allow(clippy::too_many_arguments)] fn verify_query_and_sym_openings( - proof: StarkProofView<'_, Field, FieldExtension, PI>, + proof: &ArchivedStarkProof, zetas: &[FieldElement], iota: usize, - fri_decommitment: FriDecommitmentView<'_, FieldExtension>, + fri_decommitment: &ArchivedFriDecommitment, evaluation_point_inv: FieldElement, deep_composition_evaluation: &FieldElement, deep_composition_evaluation_sym: &FieldElement, @@ -640,7 +645,7 @@ pub trait IsStarkVerifier< fn reconstruct_deep_composition_poly_evaluations_for_all_queries( challenges: &Challenges, domain: &VerifierDomain, - proof: StarkProofView<'_, Field, FieldExtension, PI>, + proof: &ArchivedStarkProof, ) -> Option> { let num_queries = challenges.iotas.len(); @@ -717,7 +722,7 @@ pub trait IsStarkVerifier< } fn reconstruct_deep_composition_poly_evaluation( - proof: StarkProofView<'_, Field, FieldExtension, PI>, + proof: &ArchivedStarkProof, evaluation_point: &FieldElement, primitive_root: &FieldElement, challenges: &Challenges, @@ -818,6 +823,11 @@ pub trait IsStarkVerifier< /// /// The transcript must be safely initialized before passing it to this method. /// The AIRs must be in the same order as the proofs in the MultiProof. + /// Owned entry point: serialize the proof to an aligned rkyv buffer and + /// verify it in place. `to_bytes` yields 16-aligned bytes, so the archived + /// access below needs no misalignment fallback. The serialized bytes are + /// byte-identical to what the prover writes, so verification accepts/rejects + /// exactly as verifying the owned proof would. fn multi_verify( airs: &[&dyn AIR], multi_proof: &MultiProof, @@ -827,41 +837,40 @@ pub trait IsStarkVerifier< where FieldElement: AsBytes + Sync + Send, FieldElement: AsBytes + Sync + Send, + MultiProof: + for<'a> rkyv::Serialize, RkyvError>>, + ArchivedMultiProof: + rkyv::Portable + for<'a> CheckBytes>, { - let views: Vec> = multi_proof - .proofs - .iter() - .map(StarkProofView::Owned) - .collect(); - Self::multi_verify_views(airs, &views, transcript, expected_bus_balance) + let bytes = match rkyv::to_bytes::(multi_proof) { + Ok(b) => b, + Err(_) => return false, + }; + let archived = match rkyv::access::, RkyvError>( + &bytes, + ) { + Ok(a) => a, + Err(_) => return false, + }; + Self::multi_verify_archived( + airs, + archived.proofs.as_slice(), + transcript, + expected_bus_balance, + ) } - /// Verifies one or more rkyv-archived STARK proofs read **in place** from - /// their archive buffer — no proof deserialization, no per-field allocation. + /// The single verification implementation: verifies one or more STARK proofs + /// read **in place** from their rkyv archive — no proof deserialization, no + /// per-field allocation. Owned callers reach it through [`Self::multi_verify`] + /// / [`Self::verify`] (which serialize first); the recursion guest and the + /// VM verifier pass their already-archived proofs straight in. fn multi_verify_archived( airs: &[&dyn AIR], proofs: &[ArchivedStarkProof], transcript: &mut (impl IsStarkTranscript + Clone), expected_bus_balance: &FieldElement, ) -> bool - where - FieldElement: AsBytes + Sync + Send, - FieldElement: AsBytes + Sync + Send, - { - let views: Vec> = - proofs.iter().map(StarkProofView::Archived).collect(); - Self::multi_verify_views(airs, &views, transcript, expected_bus_balance) - } - - /// The single verification implementation, shared by [`Self::multi_verify`] - /// (owned) and [`Self::multi_verify_archived`] (archived), operating on - /// proof views rather than either's concrete type. - fn multi_verify_views( - airs: &[&dyn AIR], - proofs: &[StarkProofView], - transcript: &mut (impl IsStarkTranscript + Clone), - expected_bus_balance: &FieldElement, - ) -> bool where FieldElement: AsBytes + Sync + Send, FieldElement: AsBytes + Sync + Send, @@ -885,7 +894,6 @@ pub trait IsStarkVerifier< // trust the prover). For normal tables, use the commitment from the proof. for (idx, (air, proof)) in airs.iter().zip(proofs).enumerate() { - let proof = *proof; // Soundness: the number of composition-poly parts is fixed by the AIR's // degree bound, NOT chosen by the prover. Deriving it from the proof would // let a malicious prover inflate the part count, widening the composition @@ -966,7 +974,6 @@ pub trait IsStarkVerifier< // the only cross-table validation. for (idx, (air, proof)) in airs.iter().zip(proofs).enumerate() { - let proof = *proof; if air.has_trace_interaction() && !proof.has_bus_public_inputs() { error!( "Table {idx}: AIR has LogUp interactions but proof is missing bus_public_inputs" @@ -989,7 +996,6 @@ pub trait IsStarkVerifier< // the prover's forking and makes per-table verification independent. for (idx, (air, proof)) in airs.iter().zip(proofs).enumerate() { - let proof = *proof; // Must match prover: fork with domain separator for multi-table, // use original transcript directly for single-table. let num_tables = airs.len(); @@ -1069,7 +1075,10 @@ pub trait IsStarkVerifier< } /// Verify a single STARK proof. - /// This is equivalent to calling `multi_verify` with a single-element slice. + /// + /// Owned entry point: serializes the proof to an aligned rkyv buffer (no + /// deep proof clone) and verifies it in place, equivalent to calling + /// `multi_verify` with a single-element slice. fn verify( proof: &StarkProof, air: &dyn AIR, @@ -1078,10 +1087,24 @@ pub trait IsStarkVerifier< where FieldElement: AsBytes + Sync + Send, FieldElement: AsBytes + Sync + Send, + StarkProof: + for<'a> rkyv::Serialize, RkyvError>>, + ArchivedStarkProof: + rkyv::Portable + for<'a> CheckBytes>, { - Self::multi_verify_views( + let bytes = match rkyv::to_bytes::(proof) { + Ok(b) => b, + Err(_) => return false, + }; + let archived = match rkyv::access::, RkyvError>( + &bytes, + ) { + Ok(a) => a, + Err(_) => return false, + }; + Self::multi_verify_archived( &[air], - &[StarkProofView::Owned(proof)], + core::slice::from_ref(archived), transcript, &FieldElement::zero(), ) @@ -1091,7 +1114,7 @@ pub trait IsStarkVerifier< /// already been replayed and the RAP challenges are known. fn replay_rounds_after_round_1( air: &dyn AIR, - proof: StarkProofView<'_, Field, FieldExtension, PI>, + proof: &ArchivedStarkProof, public_inputs: &PI, domain: &VerifierDomain, transcript: &mut impl IsStarkTranscript, @@ -1246,7 +1269,7 @@ pub trait IsStarkVerifier< /// Verifies a single table after round 1 has been replayed. fn verify_rounds_2_to_4( air: &dyn AIR, - proof: StarkProofView<'_, Field, FieldExtension, PI>, + proof: &ArchivedStarkProof, public_inputs: &PI, transcript: &mut impl IsStarkTranscript, rap_challenges: Vec>, diff --git a/prover/src/lib.rs b/prover/src/lib.rs index 77c534d48..6574b199b 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -65,8 +65,7 @@ use crate::test_utils::{ // fixed at guest build time (`recursion::Preset`). pub use stark::config::Commitment; pub use stark::proof::options::{GoldilocksCubicProofOptions, ProofOptions}; -use stark::proof::stark::MultiProof; -use stark::proof::view::StarkProofView; +use stark::proof::stark::{ArchivedMultiProof, ArchivedStarkProof, MultiProof}; /// A run-length encoded range of contiguous zero-initialized 4KB pages. /// @@ -372,16 +371,10 @@ pub fn verify_recursion_blob<'a>( let program = Elf::load(inner_elf).map_err(|e| Error::ElfLoad(format!("{e}")))?; let elf_digest = statement::elf_digest(inner_elf); - let views: Vec> = archived - .vm_proof - .proof - .proofs - .as_slice() - .iter() - .map(StarkProofView::Archived) - .collect(); + // The archived STARK sub-proofs are fed straight into the verifier — read + // in place from the blob, no wrapper, no copy (guest path stays zero-copy). let ok = verify_proof_parts( - &views, + archived.vm_proof.proof.proofs.as_slice(), &table_counts, &runtime_page_ranges, num_private_input_pages, @@ -957,12 +950,11 @@ pub(crate) fn compute_expected_commit_bus_balance( compute_commit_bus_offset(public_output_bytes, start_index, &z, &alpha) } -/// View counterpart of [`replay_transcript_phase_a`]: replays Phase A over a -/// proof view (owned or archived-in-place), with no `MultiProof` -/// deserialization required either way. -pub(crate) fn replay_transcript_phase_a_view( +/// Archived counterpart of [`replay_transcript_phase_a`]: replays Phase A over +/// rkyv-archived proofs read in place, with no `MultiProof` deserialization. +pub(crate) fn replay_transcript_phase_a_archived( airs: &[&dyn AIR], - proofs: &[StarkProofView], + proofs: &[ArchivedStarkProof], transcript: &mut DefaultTranscript, ) -> (FieldElement, FieldElement) { for (air, proof) in airs.iter().zip(proofs) { @@ -976,16 +968,16 @@ pub(crate) fn replay_transcript_phase_a_view( (z, alpha) } -/// View counterpart of [`compute_expected_commit_bus_balance`]: operates on a -/// proof view slice (owned or archived-in-place). -pub(crate) fn compute_expected_commit_bus_balance_view( +/// Archived counterpart of [`compute_expected_commit_bus_balance`]: operates on +/// rkyv-archived proofs read in place. +pub(crate) fn compute_expected_commit_bus_balance_archived( airs: &[&dyn AIR], - proofs: &[StarkProofView], + proofs: &[ArchivedStarkProof], public_output_bytes: &[u8], start_index: u64, transcript: &mut DefaultTranscript, ) -> Option> { - let (z, alpha) = replay_transcript_phase_a_view(airs, proofs, transcript); + let (z, alpha) = replay_transcript_phase_a_archived(airs, proofs, transcript); compute_commit_bus_offset(public_output_bytes, start_index, &z, &alpha) } @@ -1300,15 +1292,17 @@ pub(crate) fn verify_prepared( decode_commitment: Option, page_commitments: Option<&[(u64, Commitment)]>, ) -> Result { - let views: Vec> = vm_proof - .proof - .proofs - .iter() - .map(StarkProofView::Owned) - .collect(); + // Serialize the owned proof to an aligned rkyv buffer and verify it in + // place, funneling into the same archived verifier the recursion guest and + // CLI use. `to_bytes` yields byte-identical bytes to what the prover writes, + // so this accepts/rejects exactly as an owned verify would. + let bytes = rkyv::to_bytes::(&vm_proof.proof) + .map_err(|e| Error::Execution(format!("rkyv serialize proof failed: {e}")))?; + let archived = rkyv::access::, rkyv::rancor::Error>(&bytes) + .map_err(|e| Error::Execution(format!("rkyv access proof failed: {e}")))?; verify_proof_parts( - &views, + archived.proofs.as_slice(), &vm_proof.table_counts, &vm_proof.runtime_page_ranges, vm_proof.num_private_input_pages, @@ -1321,15 +1315,56 @@ pub(crate) fn verify_prepared( ) } +/// Zero-copy counterpart to [`verify_with_options`]: verifies straight from an +/// rkyv-archived [`VmProof`] read in place by the caller (e.g. via +/// `rkyv::access` over an aligned buffer), with no full proof deserialization. +/// Only the small metadata (table counts, page ranges) is materialized; the +/// STARK proof stays in the caller's buffer. Used by the CLI `verify` command. +pub fn verify_archived_with_options( + archived: &ArchivedVmProof, + elf_bytes: &[u8], + proof_options: &ProofOptions, + decode_commitment: Option, + page_commitments: Option<&[(u64, Commitment)]>, +) -> Result { + use rkyv::rancor::Error as RkyvError; + + let program = Elf::load(elf_bytes).map_err(|e| Error::ElfLoad(format!("{e}")))?; + let elf_digest = statement::elf_digest(elf_bytes); + + // Materialize only the small metadata; the proof stays archived in place. + let table_counts: TableCounts = + rkyv::deserialize::(&archived.table_counts) + .map_err(|e| Error::Execution(format!("rkyv deserialize table_counts failed: {e}")))?; + let runtime_page_ranges: Vec = + rkyv::deserialize::, RkyvError>(&archived.runtime_page_ranges) + .map_err(|e| Error::Execution(format!("rkyv deserialize page ranges failed: {e}")))?; + let num_private_input_pages = archived.num_private_input_pages.to_native() as usize; + let public_output: &[u8] = archived.public_output.as_slice(); + + verify_proof_parts( + archived.proof.proofs.as_slice(), + &table_counts, + &runtime_page_ranges, + num_private_input_pages, + public_output, + &program, + &elf_digest, + proof_options, + decode_commitment, + page_commitments, + ) +} + /// The single VM-proof verification implementation, given the proof's -/// metadata fields plus an already-parsed ELF and its digest. Both -/// [`verify_prepared`] (owned proof) and [`verify_recursion_blob`] (guest -/// blob, zero-copy) funnel here, passing a [`StarkProofView`] slice over -/// their respective (owned or archived) proof data — no serialization, no +/// metadata fields plus an already-parsed ELF and its digest. All callers — +/// [`verify_prepared`] / [`verify_archived_with_options`] (host) and +/// [`verify_recursion_blob`] (guest blob) — funnel here with a slice of +/// rkyv-archived STARK sub-proofs read in place: no per-field allocation, no /// duplicated verification logic, and no repeated `Elf::load`/digest. #[allow(clippy::too_many_arguments)] fn verify_proof_parts( - proofs: &[StarkProofView], + proofs: &[ArchivedStarkProof], table_counts: &TableCounts, runtime_page_ranges: &[RuntimePageRange], num_private_input_pages: usize, @@ -1411,7 +1446,7 @@ fn verify_proof_parts( // independently of the multi_verify transcript, but both must start from // the same statement-bound state. let mut transcript_for_replay = transcript.clone(); - let expected_bus_balance = match compute_expected_commit_bus_balance_view( + let expected_bus_balance = match compute_expected_commit_bus_balance_archived( &air_refs, proofs, public_output, @@ -1426,7 +1461,7 @@ fn verify_proof_parts( stark::profile_markers::step_marker::<{ stark::profile_markers::STEP_AIRS_AND_BUS_BALANCE_DONE }>( ); - Ok(Verifier::multi_verify_views( + Ok(Verifier::multi_verify_archived( &air_refs, proofs, &mut transcript,