From 49bf510ab0deefa78f96b25fccd57b749a5164a9 Mon Sep 17 00:00:00 2001 From: Al-Kindi-0 <82364884+Al-Kindi-0@users.noreply.github.com> Date: Mon, 10 Mar 2025 14:28:35 +0100 Subject: [PATCH 01/16] fix: add bound checks on polynomials defining the secret key during generation --- src/dsa/rpo_falcon512/keys/mod.rs | 1 + src/dsa/rpo_falcon512/keys/secret_key.rs | 4 +- src/dsa/rpo_falcon512/math/mod.rs | 72 +++++++++++++----------- src/dsa/rpo_falcon512/math/polynomial.rs | 14 +++++ 4 files changed, 55 insertions(+), 36 deletions(-) diff --git a/src/dsa/rpo_falcon512/keys/mod.rs b/src/dsa/rpo_falcon512/keys/mod.rs index 60cfa8d..02be3df 100644 --- a/src/dsa/rpo_falcon512/keys/mod.rs +++ b/src/dsa/rpo_falcon512/keys/mod.rs @@ -9,6 +9,7 @@ pub use public_key::{PubKeyPoly, PublicKey}; mod secret_key; pub use secret_key::SecretKey; +pub(crate) use secret_key::{WIDTH_BIG_POLY_COEFFICIENT, WIDTH_SMALL_POLY_COEFFICIENT}; // TESTS // ================================================================================================ diff --git a/src/dsa/rpo_falcon512/keys/secret_key.rs b/src/dsa/rpo_falcon512/keys/secret_key.rs index 0d4b709..5204a3a 100644 --- a/src/dsa/rpo_falcon512/keys/secret_key.rs +++ b/src/dsa/rpo_falcon512/keys/secret_key.rs @@ -22,8 +22,8 @@ use crate::dsa::rpo_falcon512::{ // CONSTANTS // ================================================================================================ -const WIDTH_BIG_POLY_COEFFICIENT: usize = 8; -const WIDTH_SMALL_POLY_COEFFICIENT: usize = 6; +pub(crate) const WIDTH_BIG_POLY_COEFFICIENT: usize = 8; +pub(crate) const WIDTH_SMALL_POLY_COEFFICIENT: usize = 6; // SECRET KEY // ================================================================================================ diff --git a/src/dsa/rpo_falcon512/math/mod.rs b/src/dsa/rpo_falcon512/math/mod.rs index e53706a..de12279 100644 --- a/src/dsa/rpo_falcon512/math/mod.rs +++ b/src/dsa/rpo_falcon512/math/mod.rs @@ -5,7 +5,7 @@ //! 1. The [reference](https://falcon-sign.info/impl/README.txt.html) implementation by Thomas //! Pornin. //! 2. The [Rust](https://github.com/aszepieniec/falcon-rust) implementation by Alan Szepieniec. -use alloc::{string::String, vec::Vec}; +use alloc::vec::Vec; use core::ops::MulAssign; #[cfg(not(feature = "std"))] @@ -14,7 +14,10 @@ use num::{BigInt, FromPrimitive, One, Zero}; use num_complex::Complex64; use rand::Rng; -use super::MODULUS; +use super::{ + keys::{WIDTH_BIG_POLY_COEFFICIENT, WIDTH_SMALL_POLY_COEFFICIENT}, + MODULUS, +}; mod fft; pub use fft::{CyclotomicFourier, FastFft}; @@ -31,6 +34,9 @@ use self::samplerz::sampler_z; mod polynomial; pub use polynomial::Polynomial; +const MAX_SMALL_POLY_COEFFICENT_SIZE: i16 = (1 << (WIDTH_SMALL_POLY_COEFFICIENT - 1)) - 1; +const MAX_BIG_POLY_COEFFICENT_SIZE: i16 = (1 << (WIDTH_BIG_POLY_COEFFICIENT - 1)) - 1; + pub trait Inverse: Copy + Zero + MulAssign + One { /// Gets the inverse of a, or zero if it is zero. fn inverse_or_zero(self) -> Self; @@ -85,6 +91,15 @@ pub(crate) fn ntru_gen(n: usize, rng: &mut R) -> [Polynomial; 4] { loop { let f = gen_poly(n, rng); let g = gen_poly(n, rng); + + // we do bound checks on the coefficients of the sampled polynomials in order to make sure + // that they will be encodable/decodable + if !(check_coefficients_bound(&f, MAX_SMALL_POLY_COEFFICENT_SIZE) + && check_coefficients_bound(&g, MAX_SMALL_POLY_COEFFICENT_SIZE)) + { + continue; + } + let f_ntt = f.map(|&i| FalconFelt::new(i)).fft(); if f_ntt.coefficients.iter().any(|e| e.is_zero()) { continue; @@ -97,12 +112,16 @@ pub(crate) fn ntru_gen(n: usize, rng: &mut R) -> [Polynomial; 4] { if let Some((capital_f, capital_g)) = ntru_solve(&f.map(|&i| i.into()), &g.map(|&i| i.into())) { - return [ - g, - -f, - capital_g.map(|i| i.try_into().unwrap()), - -capital_f.map(|i| i.try_into().unwrap()), - ]; + // we do bound checks on the coefficients of the solution polynomials in order to make + // sure that they will be encodable/decodable + let capital_f = capital_f.map(|i| i.try_into().unwrap()); + let capital_g = capital_g.map(|i| i.try_into().unwrap()); + if !(check_coefficients_bound(&capital_f, MAX_BIG_POLY_COEFFICENT_SIZE) + && check_coefficients_bound(&capital_g, MAX_BIG_POLY_COEFFICENT_SIZE)) + { + continue; + } + return [g, -f, capital_g, -capital_f]; } } } @@ -143,19 +162,7 @@ fn ntru_solve( let mut capital_f = (capital_f_prime_xsq.karatsuba(&g_minx)).reduce_by_cyclotomic(n); let mut capital_g = (capital_g_prime_xsq.karatsuba(&f_minx)).reduce_by_cyclotomic(n); - match babai_reduce(f, g, &mut capital_f, &mut capital_g) { - Ok(_) => Some((capital_f, capital_g)), - Err(_e) => { - #[cfg(test)] - { - panic!("{}", _e); - } - #[cfg(not(test))] - { - None - } - }, - } + babai_reduce(f, g, &mut capital_f, &mut capital_g).map(|()| (capital_f, capital_g)) } /// Generates a polynomial of degree at most n-1 whose coefficients are distributed according @@ -216,7 +223,7 @@ fn babai_reduce( g: &Polynomial, capital_f: &mut Polynomial, capital_g: &mut Polynomial, -) -> Result<(), String> { +) -> Option<()> { let bitsize = |bi: &BigInt| (bi.bits() + 7) & (u64::MAX ^ 7); let n = f.coefficients.len(); let size = [ @@ -282,20 +289,11 @@ fn babai_reduce( counter += 1; if counter > 1000 { - // If we get here, that means that (with high likelihood) we are in an - // infinite loop. We know it happens from time to time -- seldomly, but it - // does. It would be nice to fix that! But in order to fix it we need to be - // able to reproduce it, and for that we need test vectors. So print them - // and hope that one day they circle back to the implementor. - return Err(format!("Encountered infinite loop in babai_reduce of falcon-rust.\n\\ - Please help the developer(s) fix it! You can do this by sending them the inputs to the function that caused the behavior:\n\\ - f: {:?}\n\\ - g: {:?}\n\\ - capital_f: {:?}\n\\ - capital_g: {:?}\n", f.coefficients, g.coefficients, capital_f.coefficients, capital_g.coefficients)); + // If we get here, it means that (with high likelihood) we are in an infinite loop. + return None; } } - Ok(()) + Some(()) } /// Extended Euclidean algorithm for computing the greatest common divisor (g) and @@ -320,3 +318,9 @@ fn xgcd(a: &BigInt, b: &BigInt) -> (BigInt, BigInt, BigInt) { (old_r, old_s, old_t) } + +/// Asserts that the balanced values of the coefficients of a polynomial are within the interval +/// [-bound, bound]. +fn check_coefficients_bound(polynomial: &Polynomial, bound: i16) -> bool { + polynomial.to_balanced_values().iter().all(|c| *c <= bound && *c >= -bound) +} diff --git a/src/dsa/rpo_falcon512/math/polynomial.rs b/src/dsa/rpo_falcon512/math/polynomial.rs index 035f4d3..e32f3e0 100644 --- a/src/dsa/rpo_falcon512/math/polynomial.rs +++ b/src/dsa/rpo_falcon512/math/polynomial.rs @@ -598,6 +598,20 @@ impl Polynomial { } } +impl Polynomial { + /// Returns the coefficients of this polynomial as Miden field elements. + pub fn to_elements(&self) -> Vec { + self.coefficients.to_vec() + } +} + +impl Polynomial { + /// Returns the balanced values of the coefficients of this polynomial. + pub fn to_balanced_values(&self) -> Vec { + self.coefficients.iter().map(|c| FalconFelt::new(*c).balanced_value()).collect() + } +} + // TESTS // ================================================================================================ From 9acbf2f10cb2684d1dfbf6da823ca126bf9c77f7 Mon Sep 17 00:00:00 2001 From: Al-Kindi-0 <82364884+Al-Kindi-0@users.noreply.github.com> Date: Mon, 10 Mar 2025 14:39:48 +0100 Subject: [PATCH 02/16] doc: changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14fd3a3..de54686 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - [BREAKING] Updated Winterfell dependency to v0.12 (#374). - Added debug-only duplicate column check in `build_subtree` (#378). - Filter out empty values in concurrent version of `Smt::with_entries` to fix a panic (#383). +- Add range checks to `ntru_gen` for Falcon DSA (#391). ## 0.13.3 (2025-02-18) From cd0821961da893efef7763f407720a8caf798f6f Mon Sep 17 00:00:00 2001 From: Krushimir Date: Mon, 10 Mar 2025 19:51:16 +0100 Subject: [PATCH 03/16] test: adds property-based testing and fuzzing for `SMT` (#385) * Adds concurrent proptests * Adds fuzzing for SMT * fix: concurrent mutations without mutated entries * fix: key sorting --- .config/nextest.toml | 6 + .github/workflows/test.yml | 17 + .gitignore | 3 + CHANGELOG.md | 3 + Cargo.toml | 4 + Makefile | 11 +- README.md | 16 + fuzz/.gitignore | 4 + fuzz/Cargo.lock | 555 ++++++++++++++++++++++++ fuzz/Cargo.toml | 20 + fuzz/fuzz_targets/smt.rs | 80 ++++ src/merkle/smt/full/concurrent/mod.rs | 73 +++- src/merkle/smt/full/concurrent/tests.rs | 135 +++++- src/merkle/smt/full/mod.rs | 21 +- src/merkle/smt/full/tests.rs | 14 + 15 files changed, 939 insertions(+), 23 deletions(-) create mode 100644 fuzz/.gitignore create mode 100644 fuzz/Cargo.lock create mode 100644 fuzz/Cargo.toml create mode 100644 fuzz/fuzz_targets/smt.rs diff --git a/.config/nextest.toml b/.config/nextest.toml index 75597f9..513c7c7 100644 --- a/.config/nextest.toml +++ b/.config/nextest.toml @@ -1,3 +1,9 @@ [profile.default] failure-output = "immediate-final" fail-fast = false +default-filter = 'not test(merkle::smt::full::concurrent)' + +[profile.smt-concurrent] +failure-output = "immediate-final" +fail-fast = false +default-filter = 'test(merkle::smt::full::concurrent)' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2194ef6..386ef3f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,3 +26,20 @@ jobs: run: | rustup update --no-self-update ${{matrix.toolchain}} make test-${{matrix.args}} + + test-smt-concurrent: + name: test-smt-concurrent ${{ matrix.toolchain }} + runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' && (github.base_ref == 'main' || github.base_ref == 'next') }} + strategy: + fail-fast: false + matrix: + toolchain: [stable, nightly] + timeout-minutes: 30 + steps: + - uses: actions/checkout@main + - uses: taiki-e/install-action@nextest + - name: Perform concurrent SMT tests + run: | + rustup update --no-self-update ${{matrix.toolchain}} + make test-smt-concurrent \ No newline at end of file diff --git a/.gitignore b/.gitignore index c7b497e..3668393 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,6 @@ cmake-build-* # VS Code .vscode/ + +# Proptest +tests.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 14fd3a3..ffb739f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ - [BREAKING] Updated Winterfell dependency to v0.12 (#374). - Added debug-only duplicate column check in `build_subtree` (#378). - Filter out empty values in concurrent version of `Smt::with_entries` to fix a panic (#383). +- Added property-based testing (proptest) and fuzzing for `Smt::with_entries` and `Smt::compute_mutations` (#385). +- Sort keys in a leaf in the concurrent implementation of `Smt::with_entries`, ensuring consistency with the sequential version (#385). +- Skip unchanged leaves in the concurrent implementation of `Smt::compute_mutations` (#385). ## 0.13.3 (2025-02-18) diff --git a/Cargo.toml b/Cargo.toml index d161859..7e1125f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,3 +91,7 @@ seq-macro = { version = "0.3" } [build-dependencies] cc = { version = "1.2", optional = true, features = ["parallel"] } glob = "0.3" + +[lints.rust] +# Suppress warnings about `cfg(fuzzing)`, which is automatically set when using `cargo-fuzz`. +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] } diff --git a/Makefile b/Makefile index 6ab285d..af430a9 100644 --- a/Makefile +++ b/Makefile @@ -54,9 +54,12 @@ test-smt-hashmaps: ## Run tests with `smt_hashmaps` feature enabled test-no-std: ## Run tests with `no-default-features` (std) $(DEBUG_OVERFLOW_INFO) cargo nextest run --profile default --release --no-default-features +.PHONY: test-smt-concurrent +test-smt-concurrent: ## Run only concurrent SMT tests + $(DEBUG_OVERFLOW_INFO) cargo nextest run --profile smt-concurrent --release --all-features .PHONY: test -test: test-default test-smt-hashmaps test-no-std ## Run all tests +test: test-default test-smt-hashmaps test-no-std ## Run all tests except concurrent SMT tests # --- checking ------------------------------------------------------------------------------------ @@ -91,3 +94,9 @@ bench: ## Run crypto benchmarks .PHONY: bench-smt-concurrent bench-smt-concurrent: ## Run SMT benchmarks with concurrent feature cargo run --release --features concurrent,executable -- --size 1000000 + +# --- fuzzing -------------------------------------------------------------------------------- + +.PHONY: fuzz-smt +fuzz-smt: ## Run fuzzing for SMT + cargo +nightly fuzz run smt --release -- -max_len=10485760 diff --git a/README.md b/README.md index 1addcb8..cd59c3d 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,22 @@ We do that by enabling some special [flags](https://doc.rust-lang.org/cargo/refe RUSTFLAGS="-C debug-assertions -C overflow-checks -C debuginfo=2" cargo test --release ``` +## Fuzzing + +The `fuzz-smt` fuzz target is designed to test the `Smt` implementation. It generates random SMT entries and updates, and then compares the results of the sequential and parallel implementations. + +Before running the fuzz tests, ensure you have `cargo-fuzz` installed: + +```shell +cargo install cargo-fuzz +``` + +To run the fuzz target, use: + +```shell +make fuzz-smt +``` + ## License This project is [MIT licensed](./LICENSE). diff --git a/fuzz/.gitignore b/fuzz/.gitignore new file mode 100644 index 0000000..1a45eee --- /dev/null +++ b/fuzz/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +coverage diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock new file mode 100644 index 0000000..201f30a --- /dev/null +++ b/fuzz/Cargo.lock @@ -0,0 +1,555 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "blake3" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1230237285e3e10cde447185e8975408ae24deaa67205ce684805c25bc0c7937" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + +[[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 = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[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 = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", + "rayon", + "serde", +] + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.170" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf78f52d400cf2d84a3a973a78a592b4adc535739e0a5597a0da6f0c357adc75" +dependencies = [ + "arbitrary", + "cc", +] + +[[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + +[[package]] +name = "miden-crypto" +version = "0.14.0" +dependencies = [ + "blake3", + "cc", + "glob", + "hashbrown", + "num", + "num-complex", + "rand", + "rand_core", + "rayon", + "sha3", + "thiserror", + "winter-crypto", + "winter-math", + "winter-utils", +] + +[[package]] +name = "miden-crypto-fuzz" +version = "0.0.0" +dependencies = [ + "libfuzzer-sys", + "miden-crypto", + "rand", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[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-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +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.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "serde" +version = "1.0.218" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.218" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "unicode-ident" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winter-crypto" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32247cde9f43e5bbd05362caa7274608790ea69b14f7c81cd509aae7127c5ff2" +dependencies = [ + "blake3", + "sha3", + "winter-math", + "winter-utils", +] + +[[package]] +name = "winter-math" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "326dfe4bfa4072b7c909133a88f8807820d3e49e5dfd246f67981771f74a0ed3" +dependencies = [ + "winter-utils", +] + +[[package]] +name = "winter-utils" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d47518e6931955dcac73a584cacb04550b82ab2f45c72880cbbbdbe13adb63c" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml new file mode 100644 index 0000000..9029557 --- /dev/null +++ b/fuzz/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "miden-crypto-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.4" +miden-crypto = { path = "..", features = ["concurrent"] } +rand = { version = "0.8", default-features = false } + +[[bin]] +name = "smt" +path = "fuzz_targets/smt.rs" +test = false +doc = false +bench = false diff --git a/fuzz/fuzz_targets/smt.rs b/fuzz/fuzz_targets/smt.rs new file mode 100644 index 0000000..8304292 --- /dev/null +++ b/fuzz/fuzz_targets/smt.rs @@ -0,0 +1,80 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; +use miden_crypto::{merkle::Smt, hash::rpo::RpoDigest, Word, Felt, ONE}; +use rand::Rng; // Needed for randomizing the split percentage + +struct FuzzInput { + entries: Vec<(RpoDigest, Word)>, + updates: Vec<(RpoDigest, Word)>, +} + +impl FuzzInput { + fn from_bytes(data: &[u8]) -> Self { + let mut rng = rand::thread_rng(); + let split_percentage = rng.gen_range(20..80); // Randomly choose between 20% and 80% + + let split_index = (data.len() * split_percentage) / 100; + let (construction_data, update_data) = data.split_at(split_index); + + let entries = Self::parse_entries(construction_data); + let updates = Self::parse_entries(update_data); + + Self { entries, updates } + } + + fn parse_entries(data: &[u8]) -> Vec<(RpoDigest, Word)> { + let mut entries = Vec::new(); + let num_entries = data.len() / 40; // Each entry is 40 bytes + + for chunk in data.chunks_exact(40).take(num_entries) { + let key = RpoDigest::new([ + Felt::new(u64::from_le_bytes(chunk[0..8].try_into().unwrap())), + Felt::new(u64::from_le_bytes(chunk[8..16].try_into().unwrap())), + Felt::new(u64::from_le_bytes(chunk[16..24].try_into().unwrap())), + Felt::new(u64::from_le_bytes(chunk[24..32].try_into().unwrap())), + ]); + let value = [ + ONE, + ONE, + ONE, + Felt::new(u64::from_le_bytes(chunk[32..40].try_into().unwrap())), + ]; + entries.push((key, value)); + } + + entries + } +} + +fuzz_target!(|data: &[u8]| { + let fuzz_input = FuzzInput::from_bytes(data); + run_fuzz_smt(fuzz_input); +}); + +fn run_fuzz_smt(fuzz_input: FuzzInput) { + let sequential_result = Smt::fuzz_with_entries_sequential(fuzz_input.entries.clone()); + let parallel_result = Smt::with_entries(fuzz_input.entries); + + match (sequential_result, parallel_result) { + (Ok(sequential_smt), Ok(parallel_smt)) => { + assert_eq!(sequential_smt.root(), parallel_smt.root(), "Mismatch in SMT roots!"); + + let sequential_mutations = sequential_smt.fuzz_compute_mutations_sequential(fuzz_input.updates.clone()); + let parallel_mutations = parallel_smt.compute_mutations(fuzz_input.updates); + + assert_eq!(sequential_mutations.root(), parallel_mutations.root(), "Mismatch in mutation results!"); + assert_eq!(sequential_mutations.node_mutations(), parallel_mutations.node_mutations(), "Node mutations mismatch!"); + assert_eq!(sequential_mutations.new_pairs(), parallel_mutations.new_pairs(), "New pairs mismatch!"); + } + (Err(e1), Err(e2)) => { + assert_eq!( + format!("{:?}", e1), + format!("{:?}", e2), + "Different errors returned" + ); + } + (Ok(_), Err(e)) => panic!("Sequential succeeded but parallel failed with: {:?}", e), + (Err(e), Ok(_)) => panic!("Parallel succeeded but sequential failed with: {:?}", e), + } +} diff --git a/src/merkle/smt/full/concurrent/mod.rs b/src/merkle/smt/full/concurrent/mod.rs index 1785642..14730e6 100644 --- a/src/merkle/smt/full/concurrent/mod.rs +++ b/src/merkle/smt/full/concurrent/mod.rs @@ -4,7 +4,7 @@ use core::mem; use num::Integer; use super::{ - EmptySubtreeRoots, InnerNode, InnerNodes, LeafIndex, Leaves, MerkleError, MutationSet, + leaf, EmptySubtreeRoots, InnerNode, InnerNodes, LeafIndex, Leaves, MerkleError, MutationSet, NodeIndex, RpoDigest, Smt, SmtLeaf, SparseMerkleTree, Word, SMT_DEPTH, }; use crate::merkle::smt::{NodeMutation, NodeMutations, UnorderedMap}; @@ -89,6 +89,17 @@ impl Smt { // Convert sorted pairs into mutated leaves and capture any new pairs let (mut subtree_leaves, new_pairs) = self.sorted_pairs_to_mutated_subtree_leaves(sorted_kv_pairs); + + // If no mutations, return an empty mutation set + if subtree_leaves.is_empty() { + return MutationSet { + old_root: self.root(), + new_root: self.root(), + node_mutations: NodeMutations::default(), + new_pairs, + }; + } + let mut node_mutations = NodeMutations::default(); // Process each depth level in reverse, stepping by the subtree depth @@ -111,13 +122,22 @@ impl Smt { debug_assert!(!subtree_leaves.is_empty()); } - // Finalize the mutation set with updated roots and mutations - MutationSet { + let new_root = subtree_leaves[0][0].hash; + + // Create mutation set + let mutation_set = MutationSet { old_root: self.root(), - new_root: subtree_leaves[0][0].hash, + new_root, node_mutations, new_pairs, - } + }; + + // There should be mutations and new pairs at this point + debug_assert!( + !mutation_set.node_mutations().is_empty() && !mutation_set.new_pairs().is_empty() + ); + + mutation_set } // SUBTREE MUTATION @@ -244,7 +264,9 @@ impl Smt { /// With debug assertions on, this function panics if it detects that `pairs` is not correctly /// sorted. Without debug assertions, the returned computations will be incorrect. fn sorted_pairs_to_leaves(pairs: Vec<(RpoDigest, Word)>) -> PairComputations { - Self::process_sorted_pairs_to_leaves(pairs, Self::pairs_to_leaf) + Self::process_sorted_pairs_to_leaves(pairs, |leaf_pairs| { + Some(Self::pairs_to_leaf(leaf_pairs)) + }) } /// Constructs a single leaf from an arbitrary amount of key-value pairs. @@ -253,6 +275,7 @@ impl Smt { assert!(!pairs.is_empty()); if pairs.len() > 1 { + pairs.sort_by(|(key_1, _), (key_2, _)| leaf::cmp_keys(*key_1, *key_2)); SmtLeaf::new_multiple(pairs).unwrap() } else { let (key, value) = pairs.pop().unwrap(); @@ -278,22 +301,32 @@ impl Smt { let accumulator = Self::process_sorted_pairs_to_leaves(pairs, |leaf_pairs| { let mut leaf = self.get_leaf(&leaf_pairs[0].0); + let mut leaf_changed = false; for (key, value) in leaf_pairs { // Check if the value has changed - let old_value = - new_pairs.get(&key).cloned().unwrap_or_else(|| self.get_value(&key)); + let old_value = new_pairs.get(&key).cloned().unwrap_or_else(|| { + // Safe to unwrap: `leaf_pairs` contains keys all belonging to this leaf. + // `SmtLeaf::get_value()` only returns `None` if the key does not belong to the + // leaf, which cannot happen due to the sorting/grouping + // logic in `process_sorted_pairs_to_leaves()`. + leaf.get_value(&key).unwrap() + }); - // Skip if the value hasn't changed - if value == old_value { - continue; + if value != old_value { + // Update the leaf and track the new key-value pair + leaf = self.construct_prospective_leaf(leaf, &key, &value); + new_pairs.insert(key, value); + leaf_changed = true; } - - // Otherwise, update the leaf and track the new key-value pair - leaf = self.construct_prospective_leaf(leaf, &key, &value); - new_pairs.insert(key, value); } - leaf + if leaf_changed { + // Only return the leaf if it actually changed + Some(leaf) + } else { + // Return None if leaf hasn't changed + None + } }); (accumulator.leaves, new_pairs) } @@ -326,7 +359,7 @@ impl Smt { mut process_leaf: F, ) -> PairComputations where - F: FnMut(Vec<(RpoDigest, Word)>) -> SmtLeaf, + F: FnMut(Vec<(RpoDigest, Word)>) -> Option, { use rayon::prelude::*; debug_assert!(pairs.is_sorted_by_key(|(key, _)| Self::key_to_leaf_index(key).value())); @@ -359,9 +392,9 @@ impl Smt { // Otherwise, the next pair is a different column, or there is no next pair. Either way // it's time to swap out our buffer. let leaf_pairs = mem::take(&mut current_leaf_buffer); - let leaf = process_leaf(leaf_pairs); - - accumulator.nodes.insert(col, leaf); + if let Some(leaf) = process_leaf(leaf_pairs) { + accumulator.nodes.insert(col, leaf); + } debug_assert!(current_leaf_buffer.is_empty()); } diff --git a/src/merkle/smt/full/concurrent/tests.rs b/src/merkle/smt/full/concurrent/tests.rs index 8407c35..8e42052 100644 --- a/src/merkle/smt/full/concurrent/tests.rs +++ b/src/merkle/smt/full/concurrent/tests.rs @@ -3,6 +3,7 @@ use alloc::{ vec::Vec, }; +use proptest::prelude::*; use rand::{prelude::IteratorRandom, thread_rng, Rng}; use super::{ @@ -10,7 +11,7 @@ use super::{ Smt, SmtLeaf, SparseMerkleTree, SubtreeLeaf, SubtreeLeavesIter, UnorderedMap, COLS_PER_SUBTREE, SMT_DEPTH, SUBTREE_DEPTH, }; -use crate::{merkle::smt::Felt, Word, EMPTY_WORD, ONE}; +use crate::{merkle::smt::Felt, Word, EMPTY_WORD, ONE, ZERO}; fn smtleaf_to_subtree_leaf(leaf: &SmtLeaf) -> SubtreeLeaf { SubtreeLeaf { @@ -465,3 +466,135 @@ fn test_compute_mutations_parallel() { assert_eq!(mutations.node_mutations(), control.node_mutations()); assert_eq!(mutations.new_pairs(), control.new_pairs()); } + +#[test] +fn test_smt_construction_with_entries_unsorted() { + let entries = [ + (RpoDigest::new([ONE, ONE, Felt::new(2_u64), ONE]), [ONE; 4]), + (RpoDigest::new([ONE; 4]), [ONE; 4]), + ]; + let control = Smt::with_entries_sequential(entries).unwrap(); + let smt = Smt::with_entries(entries).unwrap(); + assert_eq!(smt.root(), control.root()); + assert_eq!(smt, control); +} + +fn arb_felt() -> impl Strategy { + prop_oneof![any::().prop_map(Felt::new), Just(ZERO), Just(ONE),] +} + +/// Generate entries that are guaranteed to be in different subtrees +fn generate_cross_subtree_entries() -> impl Strategy> { + let subtree_offsets = prop::collection::vec(0..(COLS_PER_SUBTREE * 4), 1..100); + + subtree_offsets.prop_map(|offsets| { + offsets + .into_iter() + .map(|base_col| { + let key = RpoDigest::new([ONE, ONE, ONE, Felt::new(base_col)]); + let value = [ONE, ONE, ONE, Felt::new(base_col)]; + (key, value) + }) + .collect() + }) +} + +fn arb_entries() -> impl Strategy> { + // Combine random entries with guaranteed cross-subtree entries + prop_oneof![ + // Original random entry generation + prop::collection::vec( + prop_oneof![ + // Random values case + ( + prop::array::uniform4(arb_felt()).prop_map(RpoDigest::new), + prop::array::uniform4(arb_felt()) + ), + // Edge case values + ( + Just(RpoDigest::new([ONE, ONE, ONE, Felt::new(0)])), + Just([ONE, ONE, ONE, Felt::new(u64::MAX)]) + ) + ], + 1..1000, + ), + // Guaranteed cross-subtree entries + generate_cross_subtree_entries(), + // Mix of both (combine random and cross-subtree entries) + ( + generate_cross_subtree_entries(), + prop::collection::vec( + ( + prop::array::uniform4(arb_felt()).prop_map(RpoDigest::new), + prop::array::uniform4(arb_felt()) + ), + 1..1000, + ) + ) + .prop_map(|(mut cross_subtree, mut random)| { + cross_subtree.append(&mut random); + cross_subtree + }) + ] + .prop_map(|entries| { + // Ensure uniqueness of entries as `Smt::with_entries` returns an error if multiple values + // exist for the same key. + let mut used_indices = BTreeSet::new(); + let mut used_keys = BTreeSet::new(); + let mut result = Vec::new(); + + for (key, value) in entries { + let leaf_index = LeafIndex::::from(key).value(); + if used_indices.insert(leaf_index) && used_keys.insert(key) { + result.push((key, value)); + } + } + result + }) +} + +proptest! { + #[test] + fn test_with_entries_consistency(entries in arb_entries()) { + let sequential = Smt::with_entries_sequential(entries.clone()).unwrap(); + let concurrent = Smt::with_entries(entries.clone()).unwrap(); + prop_assert_eq!(concurrent, sequential); + } + + #[test] + fn test_compute_mutations_consistency( + initial_entries in arb_entries(), + update_entries in arb_entries().prop_filter( + "Update must not be empty and must differ from initial entries", + |updates| !updates.is_empty() + ) + ) { + let tree = Smt::with_entries_sequential(initial_entries.clone()).unwrap(); + + let has_real_changes = update_entries.iter().any(|(key, value)| { + match initial_entries.iter().find(|(init_key, _)| init_key == key) { + Some((_, init_value)) => init_value != value, + None => true, + } + }); + + let sequential = tree.compute_mutations_sequential(update_entries.clone()); + let concurrent = tree.compute_mutations(update_entries.clone()); + + // If there are real changes, the root should change + if has_real_changes { + let sequential_changed = sequential.old_root != sequential.new_root; + let concurrent_changed = concurrent.old_root != concurrent.new_root; + + prop_assert!( + sequential_changed || concurrent_changed, + "Root should have changed" + ); + } + + prop_assert_eq!(sequential.old_root, concurrent.old_root); + prop_assert_eq!(sequential.new_root, concurrent.new_root); + prop_assert_eq!(sequential.node_mutations(), concurrent.node_mutations()); + prop_assert_eq!(sequential.new_pairs.len(), concurrent.new_pairs.len()); + } +} diff --git a/src/merkle/smt/full/mod.rs b/src/merkle/smt/full/mod.rs index 11c28f4..2e751fb 100644 --- a/src/merkle/smt/full/mod.rs +++ b/src/merkle/smt/full/mod.rs @@ -103,7 +103,7 @@ impl Smt { /// /// # Errors /// Returns an error if the provided entries contain multiple values for the same key. - #[cfg(any(not(feature = "concurrent"), test))] + #[cfg(any(not(feature = "concurrent"), fuzzing, test))] fn with_entries_sequential( entries: impl IntoIterator, ) -> Result { @@ -501,6 +501,25 @@ impl Deserializable for Smt { } } +// FUZZING +// ================================================================================================ + +#[cfg(fuzzing)] +impl Smt { + pub fn fuzz_with_entries_sequential( + entries: impl IntoIterator, + ) -> Result { + Self::with_entries_sequential(entries) + } + + pub fn fuzz_compute_mutations_sequential( + &self, + kv_pairs: impl IntoIterator, + ) -> MutationSet { + >::compute_mutations(self, kv_pairs) + } +} + // TESTS // ================================================================================================ diff --git a/src/merkle/smt/full/tests.rs b/src/merkle/smt/full/tests.rs index 787c01a..4863205 100644 --- a/src/merkle/smt/full/tests.rs +++ b/src/merkle/smt/full/tests.rs @@ -485,6 +485,20 @@ fn test_prospective_insertion() { assert_eq!(smt.root(), root_3); } +#[test] +fn test_mutations_no_mutations() { + let key = RpoDigest::from([ONE, ONE, ONE, ONE]); + let value = [ONE; WORD_SIZE]; + let entries = [(key, value)]; + + let tree = Smt::with_entries(entries).unwrap(); + let mutations = tree.compute_mutations(entries); + + assert_eq!(mutations.root(), mutations.old_root(), "Root should not change"); + assert!(mutations.node_mutations().is_empty(), "Node mutations should be empty"); + assert!(mutations.new_pairs().is_empty(), "There should be no new pairs"); +} + #[test] fn test_mutations_revert() { let mut smt = Smt::default(); From 222197d08f45082fa4c0972e948a5787266c5862 Mon Sep 17 00:00:00 2001 From: Krushimir Date: Thu, 13 Mar 2025 09:57:27 +0100 Subject: [PATCH 04/16] feat: optimized duplicate key detection in concurrent SMT construction (#395) --- CHANGELOG.md | 1 + src/merkle/smt/full/concurrent/mod.rs | 137 +++++++++++++++--------- src/merkle/smt/full/concurrent/tests.rs | 80 ++++++++++++-- 3 files changed, 161 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe493fd..1b39f3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Sort keys in a leaf in the concurrent implementation of `Smt::with_entries`, ensuring consistency with the sequential version (#385). - Skip unchanged leaves in the concurrent implementation of `Smt::compute_mutations` (#385). - Add range checks to `ntru_gen` for Falcon DSA (#391). +- Optimized duplicate key detection in `Smt::with_entries_concurrent` (#395). ## 0.13.3 (2025-02-18) diff --git a/src/merkle/smt/full/concurrent/mod.rs b/src/merkle/smt/full/concurrent/mod.rs index 14730e6..2862b2a 100644 --- a/src/merkle/smt/full/concurrent/mod.rs +++ b/src/merkle/smt/full/concurrent/mod.rs @@ -1,11 +1,12 @@ -use alloc::{collections::BTreeSet, vec::Vec}; +use alloc::vec::Vec; use core::mem; use num::Integer; +use rayon::prelude::*; use super::{ - leaf, EmptySubtreeRoots, InnerNode, InnerNodes, LeafIndex, Leaves, MerkleError, MutationSet, - NodeIndex, RpoDigest, Smt, SmtLeaf, SparseMerkleTree, Word, SMT_DEPTH, + leaf, EmptySubtreeRoots, InnerNode, InnerNodes, Leaves, MerkleError, MutationSet, NodeIndex, + RpoDigest, Smt, SmtLeaf, SparseMerkleTree, Word, SMT_DEPTH, }; use crate::merkle::smt::{NodeMutation, NodeMutations, UnorderedMap}; @@ -33,28 +34,25 @@ impl Smt { /// 3. These subtree roots are recursively merged to become the "leaves" for the next iteration, /// which processes the next 8 levels up. This continues until the final root of the tree is /// computed at depth 0. + /// + /// # Errors + /// Returns an error if the provided entries contain multiple values for the same key. pub(crate) fn with_entries_concurrent( entries: impl IntoIterator, ) -> Result { - let mut seen_keys = BTreeSet::new(); - let entries: Vec<_> = entries - .into_iter() - // Filter out key-value pairs whose value is empty. - .filter(|(_key, value)| *value != Self::EMPTY_VALUE) - .map(|(key, value)| { - if seen_keys.insert(key) { - Ok((key, value)) - } else { - Err(MerkleError::DuplicateValuesForIndex( - LeafIndex::::from(key).value(), - )) - } - }) - .collect::>()?; + let entries: Vec<(RpoDigest, Word)> = entries.into_iter().collect(); + if entries.is_empty() { return Ok(Self::default()); } - let (inner_nodes, leaves) = Self::build_subtrees(entries); + + let (inner_nodes, leaves) = Self::build_subtrees(entries)?; + + // All the leaves are empty + if inner_nodes.is_empty() { + return Ok(Self::default()); + } + let root = inner_nodes.get(&NodeIndex::root()).unwrap().hash(); >::from_raw_parts(inner_nodes, leaves, root) } @@ -80,8 +78,6 @@ impl Smt { where Self: Sized + Sync, { - use rayon::prelude::*; - // Collect and sort key-value pairs by their corresponding leaf index let mut sorted_kv_pairs: Vec<_> = kv_pairs.into_iter().collect(); sorted_kv_pairs.par_sort_unstable_by_key(|(key, _)| Self::key_to_leaf_index(key).value()); @@ -206,9 +202,14 @@ impl Smt { /// Computes the raw parts for a new sparse Merkle tree from a set of key-value pairs. /// - /// `entries` need not be sorted. This function will sort them. - fn build_subtrees(mut entries: Vec<(RpoDigest, Word)>) -> (InnerNodes, Leaves) { - entries.sort_by_key(|item| { + /// `entries` need not be sorted. This function will sort them using parallel sorting. + /// + /// # Errors + /// Returns an error if the provided entries contain multiple values for the same key. + fn build_subtrees( + mut entries: Vec<(RpoDigest, Word)>, + ) -> Result<(InnerNodes, Leaves), MerkleError> { + entries.par_sort_unstable_by_key(|item| { let index = Self::key_to_leaf_index(&item.0); index.value() }); @@ -219,15 +220,23 @@ impl Smt { /// /// This function is mostly an implementation detail of /// [`Smt::with_entries_concurrent()`]. - fn build_subtrees_from_sorted_entries(entries: Vec<(RpoDigest, Word)>) -> (InnerNodes, Leaves) { - use rayon::prelude::*; - + /// + /// # Errors + /// Returns an error if the provided entries contain multiple values for the same key. + fn build_subtrees_from_sorted_entries( + entries: Vec<(RpoDigest, Word)>, + ) -> Result<(InnerNodes, Leaves), MerkleError> { let mut accumulated_nodes: InnerNodes = Default::default(); let PairComputations { leaves: mut leaf_subtrees, nodes: initial_leaves, - } = Self::sorted_pairs_to_leaves(entries); + } = Self::sorted_pairs_to_leaves(entries)?; + + // If there are no leaves, we can return early + if initial_leaves.is_empty() { + return Ok((accumulated_nodes, initial_leaves)); + } for current_depth in (SUBTREE_DEPTH..=SMT_DEPTH).step_by(SUBTREE_DEPTH as usize).rev() { let (nodes, mut subtree_roots): (Vec>, Vec) = @@ -247,7 +256,7 @@ impl Smt { debug_assert!(!leaf_subtrees.is_empty()); } - (accumulated_nodes, initial_leaves) + Ok((accumulated_nodes, initial_leaves)) } // LEAF NODE CONSTRUCTION @@ -260,31 +269,46 @@ impl Smt { /// `pairs` *must* already be sorted **by leaf index column**, not simply sorted by key. If /// `pairs` is not correctly sorted, the returned computations will be incorrect. /// + /// # Errors + /// Returns an error if the provided pairs contain multiple values for the same key. + /// /// # Panics /// With debug assertions on, this function panics if it detects that `pairs` is not correctly /// sorted. Without debug assertions, the returned computations will be incorrect. - fn sorted_pairs_to_leaves(pairs: Vec<(RpoDigest, Word)>) -> PairComputations { - Self::process_sorted_pairs_to_leaves(pairs, |leaf_pairs| { - Some(Self::pairs_to_leaf(leaf_pairs)) - }) + fn sorted_pairs_to_leaves( + pairs: Vec<(RpoDigest, Word)>, + ) -> Result, MerkleError> { + Self::process_sorted_pairs_to_leaves(pairs, Self::pairs_to_leaf) } /// Constructs a single leaf from an arbitrary amount of key-value pairs. /// Those pairs must all have the same leaf index. - fn pairs_to_leaf(mut pairs: Vec<(RpoDigest, Word)>) -> SmtLeaf { + /// + /// # Errors + /// Returns a `MerkleError::DuplicateValuesForIndex` if the provided pairs contain multiple + /// values for the same key. + /// + /// # Returns + /// - `Ok(Some(SmtLeaf))` if a valid leaf is constructed. + /// - `Ok(None)` if the only provided value is `Self::EMPTY_VALUE`. + fn pairs_to_leaf(mut pairs: Vec<(RpoDigest, Word)>) -> Result, MerkleError> { assert!(!pairs.is_empty()); if pairs.len() > 1 { pairs.sort_by(|(key_1, _), (key_2, _)| leaf::cmp_keys(*key_1, *key_2)); - SmtLeaf::new_multiple(pairs).unwrap() + // Check for duplicates in a sorted list by comparing adjacent pairs + if let Some(window) = pairs.windows(2).find(|window| window[0].0 == window[1].0) { + // If we find a duplicate, return an error + let col = Self::key_to_leaf_index(&window[0].0).index.value(); + return Err(MerkleError::DuplicateValuesForIndex(col)); + } + Ok(Some(SmtLeaf::new_multiple(pairs).unwrap())) } else { let (key, value) = pairs.pop().unwrap(); - // TODO: should we ever be constructing empty leaves from pairs? if value == Self::EMPTY_VALUE { - let index = Self::key_to_leaf_index(&key); - SmtLeaf::new_empty(index) + Ok(None) } else { - SmtLeaf::new_single(key, value) + Ok(Some(SmtLeaf::new_single(key, value))) } } } @@ -322,13 +346,19 @@ impl Smt { if leaf_changed { // Only return the leaf if it actually changed - Some(leaf) + Ok(Some(leaf)) } else { // Return None if leaf hasn't changed - None + Ok(None) } }); - (accumulator.leaves, new_pairs) + // The closure is the only possible source of errors. + // Since it never returns an error - only `Ok(Some(_))` or `Ok(None)` - we can safely assume + // `accumulator` is always `Ok(_)`. + ( + accumulator.expect("process_sorted_pairs_to_leaves never fails").leaves, + new_pairs, + ) } /// Processes sorted key-value pairs to compute leaves for a subtree. @@ -352,16 +382,18 @@ impl Smt { /// - `leaves`: A collection of `SubtreeLeaf` structures representing the processed leaves. Each /// `SubtreeLeaf` includes the column index and the hash of the corresponding leaf. /// + /// # Errors + /// Returns an error if the `process_leaf` callback fails. + /// /// # Panics /// This function will panic in debug mode if the input `pairs` are not sorted by column index. fn process_sorted_pairs_to_leaves( pairs: Vec<(RpoDigest, Word)>, mut process_leaf: F, - ) -> PairComputations + ) -> Result, MerkleError> where - F: FnMut(Vec<(RpoDigest, Word)>) -> Option, + F: FnMut(Vec<(RpoDigest, Word)>) -> Result, MerkleError>, { - use rayon::prelude::*; debug_assert!(pairs.is_sorted_by_key(|(key, _)| Self::key_to_leaf_index(key).value())); let mut accumulator: PairComputations = Default::default(); @@ -392,8 +424,16 @@ impl Smt { // Otherwise, the next pair is a different column, or there is no next pair. Either way // it's time to swap out our buffer. let leaf_pairs = mem::take(&mut current_leaf_buffer); - if let Some(leaf) = process_leaf(leaf_pairs) { - accumulator.nodes.insert(col, leaf); + + // Process leaf and propagate any errors + match process_leaf(leaf_pairs) { + Ok(Some(leaf)) => { + accumulator.nodes.insert(col, leaf); + }, + Ok(None) => { + // No leaf was constructed for this column. The column will be skipped. + }, + Err(e) => return Err(e), } debug_assert!(current_leaf_buffer.is_empty()); @@ -415,7 +455,7 @@ impl Smt { // subtree boundaries as we go. Either way this function is only used at the beginning of a // parallel construction, so it should not be a critical path. accumulator.leaves = SubtreeLeavesIter::from_leaves(&mut accumulated_leaves).collect(); - accumulator + Ok(accumulator) } } @@ -539,6 +579,7 @@ fn build_subtree( // construction enforces uniqueness. However, when testing or benchmarking // `build_subtree()` in isolation, duplicate columns can appear if input // constraints are not enforced. + use alloc::collections::BTreeSet; let mut seen_cols = BTreeSet::new(); for leaf in &leaves { assert!(seen_cols.insert(leaf.col), "Duplicate column found in subtree: {}", leaf.col); diff --git a/src/merkle/smt/full/concurrent/tests.rs b/src/merkle/smt/full/concurrent/tests.rs index 8e42052..5a05c44 100644 --- a/src/merkle/smt/full/concurrent/tests.rs +++ b/src/merkle/smt/full/concurrent/tests.rs @@ -3,15 +3,19 @@ use alloc::{ vec::Vec, }; +use assert_matches::assert_matches; use proptest::prelude::*; use rand::{prelude::IteratorRandom, thread_rng, Rng}; use super::{ - build_subtree, InnerNode, LeafIndex, NodeIndex, NodeMutations, PairComputations, RpoDigest, - Smt, SmtLeaf, SparseMerkleTree, SubtreeLeaf, SubtreeLeavesIter, UnorderedMap, COLS_PER_SUBTREE, - SMT_DEPTH, SUBTREE_DEPTH, + build_subtree, InnerNode, NodeIndex, NodeMutations, PairComputations, RpoDigest, Smt, SmtLeaf, + SparseMerkleTree, SubtreeLeaf, SubtreeLeavesIter, UnorderedMap, COLS_PER_SUBTREE, SMT_DEPTH, + SUBTREE_DEPTH, +}; +use crate::{ + merkle::{smt::Felt, LeafIndex, MerkleError}, + Word, EMPTY_WORD, ONE, ZERO, }; -use crate::{merkle::smt::Felt, Word, EMPTY_WORD, ONE, ZERO}; fn smtleaf_to_subtree_leaf(leaf: &SmtLeaf) -> SubtreeLeaf { SubtreeLeaf { @@ -72,7 +76,7 @@ fn test_sorted_pairs_to_leaves() { control_subtree_leaves }; - let subtrees: PairComputations = Smt::sorted_pairs_to_leaves(entries); + let subtrees: PairComputations = Smt::sorted_pairs_to_leaves(entries).unwrap(); // This will check that the hashes, columns, and subtree assignments all match. assert_eq!(subtrees.leaves, control_subtree_leaves); // Flattening and re-separating out the leaves into subtrees should have the same result. @@ -140,7 +144,7 @@ fn test_single_subtree() { let entries = generate_entries(PAIR_COUNT); let control = Smt::with_entries_sequential(entries.clone()).unwrap(); // `entries` should already be sorted by nature of how we constructed it. - let leaves = Smt::sorted_pairs_to_leaves(entries).leaves; + let leaves = Smt::sorted_pairs_to_leaves(entries).unwrap().leaves; let leaves = leaves.into_iter().next().unwrap(); let (first_subtree, subtree_root) = build_subtree(leaves, SMT_DEPTH, SMT_DEPTH); assert!(!first_subtree.is_empty()); @@ -172,7 +176,7 @@ fn test_two_subtrees() { const PAIR_COUNT: u64 = COLS_PER_SUBTREE * 2; let entries = generate_entries(PAIR_COUNT); let control = Smt::with_entries_sequential(entries.clone()).unwrap(); - let PairComputations { leaves, .. } = Smt::sorted_pairs_to_leaves(entries); + let PairComputations { leaves, .. } = Smt::sorted_pairs_to_leaves(entries).unwrap(); // With two subtrees' worth of leaves, we should have exactly two subtrees. let [first, second]: [Vec<_>; 2] = leaves.try_into().unwrap(); assert_eq!(first.len() as u64, PAIR_COUNT / 2); @@ -220,7 +224,7 @@ fn test_singlethreaded_subtrees() { let PairComputations { leaves: mut leaf_subtrees, nodes: test_leaves, - } = Smt::sorted_pairs_to_leaves(entries); + } = Smt::sorted_pairs_to_leaves(entries).unwrap(); for current_depth in (SUBTREE_DEPTH..=SMT_DEPTH).step_by(SUBTREE_DEPTH as usize).rev() { // There's no flat_map_unzip(), so this is the best we can do. let (nodes, mut subtree_roots): (Vec>, Vec) = leaf_subtrees @@ -304,7 +308,7 @@ fn test_multithreaded_subtrees() { let PairComputations { leaves: mut leaf_subtrees, nodes: test_leaves, - } = Smt::sorted_pairs_to_leaves(entries); + } = Smt::sorted_pairs_to_leaves(entries).unwrap(); for current_depth in (SUBTREE_DEPTH..=SMT_DEPTH).step_by(SUBTREE_DEPTH as usize).rev() { let (nodes, mut subtree_roots): (Vec>, Vec) = leaf_subtrees .into_par_iter() @@ -479,6 +483,64 @@ fn test_smt_construction_with_entries_unsorted() { assert_eq!(smt, control); } +#[test] +fn test_smt_construction_with_entries_duplicate_keys() { + let entries = [ + (RpoDigest::new([ONE, ONE, ONE, Felt::new(16)]), [ONE; 4]), + (RpoDigest::new([ONE; 4]), [ONE; 4]), + (RpoDigest::new([ONE, ONE, ONE, Felt::new(16)]), [ONE; 4]), + ]; + let expected_col = Smt::key_to_leaf_index(&entries[0].0).index.value(); + let err = Smt::with_entries(entries).unwrap_err(); + assert_matches!(err, MerkleError::DuplicateValuesForIndex(col) if col == expected_col); +} + +#[test] +fn test_smt_construction_with_some_empty_values() { + let entries = [ + (RpoDigest::new([ONE, ONE, ONE, ONE]), Smt::EMPTY_VALUE), + (RpoDigest::new([ONE, ONE, ONE, Felt::new(2)]), [ONE; 4]), + ]; + + let result = Smt::with_entries(entries); + assert!(result.is_ok(), "SMT construction failed with mixed empty values"); + + let smt = result.unwrap(); + let control = Smt::with_entries_sequential(entries).unwrap(); + + assert_eq!(smt.num_leaves(), 1); + assert_eq!(smt.root(), control.root(), "Root hashes do not match"); + assert_eq!(smt, control, "SMTs are not equal"); +} + +#[test] +fn test_smt_construction_with_all_empty_values() { + let entries = [(RpoDigest::new([ONE, ONE, ONE, ONE]), Smt::EMPTY_VALUE)]; + + let result = Smt::with_entries(entries); + assert!(result.is_ok(), "SMT construction failed with all empty values"); + + let smt = result.unwrap(); + + assert_eq!( + smt.root(), + Smt::default().root(), + "SMT with all empty values should have the same root as the default SMT" + ); + assert_eq!(smt, Smt::default(), "SMT with all empty values should be empty"); +} + +#[test] +fn test_smt_construction_with_no_entries() { + let entries: [(RpoDigest, Word); 0] = []; + + let result = Smt::with_entries(entries); + assert!(result.is_ok(), "SMT construction failed with no entries"); + + let smt = result.unwrap(); + assert_eq!(smt, Smt::default(), "SMT with no entries should be empty"); +} + fn arb_felt() -> impl Strategy { prop_oneof![any::().prop_map(Felt::new), Just(ZERO), Just(ONE),] } From d5b38a8b354103e93c48f5e0dad5055d46be127e Mon Sep 17 00:00:00 2001 From: Santiago Pittella <87827390+SantiagoPittella@users.noreply.github.com> Date: Thu, 13 Mar 2025 16:13:49 -0300 Subject: [PATCH 05/16] chore: update `rand` dependency to v0.9.0 (#398) --- CHANGELOG.md | 1 + Cargo.lock | 187 ++++++++--------------- Cargo.toml | 12 +- config.toml | 2 + fuzz/Cargo.toml | 2 +- src/dsa/rpo_falcon512/keys/secret_key.rs | 4 +- src/dsa/rpo_falcon512/math/samplerz.rs | 13 +- src/main.rs | 8 +- src/merkle/smt/full/concurrent/tests.rs | 12 +- src/rand/rpo.rs | 5 - src/rand/rpx.rs | 5 - 11 files changed, 88 insertions(+), 163 deletions(-) create mode 100644 config.toml diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b39f3a..c1e09a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Skip unchanged leaves in the concurrent implementation of `Smt::compute_mutations` (#385). - Add range checks to `ntru_gen` for Falcon DSA (#391). - Optimized duplicate key detection in `Smt::with_entries_concurrent` (#395). +- [BREAKING] Moved `rand` to version `0.9` removing the `try_fill_bytes` method (#398). ## 0.13.3 (2025-02-18) diff --git a/Cargo.lock b/Cargo.lock index ce00182..11a55d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,21 +97,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" -[[package]] -name = "bit-set" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" - [[package]] name = "bitflags" version = "2.8.0" @@ -362,28 +347,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" -[[package]] -name = "errno" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" -dependencies = [ - "libc", - "windows-sys", -] - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - -[[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.4" @@ -407,10 +370,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", ] [[package]] @@ -532,12 +493,6 @@ dependencies = [ "cpufeatures", ] -[[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.169" @@ -550,12 +505,6 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - [[package]] name = "log" version = "0.4.25" @@ -577,16 +526,16 @@ dependencies = [ "cc", "clap", "criterion", - "getrandom 0.2.15", + "getrandom 0.3.1", "glob", "hashbrown", "hex", "num", "num-complex", "proptest", - "rand", - "rand_chacha", - "rand_core", + "rand 0.9.0", + "rand_chacha 0.9.0", + "rand_core 0.9.3", "rayon", "seq-macro", "serde", @@ -718,7 +667,7 @@ version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -736,26 +685,14 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" dependencies = [ - "bit-set", - "bit-vec", "bitflags", - "lazy_static", "num-traits", - "rand", - "rand_chacha", + "rand 0.8.5", + "rand_chacha 0.3.1", "rand_xorshift", - "regex-syntax", - "rusty-fork", - "tempfile", "unarray", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quote" version = "1.0.38" @@ -772,8 +709,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", + "zerocopy 0.8.23", ] [[package]] @@ -783,7 +731,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[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 0.9.3", ] [[package]] @@ -795,13 +753,22 @@ dependencies = [ "getrandom 0.2.15", ] +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.1", +] + [[package]] name = "rand_xorshift" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -853,37 +820,12 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" -[[package]] -name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", -] - [[package]] name = "rustversion" version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" -[[package]] -name = "rusty-fork" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" -dependencies = [ - "fnv", - "quick-error", - "tempfile", - "wait-timeout", -] - [[package]] name = "ryu" version = "1.0.19" @@ -970,20 +912,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "tempfile" -version = "3.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230" -dependencies = [ - "cfg-if", - "fastrand", - "getrandom 0.3.1", - "once_cell", - "rustix", - "windows-sys", -] - [[package]] name = "thiserror" version = "2.0.11" @@ -1044,15 +972,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "wait-timeout" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" -dependencies = [ - "libc", -] - [[package]] name = "walkdir" version = "2.5.0" @@ -1256,7 +1175,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6321741f063344258c80f0c0255a559ded99bc17fe99fab9577f2460065ddf" dependencies = [ - "rand", + "rand 0.8.5", "winter-utils", ] @@ -1282,7 +1201,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6" +dependencies = [ + "zerocopy-derive 0.8.23", ] [[package]] @@ -1295,3 +1223,14 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zerocopy-derive" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index 7e1125f..c09185c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,7 +55,7 @@ std = [ "blake3/std", "dep:cc", "rand/std", - "rand/std_rng", + "rand/thread_rng", "winter-crypto/std", "winter-math/std", "winter-utils/std", @@ -67,8 +67,8 @@ clap = { version = "4.5", optional = true, features = ["derive"] } hashbrown = { version = "0.15", optional = true, features = ["serde"] } num = { version = "0.4", default-features = false, features = ["alloc", "libm"] } num-complex = { version = "0.4", default-features = false } -rand = { version = "0.8", default-features = false } -rand_core = { version = "0.6", default-features = false } +rand = { version = "0.9", default-features = false } +rand_core = { version = "0.9", default-features = false } rand-utils = { version = "0.12", package = "winter-rand-utils", optional = true } rayon = { version = "1.10", optional = true } serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] } @@ -81,10 +81,10 @@ winter-utils = { version = "0.12", default-features = false } [dev-dependencies] assert_matches = { version = "1.5", default-features = false } criterion = { version = "0.5", features = ["html_reports"] } -getrandom = { version = "0.2", features = ["js"] } +getrandom = { version = "0.3", default-features = false } hex = { version = "0.4", default-features = false, features = ["alloc"] } -proptest = "1.6" -rand_chacha = { version = "0.3", default-features = false } +proptest = { version = "1.6", default-features = false, features = ["alloc"]} +rand_chacha = { version = "0.9", default-features = false } rand-utils = { version = "0.12", package = "winter-rand-utils" } seq-macro = { version = "0.3" } diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..2e07606 --- /dev/null +++ b/config.toml @@ -0,0 +1,2 @@ +[target.wasm32-unknown-unknown] +rustflags = ['--cfg', 'getrandom_backend="wasm_js"'] diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 9029557..77f1d3d 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -10,7 +10,7 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = "0.4" miden-crypto = { path = "..", features = ["concurrent"] } -rand = { version = "0.8", default-features = false } +rand = { version = "0.9", default-features = false } [[bin]] name = "smt" diff --git a/src/dsa/rpo_falcon512/keys/secret_key.rs b/src/dsa/rpo_falcon512/keys/secret_key.rs index 5204a3a..3cdf095 100644 --- a/src/dsa/rpo_falcon512/keys/secret_key.rs +++ b/src/dsa/rpo_falcon512/keys/secret_key.rs @@ -68,7 +68,7 @@ impl SecretKey { pub fn new() -> Self { use rand::{rngs::StdRng, SeedableRng}; - let mut rng = StdRng::from_entropy(); + let mut rng = StdRng::from_os_rng(); Self::with_rng(&mut rng) } @@ -117,7 +117,7 @@ impl SecretKey { pub fn sign(&self, message: Word) -> Signature { use rand::{rngs::StdRng, SeedableRng}; - let mut rng = StdRng::from_entropy(); + let mut rng = StdRng::from_os_rng(); self.sign_with_rng(message, &mut rng) } diff --git a/src/dsa/rpo_falcon512/math/samplerz.rs b/src/dsa/rpo_falcon512/math/samplerz.rs index c16c071..c047eb6 100644 --- a/src/dsa/rpo_falcon512/math/samplerz.rs +++ b/src/dsa/rpo_falcon512/math/samplerz.rs @@ -100,14 +100,14 @@ pub(crate) fn sampler_z(mu: f64, sigma: f64, sigma_min: f64, rng: &mut R let r = mu - s; let ccs = sigma_min * isigma; loop { - let z0 = base_sampler(rng.gen()); - let random_byte: u8 = rng.gen(); + let z0 = base_sampler(rng.random()); + let random_byte: u8 = rng.random(); let b = (random_byte & 1) as i16; let z = b + ((b << 1) - 1) * z0; let zf_min_r = (z as f64) - r; // x = ((z-r)^2)/(2*sigma^2) - ((z-b)^2)/(2*sigma0^2) let x = zf_min_r * zf_min_r * dss - (z0 * z0) as f64 * INV_2SIGMA_MAX_SQ; - if ber_exp(x, ccs, rng.gen()) { + if ber_exp(x, ccs, rng.random()) { return z + (s as i16); } } @@ -174,13 +174,6 @@ mod test { *d = self.next(); } } - - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { - for d in dest.iter_mut() { - *d = self.next(); - } - Ok(()) - } } #[test] diff --git a/src/main.rs b/src/main.rs index 83daef6..85045cc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,7 @@ use miden_crypto::{ merkle::{MerkleError, Smt}, Felt, Word, EMPTY_WORD, ONE, }; -use rand::{prelude::IteratorRandom, thread_rng, Rng}; +use rand::{prelude::IteratorRandom, rng, Rng}; use rand_utils::rand_value; #[derive(Parser, Debug)] @@ -141,7 +141,7 @@ pub fn batched_update( println!("Running a batched update benchmark:"); let size = tree.num_leaves(); - let mut rng = thread_rng(); + let mut rng = rng(); let new_pairs = entries @@ -149,10 +149,10 @@ pub fn batched_update( .choose_multiple(&mut rng, updates) .into_iter() .map(|(key, _)| { - let value = if rng.gen_bool(REMOVAL_PROBABILITY) { + let value = if rng.random_bool(REMOVAL_PROBABILITY) { EMPTY_WORD } else { - [ONE, ONE, ONE, Felt::new(rng.gen())] + [ONE, ONE, ONE, Felt::new(rng.random())] }; (key, value) diff --git a/src/merkle/smt/full/concurrent/tests.rs b/src/merkle/smt/full/concurrent/tests.rs index 5a05c44..97dd68b 100644 --- a/src/merkle/smt/full/concurrent/tests.rs +++ b/src/merkle/smt/full/concurrent/tests.rs @@ -5,7 +5,7 @@ use alloc::{ use assert_matches::assert_matches; use proptest::prelude::*; -use rand::{prelude::IteratorRandom, thread_rng, Rng}; +use rand::{prelude::IteratorRandom, rng, Rng}; use super::{ build_subtree, InnerNode, NodeIndex, NodeMutations, PairComputations, RpoDigest, Smt, SmtLeaf, @@ -114,7 +114,7 @@ fn generate_entries(pair_count: u64) -> Vec<(RpoDigest, Word)> { fn generate_updates(entries: Vec<(RpoDigest, Word)>, updates: usize) -> Vec<(RpoDigest, Word)> { const REMOVAL_PROBABILITY: f64 = 0.2; - let mut rng = thread_rng(); + let mut rng = rng(); // Assertion to ensure input keys are unique assert!( entries.iter().map(|(key, _)| key).collect::>().len() == entries.len(), @@ -125,10 +125,10 @@ fn generate_updates(entries: Vec<(RpoDigest, Word)>, updates: usize) -> Vec<(Rpo .choose_multiple(&mut rng, updates) .into_iter() .map(|(key, _)| { - let value = if rng.gen_bool(REMOVAL_PROBABILITY) { + let value = if rng.random_bool(REMOVAL_PROBABILITY) { EMPTY_WORD } else { - [ONE, ONE, ONE, Felt::new(rng.gen())] + [ONE, ONE, ONE, Felt::new(rng.random())] }; (key, value) }) @@ -381,11 +381,11 @@ fn test_multithreaded_subtrees() { fn test_with_entries_concurrent() { const PAIR_COUNT: u64 = COLS_PER_SUBTREE * 64; let mut entries = generate_entries(PAIR_COUNT); - let mut rng = rand::thread_rng(); + let mut rng = rand::rng(); // Set 10% of the entries to have empty words as their values. for _ in 0..PAIR_COUNT / 10 { - let random_index = rng.gen_range(0..PAIR_COUNT); + let random_index = rng.random_range(0..PAIR_COUNT); entries[random_index as usize].1 = EMPTY_WORD; } diff --git a/src/rand/rpo.rs b/src/rand/rpo.rs index 2669592..8658cbb 100644 --- a/src/rand/rpo.rs +++ b/src/rand/rpo.rs @@ -208,11 +208,6 @@ impl RngCore for RpoRandomCoin { fn fill_bytes(&mut self, dest: &mut [u8]) { impls::fill_bytes_via_next(self, dest) } - - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { - self.fill_bytes(dest); - Ok(()) - } } // SERIALIZATION diff --git a/src/rand/rpx.rs b/src/rand/rpx.rs index 2f91b0c..2661fa9 100644 --- a/src/rand/rpx.rs +++ b/src/rand/rpx.rs @@ -206,11 +206,6 @@ impl RngCore for RpxRandomCoin { fn fill_bytes(&mut self, dest: &mut [u8]) { impls::fill_bytes_via_next(self, dest) } - - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { - self.fill_bytes(dest); - Ok(()) - } } // SERIALIZATION From b5c568eb126cde39a47f0f8ac3cba3dd16034d41 Mon Sep 17 00:00:00 2001 From: Santiago Pittella <87827390+SantiagoPittella@users.noreply.github.com> Date: Thu, 13 Mar 2025 17:36:22 -0300 Subject: [PATCH 06/16] chore: update to Rust 2024 edition (#399) --- CHANGELOG.md | 2 +- Cargo.toml | 4 +-- README.md | 2 +- benches/hash.rs | 4 +-- benches/merkle.rs | 4 +-- benches/smt-subtree.rs | 6 ++--- benches/smt-with-entries.rs | 4 +-- benches/smt.rs | 4 +-- benches/store.rs | 8 +++--- fuzz/Cargo.toml | 2 +- rust-toolchain.toml | 2 +- rustfmt.toml | 2 +- src/dsa/rpo_falcon512/hash_to_point.rs | 4 +-- src/dsa/rpo_falcon512/keys/mod.rs | 4 +-- src/dsa/rpo_falcon512/keys/public_key.rs | 2 +- src/dsa/rpo_falcon512/keys/secret_key.rs | 12 ++++----- src/dsa/rpo_falcon512/math/fft.rs | 2 +- src/dsa/rpo_falcon512/math/field.rs | 2 +- src/dsa/rpo_falcon512/math/mod.rs | 4 +-- src/dsa/rpo_falcon512/math/polynomial.rs | 31 ++++++++++++------------ src/dsa/rpo_falcon512/mod.rs | 2 +- src/dsa/rpo_falcon512/signature.rs | 10 ++++---- src/hash/blake/mod.rs | 4 +-- src/hash/rescue/arch/mod.rs | 6 ++--- src/hash/rescue/mds/freq.rs | 6 ++--- src/hash/rescue/mod.rs | 2 +- src/hash/rescue/rpo/digest.rs | 14 ++++------- src/hash/rescue/rpo/mod.rs | 9 ++++--- src/hash/rescue/rpo/tests.rs | 6 ++--- src/hash/rescue/rpx/digest.rs | 14 ++++------- src/hash/rescue/rpx/mod.rs | 10 ++++---- src/hash/rescue/rpx/tests.rs | 2 +- src/hash/rescue/tests.rs | 2 +- src/lib.rs | 2 +- src/main.rs | 4 +-- src/merkle/empty_roots.rs | 2 +- src/merkle/error.rs | 4 ++- src/merkle/merkle_tree.rs | 2 +- src/merkle/mmr/error.rs | 4 +-- src/merkle/mmr/full.rs | 15 ++++++------ src/merkle/mmr/partial.rs | 8 +++--- src/merkle/mmr/tests.rs | 5 ++-- src/merkle/mod.rs | 12 ++++----- src/merkle/partial_mt/mod.rs | 6 ++--- src/merkle/partial_mt/tests.rs | 4 +-- src/merkle/path.rs | 4 +-- src/merkle/smt/full/concurrent/mod.rs | 4 +-- src/merkle/smt/full/concurrent/tests.rs | 12 ++++----- src/merkle/smt/full/error.rs | 10 +++++--- src/merkle/smt/full/leaf.rs | 2 +- src/merkle/smt/full/mod.rs | 6 ++--- src/merkle/smt/full/proof.rs | 2 +- src/merkle/smt/full/tests.rs | 6 ++--- src/merkle/smt/mod.rs | 6 ++--- src/merkle/smt/partial.rs | 4 +-- src/merkle/smt/simple/mod.rs | 6 ++--- src/merkle/smt/simple/tests.rs | 6 ++--- src/merkle/store/mod.rs | 6 ++--- src/merkle/store/tests.rs | 4 +-- src/utils/mod.rs | 8 +++--- 60 files changed, 170 insertions(+), 176 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1e09a8..4c8f41d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,5 @@ ## 0.14.0 (TBD) -- [BREAKING] Increment minimum supported Rust version to 1.84. - Removed duplicated check in RpoFalcon512 verification (#368). - Added parallel implementation of `Smt::compute_mutations` with better performance (#365). - Implemented parallel leaf hashing in `Smt::process_sorted_pairs_to_leaves` (#365). @@ -13,6 +12,7 @@ - Add range checks to `ntru_gen` for Falcon DSA (#391). - Optimized duplicate key detection in `Smt::with_entries_concurrent` (#395). - [BREAKING] Moved `rand` to version `0.9` removing the `try_fill_bytes` method (#398). +- [BREAKING] Increment minimum supported Rust version to 1.85 (#399). ## 0.13.3 (2025-02-18) diff --git a/Cargo.toml b/Cargo.toml index c09185c..a96f24e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,8 +9,8 @@ repository = "https://github.com/0xPolygonMiden/crypto" documentation = "https://docs.rs/miden-crypto/0.14.0" categories = ["cryptography", "no-std"] keywords = ["miden", "crypto", "hash", "merkle"] -edition = "2021" -rust-version = "1.84" +edition = "2024" +rust-version = "1.85" [[bin]] name = "miden-crypto" diff --git a/README.md b/README.md index cd59c3d..956a4f2 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/0xPolygonMiden/crypto/blob/main/LICENSE) [![test](https://github.com/0xPolygonMiden/crypto/actions/workflows/test.yml/badge.svg)](https://github.com/0xPolygonMiden/crypto/actions/workflows/test.yml) [![build](https://github.com/0xPolygonMiden/crypto/actions/workflows/build.yml/badge.svg)](https://github.com/0xPolygonMiden/crypto/actions/workflows/build.yml) -[![RUST_VERSION](https://img.shields.io/badge/rustc-1.84+-lightgray.svg)](https://www.rust-lang.org/tools/install) +[![RUST_VERSION](https://img.shields.io/badge/rustc-1.85+-lightgray.svg)](https://www.rust-lang.org/tools/install) [![CRATE](https://img.shields.io/crates/v/miden-crypto)](https://crates.io/crates/miden-crypto) This crate contains cryptographic primitives used in Polygon Miden. diff --git a/benches/hash.rs b/benches/hash.rs index 4f79eb8..d1d72a9 100644 --- a/benches/hash.rs +++ b/benches/hash.rs @@ -1,11 +1,11 @@ -use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion}; +use criterion::{BatchSize, Criterion, black_box, criterion_group, criterion_main}; use miden_crypto::{ + Felt, hash::{ blake::Blake3_256, rpo::{Rpo256, RpoDigest}, rpx::{Rpx256, RpxDigest}, }, - Felt, }; use rand_utils::rand_value; use winter_crypto::Hasher; diff --git a/benches/merkle.rs b/benches/merkle.rs index 7d6bb2c..b815394 100644 --- a/benches/merkle.rs +++ b/benches/merkle.rs @@ -7,8 +7,8 @@ //! `benches/smt-subtree.rs`. use std::{hint, mem, time::Duration}; -use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; -use miden_crypto::{merkle::MerkleTree, Felt, Word, ONE}; +use criterion::{BatchSize, Criterion, criterion_group, criterion_main}; +use miden_crypto::{Felt, ONE, Word, merkle::MerkleTree}; use rand_utils::prng_array; fn balanced_merkle_even(c: &mut Criterion) { diff --git a/benches/smt-subtree.rs b/benches/smt-subtree.rs index 6938a33..315ba3b 100644 --- a/benches/smt-subtree.rs +++ b/benches/smt-subtree.rs @@ -1,10 +1,10 @@ use std::{fmt::Debug, hint, mem, time::Duration}; -use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; +use criterion::{BatchSize, BenchmarkId, Criterion, criterion_group, criterion_main}; use miden_crypto::{ + Felt, ONE, Word, hash::rpo::RpoDigest, - merkle::{build_subtree_for_bench, NodeIndex, SmtLeaf, SubtreeLeaf, SMT_DEPTH}, - Felt, Word, ONE, + merkle::{NodeIndex, SMT_DEPTH, SmtLeaf, SubtreeLeaf, build_subtree_for_bench}, }; use rand_utils::prng_array; use winter_utils::Randomizable; diff --git a/benches/smt-with-entries.rs b/benches/smt-with-entries.rs index a372128..a9ca097 100644 --- a/benches/smt-with-entries.rs +++ b/benches/smt-with-entries.rs @@ -1,7 +1,7 @@ use std::{fmt::Debug, hint, mem, time::Duration}; -use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; -use miden_crypto::{hash::rpo::RpoDigest, merkle::Smt, Felt, Word, ONE}; +use criterion::{BatchSize, BenchmarkId, Criterion, criterion_group, criterion_main}; +use miden_crypto::{Felt, ONE, Word, hash::rpo::RpoDigest, merkle::Smt}; use rand_utils::prng_array; use winter_utils::Randomizable; diff --git a/benches/smt.rs b/benches/smt.rs index a5f2097..f8a81c9 100644 --- a/benches/smt.rs +++ b/benches/smt.rs @@ -1,9 +1,9 @@ use core::mem::swap; -use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use criterion::{Criterion, black_box, criterion_group, criterion_main}; use miden_crypto::{ - merkle::{LeafIndex, SimpleSmt}, Felt, Word, + merkle::{LeafIndex, SimpleSmt}, }; use rand_utils::prng_array; use seq_macro::seq; diff --git a/benches/store.rs b/benches/store.rs index 1d1e97a..571f861 100644 --- a/benches/store.rs +++ b/benches/store.rs @@ -1,11 +1,11 @@ -use criterion::{black_box, criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; +use criterion::{BatchSize, BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; use miden_crypto::{ + Felt, Word, hash::rpo::RpoDigest, merkle::{ - DefaultMerkleStore as MerkleStore, LeafIndex, MerkleTree, NodeIndex, SimpleSmt, - SMT_MAX_DEPTH, + DefaultMerkleStore as MerkleStore, LeafIndex, MerkleTree, NodeIndex, SMT_MAX_DEPTH, + SimpleSmt, }, - Felt, Word, }; use rand_utils::{rand_array, rand_value}; diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 77f1d3d..eb8ee2c 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -2,7 +2,7 @@ name = "miden-crypto-fuzz" version = "0.0.0" publish = false -edition = "2021" +edition = "2024" [package.metadata] cargo-fuzz = true diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 252a508..40d4b2d 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.84" +channel = "1.85" components = ["rustfmt", "rust-src", "clippy"] targets = ["wasm32-unknown-unknown"] profile = "minimal" diff --git a/rustfmt.toml b/rustfmt.toml index 9771bd1..cebafc6 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,4 +1,4 @@ -edition = "2021" +edition = "2024" array_width = 80 attr_fn_like_width = 80 chain_width = 80 diff --git a/src/dsa/rpo_falcon512/hash_to_point.rs b/src/dsa/rpo_falcon512/hash_to_point.rs index c5dd815..fbb9f02 100644 --- a/src/dsa/rpo_falcon512/hash_to_point.rs +++ b/src/dsa/rpo_falcon512/hash_to_point.rs @@ -2,7 +2,7 @@ use alloc::vec::Vec; use num::Zero; -use super::{math::FalconFelt, Nonce, Polynomial, Rpo256, Word, MODULUS, N, ZERO}; +use super::{MODULUS, N, Nonce, Polynomial, Rpo256, Word, ZERO, math::FalconFelt}; // HASH-TO-POINT FUNCTIONS // ================================================================================================ @@ -43,8 +43,8 @@ pub fn hash_to_point_rpo256(message: Word, nonce: &Nonce) -> Polynomial Polynomial { use sha3::{ - digest::{ExtendableOutput, Update, XofReader}, Shake256, + digest::{ExtendableOutput, Update, XofReader}, }; let mut data = vec![]; diff --git a/src/dsa/rpo_falcon512/keys/mod.rs b/src/dsa/rpo_falcon512/keys/mod.rs index 02be3df..e2f60a2 100644 --- a/src/dsa/rpo_falcon512/keys/mod.rs +++ b/src/dsa/rpo_falcon512/keys/mod.rs @@ -1,7 +1,7 @@ use super::{ - math::{FalconFelt, Polynomial}, ByteReader, ByteWriter, Deserializable, DeserializationError, Felt, Serializable, Signature, Word, + math::{FalconFelt, Polynomial}, }; mod public_key; @@ -21,7 +21,7 @@ mod tests { use winter_math::FieldElement; use winter_utils::{Deserializable, Serializable}; - use crate::{dsa::rpo_falcon512::SecretKey, Word, ONE}; + use crate::{ONE, Word, dsa::rpo_falcon512::SecretKey}; #[test] fn test_falcon_verification() { diff --git a/src/dsa/rpo_falcon512/keys/public_key.rs b/src/dsa/rpo_falcon512/keys/public_key.rs index 613beeb..36eb849 100644 --- a/src/dsa/rpo_falcon512/keys/public_key.rs +++ b/src/dsa/rpo_falcon512/keys/public_key.rs @@ -4,7 +4,7 @@ use core::ops::Deref; use num::Zero; use super::{ - super::{Rpo256, LOG_N, N, PK_LEN}, + super::{LOG_N, N, PK_LEN, Rpo256}, ByteReader, ByteWriter, Deserializable, DeserializationError, FalconFelt, Felt, Polynomial, Serializable, Signature, Word, }; diff --git a/src/dsa/rpo_falcon512/keys/secret_key.rs b/src/dsa/rpo_falcon512/keys/secret_key.rs index 3cdf095..93472a1 100644 --- a/src/dsa/rpo_falcon512/keys/secret_key.rs +++ b/src/dsa/rpo_falcon512/keys/secret_key.rs @@ -8,15 +8,15 @@ use rand::Rng; use super::{ super::{ - math::{ffldl, ffsampling, gram, normalize_tree, FalconFelt, FastFft, LdlTree, Polynomial}, + ByteReader, ByteWriter, Deserializable, DeserializationError, MODULUS, N, Nonce, + SIG_L2_BOUND, SIGMA, Serializable, ShortLatticeBasis, Signature, Word, + math::{FalconFelt, FastFft, LdlTree, Polynomial, ffldl, ffsampling, gram, normalize_tree}, signature::SignaturePoly, - ByteReader, ByteWriter, Deserializable, DeserializationError, Nonce, Serializable, - ShortLatticeBasis, Signature, Word, MODULUS, N, SIGMA, SIG_L2_BOUND, }, PubKeyPoly, PublicKey, }; use crate::dsa::rpo_falcon512::{ - hash_to_point::hash_to_point_rpo256, math::ntru_gen, SIG_NONCE_LEN, SK_LEN, + SIG_NONCE_LEN, SK_LEN, hash_to_point::hash_to_point_rpo256, math::ntru_gen, }; // CONSTANTS @@ -66,7 +66,7 @@ impl SecretKey { /// Generates a secret key from OS-provided randomness. #[cfg(feature = "std")] pub fn new() -> Self { - use rand::{rngs::StdRng, SeedableRng}; + use rand::{SeedableRng, rngs::StdRng}; let mut rng = StdRng::from_os_rng(); Self::with_rng(&mut rng) @@ -115,7 +115,7 @@ impl SecretKey { /// Signs a message with this secret key. #[cfg(feature = "std")] pub fn sign(&self, message: Word) -> Signature { - use rand::{rngs::StdRng, SeedableRng}; + use rand::{SeedableRng, rngs::StdRng}; let mut rng = StdRng::from_os_rng(); self.sign_with_rng(message, &mut rng) diff --git a/src/dsa/rpo_falcon512/math/fft.rs b/src/dsa/rpo_falcon512/math/fft.rs index 28f4baf..462f692 100644 --- a/src/dsa/rpo_falcon512/math/fft.rs +++ b/src/dsa/rpo_falcon512/math/fft.rs @@ -9,7 +9,7 @@ use num::Float; use num::{One, Zero}; use num_complex::Complex64; -use super::{field::FalconFelt, polynomial::Polynomial, Inverse}; +use super::{Inverse, field::FalconFelt, polynomial::Polynomial}; /// Implements Cyclotomic FFT without bitreversing the outputs, and using precomputed powers of the /// 2n-th primitive root of unity. diff --git a/src/dsa/rpo_falcon512/math/field.rs b/src/dsa/rpo_falcon512/math/field.rs index c695f6f..f79a785 100644 --- a/src/dsa/rpo_falcon512/math/field.rs +++ b/src/dsa/rpo_falcon512/math/field.rs @@ -3,7 +3,7 @@ use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAss use num::{One, Zero}; -use super::{fft::CyclotomicFourier, Inverse, MODULUS}; +use super::{Inverse, MODULUS, fft::CyclotomicFourier}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub struct FalconFelt(u32); diff --git a/src/dsa/rpo_falcon512/math/mod.rs b/src/dsa/rpo_falcon512/math/mod.rs index de12279..1b8221f 100644 --- a/src/dsa/rpo_falcon512/math/mod.rs +++ b/src/dsa/rpo_falcon512/math/mod.rs @@ -15,8 +15,8 @@ use num_complex::Complex64; use rand::Rng; use super::{ - keys::{WIDTH_BIG_POLY_COEFFICIENT, WIDTH_SMALL_POLY_COEFFICIENT}, MODULUS, + keys::{WIDTH_BIG_POLY_COEFFICIENT, WIDTH_SMALL_POLY_COEFFICIENT}, }; mod fft; @@ -26,7 +26,7 @@ mod field; pub use field::FalconFelt; mod ffsampling; -pub use ffsampling::{ffldl, ffsampling, gram, normalize_tree, LdlTree}; +pub use ffsampling::{LdlTree, ffldl, ffsampling, gram, normalize_tree}; mod samplerz; use self::samplerz::sampler_z; diff --git a/src/dsa/rpo_falcon512/math/polynomial.rs b/src/dsa/rpo_falcon512/math/polynomial.rs index e32f3e0..5989ba1 100644 --- a/src/dsa/rpo_falcon512/math/polynomial.rs +++ b/src/dsa/rpo_falcon512/math/polynomial.rs @@ -7,10 +7,10 @@ use core::{ use num::{One, Zero}; -use super::{field::FalconFelt, Inverse}; +use super::{Inverse, field::FalconFelt}; use crate::{ - dsa::rpo_falcon512::{MODULUS, N}, Felt, + dsa::rpo_falcon512::{MODULUS, N}, }; #[derive(Debug, Clone, Default)] @@ -27,9 +27,8 @@ where } } -impl< - F: Mul + Sub + AddAssign + Zero + Div + Clone + Inverse, - > Polynomial +impl + Sub + AddAssign + Zero + Div + Clone + Inverse> + Polynomial { pub fn hadamard_mul(&self, other: &Self) -> Self { Polynomial::new( @@ -84,16 +83,16 @@ impl Polynomial { /// The following implementations are specific to cyclotomic polynomial rings, /// i.e., F\[ X \] / , and are used extensively in Falcon. impl< - F: One - + Zero - + Clone - + Neg - + MulAssign - + AddAssign - + Div - + Sub - + PartialEq, - > Polynomial + F: One + + Zero + + Clone + + Neg + + MulAssign + + AddAssign + + Div + + Sub + + PartialEq, +> Polynomial { /// Reduce the polynomial by X^n + 1. pub fn reduce_by_cyclotomic(&self, n: usize) -> Self { @@ -617,7 +616,7 @@ impl Polynomial { #[cfg(test)] mod tests { - use super::{FalconFelt, Polynomial, N}; + use super::{FalconFelt, N, Polynomial}; #[test] fn test_negacyclic_reduction() { diff --git a/src/dsa/rpo_falcon512/mod.rs b/src/dsa/rpo_falcon512/mod.rs index 31c6f8c..36a81c6 100644 --- a/src/dsa/rpo_falcon512/mod.rs +++ b/src/dsa/rpo_falcon512/mod.rs @@ -1,7 +1,7 @@ use crate::{ + Felt, Word, ZERO, hash::rpo::Rpo256, utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}, - Felt, Word, ZERO, }; mod hash_to_point; diff --git a/src/dsa/rpo_falcon512/signature.rs b/src/dsa/rpo_falcon512/signature.rs index 8bf397a..db35051 100644 --- a/src/dsa/rpo_falcon512/signature.rs +++ b/src/dsa/rpo_falcon512/signature.rs @@ -4,11 +4,11 @@ use core::ops::Deref; use num::Zero; use super::{ + ByteReader, ByteWriter, Deserializable, DeserializationError, Felt, LOG_N, MODULUS, N, Nonce, + Rpo256, SIG_L2_BOUND, SIG_POLY_BYTE_LEN, Serializable, Word, hash_to_point::hash_to_point_rpo256, keys::PubKeyPoly, math::{FalconFelt, FastFft, Polynomial}, - ByteReader, ByteWriter, Deserializable, DeserializationError, Felt, Nonce, Rpo256, - Serializable, Word, LOG_N, MODULUS, N, SIG_L2_BOUND, SIG_POLY_BYTE_LEN, }; // FALCON SIGNATURE @@ -162,9 +162,9 @@ impl Deserializable for SignatureHeader { } if log_n != LOG_N { - return Err(DeserializationError::InvalidValue( - format!("Failed to decode signature: only supported irreducible polynomial degree is 512, 2^{log_n} was provided") - )); + return Err(DeserializationError::InvalidValue(format!( + "Failed to decode signature: only supported irreducible polynomial degree is 512, 2^{log_n} was provided" + ))); } Ok(Self(header)) diff --git a/src/hash/blake/mod.rs b/src/hash/blake/mod.rs index 88ffe08..c6c6eda 100644 --- a/src/hash/blake/mod.rs +++ b/src/hash/blake/mod.rs @@ -7,8 +7,8 @@ use core::{ use super::{Digest, ElementHasher, Felt, FieldElement, Hasher}; use crate::utils::{ - bytes_to_hex_string, hex_to_bytes, ByteReader, ByteWriter, Deserializable, - DeserializationError, HexParseError, Serializable, + ByteReader, ByteWriter, Deserializable, DeserializationError, HexParseError, Serializable, + bytes_to_hex_string, hex_to_bytes, }; #[cfg(test)] diff --git a/src/hash/rescue/arch/mod.rs b/src/hash/rescue/arch/mod.rs index ada4c4f..2ac3937 100644 --- a/src/hash/rescue/arch/mod.rs +++ b/src/hash/rescue/arch/mod.rs @@ -1,6 +1,6 @@ #[cfg(target_feature = "sve")] pub mod optimized { - use crate::{hash::rescue::STATE_WIDTH, Felt}; + use crate::{Felt, hash::rescue::STATE_WIDTH}; mod ffi { #[link(name = "rpo_sve", kind = "static")] @@ -50,8 +50,8 @@ mod x86_64_avx2; pub mod optimized { use super::x86_64_avx2::{apply_inv_sbox, apply_sbox}; use crate::{ - hash::rescue::{add_constants, STATE_WIDTH}, Felt, + hash::rescue::{STATE_WIDTH, add_constants}, }; #[inline(always)] @@ -81,7 +81,7 @@ pub mod optimized { #[cfg(not(any(target_feature = "avx2", target_feature = "sve")))] pub mod optimized { - use crate::{hash::rescue::STATE_WIDTH, Felt}; + use crate::{Felt, hash::rescue::STATE_WIDTH}; #[inline(always)] pub fn add_constants_and_apply_sbox( diff --git a/src/hash/rescue/mds/freq.rs b/src/hash/rescue/mds/freq.rs index a5659d7..e2cc12c 100644 --- a/src/hash/rescue/mds/freq.rs +++ b/src/hash/rescue/mds/freq.rs @@ -85,8 +85,8 @@ const fn ifft4_real(y: (i64, (i64, i64), i64)) -> [u64; 4] { // the MDS matrix constants. let z0 = y.0 + y.2; let z1 = y.0 - y.2; - let z2 = y.1 .0; - let z3 = -y.1 .1; + let z2 = y.1.0; + let z3 = -y.1.1; let [x0, x2] = ifft2_real([z0, z2]); let [x1, x3] = ifft2_real([z1, z3]); @@ -161,7 +161,7 @@ const fn block3(x: [i64; 3], y: [i64; 3]) -> [i64; 3] { mod tests { use proptest::prelude::*; - use super::super::{apply_mds, Felt, MDS, ZERO}; + use super::super::{Felt, MDS, ZERO, apply_mds}; const STATE_WIDTH: usize = 12; diff --git a/src/hash/rescue/mod.rs b/src/hash/rescue/mod.rs index fee20ab..3e7f200 100644 --- a/src/hash/rescue/mod.rs +++ b/src/hash/rescue/mod.rs @@ -6,7 +6,7 @@ mod arch; pub use arch::optimized::{add_constants_and_apply_inv_sbox, add_constants_and_apply_sbox}; mod mds; -use mds::{apply_mds, MDS}; +use mds::{MDS, apply_mds}; mod rpo; pub use rpo::{Rpo256, RpoDigest, RpoDigestError}; diff --git a/src/hash/rescue/rpo/digest.rs b/src/hash/rescue/rpo/digest.rs index a525892..47d09c6 100644 --- a/src/hash/rescue/rpo/digest.rs +++ b/src/hash/rescue/rpo/digest.rs @@ -9,12 +9,12 @@ use core::{ use thiserror::Error; -use super::{Digest, Felt, StarkField, DIGEST_BYTES, DIGEST_SIZE, ZERO}; +use super::{DIGEST_BYTES, DIGEST_SIZE, Digest, Felt, StarkField, ZERO}; use crate::{ rand::Randomizable, utils::{ - bytes_to_hex_string, hex_to_bytes, ByteReader, ByteWriter, Deserializable, - DeserializationError, HexParseError, Serializable, + ByteReader, ByteWriter, Deserializable, DeserializationError, HexParseError, Serializable, + bytes_to_hex_string, hex_to_bytes, }, }; @@ -162,11 +162,7 @@ impl TryFrom for [bool; DIGEST_SIZE] { fn try_from(value: RpoDigest) -> Result { fn to_bool(v: u64) -> Option { - if v <= 1 { - Some(v == 1) - } else { - None - } + if v <= 1 { Some(v == 1) } else { None } } Ok([ @@ -528,7 +524,7 @@ mod tests { use rand_utils::rand_value; - use super::{Deserializable, Felt, RpoDigest, Serializable, DIGEST_BYTES, DIGEST_SIZE}; + use super::{DIGEST_BYTES, DIGEST_SIZE, Deserializable, Felt, RpoDigest, Serializable}; use crate::utils::SliceReader; #[test] diff --git a/src/hash/rescue/rpo/mod.rs b/src/hash/rescue/rpo/mod.rs index ede5053..552004b 100644 --- a/src/hash/rescue/rpo/mod.rs +++ b/src/hash/rescue/rpo/mod.rs @@ -1,10 +1,11 @@ use core::ops::Range; use super::{ - add_constants, add_constants_and_apply_inv_sbox, add_constants_and_apply_sbox, apply_inv_sbox, - apply_mds, apply_sbox, Digest, ElementHasher, Felt, FieldElement, Hasher, StarkField, ARK1, - ARK2, BINARY_CHUNK_SIZE, CAPACITY_RANGE, DIGEST_BYTES, DIGEST_RANGE, DIGEST_SIZE, INPUT1_RANGE, - INPUT2_RANGE, MDS, NUM_ROUNDS, RATE_RANGE, RATE_WIDTH, STATE_WIDTH, ZERO, + ARK1, ARK2, BINARY_CHUNK_SIZE, CAPACITY_RANGE, DIGEST_BYTES, DIGEST_RANGE, DIGEST_SIZE, Digest, + ElementHasher, Felt, FieldElement, Hasher, INPUT1_RANGE, INPUT2_RANGE, MDS, NUM_ROUNDS, + RATE_RANGE, RATE_WIDTH, STATE_WIDTH, StarkField, ZERO, add_constants, + add_constants_and_apply_inv_sbox, add_constants_and_apply_sbox, apply_inv_sbox, apply_mds, + apply_sbox, }; mod digest; diff --git a/src/hash/rescue/rpo/tests.rs b/src/hash/rescue/rpo/tests.rs index 934c18f..26e4148 100644 --- a/src/hash/rescue/rpo/tests.rs +++ b/src/hash/rescue/rpo/tests.rs @@ -4,12 +4,12 @@ use proptest::prelude::*; use rand_utils::rand_value; use super::{ - super::{apply_inv_sbox, apply_sbox, ALPHA, INV_ALPHA}, - Felt, FieldElement, Hasher, Rpo256, RpoDigest, StarkField, STATE_WIDTH, ZERO, + super::{ALPHA, INV_ALPHA, apply_inv_sbox, apply_sbox}, + Felt, FieldElement, Hasher, Rpo256, RpoDigest, STATE_WIDTH, StarkField, ZERO, }; use crate::{ + ONE, Word, hash::rescue::{BINARY_CHUNK_SIZE, CAPACITY_RANGE, RATE_WIDTH}, - Word, ONE, }; #[test] diff --git a/src/hash/rescue/rpx/digest.rs b/src/hash/rescue/rpx/digest.rs index 07ec3c6..9b83c72 100644 --- a/src/hash/rescue/rpx/digest.rs +++ b/src/hash/rescue/rpx/digest.rs @@ -3,12 +3,12 @@ use core::{cmp::Ordering, fmt::Display, ops::Deref, slice}; use thiserror::Error; -use super::{Digest, Felt, StarkField, DIGEST_BYTES, DIGEST_SIZE, ZERO}; +use super::{DIGEST_BYTES, DIGEST_SIZE, Digest, Felt, StarkField, ZERO}; use crate::{ rand::Randomizable, utils::{ - bytes_to_hex_string, hex_to_bytes, ByteReader, ByteWriter, Deserializable, - DeserializationError, HexParseError, Serializable, + ByteReader, ByteWriter, Deserializable, DeserializationError, HexParseError, Serializable, + bytes_to_hex_string, hex_to_bytes, }, }; @@ -150,11 +150,7 @@ impl TryFrom for [bool; DIGEST_SIZE] { fn try_from(value: RpxDigest) -> Result { fn to_bool(v: u64) -> Option { - if v <= 1 { - Some(v == 1) - } else { - None - } + if v <= 1 { Some(v == 1) } else { None } } Ok([ @@ -516,7 +512,7 @@ mod tests { use rand_utils::rand_value; - use super::{Deserializable, Felt, RpxDigest, Serializable, DIGEST_BYTES, DIGEST_SIZE}; + use super::{DIGEST_BYTES, DIGEST_SIZE, Deserializable, Felt, RpxDigest, Serializable}; use crate::utils::SliceReader; #[test] diff --git a/src/hash/rescue/rpx/mod.rs b/src/hash/rescue/rpx/mod.rs index 0435cf6..4c646df 100644 --- a/src/hash/rescue/rpx/mod.rs +++ b/src/hash/rescue/rpx/mod.rs @@ -1,11 +1,11 @@ use core::ops::Range; use super::{ - add_constants, add_constants_and_apply_inv_sbox, add_constants_and_apply_sbox, apply_inv_sbox, - apply_mds, apply_sbox, CubeExtension, Digest, ElementHasher, Felt, FieldElement, Hasher, - StarkField, ARK1, ARK2, BINARY_CHUNK_SIZE, CAPACITY_RANGE, DIGEST_BYTES, DIGEST_RANGE, - DIGEST_SIZE, INPUT1_RANGE, INPUT2_RANGE, MDS, NUM_ROUNDS, RATE_RANGE, RATE_WIDTH, STATE_WIDTH, - ZERO, + ARK1, ARK2, BINARY_CHUNK_SIZE, CAPACITY_RANGE, CubeExtension, DIGEST_BYTES, DIGEST_RANGE, + DIGEST_SIZE, Digest, ElementHasher, Felt, FieldElement, Hasher, INPUT1_RANGE, INPUT2_RANGE, + MDS, NUM_ROUNDS, RATE_RANGE, RATE_WIDTH, STATE_WIDTH, StarkField, ZERO, add_constants, + add_constants_and_apply_inv_sbox, add_constants_and_apply_sbox, apply_inv_sbox, apply_mds, + apply_sbox, }; mod digest; diff --git a/src/hash/rescue/rpx/tests.rs b/src/hash/rescue/rpx/tests.rs index e8087f6..8cd94b1 100644 --- a/src/hash/rescue/rpx/tests.rs +++ b/src/hash/rescue/rpx/tests.rs @@ -4,7 +4,7 @@ use proptest::prelude::*; use rand_utils::rand_value; use super::{Felt, Hasher, Rpx256, StarkField, ZERO}; -use crate::{hash::rescue::RpxDigest, ONE}; +use crate::{ONE, hash::rescue::RpxDigest}; #[test] fn hash_elements_vs_merge() { diff --git a/src/hash/rescue/tests.rs b/src/hash/rescue/tests.rs index 94630fd..f874826 100644 --- a/src/hash/rescue/tests.rs +++ b/src/hash/rescue/tests.rs @@ -1,6 +1,6 @@ use rand_utils::rand_value; -use super::{Felt, FieldElement, ALPHA, INV_ALPHA}; +use super::{ALPHA, Felt, FieldElement, INV_ALPHA}; #[test] fn test_alphas() { diff --git a/src/lib.rs b/src/lib.rs index dc04e72..7e0a6d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,8 +16,8 @@ pub mod utils; // ================================================================================================ pub use winter_math::{ - fields::{f64::BaseElement as Felt, CubeExtension, QuadExtension}, FieldElement, StarkField, + fields::{CubeExtension, QuadExtension, f64::BaseElement as Felt}, }; // TYPE ALIASES diff --git a/src/main.rs b/src/main.rs index 85045cc..8559e9f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,11 +2,11 @@ use std::time::Instant; use clap::Parser; use miden_crypto::{ + EMPTY_WORD, Felt, ONE, Word, hash::rpo::{Rpo256, RpoDigest}, merkle::{MerkleError, Smt}, - Felt, Word, EMPTY_WORD, ONE, }; -use rand::{prelude::IteratorRandom, rng, Rng}; +use rand::{Rng, prelude::IteratorRandom, rng}; use rand_utils::rand_value; #[derive(Parser, Debug)] diff --git a/src/merkle/empty_roots.rs b/src/merkle/empty_roots.rs index 1f54a7a..5cf9ba2 100644 --- a/src/merkle/empty_roots.rs +++ b/src/merkle/empty_roots.rs @@ -1,6 +1,6 @@ use core::slice; -use super::{smt::InnerNode, Felt, RpoDigest, EMPTY_WORD}; +use super::{EMPTY_WORD, Felt, RpoDigest, smt::InnerNode}; // EMPTY NODES SUBTREES // ================================================================================================ diff --git a/src/merkle/error.rs b/src/merkle/error.rs index 5e77944..d81c740 100644 --- a/src/merkle/error.rs +++ b/src/merkle/error.rs @@ -31,6 +31,8 @@ pub enum MerkleError { NumLeavesNotPowerOfTwo(usize), #[error("root {0:?} is not in the store")] RootNotInStore(RpoDigest), - #[error("partial smt does not track the merkle path for key {0} so updating it would produce a different root compared to the same update in the full tree")] + #[error( + "partial smt does not track the merkle path for key {0} so updating it would produce a different root compared to the same update in the full tree" + )] UntrackedKey(RpoDigest), } diff --git a/src/merkle/merkle_tree.rs b/src/merkle/merkle_tree.rs index 3c885a6..4d7478e 100644 --- a/src/merkle/merkle_tree.rs +++ b/src/merkle/merkle_tree.rs @@ -290,8 +290,8 @@ mod tests { use super::*; use crate::{ - merkle::{digests_to_words, int_to_leaf, int_to_node}, Felt, WORD_SIZE, + merkle::{digests_to_words, int_to_leaf, int_to_node}, }; const LEAVES4: [RpoDigest; WORD_SIZE] = diff --git a/src/merkle/mmr/error.rs b/src/merkle/mmr/error.rs index f26cc5c..9d35d5b 100644 --- a/src/merkle/mmr/error.rs +++ b/src/merkle/mmr/error.rs @@ -10,9 +10,7 @@ pub enum MmrError { PositionNotFound(usize), #[error("mmr peaks are invalid: {0}")] InvalidPeaks(String), - #[error( - "mmr peak does not match the computed merkle root of the provided authentication path" - )] + #[error("mmr peak does not match the computed merkle root of the provided authentication path")] PeakPathMismatch, #[error("requested peak index is {peak_idx} but the number of peaks is {peaks_len}")] PeakOutOfBounds { peak_idx: usize, peaks_len: usize }, diff --git a/src/merkle/mmr/full.rs b/src/merkle/mmr/full.rs index e381f5d..31ea59c 100644 --- a/src/merkle/mmr/full.rs +++ b/src/merkle/mmr/full.rs @@ -14,9 +14,9 @@ use alloc::vec::Vec; use super::{ super::{InnerNodeInfo, MerklePath}, + MmrDelta, MmrError, MmrPeaks, MmrProof, Rpo256, RpoDigest, bit::TrueBitPositionIterator, - leaf_to_corresponding_tree, nodes_in_forest, MmrDelta, MmrError, MmrPeaks, MmrProof, Rpo256, - RpoDigest, + leaf_to_corresponding_tree, nodes_in_forest, }; // MMR @@ -202,7 +202,10 @@ impl Mmr { /// that have been merged together, followed by the new peaks of the [Mmr]. pub fn get_delta(&self, from_forest: usize, to_forest: usize) -> Result { if to_forest > self.forest || from_forest > to_forest { - return Err(MmrError::InvalidPeaks(format!("to_forest {to_forest} exceeds the current forest {} or from_forest {from_forest} exceeds to_forest", self.forest))); + return Err(MmrError::InvalidPeaks(format!( + "to_forest {to_forest} exceeds the current forest {} or from_forest {from_forest} exceeds to_forest", + self.forest + ))); } if from_forest == to_forest { @@ -439,9 +442,5 @@ impl Iterator for MmrNodes<'_> { /// Return a bitmask for the bits including and above the given position. pub(crate) const fn high_bitmask(bit: u32) -> usize { - if bit > usize::BITS - 1 { - 0 - } else { - usize::MAX << bit - } + if bit > usize::BITS - 1 { 0 } else { usize::MAX << bit } } diff --git a/src/merkle/mmr/partial.rs b/src/merkle/mmr/partial.rs index d4eec97..1cf2fd2 100644 --- a/src/merkle/mmr/partial.rs +++ b/src/merkle/mmr/partial.rs @@ -7,8 +7,8 @@ use winter_utils::{Deserializable, Serializable}; use super::{MmrDelta, MmrProof, Rpo256, RpoDigest}; use crate::merkle::{ - mmr::{leaf_to_corresponding_tree, nodes_in_forest}, InOrderIndex, InnerNodeInfo, MerklePath, MmrError, MmrPeaks, + mmr::{leaf_to_corresponding_tree, nodes_in_forest}, }; // TYPE ALIASES @@ -645,10 +645,10 @@ mod tests { use winter_utils::{Deserializable, Serializable}; use super::{ - forest_to_rightmost_index, forest_to_root_index, InOrderIndex, MmrPeaks, PartialMmr, - RpoDigest, + InOrderIndex, MmrPeaks, PartialMmr, RpoDigest, forest_to_rightmost_index, + forest_to_root_index, }; - use crate::merkle::{int_to_node, MerkleStore, Mmr, NodeIndex}; + use crate::merkle::{MerkleStore, Mmr, NodeIndex, int_to_node}; const LEAVES: [RpoDigest; 7] = [ int_to_node(0), diff --git a/src/merkle/mmr/tests.rs b/src/merkle/mmr/tests.rs index 93ea638..e47a7c3 100644 --- a/src/merkle/mmr/tests.rs +++ b/src/merkle/mmr/tests.rs @@ -2,13 +2,14 @@ use alloc::vec::Vec; use super::{ super::{InnerNodeInfo, Rpo256, RpoDigest}, + Mmr, MmrPeaks, PartialMmr, bit::TrueBitPositionIterator, full::high_bitmask, - leaf_to_corresponding_tree, nodes_in_forest, Mmr, MmrPeaks, PartialMmr, + leaf_to_corresponding_tree, nodes_in_forest, }; use crate::{ - merkle::{int_to_node, InOrderIndex, MerklePath, MerkleTree, MmrProof, NodeIndex}, Felt, Word, + merkle::{InOrderIndex, MerklePath, MerkleTree, MmrProof, NodeIndex, int_to_node}, }; #[test] diff --git a/src/merkle/mod.rs b/src/merkle/mod.rs index 39c58c5..509de41 100644 --- a/src/merkle/mod.rs +++ b/src/merkle/mod.rs @@ -1,8 +1,8 @@ //! Data structures related to Merkle trees based on RPO256 hash function. use super::{ + EMPTY_WORD, Felt, Word, ZERO, hash::rpo::{Rpo256, RpoDigest}, - Felt, Word, EMPTY_WORD, ZERO, }; // REEXPORTS @@ -15,18 +15,18 @@ mod index; pub use index::NodeIndex; mod merkle_tree; -pub use merkle_tree::{path_to_text, tree_to_text, MerkleTree}; +pub use merkle_tree::{MerkleTree, path_to_text, tree_to_text}; mod path; pub use path::{MerklePath, RootPath, ValuePath}; mod smt; -#[cfg(feature = "internal")] -pub use smt::{build_subtree_for_bench, SubtreeLeaf}; pub use smt::{ - InnerNode, LeafIndex, MutationSet, NodeMutation, PartialSmt, SimpleSmt, Smt, SmtLeaf, - SmtLeafError, SmtProof, SmtProofError, SMT_DEPTH, SMT_MAX_DEPTH, SMT_MIN_DEPTH, + InnerNode, LeafIndex, MutationSet, NodeMutation, PartialSmt, SMT_DEPTH, SMT_MAX_DEPTH, + SMT_MIN_DEPTH, SimpleSmt, Smt, SmtLeaf, SmtLeafError, SmtProof, SmtProofError, }; +#[cfg(feature = "internal")] +pub use smt::{SubtreeLeaf, build_subtree_for_bench}; mod mmr; pub use mmr::{InOrderIndex, Mmr, MmrDelta, MmrError, MmrPeaks, MmrProof, PartialMmr}; diff --git a/src/merkle/partial_mt/mod.rs b/src/merkle/partial_mt/mod.rs index ae4a04f..be7e94c 100644 --- a/src/merkle/partial_mt/mod.rs +++ b/src/merkle/partial_mt/mod.rs @@ -6,11 +6,11 @@ use alloc::{ use core::fmt; use super::{ - InnerNodeInfo, MerkleError, MerklePath, NodeIndex, Rpo256, RpoDigest, ValuePath, Word, - EMPTY_WORD, + EMPTY_WORD, InnerNodeInfo, MerkleError, MerklePath, NodeIndex, Rpo256, RpoDigest, ValuePath, + Word, }; use crate::utils::{ - word_to_hex, ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable, + ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable, word_to_hex, }; #[cfg(test)] diff --git a/src/merkle/partial_mt/tests.rs b/src/merkle/partial_mt/tests.rs index 5016da3..6723ba1 100644 --- a/src/merkle/partial_mt/tests.rs +++ b/src/merkle/partial_mt/tests.rs @@ -2,8 +2,8 @@ use alloc::{collections::BTreeMap, vec::Vec}; use super::{ super::{ - digests_to_words, int_to_node, DefaultMerkleStore as MerkleStore, MerkleTree, NodeIndex, - PartialMerkleTree, + DefaultMerkleStore as MerkleStore, MerkleTree, NodeIndex, PartialMerkleTree, + digests_to_words, int_to_node, }, Deserializable, InnerNodeInfo, RpoDigest, Serializable, ValuePath, }; diff --git a/src/merkle/path.rs b/src/merkle/path.rs index 7e3f02e..ee35c31 100644 --- a/src/merkle/path.rs +++ b/src/merkle/path.rs @@ -3,8 +3,8 @@ use core::ops::{Deref, DerefMut}; use super::{InnerNodeInfo, MerkleError, NodeIndex, Rpo256, RpoDigest}; use crate::{ - utils::{ByteReader, Deserializable, DeserializationError, Serializable}, Word, + utils::{ByteReader, Deserializable, DeserializationError, Serializable}, }; // MERKLE PATH @@ -264,7 +264,7 @@ impl Deserializable for RootPath { #[cfg(test)] mod tests { - use crate::merkle::{int_to_node, MerklePath}; + use crate::merkle::{MerklePath, int_to_node}; #[test] fn test_inner_nodes() { diff --git a/src/merkle/smt/full/concurrent/mod.rs b/src/merkle/smt/full/concurrent/mod.rs index 2862b2a..6e783cf 100644 --- a/src/merkle/smt/full/concurrent/mod.rs +++ b/src/merkle/smt/full/concurrent/mod.rs @@ -5,8 +5,8 @@ use num::Integer; use rayon::prelude::*; use super::{ - leaf, EmptySubtreeRoots, InnerNode, InnerNodes, Leaves, MerkleError, MutationSet, NodeIndex, - RpoDigest, Smt, SmtLeaf, SparseMerkleTree, Word, SMT_DEPTH, + EmptySubtreeRoots, InnerNode, InnerNodes, Leaves, MerkleError, MutationSet, NodeIndex, + RpoDigest, SMT_DEPTH, Smt, SmtLeaf, SparseMerkleTree, Word, leaf, }; use crate::merkle::smt::{NodeMutation, NodeMutations, UnorderedMap}; diff --git a/src/merkle/smt/full/concurrent/tests.rs b/src/merkle/smt/full/concurrent/tests.rs index 97dd68b..f6e5c9b 100644 --- a/src/merkle/smt/full/concurrent/tests.rs +++ b/src/merkle/smt/full/concurrent/tests.rs @@ -5,16 +5,16 @@ use alloc::{ use assert_matches::assert_matches; use proptest::prelude::*; -use rand::{prelude::IteratorRandom, rng, Rng}; +use rand::{Rng, prelude::IteratorRandom, rng}; use super::{ - build_subtree, InnerNode, NodeIndex, NodeMutations, PairComputations, RpoDigest, Smt, SmtLeaf, - SparseMerkleTree, SubtreeLeaf, SubtreeLeavesIter, UnorderedMap, COLS_PER_SUBTREE, SMT_DEPTH, - SUBTREE_DEPTH, + COLS_PER_SUBTREE, InnerNode, NodeIndex, NodeMutations, PairComputations, RpoDigest, SMT_DEPTH, + SUBTREE_DEPTH, Smt, SmtLeaf, SparseMerkleTree, SubtreeLeaf, SubtreeLeavesIter, UnorderedMap, + build_subtree, }; use crate::{ - merkle::{smt::Felt, LeafIndex, MerkleError}, - Word, EMPTY_WORD, ONE, ZERO, + EMPTY_WORD, ONE, Word, ZERO, + merkle::{LeafIndex, MerkleError, smt::Felt}, }; fn smtleaf_to_subtree_leaf(leaf: &SmtLeaf) -> SubtreeLeaf { diff --git a/src/merkle/smt/full/error.rs b/src/merkle/smt/full/error.rs index 7144d45..f122354 100644 --- a/src/merkle/smt/full/error.rs +++ b/src/merkle/smt/full/error.rs @@ -11,16 +11,20 @@ use crate::{ #[derive(Debug, Error)] pub enum SmtLeafError { #[error( - "multiple leaf requires all keys to map to the same leaf index but key1 {key_1} and key2 {key_2} map to different indices" + "multiple leaf requires all keys to map to the same leaf index but key1 {key_1} and key2 {key_2} map to different indices" )] InconsistentMultipleLeafKeys { key_1: RpoDigest, key_2: RpoDigest }, - #[error("single leaf key {key} maps to {actual_leaf_index:?} but was expected to map to {expected_leaf_index:?}")] + #[error( + "single leaf key {key} maps to {actual_leaf_index:?} but was expected to map to {expected_leaf_index:?}" + )] InconsistentSingleLeafIndices { key: RpoDigest, expected_leaf_index: LeafIndex, actual_leaf_index: LeafIndex, }, - #[error("supplied leaf index {leaf_index_supplied:?} does not match {leaf_index_from_keys:?} for multiple leaf")] + #[error( + "supplied leaf index {leaf_index_supplied:?} does not match {leaf_index_from_keys:?} for multiple leaf" + )] InconsistentMultipleLeafIndices { leaf_index_from_keys: LeafIndex, leaf_index_supplied: LeafIndex, diff --git a/src/merkle/smt/full/leaf.rs b/src/merkle/smt/full/leaf.rs index 810c93c..9c4b100 100644 --- a/src/merkle/smt/full/leaf.rs +++ b/src/merkle/smt/full/leaf.rs @@ -1,7 +1,7 @@ use alloc::{string::ToString, vec::Vec}; use core::cmp::Ordering; -use super::{Felt, LeafIndex, Rpo256, RpoDigest, SmtLeafError, Word, EMPTY_WORD, SMT_DEPTH}; +use super::{EMPTY_WORD, Felt, LeafIndex, Rpo256, RpoDigest, SMT_DEPTH, SmtLeafError, Word}; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/src/merkle/smt/full/mod.rs b/src/merkle/smt/full/mod.rs index 2e751fb..a32a525 100644 --- a/src/merkle/smt/full/mod.rs +++ b/src/merkle/smt/full/mod.rs @@ -1,8 +1,8 @@ use alloc::{string::ToString, vec::Vec}; use super::{ - EmptySubtreeRoots, Felt, InnerNode, InnerNodeInfo, InnerNodes, LeafIndex, MerkleError, - MerklePath, MutationSet, NodeIndex, Rpo256, RpoDigest, SparseMerkleTree, Word, EMPTY_WORD, + EMPTY_WORD, EmptySubtreeRoots, Felt, InnerNode, InnerNodeInfo, InnerNodes, LeafIndex, + MerkleError, MerklePath, MutationSet, NodeIndex, Rpo256, RpoDigest, SparseMerkleTree, Word, }; mod error; @@ -19,7 +19,7 @@ use winter_utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, #[cfg(feature = "concurrent")] mod concurrent; #[cfg(feature = "internal")] -pub use concurrent::{build_subtree_for_bench, SubtreeLeaf}; +pub use concurrent::{SubtreeLeaf, build_subtree_for_bench}; #[cfg(test)] mod tests; diff --git a/src/merkle/smt/full/proof.rs b/src/merkle/smt/full/proof.rs index 8455488..0b48434 100644 --- a/src/merkle/smt/full/proof.rs +++ b/src/merkle/smt/full/proof.rs @@ -1,6 +1,6 @@ use alloc::string::ToString; -use super::{MerklePath, RpoDigest, SmtLeaf, SmtProofError, Word, SMT_DEPTH}; +use super::{MerklePath, RpoDigest, SMT_DEPTH, SmtLeaf, SmtProofError, Word}; use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; /// A proof which can be used to assert membership (or non-membership) of key-value pairs in a diff --git a/src/merkle/smt/full/tests.rs b/src/merkle/smt/full/tests.rs index 4863205..d793a27 100644 --- a/src/merkle/smt/full/tests.rs +++ b/src/merkle/smt/full/tests.rs @@ -1,13 +1,13 @@ use alloc::vec::Vec; -use super::{Felt, LeafIndex, NodeIndex, Rpo256, RpoDigest, Smt, SmtLeaf, EMPTY_WORD, SMT_DEPTH}; +use super::{EMPTY_WORD, Felt, LeafIndex, NodeIndex, Rpo256, RpoDigest, SMT_DEPTH, Smt, SmtLeaf}; use crate::{ + ONE, WORD_SIZE, Word, merkle::{ - smt::{NodeMutation, SparseMerkleTree, UnorderedMap}, EmptySubtreeRoots, MerkleStore, MutationSet, + smt::{NodeMutation, SparseMerkleTree, UnorderedMap}, }, utils::{Deserializable, Serializable}, - Word, ONE, WORD_SIZE, }; // SMT // -------------------------------------------------------------------------------------------- diff --git a/src/merkle/smt/mod.rs b/src/merkle/smt/mod.rs index 5d8c933..03f1bed 100644 --- a/src/merkle/smt/mod.rs +++ b/src/merkle/smt/mod.rs @@ -5,14 +5,14 @@ use winter_utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, use super::{EmptySubtreeRoots, InnerNodeInfo, MerkleError, MerklePath, NodeIndex}; use crate::{ + EMPTY_WORD, Felt, Word, hash::rpo::{Rpo256, RpoDigest}, - Felt, Word, EMPTY_WORD, }; mod full; +pub use full::{SMT_DEPTH, Smt, SmtLeaf, SmtLeafError, SmtProof, SmtProofError}; #[cfg(feature = "internal")] -pub use full::{build_subtree_for_bench, SubtreeLeaf}; -pub use full::{Smt, SmtLeaf, SmtLeafError, SmtProof, SmtProofError, SMT_DEPTH}; +pub use full::{SubtreeLeaf, build_subtree_for_bench}; mod simple; pub use simple::SimpleSmt; diff --git a/src/merkle/smt/partial.rs b/src/merkle/smt/partial.rs index 934d2f6..93479d3 100644 --- a/src/merkle/smt/partial.rs +++ b/src/merkle/smt/partial.rs @@ -1,7 +1,7 @@ use crate::{ + EMPTY_WORD, Word, hash::rpo::RpoDigest, - merkle::{smt::SparseMerkleTree, InnerNode, MerkleError, MerklePath, Smt, SmtLeaf, SmtProof}, - Word, EMPTY_WORD, + merkle::{InnerNode, MerkleError, MerklePath, Smt, SmtLeaf, SmtProof, smt::SparseMerkleTree}, }; /// A partial version of an [`Smt`]. diff --git a/src/merkle/smt/simple/mod.rs b/src/merkle/smt/simple/mod.rs index 11f9cf3..6773cd1 100644 --- a/src/merkle/smt/simple/mod.rs +++ b/src/merkle/smt/simple/mod.rs @@ -1,9 +1,9 @@ use alloc::collections::BTreeSet; use super::{ - super::ValuePath, EmptySubtreeRoots, InnerNode, InnerNodeInfo, InnerNodes, LeafIndex, - MerkleError, MerklePath, MutationSet, NodeIndex, RpoDigest, SparseMerkleTree, Word, EMPTY_WORD, - SMT_MAX_DEPTH, SMT_MIN_DEPTH, + super::ValuePath, EMPTY_WORD, EmptySubtreeRoots, InnerNode, InnerNodeInfo, InnerNodes, + LeafIndex, MerkleError, MerklePath, MutationSet, NodeIndex, RpoDigest, SMT_MAX_DEPTH, + SMT_MIN_DEPTH, SparseMerkleTree, Word, }; #[cfg(test)] diff --git a/src/merkle/smt/simple/tests.rs b/src/merkle/smt/simple/tests.rs index 9078c52..919fed2 100644 --- a/src/merkle/smt/simple/tests.rs +++ b/src/merkle/smt/simple/tests.rs @@ -7,12 +7,12 @@ use super::{ NodeIndex, }; use crate::{ + EMPTY_WORD, Word, hash::rpo::Rpo256, merkle::{ - digests_to_words, int_to_leaf, int_to_node, smt::SparseMerkleTree, EmptySubtreeRoots, - InnerNodeInfo, LeafIndex, MerkleTree, + EmptySubtreeRoots, InnerNodeInfo, LeafIndex, MerkleTree, digests_to_words, int_to_leaf, + int_to_node, smt::SparseMerkleTree, }, - Word, EMPTY_WORD, }; // TEST DATA diff --git a/src/merkle/store/mod.rs b/src/merkle/store/mod.rs index f89f739..ac8e52f 100644 --- a/src/merkle/store/mod.rs +++ b/src/merkle/store/mod.rs @@ -2,12 +2,12 @@ use alloc::{collections::BTreeMap, vec::Vec}; use core::borrow::Borrow; use super::{ - mmr::Mmr, EmptySubtreeRoots, InnerNodeInfo, MerkleError, MerklePath, MerkleTree, NodeIndex, - PartialMerkleTree, RootPath, Rpo256, RpoDigest, SimpleSmt, Smt, ValuePath, + EmptySubtreeRoots, InnerNodeInfo, MerkleError, MerklePath, MerkleTree, NodeIndex, + PartialMerkleTree, RootPath, Rpo256, RpoDigest, SimpleSmt, Smt, ValuePath, mmr::Mmr, }; use crate::utils::{ - collections::{KvMap, RecordingMap}, ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable, + collections::{KvMap, RecordingMap}, }; #[cfg(test)] diff --git a/src/merkle/store/tests.rs b/src/merkle/store/tests.rs index c00b6da..80694b1 100644 --- a/src/merkle/store/tests.rs +++ b/src/merkle/store/tests.rs @@ -12,10 +12,10 @@ use super::{ PartialMerkleTree, RecordingMerkleStore, Rpo256, RpoDigest, }; use crate::{ + Felt, ONE, WORD_SIZE, Word, ZERO, merkle::{ - digests_to_words, int_to_leaf, int_to_node, LeafIndex, MerkleTree, SimpleSmt, SMT_MAX_DEPTH, + LeafIndex, MerkleTree, SMT_MAX_DEPTH, SimpleSmt, digests_to_words, int_to_leaf, int_to_node, }, - Felt, Word, ONE, WORD_SIZE, ZERO, }; // TEST DATA diff --git a/src/utils/mod.rs b/src/utils/mod.rs index f9da561..912125c 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -13,8 +13,8 @@ mod kv_map; // ================================================================================================ pub use winter_utils::{ - uninit_vector, ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable, - SliceReader, + ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable, SliceReader, + uninit_vector, }; pub mod collections { @@ -50,9 +50,7 @@ pub fn bytes_to_hex_string(data: [u8; N]) -> String { /// Defines errors which can occur during parsing of hexadecimal strings. #[derive(Debug, Error)] pub enum HexParseError { - #[error( - "expected hex data to have length {expected}, including the 0x prefix, found {actual}" - )] + #[error("expected hex data to have length {expected}, including the 0x prefix, found {actual}")] InvalidLength { expected: usize, actual: usize }, #[error("hex encoded data must start with 0x prefix")] MissingPrefix, From 8b1593f0ba4b26332d0a308dcedab2173ac4b4b6 Mon Sep 17 00:00:00 2001 From: Krushimir Date: Sun, 16 Mar 2025 01:13:58 +0100 Subject: [PATCH 07/16] refactor: folder structure (#397) --- Cargo.toml | 99 ++------------ Makefile | 2 +- README.md | 10 +- {fuzz => miden-crypto-fuzz}/.gitignore | 0 {fuzz => miden-crypto-fuzz}/Cargo.lock | 129 ++++++++++++++++-- {fuzz => miden-crypto-fuzz}/Cargo.toml | 2 +- .../fuzz_targets/smt.rs | 4 +- miden-crypto/Cargo.toml | 97 +++++++++++++ .../arch}/arm64-sve/rpo/library.c | 0 .../arch}/arm64-sve/rpo/library.h | 0 .../arch}/arm64-sve/rpo/rpo_hash_128bit.h | 0 .../arch}/arm64-sve/rpo/rpo_hash_256bit.h | 0 {benches => miden-crypto/benches}/README.md | 0 {benches => miden-crypto/benches}/hash.rs | 0 {benches => miden-crypto/benches}/merkle.rs | 0 .../benches}/smt-subtree.rs | 0 .../benches}/smt-with-entries.rs | 0 {benches => miden-crypto/benches}/smt.rs | 0 {benches => miden-crypto/benches}/store.rs | 0 build.rs => miden-crypto/build.rs | 0 {src => miden-crypto/src}/dsa/mod.rs | 0 .../src}/dsa/rpo_falcon512/hash_to_point.rs | 0 .../src}/dsa/rpo_falcon512/keys/mod.rs | 0 .../src}/dsa/rpo_falcon512/keys/public_key.rs | 0 .../src}/dsa/rpo_falcon512/keys/secret_key.rs | 0 .../src}/dsa/rpo_falcon512/math/ffsampling.rs | 0 .../src}/dsa/rpo_falcon512/math/fft.rs | 0 .../src}/dsa/rpo_falcon512/math/field.rs | 0 .../src}/dsa/rpo_falcon512/math/mod.rs | 0 .../src}/dsa/rpo_falcon512/math/polynomial.rs | 0 .../src}/dsa/rpo_falcon512/math/samplerz.rs | 0 .../src}/dsa/rpo_falcon512/mod.rs | 0 .../src}/dsa/rpo_falcon512/signature.rs | 0 {src => miden-crypto/src}/hash/blake/mod.rs | 0 {src => miden-crypto/src}/hash/blake/tests.rs | 0 {src => miden-crypto/src}/hash/mod.rs | 0 .../src}/hash/rescue/arch/mod.rs | 0 .../src}/hash/rescue/arch/x86_64_avx2.rs | 0 .../src}/hash/rescue/mds/freq.rs | 0 .../src}/hash/rescue/mds/mod.rs | 0 {src => miden-crypto/src}/hash/rescue/mod.rs | 0 .../src}/hash/rescue/rpo/digest.rs | 0 .../src}/hash/rescue/rpo/mod.rs | 0 .../src}/hash/rescue/rpo/tests.rs | 0 .../src}/hash/rescue/rpx/digest.rs | 0 .../src}/hash/rescue/rpx/mod.rs | 0 .../src}/hash/rescue/rpx/tests.rs | 0 .../src}/hash/rescue/tests.rs | 0 {src => miden-crypto/src}/lib.rs | 0 {src => miden-crypto/src}/main.rs | 0 .../src}/merkle/empty_roots.rs | 0 {src => miden-crypto/src}/merkle/error.rs | 0 {src => miden-crypto/src}/merkle/index.rs | 0 .../src}/merkle/merkle_tree.rs | 0 {src => miden-crypto/src}/merkle/mmr/bit.rs | 0 {src => miden-crypto/src}/merkle/mmr/delta.rs | 0 {src => miden-crypto/src}/merkle/mmr/error.rs | 0 {src => miden-crypto/src}/merkle/mmr/full.rs | 0 .../src}/merkle/mmr/inorder.rs | 0 {src => miden-crypto/src}/merkle/mmr/mod.rs | 0 .../src}/merkle/mmr/partial.rs | 0 {src => miden-crypto/src}/merkle/mmr/peaks.rs | 0 {src => miden-crypto/src}/merkle/mmr/proof.rs | 0 {src => miden-crypto/src}/merkle/mmr/tests.rs | 0 {src => miden-crypto/src}/merkle/mod.rs | 0 {src => miden-crypto/src}/merkle/node.rs | 0 .../src}/merkle/partial_mt/mod.rs | 0 .../src}/merkle/partial_mt/tests.rs | 0 {src => miden-crypto/src}/merkle/path.rs | 0 .../src}/merkle/smt/full/concurrent/mod.rs | 0 .../src}/merkle/smt/full/concurrent/tests.rs | 0 .../src}/merkle/smt/full/error.rs | 0 .../src}/merkle/smt/full/leaf.rs | 0 .../src}/merkle/smt/full/mod.rs | 0 .../src}/merkle/smt/full/proof.rs | 0 .../src}/merkle/smt/full/tests.rs | 0 {src => miden-crypto/src}/merkle/smt/mod.rs | 0 .../src}/merkle/smt/partial.rs | 0 .../src}/merkle/smt/simple/mod.rs | 0 .../src}/merkle/smt/simple/tests.rs | 0 {src => miden-crypto/src}/merkle/store/mod.rs | 0 .../src}/merkle/store/tests.rs | 0 {src => miden-crypto/src}/rand/mod.rs | 0 {src => miden-crypto/src}/rand/rpo.rs | 0 {src => miden-crypto/src}/rand/rpx.rs | 0 {src => miden-crypto/src}/utils/kv_map.rs | 0 {src => miden-crypto/src}/utils/mod.rs | 0 87 files changed, 232 insertions(+), 111 deletions(-) rename {fuzz => miden-crypto-fuzz}/.gitignore (100%) rename {fuzz => miden-crypto-fuzz}/Cargo.lock (79%) rename {fuzz => miden-crypto-fuzz}/Cargo.toml (81%) rename {fuzz => miden-crypto-fuzz}/fuzz_targets/smt.rs (95%) create mode 100644 miden-crypto/Cargo.toml rename {arch => miden-crypto/arch}/arm64-sve/rpo/library.c (100%) rename {arch => miden-crypto/arch}/arm64-sve/rpo/library.h (100%) rename {arch => miden-crypto/arch}/arm64-sve/rpo/rpo_hash_128bit.h (100%) rename {arch => miden-crypto/arch}/arm64-sve/rpo/rpo_hash_256bit.h (100%) rename {benches => miden-crypto/benches}/README.md (100%) rename {benches => miden-crypto/benches}/hash.rs (100%) rename {benches => miden-crypto/benches}/merkle.rs (100%) rename {benches => miden-crypto/benches}/smt-subtree.rs (100%) rename {benches => miden-crypto/benches}/smt-with-entries.rs (100%) rename {benches => miden-crypto/benches}/smt.rs (100%) rename {benches => miden-crypto/benches}/store.rs (100%) rename build.rs => miden-crypto/build.rs (100%) rename {src => miden-crypto/src}/dsa/mod.rs (100%) rename {src => miden-crypto/src}/dsa/rpo_falcon512/hash_to_point.rs (100%) rename {src => miden-crypto/src}/dsa/rpo_falcon512/keys/mod.rs (100%) rename {src => miden-crypto/src}/dsa/rpo_falcon512/keys/public_key.rs (100%) rename {src => miden-crypto/src}/dsa/rpo_falcon512/keys/secret_key.rs (100%) rename {src => miden-crypto/src}/dsa/rpo_falcon512/math/ffsampling.rs (100%) rename {src => miden-crypto/src}/dsa/rpo_falcon512/math/fft.rs (100%) rename {src => miden-crypto/src}/dsa/rpo_falcon512/math/field.rs (100%) rename {src => miden-crypto/src}/dsa/rpo_falcon512/math/mod.rs (100%) rename {src => miden-crypto/src}/dsa/rpo_falcon512/math/polynomial.rs (100%) rename {src => miden-crypto/src}/dsa/rpo_falcon512/math/samplerz.rs (100%) rename {src => miden-crypto/src}/dsa/rpo_falcon512/mod.rs (100%) rename {src => miden-crypto/src}/dsa/rpo_falcon512/signature.rs (100%) rename {src => miden-crypto/src}/hash/blake/mod.rs (100%) rename {src => miden-crypto/src}/hash/blake/tests.rs (100%) rename {src => miden-crypto/src}/hash/mod.rs (100%) rename {src => miden-crypto/src}/hash/rescue/arch/mod.rs (100%) rename {src => miden-crypto/src}/hash/rescue/arch/x86_64_avx2.rs (100%) rename {src => miden-crypto/src}/hash/rescue/mds/freq.rs (100%) rename {src => miden-crypto/src}/hash/rescue/mds/mod.rs (100%) rename {src => miden-crypto/src}/hash/rescue/mod.rs (100%) rename {src => miden-crypto/src}/hash/rescue/rpo/digest.rs (100%) rename {src => miden-crypto/src}/hash/rescue/rpo/mod.rs (100%) rename {src => miden-crypto/src}/hash/rescue/rpo/tests.rs (100%) rename {src => miden-crypto/src}/hash/rescue/rpx/digest.rs (100%) rename {src => miden-crypto/src}/hash/rescue/rpx/mod.rs (100%) rename {src => miden-crypto/src}/hash/rescue/rpx/tests.rs (100%) rename {src => miden-crypto/src}/hash/rescue/tests.rs (100%) rename {src => miden-crypto/src}/lib.rs (100%) rename {src => miden-crypto/src}/main.rs (100%) rename {src => miden-crypto/src}/merkle/empty_roots.rs (100%) rename {src => miden-crypto/src}/merkle/error.rs (100%) rename {src => miden-crypto/src}/merkle/index.rs (100%) rename {src => miden-crypto/src}/merkle/merkle_tree.rs (100%) rename {src => miden-crypto/src}/merkle/mmr/bit.rs (100%) rename {src => miden-crypto/src}/merkle/mmr/delta.rs (100%) rename {src => miden-crypto/src}/merkle/mmr/error.rs (100%) rename {src => miden-crypto/src}/merkle/mmr/full.rs (100%) rename {src => miden-crypto/src}/merkle/mmr/inorder.rs (100%) rename {src => miden-crypto/src}/merkle/mmr/mod.rs (100%) rename {src => miden-crypto/src}/merkle/mmr/partial.rs (100%) rename {src => miden-crypto/src}/merkle/mmr/peaks.rs (100%) rename {src => miden-crypto/src}/merkle/mmr/proof.rs (100%) rename {src => miden-crypto/src}/merkle/mmr/tests.rs (100%) rename {src => miden-crypto/src}/merkle/mod.rs (100%) rename {src => miden-crypto/src}/merkle/node.rs (100%) rename {src => miden-crypto/src}/merkle/partial_mt/mod.rs (100%) rename {src => miden-crypto/src}/merkle/partial_mt/tests.rs (100%) rename {src => miden-crypto/src}/merkle/path.rs (100%) rename {src => miden-crypto/src}/merkle/smt/full/concurrent/mod.rs (100%) rename {src => miden-crypto/src}/merkle/smt/full/concurrent/tests.rs (100%) rename {src => miden-crypto/src}/merkle/smt/full/error.rs (100%) rename {src => miden-crypto/src}/merkle/smt/full/leaf.rs (100%) rename {src => miden-crypto/src}/merkle/smt/full/mod.rs (100%) rename {src => miden-crypto/src}/merkle/smt/full/proof.rs (100%) rename {src => miden-crypto/src}/merkle/smt/full/tests.rs (100%) rename {src => miden-crypto/src}/merkle/smt/mod.rs (100%) rename {src => miden-crypto/src}/merkle/smt/partial.rs (100%) rename {src => miden-crypto/src}/merkle/smt/simple/mod.rs (100%) rename {src => miden-crypto/src}/merkle/smt/simple/tests.rs (100%) rename {src => miden-crypto/src}/merkle/store/mod.rs (100%) rename {src => miden-crypto/src}/merkle/store/tests.rs (100%) rename {src => miden-crypto/src}/rand/mod.rs (100%) rename {src => miden-crypto/src}/rand/rpo.rs (100%) rename {src => miden-crypto/src}/rand/rpx.rs (100%) rename {src => miden-crypto/src}/utils/kv_map.rs (100%) rename {src => miden-crypto/src}/utils/mod.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index a96f24e..cf00b3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,97 +1,18 @@ -[package] -name = "miden-crypto" -version = "0.14.0" -description = "Miden Cryptographic primitives" +[workspace] +members = [ + "miden-crypto" +] +exclude = [ + "miden-crypto-fuzz" +] +resolver = "3" # Use the edition 2024 dependency resolver + +[workspace.package] authors = ["miden contributors"] -readme = "README.md" license = "MIT" repository = "https://github.com/0xPolygonMiden/crypto" -documentation = "https://docs.rs/miden-crypto/0.14.0" categories = ["cryptography", "no-std"] keywords = ["miden", "crypto", "hash", "merkle"] edition = "2024" rust-version = "1.85" -[[bin]] -name = "miden-crypto" -path = "src/main.rs" -bench = false -doctest = false -required-features = ["executable"] - -[[bench]] -name = "hash" -harness = false - -[[bench]] -name = "smt" -harness = false - -[[bench]] -name = "smt-subtree" -harness = false -required-features = ["internal"] - -[[bench]] -name = "merkle" -harness = false - -[[bench]] -name = "smt-with-entries" -harness = false - -[[bench]] -name = "store" -harness = false - -[features] -concurrent = ["dep:rayon", "hashbrown?/rayon"] -default = ["std", "concurrent"] -executable = ["dep:clap", "dep:rand-utils", "std"] -smt_hashmaps = ["dep:hashbrown"] -internal = [] -serde = ["dep:serde", "serde?/alloc", "winter-math/serde"] -std = [ - "blake3/std", - "dep:cc", - "rand/std", - "rand/thread_rng", - "winter-crypto/std", - "winter-math/std", - "winter-utils/std", -] - -[dependencies] -blake3 = { version = "1.5", default-features = false } -clap = { version = "4.5", optional = true, features = ["derive"] } -hashbrown = { version = "0.15", optional = true, features = ["serde"] } -num = { version = "0.4", default-features = false, features = ["alloc", "libm"] } -num-complex = { version = "0.4", default-features = false } -rand = { version = "0.9", default-features = false } -rand_core = { version = "0.9", default-features = false } -rand-utils = { version = "0.12", package = "winter-rand-utils", optional = true } -rayon = { version = "1.10", optional = true } -serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] } -sha3 = { version = "0.10", default-features = false } -thiserror = { version = "2.0", default-features = false } -winter-crypto = { version = "0.12", default-features = false } -winter-math = { version = "0.12", default-features = false } -winter-utils = { version = "0.12", default-features = false } - -[dev-dependencies] -assert_matches = { version = "1.5", default-features = false } -criterion = { version = "0.5", features = ["html_reports"] } -getrandom = { version = "0.3", default-features = false } -hex = { version = "0.4", default-features = false, features = ["alloc"] } -proptest = { version = "1.6", default-features = false, features = ["alloc"]} -rand_chacha = { version = "0.9", default-features = false } -rand-utils = { version = "0.12", package = "winter-rand-utils" } -seq-macro = { version = "0.3" } - -[build-dependencies] -cc = { version = "1.2", optional = true, features = ["parallel"] } -glob = "0.3" - -[lints.rust] -# Suppress warnings about `cfg(fuzzing)`, which is automatically set when using `cargo-fuzz`. -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] } diff --git a/Makefile b/Makefile index af430a9..7abb8d6 100644 --- a/Makefile +++ b/Makefile @@ -99,4 +99,4 @@ bench-smt-concurrent: ## Run SMT benchmarks with concurrent feature .PHONY: fuzz-smt fuzz-smt: ## Run fuzzing for SMT - cargo +nightly fuzz run smt --release -- -max_len=10485760 + cargo +nightly fuzz run smt --release --fuzz-dir miden-crypto-fuzz -- -max_len=10485760 diff --git a/README.md b/README.md index 956a4f2..4bc9625 100644 --- a/README.md +++ b/README.md @@ -10,17 +10,17 @@ This crate contains cryptographic primitives used in Polygon Miden. ## Hash -[Hash module](./src/hash) provides a set of cryptographic hash functions which are used by the Miden VM and the Miden rollup. Currently, these functions are: +[Hash module](./miden-crypto/src/hash) provides a set of cryptographic hash functions which are used by the Miden VM and the Miden rollup. Currently, these functions are: - [BLAKE3](https://github.com/BLAKE3-team/BLAKE3) hash function with 256-bit, 192-bit, or 160-bit output. The 192-bit and 160-bit outputs are obtained by truncating the 256-bit output of the standard BLAKE3. - [RPO](https://eprint.iacr.org/2022/1577) hash function with 256-bit output. This hash function is an algebraic hash function suitable for recursive STARKs. - [RPX](https://eprint.iacr.org/2023/1045) hash function with 256-bit output. Similar to RPO, this hash function is suitable for recursive STARKs but it is about 2x faster as compared to RPO. -For performance benchmarks of these hash functions and their comparison to other popular hash functions please see [here](./benches/). +For performance benchmarks of these hash functions and their comparison to other popular hash functions please see [here](./miden-crypto/benches/). ## Merkle -[Merkle module](./src/merkle/) provides a set of data structures related to Merkle trees. All these data structures are implemented using the RPO hash function described above. The data structures are: +[Merkle module](./miden-crypto/src/merkle/) provides a set of data structures related to Merkle trees. All these data structures are implemented using the RPO hash function described above. The data structures are: - `MerkleStore`: a collection of Merkle trees of different heights designed to efficiently store trees with common subtrees. When instantiated with `RecordingMap`, a Merkle store records all accesses to the original data. - `MerkleTree`: a regular fully-balanced binary Merkle tree. The depth of this tree can be at most 64. @@ -34,7 +34,7 @@ The module also contains additional supporting components such as `NodeIndex`, ` ## Signatures -[DSA module](./src/dsa) provides a set of digital signature schemes supported by default in the Miden VM. Currently, these schemes are: +[DSA module](./miden-crypto/src/dsa) provides a set of digital signature schemes supported by default in the Miden VM. Currently, these schemes are: - `RPO Falcon512`: a variant of the [Falcon](https://falcon-sign.info/) signature scheme. This variant differs from the standard in that instead of using SHAKE256 hash function in the _hash-to-point_ algorithm we use RPO256. This makes the signature more efficient to verify in Miden VM. @@ -42,7 +42,7 @@ For the above signatures, key generation, signing, and signature verification ar ## Pseudo-Random Element Generator -[Pseudo random element generator module](./src/rand/) provides a set of traits and data structures that facilitate generating pseudo-random elements in the context of Miden VM and Miden rollup. The module currently includes: +[Pseudo random element generator module](./miden-crypto/src/rand/) provides a set of traits and data structures that facilitate generating pseudo-random elements in the context of Miden VM and Miden rollup. The module currently includes: - `FeltRng`: a trait for generating random field elements and random 4 field elements. - `RpoRandomCoin`: a struct implementing `FeltRng` as well as the [`RandomCoin`](https://github.com/facebook/winterfell/blob/main/crypto/src/random/mod.rs) trait using RPO hash function. diff --git a/fuzz/.gitignore b/miden-crypto-fuzz/.gitignore similarity index 100% rename from fuzz/.gitignore rename to miden-crypto-fuzz/.gitignore diff --git a/fuzz/Cargo.lock b/miden-crypto-fuzz/Cargo.lock similarity index 79% rename from fuzz/Cargo.lock rename to miden-crypto-fuzz/Cargo.lock index 201f30a..965dc96 100644 --- a/fuzz/Cargo.lock +++ b/miden-crypto-fuzz/Cargo.lock @@ -32,6 +32,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + [[package]] name = "blake3" version = "1.6.0" @@ -167,13 +173,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" dependencies = [ "cfg-if", "libc", "wasi", + "windows-targets", ] [[package]] @@ -344,7 +351,7 @@ version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -367,20 +374,20 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.5" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" dependencies = [ - "libc", "rand_chacha", "rand_core", + "zerocopy 0.8.23", ] [[package]] name = "rand_chacha" -version = "0.3.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", "rand_core", @@ -388,9 +395,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.4" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ "getrandom", ] @@ -502,9 +509,76 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.13.3+wasi-0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winter-crypto" @@ -533,6 +607,15 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d47518e6931955dcac73a584cacb04550b82ab2f45c72880cbbbdbe13adb63c" +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -540,7 +623,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6" +dependencies = [ + "zerocopy-derive 0.8.23", ] [[package]] @@ -553,3 +645,14 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zerocopy-derive" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/fuzz/Cargo.toml b/miden-crypto-fuzz/Cargo.toml similarity index 81% rename from fuzz/Cargo.toml rename to miden-crypto-fuzz/Cargo.toml index eb8ee2c..0391298 100644 --- a/fuzz/Cargo.toml +++ b/miden-crypto-fuzz/Cargo.toml @@ -9,7 +9,7 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = "0.4" -miden-crypto = { path = "..", features = ["concurrent"] } +miden-crypto = { path = "../miden-crypto", features = ["concurrent"] } rand = { version = "0.9", default-features = false } [[bin]] diff --git a/fuzz/fuzz_targets/smt.rs b/miden-crypto-fuzz/fuzz_targets/smt.rs similarity index 95% rename from fuzz/fuzz_targets/smt.rs rename to miden-crypto-fuzz/fuzz_targets/smt.rs index 8304292..ad43221 100644 --- a/fuzz/fuzz_targets/smt.rs +++ b/miden-crypto-fuzz/fuzz_targets/smt.rs @@ -11,8 +11,8 @@ struct FuzzInput { impl FuzzInput { fn from_bytes(data: &[u8]) -> Self { - let mut rng = rand::thread_rng(); - let split_percentage = rng.gen_range(20..80); // Randomly choose between 20% and 80% + let mut rng = rand::rng(); + let split_percentage = rng.random_range(20..80); // Randomly choose between 20% and 80% let split_index = (data.len() * split_percentage) / 100; let (construction_data, update_data) = data.split_at(split_index); diff --git a/miden-crypto/Cargo.toml b/miden-crypto/Cargo.toml new file mode 100644 index 0000000..a77e66b --- /dev/null +++ b/miden-crypto/Cargo.toml @@ -0,0 +1,97 @@ +[package] +name = "miden-crypto" +version = "0.14.0" +description = "Miden Cryptographic primitives" +authors.workspace = true +readme = "README.md" +license.workspace = true +repository.workspace = true +documentation = "https://docs.rs/miden-crypto/0.14.0" +categories.workspace = true +keywords.workspace = true +edition.workspace = true +rust-version.workspace = true + +[[bin]] +name = "miden-crypto" +path = "src/main.rs" +bench = false +doctest = false +required-features = ["executable"] + +[[bench]] +name = "hash" +harness = false + +[[bench]] +name = "smt" +harness = false + +[[bench]] +name = "smt-subtree" +harness = false +required-features = ["internal"] + +[[bench]] +name = "merkle" +harness = false + +[[bench]] +name = "smt-with-entries" +harness = false + +[[bench]] +name = "store" +harness = false + +[features] +concurrent = ["dep:rayon", "hashbrown?/rayon"] +default = ["std", "concurrent"] +executable = ["dep:clap", "dep:rand-utils", "std"] +smt_hashmaps = ["dep:hashbrown"] +internal = [] +serde = ["dep:serde", "serde?/alloc", "winter-math/serde"] +std = [ + "blake3/std", + "dep:cc", + "rand/std", + "rand/thread_rng", + "winter-crypto/std", + "winter-math/std", + "winter-utils/std", +] + +[dependencies] +blake3 = { version = "1.5", default-features = false } +clap = { version = "4.5", optional = true, features = ["derive"] } +hashbrown = { version = "0.15", optional = true, features = ["serde"] } +num = { version = "0.4", default-features = false, features = ["alloc", "libm"] } +num-complex = { version = "0.4", default-features = false } +rand = { version = "0.9", default-features = false } +rand_core = { version = "0.9", default-features = false } +rand-utils = { version = "0.12", package = "winter-rand-utils", optional = true } +rayon = { version = "1.10", optional = true } +serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] } +sha3 = { version = "0.10", default-features = false } +thiserror = { version = "2.0", default-features = false } +winter-crypto = { version = "0.12", default-features = false } +winter-math = { version = "0.12", default-features = false } +winter-utils = { version = "0.12", default-features = false } + +[dev-dependencies] +assert_matches = { version = "1.5", default-features = false } +criterion = { version = "0.5", features = ["html_reports"] } +getrandom = { version = "0.3", default-features = false } +hex = { version = "0.4", default-features = false, features = ["alloc"] } +proptest = { version = "1.6", default-features = false, features = ["alloc"]} +rand_chacha = { version = "0.9", default-features = false } +rand-utils = { version = "0.12", package = "winter-rand-utils" } +seq-macro = { version = "0.3" } + +[build-dependencies] +cc = { version = "1.2", optional = true, features = ["parallel"] } +glob = "0.3" + +[lints.rust] +# Suppress warnings about `cfg(fuzzing)`, which is automatically set when using `cargo-fuzz`. +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] } \ No newline at end of file diff --git a/arch/arm64-sve/rpo/library.c b/miden-crypto/arch/arm64-sve/rpo/library.c similarity index 100% rename from arch/arm64-sve/rpo/library.c rename to miden-crypto/arch/arm64-sve/rpo/library.c diff --git a/arch/arm64-sve/rpo/library.h b/miden-crypto/arch/arm64-sve/rpo/library.h similarity index 100% rename from arch/arm64-sve/rpo/library.h rename to miden-crypto/arch/arm64-sve/rpo/library.h diff --git a/arch/arm64-sve/rpo/rpo_hash_128bit.h b/miden-crypto/arch/arm64-sve/rpo/rpo_hash_128bit.h similarity index 100% rename from arch/arm64-sve/rpo/rpo_hash_128bit.h rename to miden-crypto/arch/arm64-sve/rpo/rpo_hash_128bit.h diff --git a/arch/arm64-sve/rpo/rpo_hash_256bit.h b/miden-crypto/arch/arm64-sve/rpo/rpo_hash_256bit.h similarity index 100% rename from arch/arm64-sve/rpo/rpo_hash_256bit.h rename to miden-crypto/arch/arm64-sve/rpo/rpo_hash_256bit.h diff --git a/benches/README.md b/miden-crypto/benches/README.md similarity index 100% rename from benches/README.md rename to miden-crypto/benches/README.md diff --git a/benches/hash.rs b/miden-crypto/benches/hash.rs similarity index 100% rename from benches/hash.rs rename to miden-crypto/benches/hash.rs diff --git a/benches/merkle.rs b/miden-crypto/benches/merkle.rs similarity index 100% rename from benches/merkle.rs rename to miden-crypto/benches/merkle.rs diff --git a/benches/smt-subtree.rs b/miden-crypto/benches/smt-subtree.rs similarity index 100% rename from benches/smt-subtree.rs rename to miden-crypto/benches/smt-subtree.rs diff --git a/benches/smt-with-entries.rs b/miden-crypto/benches/smt-with-entries.rs similarity index 100% rename from benches/smt-with-entries.rs rename to miden-crypto/benches/smt-with-entries.rs diff --git a/benches/smt.rs b/miden-crypto/benches/smt.rs similarity index 100% rename from benches/smt.rs rename to miden-crypto/benches/smt.rs diff --git a/benches/store.rs b/miden-crypto/benches/store.rs similarity index 100% rename from benches/store.rs rename to miden-crypto/benches/store.rs diff --git a/build.rs b/miden-crypto/build.rs similarity index 100% rename from build.rs rename to miden-crypto/build.rs diff --git a/src/dsa/mod.rs b/miden-crypto/src/dsa/mod.rs similarity index 100% rename from src/dsa/mod.rs rename to miden-crypto/src/dsa/mod.rs diff --git a/src/dsa/rpo_falcon512/hash_to_point.rs b/miden-crypto/src/dsa/rpo_falcon512/hash_to_point.rs similarity index 100% rename from src/dsa/rpo_falcon512/hash_to_point.rs rename to miden-crypto/src/dsa/rpo_falcon512/hash_to_point.rs diff --git a/src/dsa/rpo_falcon512/keys/mod.rs b/miden-crypto/src/dsa/rpo_falcon512/keys/mod.rs similarity index 100% rename from src/dsa/rpo_falcon512/keys/mod.rs rename to miden-crypto/src/dsa/rpo_falcon512/keys/mod.rs diff --git a/src/dsa/rpo_falcon512/keys/public_key.rs b/miden-crypto/src/dsa/rpo_falcon512/keys/public_key.rs similarity index 100% rename from src/dsa/rpo_falcon512/keys/public_key.rs rename to miden-crypto/src/dsa/rpo_falcon512/keys/public_key.rs diff --git a/src/dsa/rpo_falcon512/keys/secret_key.rs b/miden-crypto/src/dsa/rpo_falcon512/keys/secret_key.rs similarity index 100% rename from src/dsa/rpo_falcon512/keys/secret_key.rs rename to miden-crypto/src/dsa/rpo_falcon512/keys/secret_key.rs diff --git a/src/dsa/rpo_falcon512/math/ffsampling.rs b/miden-crypto/src/dsa/rpo_falcon512/math/ffsampling.rs similarity index 100% rename from src/dsa/rpo_falcon512/math/ffsampling.rs rename to miden-crypto/src/dsa/rpo_falcon512/math/ffsampling.rs diff --git a/src/dsa/rpo_falcon512/math/fft.rs b/miden-crypto/src/dsa/rpo_falcon512/math/fft.rs similarity index 100% rename from src/dsa/rpo_falcon512/math/fft.rs rename to miden-crypto/src/dsa/rpo_falcon512/math/fft.rs diff --git a/src/dsa/rpo_falcon512/math/field.rs b/miden-crypto/src/dsa/rpo_falcon512/math/field.rs similarity index 100% rename from src/dsa/rpo_falcon512/math/field.rs rename to miden-crypto/src/dsa/rpo_falcon512/math/field.rs diff --git a/src/dsa/rpo_falcon512/math/mod.rs b/miden-crypto/src/dsa/rpo_falcon512/math/mod.rs similarity index 100% rename from src/dsa/rpo_falcon512/math/mod.rs rename to miden-crypto/src/dsa/rpo_falcon512/math/mod.rs diff --git a/src/dsa/rpo_falcon512/math/polynomial.rs b/miden-crypto/src/dsa/rpo_falcon512/math/polynomial.rs similarity index 100% rename from src/dsa/rpo_falcon512/math/polynomial.rs rename to miden-crypto/src/dsa/rpo_falcon512/math/polynomial.rs diff --git a/src/dsa/rpo_falcon512/math/samplerz.rs b/miden-crypto/src/dsa/rpo_falcon512/math/samplerz.rs similarity index 100% rename from src/dsa/rpo_falcon512/math/samplerz.rs rename to miden-crypto/src/dsa/rpo_falcon512/math/samplerz.rs diff --git a/src/dsa/rpo_falcon512/mod.rs b/miden-crypto/src/dsa/rpo_falcon512/mod.rs similarity index 100% rename from src/dsa/rpo_falcon512/mod.rs rename to miden-crypto/src/dsa/rpo_falcon512/mod.rs diff --git a/src/dsa/rpo_falcon512/signature.rs b/miden-crypto/src/dsa/rpo_falcon512/signature.rs similarity index 100% rename from src/dsa/rpo_falcon512/signature.rs rename to miden-crypto/src/dsa/rpo_falcon512/signature.rs diff --git a/src/hash/blake/mod.rs b/miden-crypto/src/hash/blake/mod.rs similarity index 100% rename from src/hash/blake/mod.rs rename to miden-crypto/src/hash/blake/mod.rs diff --git a/src/hash/blake/tests.rs b/miden-crypto/src/hash/blake/tests.rs similarity index 100% rename from src/hash/blake/tests.rs rename to miden-crypto/src/hash/blake/tests.rs diff --git a/src/hash/mod.rs b/miden-crypto/src/hash/mod.rs similarity index 100% rename from src/hash/mod.rs rename to miden-crypto/src/hash/mod.rs diff --git a/src/hash/rescue/arch/mod.rs b/miden-crypto/src/hash/rescue/arch/mod.rs similarity index 100% rename from src/hash/rescue/arch/mod.rs rename to miden-crypto/src/hash/rescue/arch/mod.rs diff --git a/src/hash/rescue/arch/x86_64_avx2.rs b/miden-crypto/src/hash/rescue/arch/x86_64_avx2.rs similarity index 100% rename from src/hash/rescue/arch/x86_64_avx2.rs rename to miden-crypto/src/hash/rescue/arch/x86_64_avx2.rs diff --git a/src/hash/rescue/mds/freq.rs b/miden-crypto/src/hash/rescue/mds/freq.rs similarity index 100% rename from src/hash/rescue/mds/freq.rs rename to miden-crypto/src/hash/rescue/mds/freq.rs diff --git a/src/hash/rescue/mds/mod.rs b/miden-crypto/src/hash/rescue/mds/mod.rs similarity index 100% rename from src/hash/rescue/mds/mod.rs rename to miden-crypto/src/hash/rescue/mds/mod.rs diff --git a/src/hash/rescue/mod.rs b/miden-crypto/src/hash/rescue/mod.rs similarity index 100% rename from src/hash/rescue/mod.rs rename to miden-crypto/src/hash/rescue/mod.rs diff --git a/src/hash/rescue/rpo/digest.rs b/miden-crypto/src/hash/rescue/rpo/digest.rs similarity index 100% rename from src/hash/rescue/rpo/digest.rs rename to miden-crypto/src/hash/rescue/rpo/digest.rs diff --git a/src/hash/rescue/rpo/mod.rs b/miden-crypto/src/hash/rescue/rpo/mod.rs similarity index 100% rename from src/hash/rescue/rpo/mod.rs rename to miden-crypto/src/hash/rescue/rpo/mod.rs diff --git a/src/hash/rescue/rpo/tests.rs b/miden-crypto/src/hash/rescue/rpo/tests.rs similarity index 100% rename from src/hash/rescue/rpo/tests.rs rename to miden-crypto/src/hash/rescue/rpo/tests.rs diff --git a/src/hash/rescue/rpx/digest.rs b/miden-crypto/src/hash/rescue/rpx/digest.rs similarity index 100% rename from src/hash/rescue/rpx/digest.rs rename to miden-crypto/src/hash/rescue/rpx/digest.rs diff --git a/src/hash/rescue/rpx/mod.rs b/miden-crypto/src/hash/rescue/rpx/mod.rs similarity index 100% rename from src/hash/rescue/rpx/mod.rs rename to miden-crypto/src/hash/rescue/rpx/mod.rs diff --git a/src/hash/rescue/rpx/tests.rs b/miden-crypto/src/hash/rescue/rpx/tests.rs similarity index 100% rename from src/hash/rescue/rpx/tests.rs rename to miden-crypto/src/hash/rescue/rpx/tests.rs diff --git a/src/hash/rescue/tests.rs b/miden-crypto/src/hash/rescue/tests.rs similarity index 100% rename from src/hash/rescue/tests.rs rename to miden-crypto/src/hash/rescue/tests.rs diff --git a/src/lib.rs b/miden-crypto/src/lib.rs similarity index 100% rename from src/lib.rs rename to miden-crypto/src/lib.rs diff --git a/src/main.rs b/miden-crypto/src/main.rs similarity index 100% rename from src/main.rs rename to miden-crypto/src/main.rs diff --git a/src/merkle/empty_roots.rs b/miden-crypto/src/merkle/empty_roots.rs similarity index 100% rename from src/merkle/empty_roots.rs rename to miden-crypto/src/merkle/empty_roots.rs diff --git a/src/merkle/error.rs b/miden-crypto/src/merkle/error.rs similarity index 100% rename from src/merkle/error.rs rename to miden-crypto/src/merkle/error.rs diff --git a/src/merkle/index.rs b/miden-crypto/src/merkle/index.rs similarity index 100% rename from src/merkle/index.rs rename to miden-crypto/src/merkle/index.rs diff --git a/src/merkle/merkle_tree.rs b/miden-crypto/src/merkle/merkle_tree.rs similarity index 100% rename from src/merkle/merkle_tree.rs rename to miden-crypto/src/merkle/merkle_tree.rs diff --git a/src/merkle/mmr/bit.rs b/miden-crypto/src/merkle/mmr/bit.rs similarity index 100% rename from src/merkle/mmr/bit.rs rename to miden-crypto/src/merkle/mmr/bit.rs diff --git a/src/merkle/mmr/delta.rs b/miden-crypto/src/merkle/mmr/delta.rs similarity index 100% rename from src/merkle/mmr/delta.rs rename to miden-crypto/src/merkle/mmr/delta.rs diff --git a/src/merkle/mmr/error.rs b/miden-crypto/src/merkle/mmr/error.rs similarity index 100% rename from src/merkle/mmr/error.rs rename to miden-crypto/src/merkle/mmr/error.rs diff --git a/src/merkle/mmr/full.rs b/miden-crypto/src/merkle/mmr/full.rs similarity index 100% rename from src/merkle/mmr/full.rs rename to miden-crypto/src/merkle/mmr/full.rs diff --git a/src/merkle/mmr/inorder.rs b/miden-crypto/src/merkle/mmr/inorder.rs similarity index 100% rename from src/merkle/mmr/inorder.rs rename to miden-crypto/src/merkle/mmr/inorder.rs diff --git a/src/merkle/mmr/mod.rs b/miden-crypto/src/merkle/mmr/mod.rs similarity index 100% rename from src/merkle/mmr/mod.rs rename to miden-crypto/src/merkle/mmr/mod.rs diff --git a/src/merkle/mmr/partial.rs b/miden-crypto/src/merkle/mmr/partial.rs similarity index 100% rename from src/merkle/mmr/partial.rs rename to miden-crypto/src/merkle/mmr/partial.rs diff --git a/src/merkle/mmr/peaks.rs b/miden-crypto/src/merkle/mmr/peaks.rs similarity index 100% rename from src/merkle/mmr/peaks.rs rename to miden-crypto/src/merkle/mmr/peaks.rs diff --git a/src/merkle/mmr/proof.rs b/miden-crypto/src/merkle/mmr/proof.rs similarity index 100% rename from src/merkle/mmr/proof.rs rename to miden-crypto/src/merkle/mmr/proof.rs diff --git a/src/merkle/mmr/tests.rs b/miden-crypto/src/merkle/mmr/tests.rs similarity index 100% rename from src/merkle/mmr/tests.rs rename to miden-crypto/src/merkle/mmr/tests.rs diff --git a/src/merkle/mod.rs b/miden-crypto/src/merkle/mod.rs similarity index 100% rename from src/merkle/mod.rs rename to miden-crypto/src/merkle/mod.rs diff --git a/src/merkle/node.rs b/miden-crypto/src/merkle/node.rs similarity index 100% rename from src/merkle/node.rs rename to miden-crypto/src/merkle/node.rs diff --git a/src/merkle/partial_mt/mod.rs b/miden-crypto/src/merkle/partial_mt/mod.rs similarity index 100% rename from src/merkle/partial_mt/mod.rs rename to miden-crypto/src/merkle/partial_mt/mod.rs diff --git a/src/merkle/partial_mt/tests.rs b/miden-crypto/src/merkle/partial_mt/tests.rs similarity index 100% rename from src/merkle/partial_mt/tests.rs rename to miden-crypto/src/merkle/partial_mt/tests.rs diff --git a/src/merkle/path.rs b/miden-crypto/src/merkle/path.rs similarity index 100% rename from src/merkle/path.rs rename to miden-crypto/src/merkle/path.rs diff --git a/src/merkle/smt/full/concurrent/mod.rs b/miden-crypto/src/merkle/smt/full/concurrent/mod.rs similarity index 100% rename from src/merkle/smt/full/concurrent/mod.rs rename to miden-crypto/src/merkle/smt/full/concurrent/mod.rs diff --git a/src/merkle/smt/full/concurrent/tests.rs b/miden-crypto/src/merkle/smt/full/concurrent/tests.rs similarity index 100% rename from src/merkle/smt/full/concurrent/tests.rs rename to miden-crypto/src/merkle/smt/full/concurrent/tests.rs diff --git a/src/merkle/smt/full/error.rs b/miden-crypto/src/merkle/smt/full/error.rs similarity index 100% rename from src/merkle/smt/full/error.rs rename to miden-crypto/src/merkle/smt/full/error.rs diff --git a/src/merkle/smt/full/leaf.rs b/miden-crypto/src/merkle/smt/full/leaf.rs similarity index 100% rename from src/merkle/smt/full/leaf.rs rename to miden-crypto/src/merkle/smt/full/leaf.rs diff --git a/src/merkle/smt/full/mod.rs b/miden-crypto/src/merkle/smt/full/mod.rs similarity index 100% rename from src/merkle/smt/full/mod.rs rename to miden-crypto/src/merkle/smt/full/mod.rs diff --git a/src/merkle/smt/full/proof.rs b/miden-crypto/src/merkle/smt/full/proof.rs similarity index 100% rename from src/merkle/smt/full/proof.rs rename to miden-crypto/src/merkle/smt/full/proof.rs diff --git a/src/merkle/smt/full/tests.rs b/miden-crypto/src/merkle/smt/full/tests.rs similarity index 100% rename from src/merkle/smt/full/tests.rs rename to miden-crypto/src/merkle/smt/full/tests.rs diff --git a/src/merkle/smt/mod.rs b/miden-crypto/src/merkle/smt/mod.rs similarity index 100% rename from src/merkle/smt/mod.rs rename to miden-crypto/src/merkle/smt/mod.rs diff --git a/src/merkle/smt/partial.rs b/miden-crypto/src/merkle/smt/partial.rs similarity index 100% rename from src/merkle/smt/partial.rs rename to miden-crypto/src/merkle/smt/partial.rs diff --git a/src/merkle/smt/simple/mod.rs b/miden-crypto/src/merkle/smt/simple/mod.rs similarity index 100% rename from src/merkle/smt/simple/mod.rs rename to miden-crypto/src/merkle/smt/simple/mod.rs diff --git a/src/merkle/smt/simple/tests.rs b/miden-crypto/src/merkle/smt/simple/tests.rs similarity index 100% rename from src/merkle/smt/simple/tests.rs rename to miden-crypto/src/merkle/smt/simple/tests.rs diff --git a/src/merkle/store/mod.rs b/miden-crypto/src/merkle/store/mod.rs similarity index 100% rename from src/merkle/store/mod.rs rename to miden-crypto/src/merkle/store/mod.rs diff --git a/src/merkle/store/tests.rs b/miden-crypto/src/merkle/store/tests.rs similarity index 100% rename from src/merkle/store/tests.rs rename to miden-crypto/src/merkle/store/tests.rs diff --git a/src/rand/mod.rs b/miden-crypto/src/rand/mod.rs similarity index 100% rename from src/rand/mod.rs rename to miden-crypto/src/rand/mod.rs diff --git a/src/rand/rpo.rs b/miden-crypto/src/rand/rpo.rs similarity index 100% rename from src/rand/rpo.rs rename to miden-crypto/src/rand/rpo.rs diff --git a/src/rand/rpx.rs b/miden-crypto/src/rand/rpx.rs similarity index 100% rename from src/rand/rpx.rs rename to miden-crypto/src/rand/rpx.rs diff --git a/src/utils/kv_map.rs b/miden-crypto/src/utils/kv_map.rs similarity index 100% rename from src/utils/kv_map.rs rename to miden-crypto/src/utils/kv_map.rs diff --git a/src/utils/mod.rs b/miden-crypto/src/utils/mod.rs similarity index 100% rename from src/utils/mod.rs rename to miden-crypto/src/utils/mod.rs From 660a667de14aaa5790b4866b73afd9ccf6e2be27 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Sat, 15 Mar 2025 17:33:57 -0700 Subject: [PATCH 08/16] chore: update changelog --- config.toml => .cargo/config.toml | 0 .gitmodules | 0 CHANGELOG.md | 4 +- Cargo.lock | 149 ++++++++++++------------------ miden-crypto/Cargo.toml | 4 +- 5 files changed, 65 insertions(+), 92 deletions(-) rename config.toml => .cargo/config.toml (100%) delete mode 100644 .gitmodules diff --git a/config.toml b/.cargo/config.toml similarity index 100% rename from config.toml rename to .cargo/config.toml diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e69de29..0000000 diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c8f41d..549dc7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ -## 0.14.0 (TBD) +## 0.14.0 (2025-03-15) -- Removed duplicated check in RpoFalcon512 verification (#368). - Added parallel implementation of `Smt::compute_mutations` with better performance (#365). - Implemented parallel leaf hashing in `Smt::process_sorted_pairs_to_leaves` (#365). +- Removed duplicated check in RpoFalcon512 verification (#368). - [BREAKING] Updated Winterfell dependency to v0.12 (#374). - Added debug-only duplicate column check in `build_subtree` (#378). - Filter out empty values in concurrent version of `Smt::with_entries` to fix a panic (#383). diff --git a/Cargo.lock b/Cargo.lock index 11a55d6..1f3068a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -99,15 +99,15 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bitflags" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "blake3" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1230237285e3e10cde447185e8975408ae24deaa67205ce684805c25bc0c7937" +checksum = "675f87afced0413c9bb02843499dbbd3882a237645883f71a2b59644a6d2f753" dependencies = [ "arrayref", "arrayvec", @@ -131,12 +131,6 @@ version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "cast" version = "0.3.0" @@ -145,9 +139,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.14" +version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" dependencies = [ "jobserver", "libc", @@ -189,9 +183,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.30" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d" +checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" dependencies = [ "clap_builder", "clap_derive", @@ -199,9 +193,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.30" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c" +checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" dependencies = [ "anstream", "anstyle", @@ -211,9 +205,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.28" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck", "proc-macro2", @@ -337,9 +331,9 @@ dependencies = [ [[package]] name = "either" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "equivalent" @@ -349,9 +343,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "foldhash" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "generic-array" @@ -394,9 +388,9 @@ checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "half" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +checksum = "7db2ff139bba50379da6aa0766b52fdcb62cb5b263009b09ed58ba604e14bbd1" dependencies = [ "cfg-if", "crunchy", @@ -423,9 +417,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e" [[package]] name = "hex" @@ -435,9 +429,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "is-terminal" -version = "0.4.15" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi", "libc", @@ -461,9 +455,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jobserver" @@ -495,9 +489,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.169" +version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "libm" @@ -507,9 +501,9 @@ checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "log" -version = "0.4.25" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "memchr" @@ -623,15 +617,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.3" +version = "1.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" [[package]] name = "oorandom" -version = "11.1.4" +version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "plotters" @@ -663,18 +657,18 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.7.35", + "zerocopy", ] [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] @@ -695,9 +689,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -721,7 +715,7 @@ checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", - "zerocopy 0.8.23", + "zerocopy", ] [[package]] @@ -822,15 +816,15 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustversion" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "ryu" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" @@ -843,24 +837,24 @@ dependencies = [ [[package]] name = "seq-macro" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" +checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc" [[package]] name = "serde" -version = "1.0.217" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", @@ -869,9 +863,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.138" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", @@ -903,9 +897,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.98" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -914,18 +908,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", @@ -956,9 +950,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "utf8parse" @@ -1194,34 +1188,13 @@ dependencies = [ "bitflags", ] -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive 0.7.35", -] - [[package]] name = "zerocopy" version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6" dependencies = [ - "zerocopy-derive 0.8.23", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "zerocopy-derive", ] [[package]] diff --git a/miden-crypto/Cargo.toml b/miden-crypto/Cargo.toml index a77e66b..72f8d68 100644 --- a/miden-crypto/Cargo.toml +++ b/miden-crypto/Cargo.toml @@ -62,7 +62,7 @@ std = [ ] [dependencies] -blake3 = { version = "1.5", default-features = false } +blake3 = { version = "1.6", default-features = false } clap = { version = "4.5", optional = true, features = ["derive"] } hashbrown = { version = "0.15", optional = true, features = ["serde"] } num = { version = "0.4", default-features = false, features = ["alloc", "libm"] } @@ -94,4 +94,4 @@ glob = "0.3" [lints.rust] # Suppress warnings about `cfg(fuzzing)`, which is automatically set when using `cargo-fuzz`. -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] } \ No newline at end of file +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] } From 78e32a3824e77d3612ef73e8f8c8f39f560526df Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Sat, 15 Mar 2025 17:41:08 -0700 Subject: [PATCH 09/16] feat: added naive implementation of Smt::num_entries() --- miden-crypto/src/merkle/mmr/tests.rs | 14 +++++----- miden-crypto/src/merkle/partial_mt/mod.rs | 11 +++----- miden-crypto/src/merkle/partial_mt/tests.rs | 11 +++----- miden-crypto/src/merkle/smt/full/mod.rs | 14 ++++++++++ miden-crypto/src/merkle/smt/full/tests.rs | 30 ++++++++++++--------- 5 files changed, 46 insertions(+), 34 deletions(-) diff --git a/miden-crypto/src/merkle/mmr/tests.rs b/miden-crypto/src/merkle/mmr/tests.rs index e47a7c3..af892f4 100644 --- a/miden-crypto/src/merkle/mmr/tests.rs +++ b/miden-crypto/src/merkle/mmr/tests.rs @@ -497,14 +497,12 @@ fn test_bit_position_iterator() { assert_eq!(TrueBitPositionIterator::new(3).collect::>(), vec![0, 1],); assert_eq!(TrueBitPositionIterator::new(3).rev().collect::>(), vec![1, 0],); - assert_eq!( - TrueBitPositionIterator::new(0b11010101).collect::>(), - vec![0, 2, 4, 6, 7], - ); - assert_eq!( - TrueBitPositionIterator::new(0b11010101).rev().collect::>(), - vec![7, 6, 4, 2, 0], - ); + assert_eq!(TrueBitPositionIterator::new(0b11010101).collect::>(), vec![ + 0, 2, 4, 6, 7 + ],); + assert_eq!(TrueBitPositionIterator::new(0b11010101).rev().collect::>(), vec![ + 7, 6, 4, 2, 0 + ],); } #[test] diff --git a/miden-crypto/src/merkle/partial_mt/mod.rs b/miden-crypto/src/merkle/partial_mt/mod.rs index be7e94c..6698ba9 100644 --- a/miden-crypto/src/merkle/partial_mt/mod.rs +++ b/miden-crypto/src/merkle/partial_mt/mod.rs @@ -200,13 +200,10 @@ impl PartialMerkleTree { pub fn to_paths(&self) -> Vec<(NodeIndex, ValuePath)> { let mut paths = Vec::new(); self.leaves.iter().for_each(|&leaf| { - paths.push(( - leaf, - ValuePath { - value: self.get_node(leaf).expect("Failed to get leaf node"), - path: self.get_path(leaf).expect("Failed to get path"), - }, - )); + paths.push((leaf, ValuePath { + value: self.get_node(leaf).expect("Failed to get leaf node"), + path: self.get_path(leaf).expect("Failed to get path"), + })); }); paths } diff --git a/miden-crypto/src/merkle/partial_mt/tests.rs b/miden-crypto/src/merkle/partial_mt/tests.rs index 6723ba1..0f57ab5 100644 --- a/miden-crypto/src/merkle/partial_mt/tests.rs +++ b/miden-crypto/src/merkle/partial_mt/tests.rs @@ -215,13 +215,10 @@ fn get_paths() { let expected_paths: Vec<(NodeIndex, ValuePath)> = leaves .iter() .map(|&leaf| { - ( - leaf, - ValuePath { - value: mt.get_node(leaf).unwrap(), - path: mt.get_path(leaf).unwrap(), - }, - ) + (leaf, ValuePath { + value: mt.get_node(leaf).unwrap(), + path: mt.get_path(leaf).unwrap(), + }) }) .collect(); diff --git a/miden-crypto/src/merkle/smt/full/mod.rs b/miden-crypto/src/merkle/smt/full/mod.rs index a32a525..913d56a 100644 --- a/miden-crypto/src/merkle/smt/full/mod.rs +++ b/miden-crypto/src/merkle/smt/full/mod.rs @@ -159,10 +159,24 @@ impl Smt { } /// Returns the number of non-empty leaves in this tree. + /// + /// Note that this may return a different value from [Self::num_entries()] as a single leaf may + /// contain more than one key-value pair. pub fn num_leaves(&self) -> usize { self.leaves.len() } + /// Returns the number of key-value pairs with non-default values in this tree. + /// + /// Note that this may return a different value from [Self::num_leaves()] as a single leaf may + /// contain more than one key-value pair. + /// + /// Also note that this is currently an expensive operation is counting the number of entries + /// requires iterating over all leaves of the tree. + pub fn num_entries(&self) -> usize { + self.entries().count() + } + /// Returns the leaf to which `key` maps pub fn get_leaf(&self, key: &RpoDigest) -> SmtLeaf { >::get_leaf(self, key) diff --git a/miden-crypto/src/merkle/smt/full/tests.rs b/miden-crypto/src/merkle/smt/full/tests.rs index d793a27..d050c40 100644 --- a/miden-crypto/src/merkle/smt/full/tests.rs +++ b/miden-crypto/src/merkle/smt/full/tests.rs @@ -661,10 +661,12 @@ fn test_empty_smt_leaf_serialization() { #[test] fn test_single_smt_leaf_serialization() { - let single_leaf = SmtLeaf::new_single( - RpoDigest::from([10_u32, 11_u32, 12_u32, 13_u32]), - [1_u32.into(), 2_u32.into(), 3_u32.into(), 4_u32.into()], - ); + let single_leaf = SmtLeaf::new_single(RpoDigest::from([10_u32, 11_u32, 12_u32, 13_u32]), [ + 1_u32.into(), + 2_u32.into(), + 3_u32.into(), + 4_u32.into(), + ]); let mut serialized = single_leaf.to_bytes(); // extend buffer with random bytes @@ -677,14 +679,18 @@ fn test_single_smt_leaf_serialization() { #[test] fn test_multiple_smt_leaf_serialization_success() { let multiple_leaf = SmtLeaf::new_multiple(vec![ - ( - RpoDigest::from([10_u32, 11_u32, 12_u32, 13_u32]), - [1_u32.into(), 2_u32.into(), 3_u32.into(), 4_u32.into()], - ), - ( - RpoDigest::from([100_u32, 101_u32, 102_u32, 13_u32]), - [11_u32.into(), 12_u32.into(), 13_u32.into(), 14_u32.into()], - ), + (RpoDigest::from([10_u32, 11_u32, 12_u32, 13_u32]), [ + 1_u32.into(), + 2_u32.into(), + 3_u32.into(), + 4_u32.into(), + ]), + (RpoDigest::from([100_u32, 101_u32, 102_u32, 13_u32]), [ + 11_u32.into(), + 12_u32.into(), + 13_u32.into(), + 14_u32.into(), + ]), ]) .unwrap(); From 51dbc61583c272a7ea9bc42b2ac185db68733d77 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Sat, 15 Mar 2025 17:49:02 -0700 Subject: [PATCH 10/16] chore: fix lints --- miden-crypto/src/merkle/mmr/tests.rs | 14 +++++----- miden-crypto/src/merkle/partial_mt/mod.rs | 11 +++++--- miden-crypto/src/merkle/partial_mt/tests.rs | 11 +++++--- miden-crypto/src/merkle/smt/full/tests.rs | 30 +++++++++------------ 4 files changed, 34 insertions(+), 32 deletions(-) diff --git a/miden-crypto/src/merkle/mmr/tests.rs b/miden-crypto/src/merkle/mmr/tests.rs index af892f4..e47a7c3 100644 --- a/miden-crypto/src/merkle/mmr/tests.rs +++ b/miden-crypto/src/merkle/mmr/tests.rs @@ -497,12 +497,14 @@ fn test_bit_position_iterator() { assert_eq!(TrueBitPositionIterator::new(3).collect::>(), vec![0, 1],); assert_eq!(TrueBitPositionIterator::new(3).rev().collect::>(), vec![1, 0],); - assert_eq!(TrueBitPositionIterator::new(0b11010101).collect::>(), vec![ - 0, 2, 4, 6, 7 - ],); - assert_eq!(TrueBitPositionIterator::new(0b11010101).rev().collect::>(), vec![ - 7, 6, 4, 2, 0 - ],); + assert_eq!( + TrueBitPositionIterator::new(0b11010101).collect::>(), + vec![0, 2, 4, 6, 7], + ); + assert_eq!( + TrueBitPositionIterator::new(0b11010101).rev().collect::>(), + vec![7, 6, 4, 2, 0], + ); } #[test] diff --git a/miden-crypto/src/merkle/partial_mt/mod.rs b/miden-crypto/src/merkle/partial_mt/mod.rs index 6698ba9..be7e94c 100644 --- a/miden-crypto/src/merkle/partial_mt/mod.rs +++ b/miden-crypto/src/merkle/partial_mt/mod.rs @@ -200,10 +200,13 @@ impl PartialMerkleTree { pub fn to_paths(&self) -> Vec<(NodeIndex, ValuePath)> { let mut paths = Vec::new(); self.leaves.iter().for_each(|&leaf| { - paths.push((leaf, ValuePath { - value: self.get_node(leaf).expect("Failed to get leaf node"), - path: self.get_path(leaf).expect("Failed to get path"), - })); + paths.push(( + leaf, + ValuePath { + value: self.get_node(leaf).expect("Failed to get leaf node"), + path: self.get_path(leaf).expect("Failed to get path"), + }, + )); }); paths } diff --git a/miden-crypto/src/merkle/partial_mt/tests.rs b/miden-crypto/src/merkle/partial_mt/tests.rs index 0f57ab5..6723ba1 100644 --- a/miden-crypto/src/merkle/partial_mt/tests.rs +++ b/miden-crypto/src/merkle/partial_mt/tests.rs @@ -215,10 +215,13 @@ fn get_paths() { let expected_paths: Vec<(NodeIndex, ValuePath)> = leaves .iter() .map(|&leaf| { - (leaf, ValuePath { - value: mt.get_node(leaf).unwrap(), - path: mt.get_path(leaf).unwrap(), - }) + ( + leaf, + ValuePath { + value: mt.get_node(leaf).unwrap(), + path: mt.get_path(leaf).unwrap(), + }, + ) }) .collect(); diff --git a/miden-crypto/src/merkle/smt/full/tests.rs b/miden-crypto/src/merkle/smt/full/tests.rs index d050c40..d793a27 100644 --- a/miden-crypto/src/merkle/smt/full/tests.rs +++ b/miden-crypto/src/merkle/smt/full/tests.rs @@ -661,12 +661,10 @@ fn test_empty_smt_leaf_serialization() { #[test] fn test_single_smt_leaf_serialization() { - let single_leaf = SmtLeaf::new_single(RpoDigest::from([10_u32, 11_u32, 12_u32, 13_u32]), [ - 1_u32.into(), - 2_u32.into(), - 3_u32.into(), - 4_u32.into(), - ]); + let single_leaf = SmtLeaf::new_single( + RpoDigest::from([10_u32, 11_u32, 12_u32, 13_u32]), + [1_u32.into(), 2_u32.into(), 3_u32.into(), 4_u32.into()], + ); let mut serialized = single_leaf.to_bytes(); // extend buffer with random bytes @@ -679,18 +677,14 @@ fn test_single_smt_leaf_serialization() { #[test] fn test_multiple_smt_leaf_serialization_success() { let multiple_leaf = SmtLeaf::new_multiple(vec![ - (RpoDigest::from([10_u32, 11_u32, 12_u32, 13_u32]), [ - 1_u32.into(), - 2_u32.into(), - 3_u32.into(), - 4_u32.into(), - ]), - (RpoDigest::from([100_u32, 101_u32, 102_u32, 13_u32]), [ - 11_u32.into(), - 12_u32.into(), - 13_u32.into(), - 14_u32.into(), - ]), + ( + RpoDigest::from([10_u32, 11_u32, 12_u32, 13_u32]), + [1_u32.into(), 2_u32.into(), 3_u32.into(), 4_u32.into()], + ), + ( + RpoDigest::from([100_u32, 101_u32, 102_u32, 13_u32]), + [11_u32.into(), 12_u32.into(), 13_u32.into(), 14_u32.into()], + ), ]) .unwrap(); From 78672585f1444cb9366071b5625b50bc20fe3b15 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Sat, 15 Mar 2025 18:30:53 -0700 Subject: [PATCH 11/16] chore: update crate version to 0.15.0 --- CHANGELOG.md | 3 +++ Cargo.lock | 2 +- miden-crypto/Cargo.toml | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 549dc7b..43e5161 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.15.0 (TBD) + + ## 0.14.0 (2025-03-15) - Added parallel implementation of `Smt::compute_mutations` with better performance (#365). diff --git a/Cargo.lock b/Cargo.lock index 1f3068a..8190bc4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -513,7 +513,7 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miden-crypto" -version = "0.14.0" +version = "0.15.0" dependencies = [ "assert_matches", "blake3", diff --git a/miden-crypto/Cargo.toml b/miden-crypto/Cargo.toml index 72f8d68..b6ea545 100644 --- a/miden-crypto/Cargo.toml +++ b/miden-crypto/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "miden-crypto" -version = "0.14.0" +version = "0.15.0" description = "Miden Cryptographic primitives" authors.workspace = true readme = "README.md" license.workspace = true repository.workspace = true -documentation = "https://docs.rs/miden-crypto/0.14.0" +documentation = "https://docs.rs/miden-crypto/0.15.0" categories.workspace = true keywords.workspace = true edition.workspace = true From 5a774b383e271a2a87711fc9dfcff770109301eb Mon Sep 17 00:00:00 2001 From: Qyriad Date: Tue, 25 Feb 2025 21:02:08 +0100 Subject: [PATCH 12/16] smt: factor out MerklePath logic --- miden-crypto/src/merkle/smt/mod.rs | 36 +++++++++++++++++------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/miden-crypto/src/merkle/smt/mod.rs b/miden-crypto/src/merkle/smt/mod.rs index 03f1bed..d463dd6 100644 --- a/miden-crypto/src/merkle/smt/mod.rs +++ b/miden-crypto/src/merkle/smt/mod.rs @@ -79,28 +79,32 @@ pub(crate) trait SparseMerkleTree { // PROVIDED METHODS // --------------------------------------------------------------------------------------------- - /// Returns an opening of the leaf associated with `key`. Conceptually, an opening is a Merkle - /// path to the leaf, as well as the leaf itself. - fn open(&self, key: &Self::Key) -> Self::Opening { - let leaf = self.get_leaf(key); - + /// Returns a [MerklePath] to the specified key. + /// + /// Mostly this is an implementation detail of [`Self::open()`]. + fn path(&self, key: &Self::Key) -> MerklePath { let mut index: NodeIndex = { let leaf_index: LeafIndex = Self::key_to_leaf_index(key); leaf_index.into() }; - let merkle_path = { - let mut path = Vec::with_capacity(index.depth() as usize); - for _ in 0..index.depth() { - let is_right = index.is_value_odd(); - index.move_up(); - let InnerNode { left, right } = self.get_inner_node(index); - let value = if is_right { left } else { right }; - path.push(value); - } + let mut path = Vec::with_capacity(index.depth() as usize); + for _ in 0..index.depth() { + let is_right = index.is_value_odd(); + index.move_up(); + let InnerNode { left, right } = self.get_inner_node(index); + let value = if is_right { left } else { right }; + path.push(value); + } - MerklePath::new(path) - }; + MerklePath::new(path) + } + + /// Returns an opening of the leaf associated with `key`. Conceptually, an opening is a Merkle + /// path to the leaf, as well as the leaf itself. + fn open(&self, key: &Self::Key) -> Self::Opening { + let leaf = self.get_leaf(key); + let merkle_path = self.path(key); Self::path_and_leaf_to_opening(merkle_path, leaf) } From 05ff3f034b93acbbd6fce4c94f3643175be48c0d Mon Sep 17 00:00:00 2001 From: Qyriad Date: Thu, 27 Feb 2025 17:26:28 +0100 Subject: [PATCH 13/16] smt: add SparseMerklePath --- CHANGELOG.md | 1 + miden-crypto/src/merkle/mod.rs | 3 + miden-crypto/src/merkle/sparse_path.rs | 115 +++++++++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 miden-crypto/src/merkle/sparse_path.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 43e5161..48247a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - Optimized duplicate key detection in `Smt::with_entries_concurrent` (#395). - [BREAKING] Moved `rand` to version `0.9` removing the `try_fill_bytes` method (#398). - [BREAKING] Increment minimum supported Rust version to 1.85 (#399). +- Added `SparseMerklePath`, a compact representation of `MerklePath` which compacts empty nodes into a bitmask (#389). ## 0.13.3 (2025-02-18) diff --git a/miden-crypto/src/merkle/mod.rs b/miden-crypto/src/merkle/mod.rs index 509de41..7cc741f 100644 --- a/miden-crypto/src/merkle/mod.rs +++ b/miden-crypto/src/merkle/mod.rs @@ -20,6 +20,9 @@ pub use merkle_tree::{MerkleTree, path_to_text, tree_to_text}; mod path; pub use path::{MerklePath, RootPath, ValuePath}; +mod sparse_path; +pub use sparse_path::SparseMerklePath; + mod smt; pub use smt::{ InnerNode, LeafIndex, MutationSet, NodeMutation, PartialSmt, SMT_DEPTH, SMT_MAX_DEPTH, diff --git a/miden-crypto/src/merkle/sparse_path.rs b/miden-crypto/src/merkle/sparse_path.rs new file mode 100644 index 0000000..765dc05 --- /dev/null +++ b/miden-crypto/src/merkle/sparse_path.rs @@ -0,0 +1,115 @@ +use alloc::vec::Vec; +use core::iter; + +use super::{EmptySubtreeRoots, MerklePath, RpoDigest, SMT_MAX_DEPTH}; + +/// A different representation of [`MerklePath`] designed for memory efficiency for Merkle paths +/// with empty nodes. +/// +/// Empty nodes in the path are stored only as their position, represented with a bitmask. A +/// maximum of 64 nodes in the path can be empty. The number of empty nodes has no effect on memory +/// usage by this struct, but will incur overhead during iteration or conversion to a +/// [`MerklePath`], for each empty node. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub struct SparseMerklePath { + /// A bitmask representing empty nodes. The set bit corresponds to the depth of an empty node. + empty_nodes: u64, + /// The non-empty nodes, stored in depth-order, but not contiguous across depth. + nodes: Vec, +} + +impl SparseMerklePath { + /// Converts a Merkle path to a sparse representation. + /// + /// Returns `None` if `path` contains more elements than we can represent ([`SMT_MAX_DEPTH`]). + pub fn from_path(tree_depth: u8, path: MerklePath) -> Option { + // Note that the path does not include the node itself that it is a path to. + // That is to say, the path is not inclusive of its ending. + + if path.len() > SMT_MAX_DEPTH.into() { + return None; + } + let path_depth: u8 = path.len().try_into().unwrap(); + + let mut nodes: Vec = Default::default(); + let mut empty_nodes: u64 = 0; + + for (depth, node) in iter::zip(0..path_depth, path) { + let &equivalent_empty_node = EmptySubtreeRoots::entry(tree_depth, depth); + if node == equivalent_empty_node { + // FIXME: should we just fallback to the Vec if we're out of bits? + assert!(depth < 64, "SparseMerklePath may have at most 64 empty nodes"); + empty_nodes |= u64::checked_shl(1, depth.into()).unwrap(); + } else { + nodes.push(node); + } + } + + Some(Self { empty_nodes, nodes }) + } + + /// Converts this sparse representation back to a normal [`MerklePath`]. + pub fn into_path(mut self, tree_depth: u8) -> MerklePath { + let path_depth = self.depth(); + let mut nodes: Vec = Default::default(); + let mut sparse_nodes = self.nodes.drain(..); + + for depth in 0..path_depth { + let empty_bit = u64::checked_shl(1, depth.into()).unwrap(); + let is_empty = (self.empty_nodes & empty_bit) != 0; + if is_empty { + let &equivalent_empty_node = EmptySubtreeRoots::entry(tree_depth, depth); + nodes.push(equivalent_empty_node); + } else { + nodes.push(sparse_nodes.next().unwrap()); + } + } + + debug_assert_eq!(sparse_nodes.next(), None); + drop(sparse_nodes); + + debug_assert!(self.nodes.is_empty()); + + MerklePath::from(nodes) + } + + pub fn depth(&self) -> u8 { + (self.nodes.len() + self.empty_nodes.count_ones() as usize) as u8 + } +} + +#[cfg(test)] +mod tests { + use alloc::vec::Vec; + + use super::SparseMerklePath; + use crate::{ + hash::rpo::RpoDigest, + merkle::{smt::SparseMerkleTree, Smt, SMT_DEPTH}, + Felt, Word, ONE, + }; + + #[test] + fn roundtrip() { + let pair_count: u64 = 8192; + let entries: Vec<(RpoDigest, Word)> = (0..pair_count) + .map(|n| { + let leaf_index = ((n as f64 / pair_count as f64) * 255.0) as u64; + let key = RpoDigest::new([ONE, ONE, Felt::new(n), Felt::new(leaf_index)]); + let value = [ONE, ONE, ONE, ONE]; + (key, value) + }) + .collect(); + let tree = Smt::with_entries(entries).unwrap(); + + for (key, _value) in tree.entries() { + let control_path = tree.path(key); + let sparse_path = SparseMerklePath::from_path(SMT_DEPTH, control_path.clone()).unwrap(); + assert_eq!(control_path.depth(), sparse_path.depth()); + let test_path = sparse_path.into_path(SMT_DEPTH); + + assert_eq!(control_path, test_path); + } + } +} From 7cfd0700ce39c98ecdd258354358dad60b8a4db8 Mon Sep 17 00:00:00 2001 From: Qyriad Date: Fri, 21 Mar 2025 13:43:43 +0100 Subject: [PATCH 14/16] SparseMerklePath: implement random access --- miden-crypto/src/merkle/sparse_path.rs | 67 ++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/miden-crypto/src/merkle/sparse_path.rs b/miden-crypto/src/merkle/sparse_path.rs index 765dc05..cb97903 100644 --- a/miden-crypto/src/merkle/sparse_path.rs +++ b/miden-crypto/src/merkle/sparse_path.rs @@ -77,6 +77,44 @@ impl SparseMerklePath { pub fn depth(&self) -> u8 { (self.nodes.len() + self.empty_nodes.count_ones() as usize) as u8 } + + /// Get a specific node in this path at a given depth. + /// + /// # Panics + /// With debug assertions enabled, this function panics if `node_depth` is greater than + /// `tree_depth` (as it is impossible to have a node of greater depth than the tree it is + /// contained in). + pub fn get(&self, tree_depth: u8, node_depth: u8) -> Option { + if node_depth == tree_depth || node_depth > self.depth() { + return None; + } + + debug_assert!( + tree_depth >= node_depth, + "tree depth {tree_depth} must be greater than node depth {node_depth}", + ); + + let empty_bit = u64::checked_shl(1, node_depth.into()).unwrap(); + let is_empty = (self.empty_nodes & empty_bit) != 0; + + if is_empty { + return Some(*EmptySubtreeRoots::entry(tree_depth, node_depth)); + } + + // Our index needs to account for all the empty nodes that aren't in `self.nodes`. + let nonempty_index: usize = { + // TODO: this could also be u64::unbounded_shl(1, node_depth + 1).wrapping_sub(1). + // We should check if that has any performance benefits over using 128-bit integers. + let mask: u64 = ((1u128 << (node_depth + 1)) - 1u128).try_into().unwrap(); + + let empty_before = u64::count_ones(self.empty_nodes & mask); + u64::checked_sub(node_depth as u64, empty_before as u64) + .unwrap() + .try_into() + .unwrap() + }; + Some(self.nodes[nonempty_index]) + } } #[cfg(test)] @@ -90,6 +128,19 @@ mod tests { Felt, Word, ONE, }; + fn make_smt(pair_count: u64) -> Smt { + let entries: Vec<(RpoDigest, Word)> = (0..pair_count) + .map(|n| { + let leaf_index = ((n as f64 / pair_count as f64) * 255.0) as u64; + let key = RpoDigest::new([ONE, ONE, Felt::new(n), Felt::new(leaf_index)]); + let value = [ONE, ONE, ONE, ONE]; + (key, value) + }) + .collect(); + + Smt::with_entries(entries).unwrap() + } + #[test] fn roundtrip() { let pair_count: u64 = 8192; @@ -112,4 +163,20 @@ mod tests { assert_eq!(control_path, test_path); } } + + #[test] + fn random_access() { + let tree = make_smt(8192); + + for (i, (key, _value)) in tree.entries().enumerate() { + let control_path = tree.path(key); + let sparse_path = SparseMerklePath::from_path(SMT_DEPTH, control_path.clone()).unwrap(); + assert_eq!(control_path.depth(), sparse_path.depth()); + + for (depth, control_node) in control_path.iter().enumerate() { + let test_node = sparse_path.get(SMT_DEPTH, depth as u8).unwrap(); + assert_eq!(*control_node, test_node, "at depth {depth} for entry {i}"); + } + } + } } From 8c5a33f203d7ac7d50fbcd05ffe225cd70076864 Mon Sep 17 00:00:00 2001 From: Qyriad Date: Fri, 21 Mar 2025 13:59:36 +0100 Subject: [PATCH 15/16] SparseMerklePath: implement iterators --- miden-crypto/src/merkle/sparse_path.rs | 106 +++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/miden-crypto/src/merkle/sparse_path.rs b/miden-crypto/src/merkle/sparse_path.rs index cb97903..dad8cb7 100644 --- a/miden-crypto/src/merkle/sparse_path.rs +++ b/miden-crypto/src/merkle/sparse_path.rs @@ -117,6 +117,87 @@ impl SparseMerklePath { } } +// ITERATORS +// ================================================================================================ + +impl IntoIterator for SparseMerklePath { + type Item = ::Item; + type IntoIter = SparseMerkleIter; + + fn into_iter(self) -> SparseMerkleIter { + let tree_depth = self.depth(); + SparseMerkleIter { + path: self, + next_depth: Some(0), + tree_depth, + } + } +} + +/// Owning iterator for [`SparseMerklePath`]. +// TODO: add a non-owning iterator too. +pub struct SparseMerkleIter { + /// The "inner" value we're iterating over. + path: SparseMerklePath, + + /// The depth a `next()` call will get. It will only be None if someone calls `next_back()` at + /// depth 0, to indicate that all further `next_back()` calls must also return `None`. + next_depth: Option, + + /// "Cached" value of `path.depth()`. + tree_depth: u8, +} + +impl Iterator for SparseMerkleIter { + type Item = RpoDigest; + + fn next(&mut self) -> Option { + // If `next_depth` is None, then someone called `next_back()` at depth 0. + let next_depth = self.next_depth.unwrap_or(0); + if next_depth > self.tree_depth { + return None; + } + + match self.path.get(self.tree_depth, next_depth) { + Some(node) => { + self.next_depth = Some(next_depth + 1); + Some(node) + }, + None => None, + } + } + + // SparseMerkleIter always knows its exact size. + fn size_hint(&self) -> (usize, Option) { + let next_depth = self.next_depth.unwrap_or(0); + let len: usize = self.path.depth().into(); + let remaining = len - next_depth as usize; + (remaining, Some(remaining)) + } +} + +impl ExactSizeIterator for SparseMerkleIter { + fn len(&self) -> usize { + let next_depth = self.next_depth.unwrap_or(0); + (self.path.depth() - next_depth) as usize + } +} + +impl DoubleEndedIterator for SparseMerkleIter { + fn next_back(&mut self) -> Option { + // While `next_depth` is None, all calls to `next_back()` also return `None`. + let next_depth = self.next_depth?; + + match self.path.get(self.tree_depth, next_depth) { + Some(node) => { + self.next_depth = if next_depth == 0 { None } else { Some(next_depth - 1) }; + Some(node) + }, + None => None, + } + } +} + #[cfg(test)] mod tests { use alloc::vec::Vec; @@ -179,4 +260,29 @@ mod tests { } } } + + #[test] + fn iterator() { + let tree = make_smt(8192); + + for (i, (key, _value)) in tree.entries().enumerate() { + let path = tree.path(key); + let sparse_path = SparseMerklePath::from_path(SMT_DEPTH, path.clone()).unwrap(); + assert_eq!(path.depth(), sparse_path.depth()); + assert_eq!(sparse_path.depth(), SMT_DEPTH); + for (depth, iter_node) in sparse_path.clone().into_iter().enumerate() { + let control_node = sparse_path.get(SMT_DEPTH, depth as u8).unwrap(); + assert_eq!(control_node, iter_node, "at depth {depth} for entry {i}"); + } + + let iter = sparse_path.clone().into_iter().enumerate().rev().skip(1); + for (depth, iter_node) in iter { + let control_node = sparse_path.get(SMT_DEPTH, depth as u8).unwrap(); + assert_eq!( + control_node, iter_node, + "at depth {depth} for entry {i} during reverse-iteration", + ); + } + } + } } From e53b2cf23f7c62c40b9a1317d4ca52be3928f457 Mon Sep 17 00:00:00 2001 From: Qyriad Date: Fri, 21 Mar 2025 14:07:54 +0100 Subject: [PATCH 16/16] SparseMerklePath: implement Serializable and Deserializable --- miden-crypto/src/merkle/sparse_path.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/miden-crypto/src/merkle/sparse_path.rs b/miden-crypto/src/merkle/sparse_path.rs index dad8cb7..a4f4209 100644 --- a/miden-crypto/src/merkle/sparse_path.rs +++ b/miden-crypto/src/merkle/sparse_path.rs @@ -1,6 +1,8 @@ use alloc::vec::Vec; use core::iter; +use winter_utils::{Deserializable, DeserializationError, Serializable}; + use super::{EmptySubtreeRoots, MerklePath, RpoDigest, SMT_MAX_DEPTH}; /// A different representation of [`MerklePath`] designed for memory efficiency for Merkle paths @@ -198,6 +200,29 @@ impl DoubleEndedIterator for SparseMerkleIter { } } +// SERIALIZATION +// ================================================================================================ + +impl Serializable for SparseMerklePath { + fn write_into(&self, target: &mut W) { + target.write_u8(self.depth()); + target.write_u64(self.empty_nodes); + target.write_many(&self.nodes); + } +} + +impl Deserializable for SparseMerklePath { + fn read_from( + source: &mut R, + ) -> Result { + let depth = source.read_u8()?; + let empty_nodes = source.read_u64()?; + let count = depth as u32 - empty_nodes.count_ones(); + let nodes = source.read_many::(count as usize)?; + Ok(Self { empty_nodes, nodes }) + } +} + #[cfg(test)] mod tests { use alloc::vec::Vec;