diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b2b4cfe5..faefad26 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -6,9 +6,9 @@ permissions: on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] jobs: build: @@ -24,6 +24,20 @@ jobs: command: build args: --examples --benches --verbose + build-no-std: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: no_std build + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: thumbv7em-none-eabi + - uses: actions-rs/cargo@v1 + with: + command: build + args: --no-default-features --target thumbv7em-none-eabi + build-wasm: runs-on: ubuntu-latest steps: @@ -79,10 +93,24 @@ jobs: command: clippy args: --all-targets -- -D warnings + clippy-no-default-features: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Check clippy warnings with --no-default-features + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + components: clippy + - uses: actions-rs/cargo@v1 + with: + command: clippy + args: --no-default-features + spelling: runs-on: ubuntu-latest steps: - - name: Checkout Actions Repository - uses: actions/checkout@v3 - - name: Spell Check Repo - uses: crate-ci/typos@685eb3d55be2f85191e8c84acb9f44d7756f84ab + - name: Checkout Actions Repository + uses: actions/checkout@v3 + - name: Spell Check Repo + uses: crate-ci/typos@685eb3d55be2f85191e8c84acb9f44d7756f84ab diff --git a/Cargo.toml b/Cargo.toml index be481991..46eba72f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,29 +12,53 @@ keywords = ["zkSNARKs", "cryptography", "proofs"] rust-version = "1.79.0" [dependencies] -ff = { version = "0.13.0", features = ["derive"] } -digest = "0.10" -sha3 = "0.10" -rayon = "1.10" +ff = { version = "0.13.0", features = [ + "derive", + "bits", +], default-features = false } +digest = { version = "0.10", default-features = false } +sha3 = { version = "0.10", default-features = false } + rand_core = { version = "0.6", default-features = false } -rand_chacha = "0.3" -subtle = "2.6.1" -halo2curves = { version = "0.8.0", features = ["bits", "derive_serde"] } -generic-array = "1.2.0" -num-bigint = { version = "0.4.6", features = ["serde", "rand"] } -num-traits = "0.2.19" -num-integer = "0.1.46" -serde = { version = "1.0.217", features = ["derive"] } -bincode = "1.3" -bitvec = "1.0" +subtle = { version = "2.6.1", default-features = false } + +generic-array = { version = "1.2.0", default-features = false } +num-bigint = { version = "0.4.6", features = [ + "serde", + "rand", +], default-features = false } +num-traits = { version = "0.2.19", default-features = false } +num-integer = { version = "0.1.46", default-features = false } +serde = { version = "1.0.217", features = ["derive"], default-features = false } +bincode = { version = "2.0.0-rc.3", default-features = false, features = [ + "alloc", + "derive", + "serde", +] } blitzar = { version = "4.4.2", optional = true } -byteorder = "1.4.3" -thiserror = "2.0.11" -once_cell = "1.18.0" -itertools = "0.14.0" +bitvec = { version = "1.0", default-features = false } +thiserror = { version = "2.0.11", default-features = false } +itertools = { version = "0.14.0", default-features = false } +libm = { version = "0.2.11", default-features = false } +hashbrown = { version = "0.15.2" } +plonky2_maybe_rayon = { version = "1.0.0", default-features = false } +# ! TODO -> Change from this to original halo2curves when PR is accepted +halo2curves = { git = "https://github.com/milosdjurica/halo2curves", branch = "no_std", features = [ + "bits", + "derive_serde", +], default-features = false } + + +# ! OPTIONAL +rayon = { version = "1.10", optional = true, default-features = false } +once_cell = { version = "1.18.0", optional = true } +byteorder = { version = "1.4.3", optional = true } + [target.'cfg(target_arch = "wasm32")'.dependencies] -getrandom = { version = "0.2.15", default-features = false, features = ["js"] } +getrandom = { version = "0.2.15", default-features = false, features = [ + "js", +], optional = true } [dev-dependencies] criterion = { version = "0.5", features = ["html_reports"] } @@ -46,6 +70,8 @@ sha2 = "0.10.7" proptest = "1.6.0" rand = "0.8.5" expect-test = "1.5.1" +rand_chacha = { version = "0.3", default-features = false } + [[bench]] name = "recursive-snark" @@ -68,6 +94,17 @@ name = "commit" harness = false [features] -default = ["halo2curves/asm"] +default = ["std"] +std = [ + "halo2curves/std", + "sha3/std", + "rand_core/getrandom", + "getrandom", + "once_cell", + "byteorder", + "plonky2_maybe_rayon/parallel", + "rayon", +] +asm = ["halo2curves/asm"] flamegraph = ["pprof2/flamegraph", "pprof2/criterion"] experimental = [] diff --git a/examples/and.rs b/examples/and.rs index 7c530fce..d4a8a55b 100644 --- a/examples/and.rs +++ b/examples/and.rs @@ -1,9 +1,9 @@ //! This example executes a batch of 64-bit AND operations. //! It performs the AND operation by first decomposing the operands into bits and then performing the operation bit-by-bit. //! We execute a configurable number of AND operations per step of Nova's recursion. +use bincode::config::legacy; use core::marker::PhantomData; use ff::{Field, PrimeField, PrimeFieldBits}; -use flate2::{write::ZlibEncoder, Compression}; use nova_snark::{ frontend::{ num::AllocatedNum, AllocatedBit, ConstraintSystem, LinearCombination, SynthesisError, @@ -286,9 +286,8 @@ fn main() { assert!(res.is_ok()); let compressed_snark = res.unwrap(); - let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default()); - bincode::serialize_into(&mut encoder, &compressed_snark).unwrap(); - let compressed_snark_encoded = encoder.finish().unwrap(); + let compressed_snark_encoded = + bincode::serde::encode_to_vec(&compressed_snark, legacy()).unwrap(); println!( "CompressedSNARK::len {:?} bytes", compressed_snark_encoded.len() diff --git a/examples/hashchain.rs b/examples/hashchain.rs index 0bee9f4e..06a4b931 100644 --- a/examples/hashchain.rs +++ b/examples/hashchain.rs @@ -1,7 +1,7 @@ //! This example proves the knowledge of preimage to a hash chain tail, with a configurable number of elements per hash chain node. //! The output of each step tracks the current tail of the hash chain +use bincode::config::legacy; use ff::Field; -use flate2::{write::ZlibEncoder, Compression}; use generic_array::typenum::U24; use nova_snark::{ frontend::{ @@ -179,9 +179,8 @@ fn main() { assert!(res.is_ok()); let compressed_snark = res.unwrap(); - let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default()); - bincode::serialize_into(&mut encoder, &compressed_snark).unwrap(); - let compressed_snark_encoded = encoder.finish().unwrap(); + let compressed_snark_encoded = + bincode::serde::encode_to_vec(&compressed_snark, legacy()).unwrap(); println!( "CompressedSNARK::len {:?} bytes", compressed_snark_encoded.len() diff --git a/examples/minroot.rs b/examples/minroot.rs index 7d0cfae4..bcd4a759 100644 --- a/examples/minroot.rs +++ b/examples/minroot.rs @@ -1,8 +1,8 @@ //! Demonstrates how to use Nova to produce a recursive proof of the correct execution of //! iterations of the `MinRoot` function, thereby realizing a Nova-based verifiable delay function (VDF). //! We execute a configurable number of iterations of the `MinRoot` function per step of Nova's recursion. +use bincode::config::legacy; use ff::Field; -use flate2::{write::ZlibEncoder, Compression}; use nova_snark::{ frontend::{num::AllocatedNum, ConstraintSystem, SynthesisError}, nova::{CompressedSNARK, PublicParams, RecursiveSNARK}, @@ -248,9 +248,8 @@ fn main() { assert!(res.is_ok()); let compressed_snark = res.unwrap(); - let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default()); - bincode::serialize_into(&mut encoder, &compressed_snark).unwrap(); - let compressed_snark_encoded = encoder.finish().unwrap(); + let compressed_snark_encoded = + bincode::serde::encode_to_vec(&compressed_snark, legacy()).unwrap(); println!( "CompressedSNARK::len {:?} bytes", compressed_snark_encoded.len() diff --git a/src/digest.rs b/src/digest.rs index ce74534e..c6da5625 100644 --- a/src/digest.rs +++ b/src/digest.rs @@ -1,14 +1,16 @@ -use crate::constants::NUM_HASH_BITS; -use bincode::Options; +#[cfg(not(feature = "std"))] +use crate::prelude::*; +use crate::{constants::NUM_HASH_BITS, errors::NovaError}; +use bincode::config::legacy; +use core::marker::PhantomData; use ff::PrimeField; use serde::Serialize; use sha3::{Digest, Sha3_256}; -use std::{io, marker::PhantomData}; /// Trait for components with potentially discrete digests to be included in their container's digest. pub trait Digestible { - /// Write the byte representation of Self in a byte buffer - fn write_bytes(&self, byte_sink: &mut W) -> Result<(), io::Error>; + /// Write the byte representation of Self. Returns a byte vector. + fn to_bytes(&self) -> Result, NovaError>; } /// Marker trait to be implemented for types that implement `Digestible` and `Serialize`. @@ -16,14 +18,10 @@ pub trait Digestible { pub trait SimpleDigestible: Serialize {} impl Digestible for T { - fn write_bytes(&self, byte_sink: &mut W) -> Result<(), io::Error> { - let config = bincode::DefaultOptions::new() - .with_little_endian() - .with_fixint_encoding(); - // Note: bincode recursively length-prefixes every field! - config - .serialize_into(byte_sink, self) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + fn to_bytes(&self) -> Result, NovaError> { + bincode::serde::encode_to_vec(self, legacy()).map_err(|e| NovaError::DigestError { + reason: e.to_string(), + }) } } @@ -65,23 +63,28 @@ impl<'a, F: PrimeField, T: Digestible> DigestComputer<'a, F, T> { } /// Compute the digest of a `Digestible` instance. - pub fn digest(&self) -> Result { + pub fn digest(&self) -> Result { + let bytes = self.inner.to_bytes().expect("Serialization error"); + let mut hasher = Self::hasher(); - self - .inner - .write_bytes(&mut hasher) - .expect("Serialization error"); - let bytes: [u8; 32] = hasher.finalize().into(); + hasher.update(&bytes); + let final_bytes = hasher.finalize(); + let bytes: Vec = final_bytes.to_vec(); + + // Now map to the field or handle it as necessary Ok(Self::map_to_field(&bytes)) } } #[cfg(test)] mod tests { + #[cfg(feature = "std")] + use once_cell::sync::OnceCell; + use super::{DigestComputer, SimpleDigestible}; use crate::{provider::PallasEngine, traits::Engine}; + use bincode::config::legacy; use ff::Field; - use once_cell::sync::OnceCell; use serde::{Deserialize, Serialize}; type E = PallasEngine; @@ -103,12 +106,21 @@ mod tests { } } - fn digest(&self) -> E::Scalar { - self + fn digest(&mut self) -> E::Scalar { + #[cfg(feature = "std")] + let res = self .digest .get_or_try_init(|| DigestComputer::new(self).digest()) .cloned() - .unwrap() + .expect("Failure in retrieving digest"); + #[cfg(not(feature = "std"))] + let res = *self.digest.get_or_init(|| { + DigestComputer::new(self) + .digest() + .expect("Failure in retrieving digest") + }); + + res } } @@ -120,7 +132,7 @@ mod tests { let oc = OnceCell::new(); oc.set(::Scalar::ONE).unwrap(); - let s2: S = S { i: 42, digest: oc }; + let mut s2: S = S { i: 42, digest: oc }; assert_eq!( DigestComputer::<::Scalar, _>::new(&s1) @@ -143,19 +155,21 @@ mod tests { #[test] fn test_digest_impervious_to_serialization() { - let good_s = S::::new(42); - // let's set up a struct with a weird digest field to confuse deserializers let oc = OnceCell::new(); oc.set(::Scalar::ONE).unwrap(); - let bad_s: S = S { i: 42, digest: oc }; + let mut good_s = S::::new(42); + let mut bad_s: S = S { i: 42, digest: oc }; + // this justifies the adjective "bad" assert_ne!(good_s.digest(), bad_s.digest()); - let naughty_bytes = bincode::serialize(&bad_s).unwrap(); + let naughty_bytes = bincode::serde::encode_to_vec(&bad_s, legacy()).unwrap(); + let mut retrieved_s: S = bincode::serde::decode_from_slice(&naughty_bytes, legacy()) + .unwrap() + .0; - let retrieved_s: S = bincode::deserialize(&naughty_bytes).unwrap(); assert_eq!(good_s.digest(), retrieved_s.digest()) } } diff --git a/src/errors.rs b/src/errors.rs index 2133eb53..5971b86e 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,5 +1,7 @@ //! This module defines errors returned by the library. use crate::frontend::SynthesisError; +#[cfg(not(feature = "std"))] +use crate::prelude::*; use core::fmt::Debug; use thiserror::Error; @@ -70,10 +72,17 @@ pub enum NovaError { }, /// returned when there is an error creating a digest #[error("DigestError")] - DigestError, + DigestError { + /// The reason for digest creation failure + reason: String, + }, /// returned when the prover cannot prove the provided statement due to completeness error #[error("InternalError")] InternalError, + + /// returned when trying to use random number in no_std environment. In order to get safe random number, turn "std" feature ON. + #[error("UnsafeRandomNumber")] + UnsafeRandomNumber, } impl From for NovaError { diff --git a/src/frontend/constraint_system.rs b/src/frontend/constraint_system.rs index 51306f71..9929958b 100644 --- a/src/frontend/constraint_system.rs +++ b/src/frontend/constraint_system.rs @@ -1,8 +1,8 @@ -use std::{io, marker::PhantomData}; - -use ff::PrimeField; - use super::lc::{Index, LinearCombination, Variable}; +#[cfg(not(feature = "std"))] +use crate::prelude::*; +use core::marker::PhantomData; +use ff::PrimeField; /// Computations are expressed in terms of arithmetic circuits, in particular /// rank-1 quadratic constraint systems. The `Circuit` trait represents a @@ -33,9 +33,6 @@ pub enum SynthesisError { /// During proof generation, we encountered an identity in the CRS #[error("encountered an identity element in the CRS")] UnexpectedIdentity, - /// During proof generation, we encountered an I/O error with the CRS - #[error("encountered an I/O error: {0}")] - IoError(#[from] io::Error), /// During verification, our verifying key was malformed. #[error("malformed verifying key")] MalformedVerifyingKey, diff --git a/src/frontend/gadgets/boolean.rs b/src/frontend/gadgets/boolean.rs index f5f0c656..fbc77e45 100644 --- a/src/frontend/gadgets/boolean.rs +++ b/src/frontend/gadgets/boolean.rs @@ -1,8 +1,9 @@ //! Gadgets for allocating bits in the circuit and performing boolean logic. -use ff::{PrimeField, PrimeFieldBits}; - use crate::frontend::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; +#[cfg(not(feature = "std"))] +use crate::prelude::*; +use ff::{PrimeField, PrimeFieldBits}; /// Represents a variable in the constraint system which is guaranteed /// to be either zero or one. diff --git a/src/frontend/gadgets/multieq.rs b/src/frontend/gadgets/multieq.rs index 35a38800..ddde11f8 100644 --- a/src/frontend/gadgets/multieq.rs +++ b/src/frontend/gadgets/multieq.rs @@ -1,6 +1,7 @@ -use ff::PrimeField; - use crate::frontend::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; +#[cfg(not(feature = "std"))] +use crate::prelude::*; +use ff::PrimeField; #[derive(Debug)] pub struct MultiEq> { diff --git a/src/frontend/gadgets/num.rs b/src/frontend/gadgets/num.rs index f143892f..d648cd5a 100644 --- a/src/frontend/gadgets/num.rs +++ b/src/frontend/gadgets/num.rs @@ -1,12 +1,14 @@ //! Gadgets representing numbers in the scalar field of the underlying curve. +use crate::frontend::{ + gadgets::boolean::{self, AllocatedBit, Boolean}, + ConstraintSystem, LinearCombination, SynthesisError, Variable, +}; +#[cfg(not(feature = "std"))] +use crate::prelude::*; use ff::{PrimeField, PrimeFieldBits}; use serde::{Deserialize, Serialize}; -use crate::frontend::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; - -use crate::frontend::gadgets::boolean::{self, AllocatedBit, Boolean}; - /// Represents an allocated number in the circuit. #[derive(Debug, Serialize, Deserialize)] pub struct AllocatedNum { diff --git a/src/frontend/gadgets/poseidon/circuit2.rs b/src/frontend/gadgets/poseidon/circuit2.rs index e0085d78..8025b2b0 100644 --- a/src/frontend/gadgets/poseidon/circuit2.rs +++ b/src/frontend/gadgets/poseidon/circuit2.rs @@ -10,8 +10,11 @@ use crate::frontend::{ num::{self, AllocatedNum}, Boolean, ConstraintSystem, LinearCombination, SynthesisError, }; +#[cfg(not(feature = "std"))] +use crate::prelude::*; use ff::PrimeField; -use std::marker::PhantomData; +#[cfg(feature = "std")] +use std::{iter, marker::PhantomData}; /// Similar to `num::Num`, we use `Elt` to accumulate both values and linear combinations, then eventually /// extract into a `num::AllocatedNum`, enforcing that the linear combination corresponds to the result. @@ -398,7 +401,7 @@ where } fn initial_elements>() -> Vec> { - std::iter::repeat(Elt::num_from_fr::(Scalar::ZERO)) + iter::repeat(Elt::num_from_fr::(Scalar::ZERO)) .take(A::to_usize() + 1) .collect() } diff --git a/src/frontend/gadgets/poseidon/matrix.rs b/src/frontend/gadgets/poseidon/matrix.rs index b260ba06..7fbd3de3 100644 --- a/src/frontend/gadgets/poseidon/matrix.rs +++ b/src/frontend/gadgets/poseidon/matrix.rs @@ -1,8 +1,9 @@ // Allow `&Matrix` in function signatures. #![allow(clippy::ptr_arg)] +#[cfg(not(feature = "std"))] +use crate::prelude::*; use ff::PrimeField; - /// Matrix functions here are, at least for now, quick and dirty — intended only to support precomputation of poseidon optimization. /// /// Matrix represented as a Vec of rows, so that m[i][j] represents the jth column of the ith row in Matrix, m. diff --git a/src/frontend/gadgets/poseidon/mds.rs b/src/frontend/gadgets/poseidon/mds.rs index 9929c55c..dff64313 100644 --- a/src/frontend/gadgets/poseidon/mds.rs +++ b/src/frontend/gadgets/poseidon/mds.rs @@ -1,9 +1,6 @@ // Allow `&Matrix` in function signatures. #![allow(clippy::ptr_arg)] -use ff::PrimeField; -use serde::{Deserialize, Serialize}; - use super::{ matrix, matrix::{ @@ -11,6 +8,10 @@ use super::{ Matrix, }, }; +#[cfg(not(feature = "std"))] +use crate::prelude::*; +use ff::PrimeField; +use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct MdsMatrices { diff --git a/src/frontend/gadgets/poseidon/mod.rs b/src/frontend/gadgets/poseidon/mod.rs index b01d2deb..a089a980 100644 --- a/src/frontend/gadgets/poseidon/mod.rs +++ b/src/frontend/gadgets/poseidon/mod.rs @@ -1,4 +1,6 @@ //! The underlying Poseidon sponge code is ported from https://github.com/argumentcomputer/neptune. +#[cfg(not(feature = "std"))] +use crate::prelude::*; use ff::PrimeField; use serde::{Deserialize, Serialize}; diff --git a/src/frontend/gadgets/poseidon/poseidon_inner.rs b/src/frontend/gadgets/poseidon/poseidon_inner.rs index 97316bf3..1f085c01 100644 --- a/src/frontend/gadgets/poseidon/poseidon_inner.rs +++ b/src/frontend/gadgets/poseidon/poseidon_inner.rs @@ -1,7 +1,9 @@ -use std::marker::PhantomData; - +#[cfg(not(feature = "std"))] +use crate::prelude::*; use ff::PrimeField; use generic_array::{sequence::GenericSequence, typenum, ArrayLength, GenericArray}; +#[cfg(feature = "std")] +use std::{marker::PhantomData, mem}; use typenum::*; use super::{ @@ -453,12 +455,12 @@ where } } - let _ = std::mem::replace(&mut self.elements, result); + let _ = mem::replace(&mut self.elements, result); } pub(crate) fn product_mds_with_matrix_left(&mut self, matrix: &Matrix) { let result = left_apply_matrix(matrix, &self.elements); - let _ = std::mem::replace( + let _ = mem::replace( &mut self.elements, GenericArray::::generate(|i| result[i]), ); @@ -485,6 +487,6 @@ where val.add_assign(&tmp); } - let _ = std::mem::replace(&mut self.elements, result); + let _ = mem::replace(&mut self.elements, result); } } diff --git a/src/frontend/gadgets/poseidon/preprocessing.rs b/src/frontend/gadgets/poseidon/preprocessing.rs index 750ffa9b..c8a84973 100644 --- a/src/frontend/gadgets/poseidon/preprocessing.rs +++ b/src/frontend/gadgets/poseidon/preprocessing.rs @@ -3,6 +3,8 @@ use super::{ mds::MdsMatrices, quintic_s_box, }; +#[cfg(not(feature = "std"))] +use crate::prelude::*; use ff::PrimeField; // - Compress constants by pushing them back through linear layers and through the identity components of partial layers. diff --git a/src/frontend/gadgets/poseidon/round_constants.rs b/src/frontend/gadgets/poseidon/round_constants.rs index c5182432..e72fbf2b 100644 --- a/src/frontend/gadgets/poseidon/round_constants.rs +++ b/src/frontend/gadgets/poseidon/round_constants.rs @@ -1,3 +1,5 @@ +#[cfg(not(feature = "std"))] +use crate::prelude::*; use ff::PrimeField; /// From the paper (): diff --git a/src/frontend/gadgets/poseidon/round_numbers.rs b/src/frontend/gadgets/poseidon/round_numbers.rs index a4434ca3..726adcd7 100644 --- a/src/frontend/gadgets/poseidon/round_numbers.rs +++ b/src/frontend/gadgets/poseidon/round_numbers.rs @@ -3,6 +3,11 @@ //! from Python2 to Rust for a (roughly) 256-bit prime field (e.g. BLS12-381's scalar field) and //! 128-bit security level. +use libm::log2; +#[allow(unused_imports)] +#[cfg(not(feature = "std"))] +use num_traits::float::FloatCore; + // The number of bits of the Poseidon prime field modulus. Denoted `n` in the Poseidon paper // (where `n = ceil(log2(p))`). Note that BLS12-381's scalar field modulus is 255 bits, however we // use 256 bits for simplicity when operating on bytes as the single bit difference does not affect @@ -79,7 +84,7 @@ fn round_numbers_are_secure(t: usize, rf: usize, rp: usize) -> bool { } else { 10.0 }; - let rf_interp = 0.43 * m + t.log2() - rp; + let rf_interp = 0.43 * m + log2(t as f64) as f32 - rp; let rf_grob_1 = 0.21 * n - rp; let rf_grob_2 = (0.14 * n - 1.0 - rp) / (t - 1.0); let rf_max = [rf_stat, rf_interp, rf_grob_1, rf_grob_2] diff --git a/src/frontend/gadgets/poseidon/serde_impl.rs b/src/frontend/gadgets/poseidon/serde_impl.rs index 0439243d..57d86981 100644 --- a/src/frontend/gadgets/poseidon/serde_impl.rs +++ b/src/frontend/gadgets/poseidon/serde_impl.rs @@ -1,16 +1,18 @@ +use super::{ + hash_type::HashType, + poseidon_inner::{Arity, PoseidonConstants}, +}; +#[cfg(not(feature = "std"))] +use crate::prelude::*; use ff::PrimeField; use serde::{ de::{self, Deserializer, MapAccess, SeqAccess, Visitor}, ser::{SerializeStruct, Serializer}, Deserialize, Serialize, }; +#[cfg(feature = "std")] use std::{fmt, marker::PhantomData}; -use super::{ - hash_type::HashType, - poseidon_inner::{Arity, PoseidonConstants}, -}; - impl Serialize for PoseidonConstants where F: PrimeField + Serialize, diff --git a/src/frontend/gadgets/poseidon/sponge/api.rs b/src/frontend/gadgets/poseidon/sponge/api.rs index 3063689d..622f964a 100644 --- a/src/frontend/gadgets/poseidon/sponge/api.rs +++ b/src/frontend/gadgets/poseidon/sponge/api.rs @@ -3,8 +3,9 @@ /// The API is defined by the `SpongeAPI` trait, which is implemented in terms of the `InnerSpongeAPI` trait. /// `Neptune` provides implementations of `InnerSpongeAPI` for both `sponge::Sponge` and `sponge_circuit::SpongeCircuit`. use crate::frontend::gadgets::poseidon::poseidon_inner::Arity; +#[cfg(not(feature = "std"))] +use crate::prelude::*; use ff::PrimeField; - #[derive(Debug)] pub enum Error { ParameterUsageMismatch, diff --git a/src/frontend/gadgets/poseidon/sponge/circuit.rs b/src/frontend/gadgets/poseidon/sponge/circuit.rs index e98a66fd..e3026b0c 100644 --- a/src/frontend/gadgets/poseidon/sponge/circuit.rs +++ b/src/frontend/gadgets/poseidon/sponge/circuit.rs @@ -10,8 +10,11 @@ use crate::frontend::{ util_cs::witness_cs::SizedWitness, ConstraintSystem, Namespace, SynthesisError, }; +#[cfg(not(feature = "std"))] +use crate::prelude::*; use ff::PrimeField; +#[cfg(feature = "std")] use std::{collections::VecDeque, marker::PhantomData}; /// The Poseidon sponge circuit diff --git a/src/frontend/gadgets/poseidon/sponge/vanilla.rs b/src/frontend/gadgets/poseidon/sponge/vanilla.rs index c3cd31f9..35702b45 100644 --- a/src/frontend/gadgets/poseidon/sponge/vanilla.rs +++ b/src/frontend/gadgets/poseidon/sponge/vanilla.rs @@ -4,7 +4,10 @@ use crate::frontend::gadgets::poseidon::{ sponge::api::{IOPattern, InnerSpongeAPI}, PoseidonError, Strength, }; +#[cfg(not(feature = "std"))] +use crate::prelude::*; use ff::PrimeField; +#[cfg(feature = "std")] use std::collections::VecDeque; // General information on sponge construction: https://keccak.team/files/CSF-0.1.pdf diff --git a/src/frontend/gadgets/sha256.rs b/src/frontend/gadgets/sha256.rs index a0408ad7..11690ba4 100644 --- a/src/frontend/gadgets/sha256.rs +++ b/src/frontend/gadgets/sha256.rs @@ -5,10 +5,11 @@ #![allow(clippy::many_single_char_names)] -use ff::PrimeField; - use super::{boolean::Boolean, multieq::MultiEq, uint32::UInt32}; use crate::frontend::{ConstraintSystem, SynthesisError}; +#[cfg(not(feature = "std"))] +use crate::prelude::*; +use ff::PrimeField; #[allow(clippy::unreadable_literal)] const ROUND_CONSTANTS: [u32; 64] = [ diff --git a/src/frontend/gadgets/uint32.rs b/src/frontend/gadgets/uint32.rs index ef0467e8..26cb0d12 100644 --- a/src/frontend/gadgets/uint32.rs +++ b/src/frontend/gadgets/uint32.rs @@ -1,14 +1,14 @@ //! Circuit representation of a [`u32`], with helpers for the [`sha256`] //! gadgets. -use ff::PrimeField; - -use crate::frontend::{ConstraintSystem, LinearCombination, SynthesisError}; - use super::{ boolean::{AllocatedBit, Boolean}, multieq::MultiEq, }; +use crate::frontend::{ConstraintSystem, LinearCombination, SynthesisError}; +#[cfg(not(feature = "std"))] +use crate::prelude::*; +use ff::PrimeField; /// Represents an interpretation of 32 `Boolean` objects as an /// unsigned integer. diff --git a/src/frontend/lc.rs b/src/frontend/lc.rs index cf6d89ea..2bcc48aa 100644 --- a/src/frontend/lc.rs +++ b/src/frontend/lc.rs @@ -1,5 +1,6 @@ -use std::ops::{Add, Sub}; - +#[cfg(not(feature = "std"))] +use crate::prelude::*; +use core::ops::{Add, Sub}; use ff::PrimeField; use serde::{Deserialize, Serialize}; diff --git a/src/frontend/shape_cs.rs b/src/frontend/shape_cs.rs index 05a4e996..517d4fd0 100644 --- a/src/frontend/shape_cs.rs +++ b/src/frontend/shape_cs.rs @@ -1,5 +1,7 @@ //! Support for generating R1CS shape using bellpepper. +#[cfg(not(feature = "std"))] +use crate::prelude::*; use crate::{ frontend::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}, traits::Engine, diff --git a/src/frontend/test_shape_cs.rs b/src/frontend/test_shape_cs.rs index dfa2e419..9e619f14 100644 --- a/src/frontend/test_shape_cs.rs +++ b/src/frontend/test_shape_cs.rs @@ -1,6 +1,9 @@ //! Support for generating R1CS shape using bellpepper. //! `TestShapeCS` implements a superset of `ShapeCS`, adding non-trivial namespace support for use in testing. +#[cfg(not(feature = "std"))] +use crate::prelude::*; +#[cfg(feature = "std")] use std::{ cmp::Ordering, collections::{BTreeMap, HashMap}, diff --git a/src/frontend/util_cs/test_cs.rs b/src/frontend/util_cs/test_cs.rs index a8b9fa46..90e3f91c 100644 --- a/src/frontend/util_cs/test_cs.rs +++ b/src/frontend/util_cs/test_cs.rs @@ -1,5 +1,8 @@ //! Test constraint system for use in tests. +#[cfg(not(feature = "std"))] +use crate::prelude::*; +#[cfg(feature = "std")] use std::collections::HashMap; use crate::frontend::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; @@ -115,8 +118,9 @@ impl TestConstraintSystem { /// Check if the constraint system is satisfied. pub fn is_satisfied(&self) -> bool { match self.which_is_unsatisfied() { - Some(b) => { - println!("fail: {:?}", b); + Some(_b) => { + #[cfg(feature = "std")] + println!("fail: {:?}", _b); false } None => true, diff --git a/src/frontend/util_cs/witness_cs.rs b/src/frontend/util_cs/witness_cs.rs index 40df6a74..ce412559 100644 --- a/src/frontend/util_cs/witness_cs.rs +++ b/src/frontend/util_cs/witness_cs.rs @@ -1,8 +1,8 @@ //! Support for efficiently generating R1CS witness using bellperson. - -use ff::PrimeField; - use crate::frontend::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; +#[cfg(not(feature = "std"))] +use crate::prelude::*; +use ff::PrimeField; /// A [`ConstraintSystem`] trait pub trait SizedWitness { diff --git a/src/gadgets/ecc.rs b/src/gadgets/ecc.rs index a262a9c8..111056e2 100644 --- a/src/gadgets/ecc.rs +++ b/src/gadgets/ecc.rs @@ -1,5 +1,7 @@ //! This module implements various elliptic curve gadgets #![allow(non_snake_case)] +#[cfg(not(feature = "std"))] +use crate::prelude::*; use crate::{ constants::{BN_LIMB_WIDTH as LIMB_WIDTH, BN_N_LIMBS as N_LIMBS}, frontend::{ diff --git a/src/gadgets/nonnative/bignat.rs b/src/gadgets/nonnative/bignat.rs index 22f60e7d..0500c11b 100644 --- a/src/gadgets/nonnative/bignat.rs +++ b/src/gadgets/nonnative/bignat.rs @@ -3,14 +3,18 @@ use super::{ OptionExt, }; use crate::frontend::{ConstraintSystem, LinearCombination, SynthesisError}; -use ff::PrimeField; -use num_bigint::BigInt; -use num_traits::cast::ToPrimitive; -use std::{ +#[cfg(not(feature = "std"))] +use crate::prelude::*; +use core::{ borrow::Borrow, cmp::{max, min}, convert::From, + iter, mem, }; +use ff::PrimeField; +use libm::log2; +use num_bigint::BigInt; +use num_traits::cast::ToPrimitive; /// Compute the natural number represented by an array of limbs. /// The limbs are assumed to be based the `limb_width` power of 2. @@ -52,6 +56,7 @@ pub fn nat_to_limbs( .collect(), ) } else { + #[cfg(feature = "std")] eprintln!("nat {nat} does not fit in {n_limbs} limbs of width {limb_width}"); Err(SynthesisError::Unsatisfiable) } @@ -132,6 +137,7 @@ impl BigNat { || match values_cell { Ok(ref vs) => { if vs.len() != n_limbs { + #[cfg(feature = "std")] eprintln!("Values do not match stated limb count"); return Err(SynthesisError::Unsatisfiable); } @@ -144,10 +150,12 @@ impl BigNat { Ok(vs[limb_i]) } // Hack b/c SynthesisError and io::Error don't implement Clone - Err(ref e) => Err(SynthesisError::from(std::io::Error::new( - std::io::ErrorKind::Other, - format!("{e}"), - ))), + Err(ref _e) => { + // print in std feature + #[cfg(feature = "std")] + eprintln!("{_e}"); + Err(SynthesisError::AssignmentMissing) + } }, ) .map(|v| LinearCombination::zero() + v) @@ -196,10 +204,12 @@ impl BigNat { Ok(vs[limb_i]) } // Hack b/c SynthesisError and io::Error don't implement Clone - Err(ref e) => Err(SynthesisError::from(std::io::Error::new( - std::io::ErrorKind::Other, - format!("{e}"), - ))), + Err(ref _e) => { + // print in std feature + #[cfg(feature = "std")] + eprintln!("{_e}"); + Err(SynthesisError::AssignmentMissing) + } }, ) .map(|v| LinearCombination::zero() + v) @@ -318,14 +328,15 @@ impl BigNat { pub fn enforce_limb_width_agreement( &self, other: &Self, - location: &str, + _location: &str, ) -> Result { if self.params.limb_width == other.params.limb_width { Ok(self.params.limb_width) } else { + #[cfg(feature = "std")] eprintln!( "Limb widths {}, {}, do not agree at {}", - self.params.limb_width, other.params.limb_width, location + self.params.limb_width, other.params.limb_width, _location ); Err(SynthesisError::Unsatisfiable) } @@ -361,7 +372,7 @@ impl BigNat { let target_base = BigInt::from(1u8) << self.params.limb_width as u32; let mut accumulated_extra = BigInt::from(0usize); let max_word = max(&self.params.max_word, &other.params.max_word); - let carry_bits = (((max_word.to_f64().unwrap() * 2.0).log2() - self.params.limb_width as f64) + let carry_bits = ((log2(max_word.to_f64().unwrap() * 2.0) - self.params.limb_width as f64) .ceil() + 0.1) as usize; let mut carry_in = Num::new(Some(Scalar::ZERO), LinearCombination::zero()); @@ -440,7 +451,7 @@ impl BigNat { ) -> Result<(), SynthesisError> { self.enforce_limb_width_agreement(other, "equal_when_carried_regroup")?; let max_word = max(&self.params.max_word, &other.params.max_word); - let carry_bits = (((max_word.to_f64().unwrap() * 2.0).log2() - self.params.limb_width as f64) + let carry_bits = ((log2(max_word.to_f64().unwrap() * 2.0) - self.params.limb_width as f64) .ceil() + 0.1) as usize; let limbs_per_group = (Scalar::CAPACITY as usize - carry_bits) / self.params.limb_width; @@ -642,8 +653,7 @@ impl BigNat { shift = Scalar::ONE; } limbs[i / limbs_per_group] = - std::mem::replace(&mut limbs[i / limbs_per_group], LinearCombination::zero()) - + (shift, limb); + mem::replace(&mut limbs[i / limbs_per_group], LinearCombination::zero()) + (shift, limb); shift.mul_assign(&limb_block); } limbs @@ -685,7 +695,7 @@ impl Polynomial { let n_product_coeffs = self.coefficients.len() + other.coefficients.len() - 1; let values = self.values.as_ref().and_then(|self_vs| { other.values.as_ref().map(|other_vs| { - let mut values: Vec = std::iter::repeat_with(|| Scalar::ZERO) + let mut values: Vec = iter::repeat_with(|| Scalar::ZERO) .take(n_product_coeffs) .collect(); for (self_i, self_v) in self_vs.iter().enumerate() { diff --git a/src/gadgets/nonnative/util.rs b/src/gadgets/nonnative/util.rs index e0243566..70ea9239 100644 --- a/src/gadgets/nonnative/util.rs +++ b/src/gadgets/nonnative/util.rs @@ -2,13 +2,12 @@ use super::{BitAccess, OptionExt}; use crate::frontend::{ num::AllocatedNum, ConstraintSystem, LinearCombination, SynthesisError, Variable, }; -use byteorder::WriteBytesExt; +#[cfg(not(feature = "std"))] +use crate::prelude::*; use ff::PrimeField; use num_bigint::{BigInt, Sign}; -use std::{ - convert::From, - io::{self, Write}, -}; +#[cfg(feature = "std")] +use std::convert::From; #[derive(Clone)] /// A representation of a bit @@ -232,18 +231,21 @@ impl From> for Num { } } -fn write_be(f: &F, mut writer: W) -> io::Result<()> { - for digit in f.to_repr().as_ref().iter().rev() { - writer.write_u8(*digit)?; +// Assuming PrimeField is a trait from some elliptic curve or cryptographic library +pub fn write_be(f: &F, buffer: &mut [u8]) -> Result<(), ()> { + for (offset, digit) in f.to_repr().as_ref().iter().rev().enumerate() { + if offset >= buffer.len() { + return Err(()); // Overflow: not enough space in buffer + } + buffer[offset] = *digit; } - Ok(()) } /// Convert a field element to a natural number pub fn f_to_nat(f: &Scalar) -> BigInt { - let mut s = Vec::new(); - write_be(f, &mut s).unwrap(); // f.to_repr().write_be(&mut s).unwrap(); + let mut buffer = [0u8; 256]; // Fixed-size buffer for the serialized representation of the field element + write_be(f, &mut buffer).unwrap(); BigInt::from_bytes_le(Sign::Plus, f.to_repr().as_ref()) } diff --git a/src/gadgets/utils.rs b/src/gadgets/utils.rs index e570d14d..5cd4c414 100644 --- a/src/gadgets/utils.rs +++ b/src/gadgets/utils.rs @@ -1,5 +1,7 @@ //! This module implements various low-level gadgets use super::nonnative::bignat::{nat_to_limbs, BigNat}; +#[cfg(not(feature = "std"))] +use crate::prelude::*; use crate::{ constants::{BN_LIMB_WIDTH, BN_N_LIMBS}, frontend::{ diff --git a/src/lib.rs b/src/lib.rs index 43c83664..f0d3c339 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ )] #![allow(non_snake_case)] #![forbid(unsafe_code)] +#![cfg_attr(not(feature = "std"), no_std)] // main APIs exposed by this library pub mod nova; @@ -36,3 +37,32 @@ type CommitmentKey = <::CE as CommitmentEngineTrait>::Commitm type DerandKey = <::CE as CommitmentEngineTrait>::DerandKey; type Commitment = <::CE as CommitmentEngineTrait>::Commitment; type CE = ::CE; + +#[cfg(not(feature = "std"))] +pub(crate) mod prelude { + extern crate alloc; + pub use alloc::borrow::ToOwned; + pub use alloc::boxed::Box; + pub use alloc::format; + pub use alloc::string::String; + pub use alloc::string::ToString; + pub use alloc::vec; + pub use alloc::vec::Vec; + + pub use core::cell::OnceCell; + pub use core::convert::From; + + pub use alloc::collections::BTreeMap; + pub use alloc::collections::VecDeque; + + pub use core::borrow::Borrow; + pub use core::cmp::Ordering; + pub use core::fmt; + pub use core::iter; + pub use core::marker::PhantomData; + pub use core::mem; + pub use core::ops::{Add, Sub}; + pub use hashbrown::HashMap; + pub use itertools::Itertools; + pub use num_traits::float::FloatCore; +} diff --git a/src/nova/circuit/mod.rs b/src/nova/circuit/mod.rs index e7adea80..6a7c9319 100644 --- a/src/nova/circuit/mod.rs +++ b/src/nova/circuit/mod.rs @@ -4,6 +4,8 @@ //! of the running instances. Each of these hashes is H(params = H(shape, ck), i, z0, zi, U). //! Each circuit folds the last invocation of the other into the running instance +#[cfg(not(feature = "std"))] +use crate::prelude::*; use crate::{ constants::NUM_HASH_BITS, frontend::{ @@ -43,6 +45,7 @@ pub struct NovaAugmentedCircuitInputs { impl NovaAugmentedCircuitInputs { /// Create new inputs/witness for the verification circuit + #[allow(unused)] // allowing unused because in no_std mode it is never called pub fn new( pp_digest: E::Scalar, i: E::Base, diff --git a/src/nova/circuit/r1cs.rs b/src/nova/circuit/r1cs.rs index 8dc42401..2ba3c81d 100644 --- a/src/nova/circuit/r1cs.rs +++ b/src/nova/circuit/r1cs.rs @@ -1,4 +1,6 @@ //! This module implements various gadgets necessary for folding R1CS types. +#[cfg(not(feature = "std"))] +use crate::prelude::*; use crate::{ constants::{BN_LIMB_WIDTH, BN_N_LIMBS, NUM_CHALLENGE_BITS}, frontend::{num::AllocatedNum, Assignment, Boolean, ConstraintSystem, SynthesisError}, diff --git a/src/nova/mod.rs b/src/nova/mod.rs index 493dc059..f1b8ebbc 100644 --- a/src/nova/mod.rs +++ b/src/nova/mod.rs @@ -1,15 +1,16 @@ //! This module implements Nova's IVC scheme including its folding scheme. +#[cfg(not(feature = "std"))] +use crate::prelude::*; +#[cfg(feature = "std")] +use crate::frontend::{ + r1cs::NovaWitness, solver::SatisfyingAssignment, ConstraintSystem, SynthesisError, +}; use crate::{ constants::NUM_HASH_BITS, digest::{DigestComputer, SimpleDigestible}, errors::NovaError, - frontend::{ - r1cs::{NovaShape, NovaWitness}, - shape_cs::ShapeCS, - solver::SatisfyingAssignment, - ConstraintSystem, SynthesisError, - }, + frontend::{r1cs::NovaShape, shape_cs::ShapeCS}, gadgets::utils::{base_as_scalar, scalar_as_base}, r1cs::{ CommitmentKeyHint, R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, @@ -25,14 +26,19 @@ use crate::{ }; use core::marker::PhantomData; use ff::Field; +#[cfg(feature = "std")] use once_cell::sync::OnceCell; +use plonky2_maybe_rayon::join; +#[cfg(feature = "std")] use rand_core::OsRng; use serde::{Deserialize, Serialize}; mod circuit; pub(crate) mod nifs; -use circuit::{NovaAugmentedCircuit, NovaAugmentedCircuitInputs}; +use circuit::NovaAugmentedCircuit; +#[cfg(feature = "std")] +use circuit::NovaAugmentedCircuitInputs; use nifs::{NIFSRelaxed, NIFS}; /// A type that holds public parameters of Nova @@ -177,11 +183,20 @@ where /// Retrieve the digest of the public parameters. pub fn digest(&self) -> E1::Scalar { - self + #[cfg(feature = "std")] + let res = self .digest .get_or_try_init(|| DigestComputer::new(self).digest()) .cloned() - .expect("Failure in retrieving digest") + .expect("Failure in retrieving digest"); + #[cfg(not(feature = "std"))] + let res = *self.digest.get_or_init(|| { + DigestComputer::new(self) + .digest() + .expect("Failure in retrieving digest") + }); + + res } /// Returns the number of constraints in the primary and secondary circuits @@ -236,7 +251,14 @@ where E2: Engine::Scalar>, C: StepCircuit, { + /// This method is using Random Number Generator, so it is only allowed when `STD` feature is turned ON. Calling this method without `STD` feature is unsafe. + #[cfg(not(feature = "std"))] + pub fn new(_pp: &PublicParams, _c: &C, _z0: &[E1::Scalar]) -> Result { + Err(NovaError::UnsafeRandomNumber) + } + /// Create new instance of recursive SNARK + #[cfg(feature = "std")] pub fn new(pp: &PublicParams, c: &C, z0: &[E1::Scalar]) -> Result { if z0.len() != pp.F_arity { return Err(NovaError::InvalidInitialInputLength); @@ -338,7 +360,14 @@ where }) } + /// This method is using Random Number Generator, so it is only allowed when `STD` feature is turned ON. Calling this method without `STD` feature is unsafe. + #[cfg(not(feature = "std"))] + pub fn prove_step(&mut self, _pp: &PublicParams, _c: &C) -> Result<(), NovaError> { + Err(NovaError::UnsafeRandomNumber) + } + /// Updates the provided `RecursiveSNARK` by executing a step of the incremental computation + #[cfg(feature = "std")] pub fn prove_step(&mut self, pp: &PublicParams, c: &C) -> Result<(), NovaError> { // first step was already done in the constructor if self.i == 0 { @@ -517,13 +546,13 @@ where } // check the satisfiability of the provided instances - let (res_r_primary, (res_r_secondary, res_l_secondary)) = rayon::join( + let (res_r_primary, (res_r_secondary, res_l_secondary)) = join( || { pp.r1cs_shape_primary .is_sat_relaxed(&pp.ck_primary, &self.r_U_primary, &self.r_W_primary) }, || { - rayon::join( + join( || { pp.r1cs_shape_secondary.is_sat_relaxed( &pp.ck_secondary, @@ -742,7 +771,7 @@ where ); // create SNARKs proving the knowledge of Wn primary/secondary - let (snark_primary, snark_secondary) = rayon::join( + let (snark_primary, snark_secondary) = join( || { S1::prove( &pp.ck_primary, @@ -891,7 +920,7 @@ where // check the satisfiability of the folded instances using // SNARKs proving the knowledge of their satisfying witnesses - let (res_primary, res_secondary) = rayon::join( + let (res_primary, res_secondary) = join( || { self .snark_primary diff --git a/src/nova/nifs.rs b/src/nova/nifs.rs index 27bd0f2d..747b232d 100644 --- a/src/nova/nifs.rs +++ b/src/nova/nifs.rs @@ -8,7 +8,9 @@ use crate::{ traits::{AbsorbInROTrait, Engine, ROConstants, ROTrait}, Commitment, CommitmentKey, }; +#[cfg(feature = "std")] use ff::Field; +#[cfg(feature = "std")] use rand_core::OsRng; use serde::{Deserialize, Serialize}; @@ -21,6 +23,21 @@ pub struct NIFS { } impl NIFS { + /// This method is using Random Number Generator, so it is only allowed when `STD` feature is turned ON. Calling this method without `STD` feature is unsafe. + #[cfg(not(feature = "std"))] + pub fn prove( + _ck: &CommitmentKey, + _ro_consts: &ROConstants, + _pp_digest: &E::Scalar, + _S: &R1CSShape, + _U1: &RelaxedR1CSInstance, + _W1: &RelaxedR1CSWitness, + _U2: &R1CSInstance, + _W2: &R1CSWitness, + ) -> Result<(NIFS, (RelaxedR1CSInstance, RelaxedR1CSWitness)), NovaError> { + Err(NovaError::UnsafeRandomNumber) + } + /// Takes as input a Relaxed R1CS instance-witness tuple `(U1, W1)` and /// an R1CS instance-witness tuple `(U2, W2)` with the same structure `shape` /// and defined with respect to the same `ck`, and outputs @@ -33,6 +50,7 @@ impl NIFS { /// In particular, it requires that `U1` and `U2` are such that the hash of `U1` is stored in the public IO of `U2`. /// In this particular setting, this means that if `U2` is absorbed in the RO, it implicitly absorbs `U1` as well. /// So the code below avoids absorbing `U1` in the RO. + #[cfg(feature = "std")] pub fn prove( ck: &CommitmentKey, ro_consts: &ROConstants, @@ -54,6 +72,7 @@ impl NIFS { // compute a commitment to the cross-term let r_T = E::Scalar::random(&mut OsRng); + let (T, comm_T) = S.commit_T(ck, U1, W1, U2, W2, &r_T)?; // append `comm_T` to the transcript and obtain a challenge @@ -116,7 +135,29 @@ pub struct NIFSRelaxed { } impl NIFSRelaxed { + /// This method is using Random Number Generator, so it is only allowed when `STD` feature is turned ON. Calling this method without `STD` feature is unsafe. + #[cfg(not(feature = "std"))] + pub fn prove( + _ck: &CommitmentKey, + _ro_consts: &ROConstants, + _vk: &E::Scalar, + _S: &R1CSShape, + _U1: &RelaxedR1CSInstance, + _W1: &RelaxedR1CSWitness, + _U2: &RelaxedR1CSInstance, + _W2: &RelaxedR1CSWitness, + ) -> Result< + ( + NIFSRelaxed, + (RelaxedR1CSInstance, RelaxedR1CSWitness), + ), + NovaError, + > { + Err(NovaError::UnsafeRandomNumber) + } + /// Same as `prove`, but takes two Relaxed R1CS Instance/Witness pairs + #[cfg(feature = "std")] pub fn prove( ck: &CommitmentKey, ro_consts: &ROConstants, diff --git a/src/provider/bn256_grumpkin.rs b/src/provider/bn256_grumpkin.rs index 9cac4758..cc32a7d4 100644 --- a/src/provider/bn256_grumpkin.rs +++ b/src/provider/bn256_grumpkin.rs @@ -1,4 +1,6 @@ //! This module implements the Nova traits for `bn256::Point`, `bn256::Scalar`, `grumpkin::Point`, `grumpkin::Scalar`. +#[cfg(not(feature = "std"))] +use crate::prelude::*; use crate::{ impl_traits, provider::{ @@ -7,6 +9,8 @@ use crate::{ }, traits::{Group, PrimeFieldExt, TranscriptReprTrait}, }; +#[cfg(not(feature = "std"))] +use digest::XofReader; use digest::{ExtendableOutput, Update}; use ff::FromUniformBytes; use halo2curves::{ @@ -19,8 +23,9 @@ use halo2curves::{ use num_bigint::BigInt; use num_integer::Integer; use num_traits::{Num, ToPrimitive}; -use rayon::prelude::*; +use plonky2_maybe_rayon::*; use sha3::Shake256; +#[cfg(feature = "std")] use std::io::Read; /// Re-exports that give access to the standard aliases used in the code base, for bn256 diff --git a/src/provider/hyperkzg.rs b/src/provider/hyperkzg.rs index 4563b4c3..08e01e43 100644 --- a/src/provider/hyperkzg.rs +++ b/src/provider/hyperkzg.rs @@ -6,23 +6,23 @@ //! (2) HyperKZG is specialized to use KZG as the univariate commitment scheme, so it includes several optimizations (both during the transformation of multilinear-to-univariate claims //! and within the KZG commitment scheme implementation itself). #![allow(non_snake_case)] +#[cfg(not(feature = "std"))] +use crate::prelude::*; +#[cfg(feature = "std")] +use crate::provider::{ptau::PtauFileError, read_ptau, write_ptau}; use crate::{ errors::NovaError, gadgets::utils::to_bignat_repr, - provider::{ - ptau::PtauFileError, - read_ptau, - traits::{DlogGroup, DlogGroupExt, PairingGroup}, - write_ptau, - }, + provider::traits::{DlogGroup, DlogGroupExt, PairingGroup}, traits::{ commitment::{CommitmentEngineTrait, CommitmentTrait, Len}, evaluation::EvaluationEngineTrait, AbsorbInRO2Trait, AbsorbInROTrait, Engine, ROTrait, TranscriptEngineTrait, TranscriptReprTrait, }, }; +use core::cmp::max; use core::{ - iter, + array, iter, marker::PhantomData, ops::{Add, Mul, MulAssign}, slice, @@ -30,8 +30,9 @@ use core::{ use ff::{Field, PrimeFieldBits}; use num_integer::Integer; use num_traits::ToPrimitive; +use plonky2_maybe_rayon::*; +#[cfg(feature = "std")] use rand_core::OsRng; -use rayon::prelude::*; use serde::{Deserialize, Serialize}; /// Alias to points on G1 that are in preprocessed form @@ -135,6 +136,7 @@ impl CommitmentKey where E::GE: PairingGroup, { + #[cfg(feature = "std")] /// Save keys pub fn save_to( &self, @@ -340,12 +342,16 @@ where } fn compute_powers_par(tau: E::Scalar, n: usize) -> Vec { + #[cfg(feature = "std")] let num_threads = rayon::current_num_threads(); + #[cfg(not(feature = "std"))] + let num_threads = 1; + (0..n) .collect::>() - .par_chunks(std::cmp::max(n / num_threads, 1)) + .par_chunks(max(n / num_threads, 1)) .into_par_iter() - .map(|sub_list| { + .flat_map(|sub_list| { let mut res = Vec::with_capacity(sub_list.len()); res.push(tau.pow([sub_list[0] as u64])); for i in 1..sub_list.len() { @@ -353,7 +359,6 @@ where } res }) - .flatten() .collect::>() } } @@ -412,10 +417,9 @@ fn fixed_base_exp_comb_batch< precompute_res.insert(0, [zero; V]); - let precomputed_g: [_; POW_2_H] = std::array::from_fn(|j| precompute_res[j]); + let precomputed_g: [_; POW_2_H] = array::from_fn(|j| precompute_res[j]); let zero = G::zero(); - scalars .par_iter() .map(|e| { @@ -455,6 +459,13 @@ where type CommitmentKey = CommitmentKey; type DerandKey = DerandKey; + /// This method is using Random Number Generator, so it is only allowed when `STD` feature is turned ON. Calling this method without `STD` feature is unsafe. + #[cfg(not(feature = "std"))] + fn setup(_label: &'static [u8], _n: usize) -> Self::CommitmentKey { + panic!("To use this method you need to turn on the STD feature.") + } + + #[cfg(feature = "std")] fn setup(label: &'static [u8], n: usize) -> Self::CommitmentKey { // NOTE: this is for testing purposes and should not be used in production Self::CommitmentKey::setup_from_rng(label, n, OsRng) @@ -537,6 +548,7 @@ where } } + #[cfg(feature = "std")] fn load_setup( reader: &mut (impl std::io::Read + std::io::Seek), label: &'static [u8], @@ -985,7 +997,7 @@ mod tests { provider::{hyperkzg, keccak::Keccak256Transcript, Bn256EngineKZG}, spartan::polys::multilinear::MultilinearPolynomial, }; - use bincode::Options; + use bincode::config::legacy; use rand::SeedableRng; type E = Bn256EngineKZG; @@ -1078,11 +1090,8 @@ mod tests { // check if the prover transcript and verifier transcript are kept in the same state assert_eq!(post_c_p, post_c_v); - let proof_bytes = bincode::DefaultOptions::new() - .with_big_endian() - .with_fixint_encoding() - .serialize(&proof) - .unwrap(); + let proof_bytes = bincode::serde::encode_to_vec(&proof, legacy()).unwrap(); + assert_eq!(proof_bytes.len(), 336); // Change the proof and expect verification to fail diff --git a/src/provider/ipa_pc.rs b/src/provider/ipa_pc.rs index 63680dca..2bcd1cda 100644 --- a/src/provider/ipa_pc.rs +++ b/src/provider/ipa_pc.rs @@ -1,4 +1,6 @@ //! This module implements `EvaluationEngine` using an IPA-based polynomial commitment scheme +#[cfg(not(feature = "std"))] +use crate::prelude::*; use crate::{ errors::NovaError, provider::{pedersen::CommitmentKeyExtTrait, traits::DlogGroup}, @@ -9,11 +11,10 @@ use crate::{ }, Commitment, CommitmentKey, CE, }; -use core::iter; +use core::{iter, marker::PhantomData}; use ff::Field; -use rayon::prelude::*; +use plonky2_maybe_rayon::*; use serde::{Deserialize, Serialize}; -use std::marker::PhantomData; /// Provides an implementation of the prover key #[derive(Clone, Debug, Serialize, Deserialize)] @@ -100,10 +101,17 @@ where fn inner_product(a: &[T], b: &[T]) -> T { assert_eq!(a.len(), b.len()); - (0..a.len()) + #[cfg(feature = "std")] + let res = (0..a.len()) .into_par_iter() .map(|i| a[i] * b[i]) - .reduce(|| T::ZERO, |x, y| x + y) + .reduce(|| T::ZERO, |x, y| x + y); + #[cfg(not(feature = "std"))] + let res = (0..a.len()) + .map(|i| a[i] * b[i]) + .fold(T::ZERO, |x, y| x + y); + + res } /// An inner product instance consists of a commitment to a vector `a` and another vector `b` @@ -350,6 +358,7 @@ where .into_par_iter() .map(|i| r[i] * r[i]) .collect(); + let r_inverse = batch_invert(&r)?; let r_inverse_square: Vec = (0..self.L_vec.len()) .into_par_iter() diff --git a/src/provider/mod.rs b/src/provider/mod.rs index 2bbc44a1..e2a36c0a 100644 --- a/src/provider/mod.rs +++ b/src/provider/mod.rs @@ -4,6 +4,7 @@ pub mod bn256_grumpkin; pub mod hyperkzg; pub mod ipa_pc; +pub mod msm; pub mod pasta; pub mod poseidon; pub mod secp_secq; @@ -16,20 +17,21 @@ pub(crate) mod pedersen; pub(crate) mod ptau; pub(crate) mod traits; -mod msm; - +use crate::provider::{ + bn256_grumpkin::{bn256, grumpkin}, + hyperkzg::CommitmentEngine as HyperKZGCommitmentEngine, + pasta::{pallas, vesta}, + secp_secq::{secp256k1, secq256k1}, +}; use crate::{ provider::{ - bn256_grumpkin::{bn256, grumpkin}, - hyperkzg::CommitmentEngine as HyperKZGCommitmentEngine, keccak::Keccak256Transcript, - pasta::{pallas, vesta}, pedersen::CommitmentEngine as PedersenCommitmentEngine, poseidon::{PoseidonRO, PoseidonROCircuit}, - secp_secq::{secp256k1, secq256k1}, }, traits::Engine, }; +#[cfg(feature = "std")] pub use ptau::{check_sanity_of_ptau_file, read_ptau, write_ptau}; use serde::{Deserialize, Serialize}; diff --git a/src/provider/msm.rs b/src/provider/msm.rs index 62f072e1..a187d58d 100644 --- a/src/provider/msm.rs +++ b/src/provider/msm.rs @@ -1,11 +1,16 @@ //! This module provides a multi-scalar multiplication routine //! The generic implementation is adapted from halo2; we add an optimization to commit to bits more efficiently //! The specialized implementations are adapted from jolt, with additional optimizations and parallelization. +#[cfg(not(feature = "std"))] +use crate::prelude::*; use ff::{Field, PrimeField}; use halo2curves::{group::Group, CurveAffine}; +use libm::log; use num_integer::Integer; use num_traits::{ToPrimitive, Zero}; -use rayon::{current_num_threads, prelude::*}; +use plonky2_maybe_rayon::*; +#[cfg(feature = "std")] +use rayon::current_num_threads; #[derive(Clone, Copy)] enum Bucket { @@ -38,7 +43,7 @@ fn cpu_msm_serial(coeffs: &[C::Scalar], bases: &[C]) -> C::Curve } else if bases.len() < 32 { 3 } else { - (f64::from(bases.len() as u32)).ln().ceil() as usize + log(f64::from(bases.len() as u32)).ceil() as usize }; fn get_at(segment: usize, c: usize, bytes: &F::Repr) -> usize { @@ -113,14 +118,27 @@ fn cpu_msm_serial(coeffs: &[C::Scalar], bases: &[C]) -> C::Curve pub fn msm(coeffs: &[C::Scalar], bases: &[C]) -> C::Curve { assert_eq!(coeffs.len(), bases.len()); + #[cfg(feature = "std")] let num_threads = current_num_threads(); + #[cfg(not(feature = "std"))] + let num_threads = 1; + if coeffs.len() > num_threads { let chunk = coeffs.len() / num_threads; - coeffs + #[cfg(feature = "std")] + let res = coeffs + .par_chunks(chunk) + .zip(bases.par_chunks(chunk)) + .map(|(coeffs, bases)| cpu_msm_serial(coeffs, bases)) + .reduce(C::Curve::identity, |sum, evl| sum + evl); + #[cfg(not(feature = "std"))] + let res = coeffs .par_chunks(chunk) .zip(bases.par_chunks(chunk)) .map(|(coeffs, bases)| cpu_msm_serial(coeffs, bases)) - .reduce(C::Curve::identity, |sum, evl| sum + evl) + .fold(C::Curve::identity(), |sum, evl| sum + evl); + + res } else { cpu_msm_serial(coeffs, bases) } @@ -152,7 +170,10 @@ pub fn msm_small + Copy + Sync + ToPrimit fn msm_binary(scalars: &[T], bases: &[C]) -> C::Curve { assert_eq!(scalars.len(), bases.len()); + #[cfg(feature = "std")] let num_threads = current_num_threads(); + #[cfg(not(feature = "std"))] + let num_threads = 1; let process_chunk = |scalars: &[T], bases: &[C]| { let mut acc = C::Curve::identity(); scalars @@ -167,11 +188,20 @@ fn msm_binary(scalars: &[T], bases: &[C]) -> if scalars.len() > num_threads { let chunk = scalars.len() / num_threads; - scalars + #[cfg(feature = "std")] + let res = scalars .par_chunks(chunk) .zip(bases.par_chunks(chunk)) .map(|(scalars, bases)| process_chunk(scalars, bases)) - .reduce(C::Curve::identity, |sum, evl| sum + evl) + .reduce(C::Curve::identity, |sum, evl| sum + evl); + #[cfg(not(feature = "std"))] + let res = scalars + .par_chunks(chunk) + .zip(bases.par_chunks(chunk)) + .map(|(scalars, bases)| process_chunk(scalars, bases)) + .fold(C::Curve::identity(), |sum, evl| sum + evl); + + res } else { process_chunk(scalars, bases) } @@ -209,14 +239,27 @@ fn msm_10 + Zero + Copy + Sync>( result } + #[cfg(feature = "std")] let num_threads = current_num_threads(); + #[cfg(not(feature = "std"))] + let num_threads = 1; + if scalars.len() > num_threads { let chunk_size = scalars.len() / num_threads; - scalars + #[cfg(feature = "std")] + let res = scalars .par_chunks(chunk_size) .zip(bases.par_chunks(chunk_size)) .map(|(scalars_chunk, bases_chunk)| msm_10_serial(scalars_chunk, bases_chunk, max_num_bits)) - .reduce(C::Curve::identity, |sum, evl| sum + evl) + .reduce(C::Curve::identity, |sum, evl| sum + evl); + #[cfg(not(feature = "std"))] + let res = scalars + .par_chunks(chunk_size) + .zip(bases.par_chunks(chunk_size)) + .map(|(scalars_chunk, bases_chunk)| msm_10_serial(scalars_chunk, bases_chunk, max_num_bits)) + .fold(C::Curve::identity(), |sum, evl| sum + evl); + + res } else { msm_10_serial(scalars, bases, max_num_bits) } @@ -319,16 +362,32 @@ fn msm_small_rest + Zero + Copy + Sync>( }) } + #[cfg(feature = "std")] let num_threads = current_num_threads(); + #[cfg(not(feature = "std"))] + let num_threads = 1; + if scalars.len() > num_threads { let chunk_size = scalars.len() / num_threads; - scalars + #[cfg(feature = "std")] + let res = scalars + .par_chunks(chunk_size) + .zip(bases.par_chunks(chunk_size)) + .map(|(scalars_chunk, bases_chunk)| { + msm_small_rest_serial(scalars_chunk, bases_chunk, max_num_bits) + }) + .reduce(C::Curve::identity, |sum, evl| sum + evl); + + #[cfg(not(feature = "std"))] + let res = scalars .par_chunks(chunk_size) .zip(bases.par_chunks(chunk_size)) .map(|(scalars_chunk, bases_chunk)| { msm_small_rest_serial(scalars_chunk, bases_chunk, max_num_bits) }) - .reduce(C::Curve::identity, |sum, evl| sum + evl) + .fold(C::Curve::identity(), |sum, evl| sum + evl); + + res } else { msm_small_rest_serial(scalars, bases, max_num_bits) } diff --git a/src/provider/pasta.rs b/src/provider/pasta.rs index 39ff24a0..b2b1bc61 100644 --- a/src/provider/pasta.rs +++ b/src/provider/pasta.rs @@ -1,4 +1,6 @@ //! This module implements the Nova traits for `pallas::Point`, `pallas::Scalar`, `vesta::Point`, `vesta::Scalar`. +#[cfg(not(feature = "std"))] +use crate::prelude::*; use crate::{ impl_traits, provider::{ @@ -7,6 +9,8 @@ use crate::{ }, traits::{Group, PrimeFieldExt, TranscriptReprTrait}, }; +#[cfg(not(feature = "std"))] +use digest::XofReader; use digest::{ExtendableOutput, Update}; use ff::FromUniformBytes; use halo2curves::{ @@ -17,8 +21,9 @@ use halo2curves::{ use num_bigint::BigInt; use num_integer::Integer; use num_traits::{Num, ToPrimitive}; -use rayon::prelude::*; +use plonky2_maybe_rayon::*; use sha3::Shake256; +#[cfg(feature = "std")] use std::io::Read; /// Re-exports that give access to the standard aliases used in the code base, for pallas diff --git a/src/provider/pedersen.rs b/src/provider/pedersen.rs index ff51cd4c..f313234f 100644 --- a/src/provider/pedersen.rs +++ b/src/provider/pedersen.rs @@ -1,16 +1,18 @@ //! This module provides an implementation of a commitment engine +#[cfg(feature = "std")] +use super::ptau::{read_points, write_points, PtauFileError}; +#[cfg(not(feature = "std"))] +use crate::prelude::*; use crate::{ errors::NovaError, gadgets::utils::to_bignat_repr, - provider::{ - ptau::{read_points, write_points, PtauFileError}, - traits::{DlogGroup, DlogGroupExt}, - }, + provider::traits::{DlogGroup, DlogGroupExt}, traits::{ commitment::{CommitmentEngineTrait, CommitmentTrait, Len}, AbsorbInRO2Trait, AbsorbInROTrait, Engine, ROTrait, TranscriptReprTrait, }, }; + use core::{ fmt::Debug, marker::PhantomData, @@ -19,9 +21,10 @@ use core::{ use ff::Field; use num_integer::Integer; use num_traits::ToPrimitive; -use rayon::prelude::*; +use plonky2_maybe_rayon::*; use serde::{Deserialize, Serialize}; +#[cfg(feature = "std")] const KEY_FILE_HEAD: [u8; 12] = *b"PEDERSEN_KEY"; /// A type that holds commitment generators @@ -192,6 +195,7 @@ impl CommitmentKey where E::GE: DlogGroup, { + #[cfg(feature = "std")] pub fn save_to(&self, writer: &mut impl std::io::Write) -> Result<(), PtauFileError> { writer.write_all(&KEY_FILE_HEAD)?; let mut points = Vec::with_capacity(self.ck.len() + 1); @@ -256,6 +260,7 @@ where } } + #[cfg(feature = "std")] fn load_setup( reader: &mut (impl std::io::Read + std::io::Seek), _label: &'static [u8], diff --git a/src/provider/poseidon.rs b/src/provider/poseidon.rs index 97775787..dc7dd150 100644 --- a/src/provider/poseidon.rs +++ b/src/provider/poseidon.rs @@ -1,4 +1,6 @@ //! Poseidon Constants and Poseidon-based RO used in Nova +#[cfg(not(feature = "std"))] +use crate::prelude::*; use crate::{ frontend::{ gadgets::poseidon::{ diff --git a/src/provider/ptau.rs b/src/provider/ptau.rs index b4158b41..a16d5405 100644 --- a/src/provider/ptau.rs +++ b/src/provider/ptau.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "std")] use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use ff::PrimeField; use halo2curves::CurveAffine; diff --git a/src/provider/secp_secq.rs b/src/provider/secp_secq.rs index 332b0a1a..f075af58 100644 --- a/src/provider/secp_secq.rs +++ b/src/provider/secp_secq.rs @@ -1,4 +1,6 @@ //! This module implements the Nova traits for `secp::Point`, `secp::Scalar`, `secq::Point`, `secq::Scalar`. +#[cfg(not(feature = "std"))] +use crate::prelude::*; use crate::{ impl_traits, provider::{ @@ -7,6 +9,8 @@ use crate::{ }, traits::{Group, PrimeFieldExt, TranscriptReprTrait}, }; +#[cfg(not(feature = "std"))] +use digest::XofReader; use digest::{ExtendableOutput, Update}; use ff::FromUniformBytes; use halo2curves::{ @@ -18,8 +22,9 @@ use halo2curves::{ use num_bigint::BigInt; use num_integer::Integer; use num_traits::{Num, ToPrimitive}; -use rayon::prelude::*; +use plonky2_maybe_rayon::*; use sha3::Shake256; +#[cfg(feature = "std")] use std::io::Read; /// Re-exports that give access to the standard aliases used in the code base, for secp diff --git a/src/provider/traits.rs b/src/provider/traits.rs index d71319a2..e8904851 100644 --- a/src/provider/traits.rs +++ b/src/provider/traits.rs @@ -1,3 +1,5 @@ +#[cfg(not(feature = "std"))] +use crate::prelude::*; use crate::traits::{commitment::ScalarMul, Group, TranscriptReprTrait}; use core::{ fmt::Debug, @@ -6,7 +8,7 @@ use core::{ use halo2curves::{serde::SerdeObject, CurveAffine}; use num_integer::Integer; use num_traits::ToPrimitive; -use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; +use plonky2_maybe_rayon::*; use serde::{Deserialize, Serialize}; /// A helper trait for types with a group operation. @@ -159,7 +161,10 @@ macro_rules! impl_traits_no_dlog_ext { let mut uniform_bytes_vec = Vec::new(); for _ in 0..n { let mut uniform_bytes = [0u8; 32]; + #[cfg(feature = "std")] reader.read_exact(&mut uniform_bytes).unwrap(); + #[cfg(not(feature = "std"))] + reader.read(&mut uniform_bytes); uniform_bytes_vec.push(uniform_bytes); } let gens_proj: Vec<$name_curve> = (0..n) @@ -170,11 +175,16 @@ macro_rules! impl_traits_no_dlog_ext { }) .collect(); + #[cfg(feature = "std")] let num_threads = rayon::current_num_threads(); + #[cfg(not(feature = "std"))] + let num_threads = 1; + if gens_proj.len() > num_threads { let chunk = (gens_proj.len() as f64 / num_threads as f64).ceil() as usize; - (0..num_threads) - .into_par_iter() + let iter = (0..num_threads).into_par_iter(); + + iter .flat_map(|i| { let start = i * chunk; let end = if i == num_threads - 1 { diff --git a/src/r1cs/mod.rs b/src/r1cs/mod.rs index dbc09ee6..c9911f1f 100644 --- a/src/r1cs/mod.rs +++ b/src/r1cs/mod.rs @@ -1,4 +1,6 @@ //! This module defines R1CS related types and a folding scheme for Relaxed R1CS +#[cfg(not(feature = "std"))] +use crate::prelude::*; use crate::{ constants::{BN_LIMB_WIDTH, BN_N_LIMBS}, digest::{DigestComputer, SimpleDigestible}, @@ -15,9 +17,11 @@ use crate::{ }; use core::cmp::max; use ff::Field; +#[cfg(feature = "std")] use once_cell::sync::OnceCell; +use plonky2_maybe_rayon::*; +#[cfg(feature = "std")] use rand_core::OsRng; -use rayon::prelude::*; use serde::{Deserialize, Serialize}; mod sparse; @@ -135,11 +139,19 @@ impl R1CSShape { /// returned the digest of the `R1CSShape` pub fn digest(&self) -> E::Scalar { - self + #[cfg(feature = "std")] + let res = self .digest .get_or_try_init(|| DigestComputer::new(self).digest()) .cloned() - .expect("Failure retrieving digest") + .expect("Failure retrieving digest"); + #[cfg(not(feature = "std"))] + let res = *self.digest.get_or_init(|| { + DigestComputer::new(self) + .digest() + .expect("Failure retrieving digest") + }); + res } // Checks regularity conditions on the R1CSShape, required in Spartan-class SNARKs @@ -160,9 +172,9 @@ impl R1CSShape { return Err(NovaError::InvalidWitnessLength); } - let (Az, (Bz, Cz)) = rayon::join( + let (Az, (Bz, Cz)) = join( || self.A.multiply_vec(z), - || rayon::join(|| self.B.multiply_vec(z), || self.C.multiply_vec(z)), + || join(|| self.B.multiply_vec(z), || self.C.multiply_vec(z)), ); Ok((Az, Bz, Cz)) @@ -192,10 +204,11 @@ impl R1CSShape { // verify if comm_E and comm_W are commitments to E and W let res_comm = { - let (comm_W, comm_E) = rayon::join( + let (comm_W, comm_E) = join( || CE::::commit(ck, &W.W, &W.r_W), || CE::::commit(ck, &W.E, &W.r_E), ); + U.comm_W == comm_W && U.comm_E == comm_E }; @@ -274,8 +287,8 @@ impl R1CSShape { .zip(Z2.into_par_iter()) .map(|(z1, z2)| z1 + z2) .collect::>(); - let u = U1.u + E::Scalar::ONE; // U2.u = 1 + let u = U1.u + E::Scalar::ONE; // U2.u = 1 let (AZ, BZ, CZ) = self.multiply_vec(&Z)?; let T = AZ @@ -312,8 +325,8 @@ impl R1CSShape { .zip(Z2.into_par_iter()) .map(|(z1, z2)| z1 + z2) .collect::>(); - let u = U1.u + U2.u; + let u = U1.u + U2.u; let (AZ, BZ, CZ) = self.multiply_vec(&Z)?; let T = AZ @@ -391,7 +404,17 @@ impl R1CSShape { } } + /// This method is using Random Number Generator, so it is only allowed when `STD` feature is turned ON. Calling this method without `STD` feature is unsafe. + #[cfg(not(feature = "std"))] + pub fn sample_random_instance_witness( + &self, + _ck: &CommitmentKey, + ) -> Result<(RelaxedR1CSInstance, RelaxedR1CSWitness), NovaError> { + Err(NovaError::UnsafeRandomNumber) + } + /// Samples a new random `RelaxedR1CSInstance`/`RelaxedR1CSWitness` pair + #[cfg(feature = "std")] pub fn sample_random_instance_witness( &self, ck: &CommitmentKey, @@ -418,7 +441,7 @@ impl R1CSShape { .collect::>(); // compute commitments to W,E in parallel - let (comm_W, comm_E) = rayon::join( + let (comm_W, comm_E) = join( || CE::::commit(ck, &Z[..self.num_vars], &r_W), || CE::::commit(ck, &E, &r_E), ); @@ -441,7 +464,14 @@ impl R1CSShape { } impl R1CSWitness { + /// This method is using Random Number Generator, so it is only allowed when `STD` feature is turned ON. Calling this method without `STD` feature is unsafe. + #[cfg(not(feature = "std"))] + pub fn new(_S: &R1CSShape, _W: &[E::Scalar]) -> Result, NovaError> { + Err(NovaError::UnsafeRandomNumber) + } + /// A method to create a witness object using a vector of scalars + #[cfg(feature = "std")] pub fn new(S: &R1CSShape, W: &[E::Scalar]) -> Result, NovaError> { let mut W = W.to_vec(); W.resize(S.num_vars, E::Scalar::ZERO); @@ -556,6 +586,7 @@ impl RelaxedR1CSWitness { .zip(W2) .map(|(a, b)| *a + *r * *b) .collect::>(); + let E = E1 .par_iter() .zip(T) @@ -589,6 +620,7 @@ impl RelaxedR1CSWitness { .zip(W2) .map(|(a, b)| *a + *r * *b) .collect::>(); + let E = E1 .par_iter() .zip(T) @@ -688,6 +720,7 @@ impl RelaxedR1CSInstance { .zip(X2) .map(|(a, b)| *a + *r * *b) .collect::>(); + let comm_W = *comm_W_1 + *comm_W_2 * *r; let comm_E = *comm_E_1 + *comm_T * *r; let u = *u1 + *r; @@ -717,6 +750,7 @@ impl RelaxedR1CSInstance { .zip(X2) .map(|(a, b)| *a + *r * *b) .collect::>(); + let comm_W = *comm_W_1 + *comm_W_2 * *r; let comm_E = *comm_E_1 + *comm_T * *r + *comm_E_2 * *r * *r; let u = *u1 + *r * *u2; diff --git a/src/r1cs/sparse.rs b/src/r1cs/sparse.rs index 5ec86b0c..586625aa 100644 --- a/src/r1cs/sparse.rs +++ b/src/r1cs/sparse.rs @@ -3,7 +3,10 @@ //! This module defines a custom implementation of CSR/CSC sparse matrices. //! Specifically, we implement sparse matrix / dense vector multiplication //! to compute the `A z`, `B z`, and `C z` in Nova. +#[cfg(not(feature = "std"))] +use crate::prelude::*; use ff::PrimeField; +#[cfg(feature = "std")] use rayon::prelude::*; use serde::{Deserialize, Serialize}; @@ -84,9 +87,12 @@ impl SparseMatrix { /// Multiply by a dense vector; uses rayon/gpu. /// This does not check that the shape of the matrix/vector are compatible. pub fn multiply_vec_unchecked(&self, vector: &[F]) -> Vec { - self - .indptr - .par_windows(2) + #[cfg(feature = "std")] + let windows = self.indptr.par_windows(2); + #[cfg(not(feature = "std"))] + let windows = self.indptr.windows(2); + + windows .map(|ptrs| { self .get_row_unchecked(ptrs.try_into().unwrap()) diff --git a/src/spartan/direct.rs b/src/spartan/direct.rs index 31b6b004..1c2bcf92 100644 --- a/src/spartan/direct.rs +++ b/src/spartan/direct.rs @@ -1,6 +1,8 @@ //! This module provides interfaces to directly prove a step circuit by using Spartan SNARK. //! In particular, it supports any SNARK that implements `RelaxedR1CSSNARK` trait //! (e.g., with the SNARKs implemented in ppsnark.rs or snark.rs). +#[cfg(not(feature = "std"))] +use crate::prelude::*; use crate::{ errors::NovaError, frontend::{ diff --git a/src/spartan/mod.rs b/src/spartan/mod.rs index c83ab26b..39edace1 100644 --- a/src/spartan/mod.rs +++ b/src/spartan/mod.rs @@ -15,6 +15,8 @@ pub(crate) mod math; pub(crate) mod polys; pub(crate) mod sumcheck; +#[cfg(not(feature = "std"))] +use crate::prelude::*; use crate::{ r1cs::{R1CSShape, SparseMatrix}, traits::Engine, @@ -22,7 +24,7 @@ use crate::{ }; use ff::Field; use itertools::Itertools as _; -use rayon::{iter::IntoParallelRefIterator, prelude::*}; +use plonky2_maybe_rayon::*; // Creates a vector of the first `n` powers of `s`. fn powers(s: &E::Scalar, n: usize) -> Vec { @@ -52,7 +54,11 @@ impl PolyEvalWitness { let size_max = W.iter().map(|w| w.p.len()).max().unwrap(); // Scale the input polynomials by the power of s + #[cfg(feature = "std")] let num_chunks = rayon::current_num_threads().next_power_of_two(); + #[cfg(not(feature = "std"))] + let num_chunks = 1; + let chunk_size = size_max / num_chunks; let p = if chunk_size > 0 { @@ -76,32 +82,48 @@ impl PolyEvalWitness { }) .collect::>() } else { - W.into_par_iter() + let temp = W + .into_par_iter() .zip_eq(powers.par_iter()) .map(|(mut w, s)| { if *s != E::Scalar::ONE { w.p.par_iter_mut().for_each(|e| *e *= s); } w.p - }) - .reduce( - || vec![E::Scalar::ZERO; size_max], - |left, right| { - // Sum into the largest polynomial - let (mut big, small) = if left.len() > right.len() { - (left, right) - } else { - (right, left) - }; - - big - .par_iter_mut() - .zip(small.par_iter()) - .for_each(|(b, s)| *b += s); - - big - }, - ) + }); + + #[cfg(feature = "std")] + let res = temp.reduce( + || vec![E::Scalar::ZERO; size_max], + |left, right| { + // Sum into the largest polynomial + let (mut big, small) = if left.len() > right.len() { + (left, right) + } else { + (right, left) + }; + + big + .par_iter_mut() + .zip(small.par_iter()) + .for_each(|(b, s)| *b += s); + + big + }, + ); + #[cfg(not(feature = "std"))] + let res = temp.fold(vec![E::Scalar::ZERO; size_max], |left, right| { + // Sum into the largest polynomial + let (mut big, small) = if left.len() > right.len() { + (left, right) + } else { + (right, left) + }; + big.iter_mut().zip(small).for_each(|(b, s)| *b += s); + + big + }); + res }; PolyEvalWitness { p } @@ -121,7 +143,11 @@ impl PolyEvalWitness { let powers_of_s = powers::(s, p_vec.len()); + #[cfg(feature = "std")] let num_chunks = rayon::current_num_threads().next_power_of_two(); + #[cfg(not(feature = "std"))] + let num_chunks = 1; + let chunk_size = p_vec[0].len() / num_chunks; let p = if chunk_size > 0 { @@ -145,17 +171,25 @@ impl PolyEvalWitness { }) .collect::>() } else { - zip_with!(par_iter, (p_vec, powers_of_s), |v, weight| { + let temp = zip_with!(par_iter, (p_vec, powers_of_s), |v, weight| { // compute the weighted sum for each vector v.iter().map(|&x| x * *weight).collect::>() - }) - .reduce( + }); + + #[cfg(feature = "std")] + let res = temp.reduce( || vec![E::Scalar::ZERO; p_vec[0].len()], |acc, v| { // perform vector addition to combine the weighted vectors zip_with!((acc.into_iter(), v), |x, y| x + y).collect() }, - ) + ); + #[cfg(not(feature = "std"))] + let res = temp.fold(vec![E::Scalar::ZERO; p_vec[0].len()], |acc, v| { + // perform vector addition to combine the weighted vectors + zip_with!((acc.into_iter(), v), |x, y| x + y).collect() + }); + res }; PolyEvalWitness { p } @@ -226,9 +260,14 @@ impl PolyEvalInstance { let powers_of_s = powers::(s, num_instances); // Weighted sum of evaluations let e = zip_with!(par_iter, (e_vec, powers_of_s), |e, p| *e * p).sum(); + // Weighted sum of commitments + #[cfg(feature = "std")] let c = zip_with!(par_iter, (c_vec, powers_of_s), |c, p| *c * *p) .reduce(Commitment::::default, |acc, item| acc + item); + #[cfg(not(feature = "std"))] + let c = zip_with!(iter, (c_vec, powers_of_s), |c, p| *c * *p) + .fold(Commitment::::default(), |acc, item| acc + item); PolyEvalInstance { c, @@ -253,6 +292,7 @@ fn compute_eval_table_sparse( } }; + #[cfg(feature = "std")] let (A_evals, (B_evals, C_evals)) = rayon::join( || { let mut A_evals: Vec = vec![E::Scalar::ZERO; 2 * S.num_vars]; @@ -275,5 +315,21 @@ fn compute_eval_table_sparse( }, ); + // Sequential execution instead of rayon::join + #[cfg(not(feature = "std"))] + let mut A_evals: Vec = vec![E::Scalar::ZERO; 2 * S.num_vars]; + #[cfg(not(feature = "std"))] + inner(&S.A, &mut A_evals); + + #[cfg(not(feature = "std"))] + let mut B_evals: Vec = vec![E::Scalar::ZERO; 2 * S.num_vars]; + #[cfg(not(feature = "std"))] + inner(&S.B, &mut B_evals); + + #[cfg(not(feature = "std"))] + let mut C_evals: Vec = vec![E::Scalar::ZERO; 2 * S.num_vars]; + #[cfg(not(feature = "std"))] + inner(&S.C, &mut C_evals); + (A_evals, B_evals, C_evals) } diff --git a/src/spartan/polys/eq.rs b/src/spartan/polys/eq.rs index 84f76deb..0dfa1a68 100644 --- a/src/spartan/polys/eq.rs +++ b/src/spartan/polys/eq.rs @@ -1,6 +1,8 @@ //! `EqPolynomial`: Represents multilinear extension of equality polynomials, evaluated based on binary input values. +#[cfg(not(feature = "std"))] +use crate::prelude::*; use ff::PrimeField; -use rayon::prelude::*; +use plonky2_maybe_rayon::*; /// Represents the multilinear extension polynomial (MLE) of the equality polynomial $eq(x,e)$, denoted as $\tilde{eq}(x, e)$. /// diff --git a/src/spartan/polys/masked_eq.rs b/src/spartan/polys/masked_eq.rs index 39db1607..5a1e0cde 100644 --- a/src/spartan/polys/masked_eq.rs +++ b/src/spartan/polys/masked_eq.rs @@ -1,5 +1,7 @@ //! `MaskedEqPolynomial`: Represents the `eq` polynomial over n variables, where the first 2^m entries are 0. +#[cfg(not(feature = "std"))] +use crate::prelude::*; use crate::spartan::polys::eq::EqPolynomial; use ff::PrimeField; use itertools::zip_eq; diff --git a/src/spartan/polys/multilinear.rs b/src/spartan/polys/multilinear.rs index 3ebdaa81..e25a3496 100644 --- a/src/spartan/polys/multilinear.rs +++ b/src/spartan/polys/multilinear.rs @@ -2,11 +2,13 @@ //! - `MultilinearPolynomial`: Dense representation of multilinear polynomials, represented by evaluations over all possible binary inputs. //! - `SparsePolynomial`: Efficient representation of sparse multilinear polynomials, storing only non-zero evaluations. +#[cfg(not(feature = "std"))] +use crate::prelude::*; use crate::spartan::{math::Math, polys::eq::EqPolynomial}; use core::ops::{Add, Index}; use ff::PrimeField; use itertools::Itertools as _; -use rayon::prelude::*; +use plonky2_maybe_rayon::*; use serde::{Deserialize, Serialize}; /// A multilinear extension of a polynomial $Z(\cdot)$, denote it as $\tilde{Z}(x_1, ..., x_m)$ diff --git a/src/spartan/polys/power.rs b/src/spartan/polys/power.rs index 27065313..dfb2356e 100644 --- a/src/spartan/polys/power.rs +++ b/src/spartan/polys/power.rs @@ -1,5 +1,7 @@ //! `PowPolynomial`: Represents multilinear extension of power polynomials +#[cfg(not(feature = "std"))] +use crate::prelude::*; use core::iter::successors; use ff::PrimeField; diff --git a/src/spartan/polys/univariate.rs b/src/spartan/polys/univariate.rs index a5091153..e4a6d6a4 100644 --- a/src/spartan/polys/univariate.rs +++ b/src/spartan/polys/univariate.rs @@ -1,10 +1,12 @@ //! Main components: //! - `UniPoly`: an univariate dense polynomial in coefficient form (big endian), //! - `CompressedUniPoly`: a univariate dense polynomial, compressed (omitted linear term), in coefficient form (little endian), +#[cfg(not(feature = "std"))] +use crate::prelude::*; use crate::traits::{AbsorbInRO2Trait, Engine, Group, ROTrait, TranscriptReprTrait}; use core::panic; use ff::PrimeField; -use rayon::prelude::{IntoParallelIterator, ParallelIterator}; +use plonky2_maybe_rayon::*; use serde::{Deserialize, Serialize}; // ax^2 + bx + c stored as vec![c, b, a] @@ -133,6 +135,7 @@ pub fn gaussian_elimination(matrix: &mut [Vec]) -> Vec { // Disable cargo clippy warnings about needless range loops. // Checking the diagonal like this is simpler than any alternative. #[allow(clippy::needless_range_loop)] + #[cfg(feature = "std")] for i in 0..size { if matrix[i][i] == F::ZERO { println!("Infinitely many solutions"); diff --git a/src/spartan/ppsnark.rs b/src/spartan/ppsnark.rs index 0225abc8..4086b7b7 100644 --- a/src/spartan/ppsnark.rs +++ b/src/spartan/ppsnark.rs @@ -4,6 +4,8 @@ //! polynomial commitment scheme in which the verifier's costs is succinct. //! This code includes experimental optimizations to reduce runtimes and proof sizes. //! We have not yet proven the security of these optimizations, so this code is subject to significant changes in the future. +#[cfg(not(feature = "std"))] +use crate::prelude::*; use crate::{ digest::{DigestComputer, SimpleDigestible}, errors::NovaError, @@ -32,8 +34,9 @@ use crate::{ use core::cmp::max; use ff::Field; use itertools::Itertools as _; +#[cfg(feature = "std")] use once_cell::sync::OnceCell; -use rayon::prelude::*; +use plonky2_maybe_rayon::*; use serde::{Deserialize, Serialize}; fn padded(v: &[E::Scalar], n: usize, e: &E::Scalar) -> Vec { @@ -147,8 +150,7 @@ impl R1CSShapeSparkRepr { }; // timestamp polynomials for row - let (ts_row, ts_col) = - rayon::join(|| timestamp_calc(N, N, &row), || timestamp_calc(N, N, &col)); + let (ts_row, ts_col) = join(|| timestamp_calc(N, N, &row), || timestamp_calc(N, N, &col)); // a routine to turn a vector of usize into a vector scalars let to_vec_scalar = |v: &[usize]| -> Vec { @@ -376,7 +378,7 @@ impl MemorySumcheckInstance { -> (Vec, Vec) { let hash_func = |addr: &E::Scalar, val: &E::Scalar| -> E::Scalar { *val * gamma + *addr }; assert_eq!(addr.len(), lookups.len()); - rayon::join( + let (result_1, result_2) = join( || { (0..mem.len()) .map(|i| hash_func(&E::Scalar::from(i as u64), &mem[i])) @@ -387,10 +389,12 @@ impl MemorySumcheckInstance { .map(|i| hash_func(&addr[i], &lookups[i])) .collect::>() }, - ) + ); + + (result_1, result_2) }; - let ((T_row, W_row), (T_col, W_col)) = rayon::join( + let ((T_row, W_row), (T_col, W_col)) = join( || hash_func_vec(mem_row, addr_row, L_row), || hash_func_vec(mem_col, addr_col, L_col), ); @@ -437,9 +441,9 @@ impl MemorySumcheckInstance { Result, NovaError>, ), ) { - rayon::join( + let ((inv_T_plus_r, inv_W_plus_r), (T_plus_r, W_plus_r)) = join( || { - rayon::join( + join( || { let inv = batch_invert(&T.par_iter().map(|e| *e + *r).collect::>())?; @@ -453,18 +457,20 @@ impl MemorySumcheckInstance { ) }, || { - rayon::join( + join( || Ok(T.par_iter().map(|e| *e + *r).collect::>()), || Ok(W.par_iter().map(|e| *e + *r).collect::>()), ) }, - ) + ); + + ((inv_T_plus_r, inv_W_plus_r), (T_plus_r, W_plus_r)) }; let ( ((t_plus_r_inv_row, w_plus_r_inv_row), (t_plus_r_row, w_plus_r_row)), ((t_plus_r_inv_col, w_plus_r_inv_col), (t_plus_r_col, w_plus_r_col)), - ) = rayon::join( + ) = join( || helper(&T_row, &W_row, ts_row, r), || helper(&T_col, &W_col, ts_col, r), ); @@ -477,15 +483,15 @@ impl MemorySumcheckInstance { let ( (comm_t_plus_r_inv_row, comm_w_plus_r_inv_row), (comm_t_plus_r_inv_col, comm_w_plus_r_inv_col), - ) = rayon::join( + ) = join( || { - rayon::join( + join( || E::CE::commit(ck, &t_plus_r_inv_row, &E::Scalar::ZERO), || E::CE::commit(ck, &w_plus_r_inv_row, &E::Scalar::ZERO), ) }, || { - rayon::join( + join( || E::CE::commit(ck, &t_plus_r_inv_col, &E::Scalar::ZERO), || E::CE::commit(ck, &w_plus_r_inv_col, &E::Scalar::ZERO), ) @@ -966,9 +972,9 @@ impl> RelaxedR1CSSNARK { let mut cubic_polys: Vec> = Vec::new(); let num_rounds = mem.size().log_2(); for _ in 0..num_rounds { - let ((evals_mem, evals_outer), (evals_inner, evals_witness)) = rayon::join( - || rayon::join(|| mem.evaluation_points(), || outer.evaluation_points()), - || rayon::join(|| inner.evaluation_points(), || witness.evaluation_points()), + let ((evals_mem, evals_outer), (evals_inner, evals_witness)) = join( + || join(|| mem.evaluation_points(), || outer.evaluation_points()), + || join(|| inner.evaluation_points(), || witness.evaluation_points()), ); let evals: Vec> = evals_mem @@ -998,9 +1004,9 @@ impl> RelaxedR1CSSNARK { let r_i = transcript.squeeze(b"c")?; r.push(r_i); - let _ = rayon::join( - || rayon::join(|| mem.bound(&r_i), || outer.bound(&r_i)), - || rayon::join(|| inner.bound(&r_i), || witness.bound(&r_i)), + let _ = join( + || join(|| mem.bound(&r_i), || outer.bound(&r_i)), + || join(|| inner.bound(&r_i), || witness.bound(&r_i)), ); e = poly.evaluate(&r_i); @@ -1042,14 +1048,22 @@ impl> VerifierKey { impl> DigestHelperTrait for VerifierKey { /// Returns the digest of the verifier's key fn digest(&self) -> E::Scalar { - self + #[cfg(feature = "std")] + let res = self .digest .get_or_try_init(|| { let dc = DigestComputer::new(self); dc.digest() }) .cloned() - .expect("Failure to retrieve digest!") + .expect("Failure to retrieve digest!"); + #[cfg(not(feature = "std"))] + let res = *self.digest.get_or_init(|| { + let dc = DigestComputer::new(self); + dc.digest().expect("Failure to retrieve digest!") + }); + + res } } @@ -1119,10 +1133,10 @@ impl> RelaxedR1CSSNARKTrait for Relax let (mut Az, mut Bz, mut Cz) = S.multiply_vec(&z)?; // commit to Az, Bz, Cz - let (comm_Az, (comm_Bz, comm_Cz)) = rayon::join( + let (comm_Az, (comm_Bz, comm_Cz)) = join( || E::CE::commit(ck, &Az, &E::Scalar::ZERO), || { - rayon::join( + join( || E::CE::commit(ck, &Bz, &E::Scalar::ZERO), || E::CE::commit(ck, &Cz, &E::Scalar::ZERO), ) @@ -1152,6 +1166,7 @@ impl> RelaxedR1CSSNARKTrait for Relax .into_par_iter() .map(|p| MultilinearPolynomial::evaluate_with(p, &tau)) .collect::>(); + (evals_at_tau[0], evals_at_tau[1], evals_at_tau[2]) }; @@ -1159,7 +1174,7 @@ impl> RelaxedR1CSSNARKTrait for Relax // L_row(i) = eq(tau, row(i)) for all i // L_col(i) = z(col(i)) for all i let (mem_row, mem_col, L_row, L_col) = pk.S_repr.evaluation_oracles(&S, &tau, &z); - let (comm_L_row, comm_L_col) = rayon::join( + let (comm_L_row, comm_L_col) = join( || E::CE::commit(ck, &L_row, &E::Scalar::ZERO), || E::CE::commit(ck, &L_col, &E::Scalar::ZERO), ); @@ -1186,7 +1201,7 @@ impl> RelaxedR1CSSNARKTrait for Relax let gamma = transcript.squeeze(b"g")?; let r = transcript.squeeze(b"r")?; - let ((mut outer_sc_inst, mut inner_sc_inst), mem_res) = rayon::join( + let ((mut outer_sc_inst, mut inner_sc_inst), mem_res) = join( || { // a sum-check instance to prove the first claim let outer_sc_inst = OuterSumcheckInstance::new( @@ -1287,6 +1302,7 @@ impl> RelaxedR1CSSNARKTrait for Relax let eval_W = claims_witness[0][0]; // compute the remaining claims that did not come for free from the sum-check prover + let (eval_Cz, eval_E, eval_val_A, eval_val_B, eval_val_C, eval_row, eval_col) = { let e = [ &Cz, @@ -1300,6 +1316,7 @@ impl> RelaxedR1CSSNARKTrait for Relax .into_par_iter() .map(|p| MultilinearPolynomial::evaluate_with(p, &rand_sc)) .collect::>(); + (e[0], e[1], e[2], e[3], e[4], e[5], e[6]) }; diff --git a/src/spartan/snark.rs b/src/spartan/snark.rs index 76692370..6434b6ec 100644 --- a/src/spartan/snark.rs +++ b/src/spartan/snark.rs @@ -4,6 +4,8 @@ //! description of R1CS matrices. This is essentially optimal for the verifier when using //! an IPA-based polynomial commitment scheme. +#[cfg(not(feature = "std"))] +use crate::prelude::*; use crate::{ digest::{DigestComputer, SimpleDigestible}, errors::NovaError, @@ -25,8 +27,11 @@ use crate::{ }; use ff::Field; use itertools::Itertools as _; +#[cfg(feature = "std")] use once_cell::sync::OnceCell; -use rayon::prelude::*; +use plonky2_maybe_rayon::*; +#[cfg(feature = "std")] +use rayon::slice::ParallelSlice; use serde::{Deserialize, Serialize}; /// A type that represents the prover's key @@ -62,14 +67,22 @@ impl> VerifierKey { impl> DigestHelperTrait for VerifierKey { /// Returns the digest of the verifier's key. fn digest(&self) -> E::Scalar { - self + #[cfg(feature = "std")] + let res = self .digest .get_or_try_init(|| { let dc = DigestComputer::::new(self); dc.digest() }) .cloned() - .expect("Failure to retrieve digest!") + .expect("Failure to retrieve digest!"); + #[cfg(not(feature = "std"))] + let res = *self.digest.get_or_init(|| { + let dc = DigestComputer::::new(self); + dc.digest().expect("Failure to retrieve digest!") + }); + + res } } @@ -196,6 +209,7 @@ impl> RelaxedR1CSSNARKTrait for Relax assert_eq!(evals_A.len(), evals_B.len()); assert_eq!(evals_A.len(), evals_C.len()); + (0..evals_A.len()) .into_par_iter() .map(|i| evals_A[i] + r * evals_B[i] + r * r * evals_C[i]) @@ -341,8 +355,12 @@ impl> RelaxedR1CSSNARKTrait for Relax -> Vec { let evaluate_with_table = |M: &SparseMatrix, T_x: &[E::Scalar], T_y: &[E::Scalar]| -> E::Scalar { - M.indptr - .par_windows(2) + #[cfg(feature = "std")] + let windows = M.indptr.par_windows(2); + #[cfg(not(feature = "std"))] + let windows = M.indptr.windows(2); + + windows .enumerate() .map(|(row_idx, ptrs)| { M.get_row_unchecked(ptrs.try_into().unwrap()) @@ -352,7 +370,7 @@ impl> RelaxedR1CSSNARKTrait for Relax .sum() }; - let (T_x, T_y) = rayon::join( + let (T_x, T_y) = join( || EqPolynomial::evals_from_points(r_x), || EqPolynomial::evals_from_points(r_y), ); diff --git a/src/spartan/sumcheck.rs b/src/spartan/sumcheck.rs index f4590e74..055bdd50 100644 --- a/src/spartan/sumcheck.rs +++ b/src/spartan/sumcheck.rs @@ -1,3 +1,5 @@ +#[cfg(not(feature = "std"))] +use crate::prelude::*; use crate::{ errors::NovaError, spartan::polys::{ @@ -8,7 +10,7 @@ use crate::{ }; use ff::Field; use itertools::Itertools as _; -use rayon::prelude::*; +use plonky2_maybe_rayon::*; use serde::{Deserialize, Serialize}; /// Defines a trait for implementing sum-check in a generic manner @@ -129,22 +131,31 @@ impl SumcheckProof { F: Fn(&E::Scalar, &E::Scalar) -> E::Scalar + Sync, { let len = poly_A.len() / 2; - (0..len) - .into_par_iter() - .map(|i| { - // eval 0: bound_func is A(low) - let eval_point_0 = comb_func(&poly_A[i], &poly_B[i]); - - // eval 2: bound_func is -A(low) + 2*A(high) - let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i]; - let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i]; - let eval_point_2 = comb_func(&poly_A_bound_point, &poly_B_bound_point); - (eval_point_0, eval_point_2) - }) - .reduce( - || (E::Scalar::ZERO, E::Scalar::ZERO), - |a, b| (a.0 + b.0, a.1 + b.1), - ) + let iter = 0..len; + + let iter = iter.into_par_iter(); + + let eval_fn = |i| { + let eval_point_0 = comb_func(&poly_A[i], &poly_B[i]); + let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i]; + let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i]; + let eval_point_2 = comb_func(&poly_A_bound_point, &poly_B_bound_point); + (eval_point_0, eval_point_2) + }; + + #[cfg(feature = "std")] + let res = iter.map(eval_fn).reduce( + || (E::Scalar::ZERO, E::Scalar::ZERO), + |a, b| (a.0 + b.0, a.1 + b.1), + ); + #[cfg(not(feature = "std"))] + let res = iter + .map(eval_fn) + .fold((E::Scalar::ZERO, E::Scalar::ZERO), |a, b| { + (a.0 + b.0, a.1 + b.1) + }); + + res } pub fn prove_quad( @@ -182,10 +193,15 @@ impl SumcheckProof { claim_per_round = poly.evaluate(&r_i); // bind all tables to the verifier's challenge + #[cfg(feature = "std")] rayon::join( || poly_A.bind_poly_var_top(&r_i), || poly_B.bind_poly_var_top(&r_i), ); + #[cfg(not(feature = "std"))] + poly_A.bind_poly_var_top(&r_i); + #[cfg(not(feature = "std"))] + poly_B.bind_poly_var_top(&r_i); } Ok(( @@ -247,6 +263,7 @@ impl SumcheckProof { for current_round in 0..num_rounds_max { let remaining_rounds = num_rounds_max - current_round; + let evals: Vec<(E::Scalar, E::Scalar)> = zip_with!( par_iter, (num_rounds, claims, poly_A_vec, poly_B_vec), @@ -284,7 +301,7 @@ impl SumcheckProof { ), |num_rounds, poly_A, poly_B| { if remaining_rounds <= *num_rounds { - let _ = rayon::join( + join( || poly_A.bind_poly_var_top(&r_i), || poly_B.bind_poly_var_top(&r_i), ); @@ -331,37 +348,44 @@ impl SumcheckProof { F: Fn(&E::Scalar, &E::Scalar, &E::Scalar) -> E::Scalar + Sync, { let len = poly_A.len() / 2; - (0..len) - .into_par_iter() - .map(|i| { - // eval 0: bound_func is A(low) - let eval_point_0 = comb_func(&poly_A[i], &poly_B[i], &poly_C[i]); - - // eval 2: bound_func is -A(low) + 2*A(high) - let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i]; - let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i]; - let poly_C_bound_point = poly_C[len + i] + poly_C[len + i] - poly_C[i]; - let eval_point_2 = comb_func( - &poly_A_bound_point, - &poly_B_bound_point, - &poly_C_bound_point, - ); + let map_func = |i| { + // eval 0: bound_func is A(low) + let eval_point_0 = comb_func(&poly_A[i], &poly_B[i], &poly_C[i]); + + // eval 2: bound_func is -A(low) + 2*A(high) + let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i]; + let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i]; + let poly_C_bound_point = poly_C[len + i] + poly_C[len + i] - poly_C[i]; + let eval_point_2 = comb_func( + &poly_A_bound_point, + &poly_B_bound_point, + &poly_C_bound_point, + ); - // eval 3: bound_func is -2A(low) + 3A(high); computed incrementally with bound_func applied to eval(2) - let poly_A_bound_point = poly_A_bound_point + poly_A[len + i] - poly_A[i]; - let poly_B_bound_point = poly_B_bound_point + poly_B[len + i] - poly_B[i]; - let poly_C_bound_point = poly_C_bound_point + poly_C[len + i] - poly_C[i]; - let eval_point_3 = comb_func( - &poly_A_bound_point, - &poly_B_bound_point, - &poly_C_bound_point, - ); - (eval_point_0, eval_point_2, eval_point_3) - }) - .reduce( - || (E::Scalar::ZERO, E::Scalar::ZERO, E::Scalar::ZERO), - |a, b| (a.0 + b.0, a.1 + b.1, a.2 + b.2), - ) + // eval 3: bound_func is -2A(low) + 3A(high); computed incrementally with bound_func applied to eval(2) + let poly_A_bound_point = poly_A_bound_point + poly_A[len + i] - poly_A[i]; + let poly_B_bound_point = poly_B_bound_point + poly_B[len + i] - poly_B[i]; + let poly_C_bound_point = poly_C_bound_point + poly_C[len + i] - poly_C[i]; + let eval_point_3 = comb_func( + &poly_A_bound_point, + &poly_B_bound_point, + &poly_C_bound_point, + ); + (eval_point_0, eval_point_2, eval_point_3) + }; + + #[cfg(feature = "std")] + let res = (0..len).into_par_iter().map(map_func).reduce( + || (E::Scalar::ZERO, E::Scalar::ZERO, E::Scalar::ZERO), + |a, b| (a.0 + b.0, a.1 + b.1, a.2 + b.2), + ); + #[cfg(not(feature = "std"))] + let res = (0..len).map(map_func).fold( + (E::Scalar::ZERO, E::Scalar::ZERO, E::Scalar::ZERO), + |a, b| (a.0 + b.0, a.1 + b.1, a.2 + b.2), + ); + + res } #[inline] @@ -376,41 +400,49 @@ impl SumcheckProof { F: Fn(&E::Scalar, &E::Scalar, &E::Scalar, &E::Scalar) -> E::Scalar + Sync, { let len = poly_A.len() / 2; - (0..len) - .into_par_iter() - .map(|i| { - // eval 0: bound_func is A(low) - let eval_point_0 = comb_func(&poly_A[i], &poly_B[i], &poly_C[i], &poly_D[i]); - - // eval 2: bound_func is -A(low) + 2*A(high) - let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i]; - let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i]; - let poly_C_bound_point = poly_C[len + i] + poly_C[len + i] - poly_C[i]; - let poly_D_bound_point = poly_D[len + i] + poly_D[len + i] - poly_D[i]; - let eval_point_2 = comb_func( - &poly_A_bound_point, - &poly_B_bound_point, - &poly_C_bound_point, - &poly_D_bound_point, - ); - // eval 3: bound_func is -2A(low) + 3A(high); computed incrementally with bound_func applied to eval(2) - let poly_A_bound_point = poly_A_bound_point + poly_A[len + i] - poly_A[i]; - let poly_B_bound_point = poly_B_bound_point + poly_B[len + i] - poly_B[i]; - let poly_C_bound_point = poly_C_bound_point + poly_C[len + i] - poly_C[i]; - let poly_D_bound_point = poly_D_bound_point + poly_D[len + i] - poly_D[i]; - let eval_point_3 = comb_func( - &poly_A_bound_point, - &poly_B_bound_point, - &poly_C_bound_point, - &poly_D_bound_point, - ); - (eval_point_0, eval_point_2, eval_point_3) - }) - .reduce( - || (E::Scalar::ZERO, E::Scalar::ZERO, E::Scalar::ZERO), - |a, b| (a.0 + b.0, a.1 + b.1, a.2 + b.2), - ) + let map_func = |i| { + // eval 0: bound_func is A(low) + let eval_point_0 = comb_func(&poly_A[i], &poly_B[i], &poly_C[i], &poly_D[i]); + + // eval 2: bound_func is -A(low) + 2*A(high) + let poly_A_bound_point = poly_A[len + i] + poly_A[len + i] - poly_A[i]; + let poly_B_bound_point = poly_B[len + i] + poly_B[len + i] - poly_B[i]; + let poly_C_bound_point = poly_C[len + i] + poly_C[len + i] - poly_C[i]; + let poly_D_bound_point = poly_D[len + i] + poly_D[len + i] - poly_D[i]; + let eval_point_2 = comb_func( + &poly_A_bound_point, + &poly_B_bound_point, + &poly_C_bound_point, + &poly_D_bound_point, + ); + + // eval 3: bound_func is -2A(low) + 3A(high); computed incrementally with bound_func applied to eval(2) + let poly_A_bound_point = poly_A_bound_point + poly_A[len + i] - poly_A[i]; + let poly_B_bound_point = poly_B_bound_point + poly_B[len + i] - poly_B[i]; + let poly_C_bound_point = poly_C_bound_point + poly_C[len + i] - poly_C[i]; + let poly_D_bound_point = poly_D_bound_point + poly_D[len + i] - poly_D[i]; + let eval_point_3 = comb_func( + &poly_A_bound_point, + &poly_B_bound_point, + &poly_C_bound_point, + &poly_D_bound_point, + ); + (eval_point_0, eval_point_2, eval_point_3) + }; + + #[cfg(feature = "std")] + let res = (0..len).into_par_iter().map(map_func).reduce( + || (E::Scalar::ZERO, E::Scalar::ZERO, E::Scalar::ZERO), + |a, b| (a.0 + b.0, a.1 + b.1, a.2 + b.2), + ); + #[cfg(not(feature = "std"))] + let res = (0..len).map(map_func).fold( + (E::Scalar::ZERO, E::Scalar::ZERO, E::Scalar::ZERO), + |a, b| (a.0 + b.0, a.1 + b.1, a.2 + b.2), + ); + + res } pub fn prove_cubic_with_additive_term( @@ -459,6 +491,7 @@ impl SumcheckProof { claim_per_round = poly.evaluate(&r_i); // bound all tables to the verifier's challenge + #[cfg(feature = "std")] rayon::join( || { rayon::join( @@ -473,6 +506,14 @@ impl SumcheckProof { ) }, ); + #[cfg(not(feature = "std"))] + poly_A.bind_poly_var_top(&r_i); + #[cfg(not(feature = "std"))] + poly_B.bind_poly_var_top(&r_i); + #[cfg(not(feature = "std"))] + poly_C.bind_poly_var_top(&r_i); + #[cfg(not(feature = "std"))] + poly_D.bind_poly_var_top(&r_i); } Ok(( diff --git a/src/traits/circuit.rs b/src/traits/circuit.rs index 1ebdf5e5..148197e1 100644 --- a/src/traits/circuit.rs +++ b/src/traits/circuit.rs @@ -1,5 +1,7 @@ //! This module defines traits that a step function must implement use crate::frontend::{num::AllocatedNum, ConstraintSystem, SynthesisError}; +#[cfg(not(feature = "std"))] +use crate::prelude::*; use core::marker::PhantomData; use ff::PrimeField; diff --git a/src/traits/commitment.rs b/src/traits/commitment.rs index 1f08a069..f2cbbea7 100644 --- a/src/traits/commitment.rs +++ b/src/traits/commitment.rs @@ -1,16 +1,17 @@ //! This module defines a collection of traits that define the behavior of a commitment engine //! We require the commitment engine to provide a commitment to vectors with a single group element -use crate::{ - provider::ptau::PtauFileError, - traits::{AbsorbInRO2Trait, AbsorbInROTrait, Engine, TranscriptReprTrait}, -}; +#[cfg(not(feature = "std"))] +use crate::prelude::*; +#[cfg(feature = "std")] +use crate::provider::ptau::PtauFileError; +use crate::traits::{AbsorbInRO2Trait, AbsorbInROTrait, Engine, TranscriptReprTrait}; use core::{ fmt::Debug, ops::{Add, Mul, MulAssign}, }; use num_integer::Integer; use num_traits::ToPrimitive; -use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; +use plonky2_maybe_rayon::*; use serde::{Deserialize, Serialize}; /// A helper trait for types implementing scalar multiplication. @@ -61,6 +62,7 @@ pub trait CommitmentEngineTrait: Clone + Send + Sync { type Commitment: CommitmentTrait; /// Load keys + #[cfg(feature = "std")] fn load_setup( reader: &mut (impl std::io::Read + std::io::Seek), label: &'static [u8], diff --git a/src/traits/mod.rs b/src/traits/mod.rs index 5a0e97e4..1e1d2469 100644 --- a/src/traits/mod.rs +++ b/src/traits/mod.rs @@ -1,4 +1,6 @@ //! This module defines various traits required by the users of the library to implement. +#[cfg(not(feature = "std"))] +use crate::prelude::*; use crate::{ errors::NovaError, frontend::{num::AllocatedNum, AllocatedBit, ConstraintSystem, SynthesisError}, diff --git a/src/traits/snark.rs b/src/traits/snark.rs index 1dcc2d82..470a119b 100644 --- a/src/traits/snark.rs +++ b/src/traits/snark.rs @@ -1,4 +1,6 @@ //! This module defines a collection of traits that define the behavior of a `zkSNARK` for `RelaxedR1CS` +#[cfg(not(feature = "std"))] +use crate::prelude::*; use crate::{ errors::NovaError, r1cs::{R1CSShape, RelaxedR1CSInstance, RelaxedR1CSWitness}, @@ -24,7 +26,11 @@ pub trait RelaxedR1CSSNARKTrait: type ProverKey: Send + Sync + Serialize + for<'de> Deserialize<'de>; /// A type that represents the verifier's key + #[cfg(feature = "std")] type VerifierKey: Send + Sync + Serialize + for<'de> Deserialize<'de> + DigestHelperTrait; + /// A type that represents the verifier's key + #[cfg(not(feature = "std"))] + type VerifierKey: Send + Serialize + for<'de> Deserialize<'de> + DigestHelperTrait; /// This associated function (not a method) provides a hint that offers /// a minimum sizing cue for the commitment key used by this SNARK