Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
aca9049
cargo.toml update
milosdjurica Feb 10, 2025
42e7341
Merge branch 'microsoft:main' into no_std_latest
milosdjurica Feb 10, 2025
9486641
Merge branch 'microsoft:main' into no_std_latest
milosdjurica Mar 3, 2025
3aaff41
halo2curves only on STD, prelude crate
milosdjurica Feb 14, 2025
04ee879
no_std pasta curves impl
milosdjurica Feb 18, 2025
0628d50
no_std pasta_curves impl + comments in cargo.toml
milosdjurica Mar 3, 2025
b7782a0
bincode and byteorder fix, added comments for later, TODO delete comm…
milosdjurica Mar 3, 2025
3cb4f0f
bincode fix
milosdjurica Mar 3, 2025
670a842
rayon paralel only for STD, sequential for NO_STD
milosdjurica Mar 4, 2025
96fb896
Prelude changes (#11)
milosdjurica Mar 4, 2025
4aa9ebd
Merge branch 'microsoft:main' into no_std_latest
milosdjurica Mar 4, 2025
c81aecb
getrandom optional for wasm32
milosdjurica Mar 4, 2025
c49fa4f
once_cell from core for no_std and allow unused imports for FloatCore
milosdjurica Mar 11, 2025
577e8d5
removed comments
milosdjurica Mar 11, 2025
30ddc84
moving core::cell:OnceCell into prelude
milosdjurica Mar 11, 2025
bff6b4d
Merge main (#12)
milosdjurica Mar 13, 2025
31b310c
Merge branch 'main' into no_std_latest
milosdjurica Mar 13, 2025
13448ee
comment formatting
milosdjurica Mar 13, 2025
b51c4cb
removed cs and vk
milosdjurica Mar 13, 2025
46866bf
removed comments
milosdjurica Mar 14, 2025
58b6783
maybe_rayon (#13)
milosdjurica Mar 14, 2025
db76abf
merge main
milosdjurica Mar 14, 2025
ec35376
no_std job
milosdjurica Mar 14, 2025
84059a1
clippy fix
milosdjurica Mar 14, 2025
90679f9
std::marker::PhantomData -> core::marker::PhantomData
milosdjurica Mar 14, 2025
5804c19
OnceCell instead of Option
milosdjurica Mar 17, 2025
339994b
clippy-no-default-features workflow & clippy fix
milosdjurica Mar 17, 2025
b108ab8
removed comments
milosdjurica Mar 17, 2025
6a91fba
write_bytes -> to_bytes
milosdjurica Mar 18, 2025
e87ed1d
result -> digest
milosdjurica Mar 18, 2025
5b26ac4
removed comments, updated imports from std to core
milosdjurica Mar 18, 2025
6d4c959
both STD and NO_STD using same msm
milosdjurica Mar 18, 2025
ae8a034
merge main
milosdjurica Mar 20, 2025
27a4de4
merge main
milosdjurica Mar 21, 2025
b45a5db
Changed from pasta_curves in no_std to halo2curves for both (#14)
milosdjurica Mar 24, 2025
402073b
removed chacha random number
milosdjurica Mar 25, 2025
3514046
merge main
milosdjurica Mar 26, 2025
72b4521
merge main
milosdjurica Apr 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 34 additions & 6 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ permissions:

on:
push:
branches: [ main ]
branches: [main]
pull_request:
branches: [ main ]
branches: [main]

jobs:
build:
Expand All @@ -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:
Expand Down Expand Up @@ -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
77 changes: 57 additions & 20 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand All @@ -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"
Expand All @@ -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 = []
7 changes: 3 additions & 4 deletions examples/and.rs
Original file line number Diff line number Diff line change
@@ -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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an alternative to this? May be I'm misunderstanding this. Are we at the risk of this getting deprecated soon?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, there is no risk of getting deprecated. legacy() is just a function to get same encoding configuration that was used in v1.x.x . I used legacy() over standard, just in order to make it backward compatible with previous version of Nova.
Here is the bincode code explaining the difference between the standard() and legacy() functions.

https://github.com/bincode-org/bincode/blob/508a00bb17fda81da1ad2b3856669c27b7fe5dbd/src/config.rs#L52

/// The default config for bincode 2.0. By default this will be:
/// - Little endian
/// - Variable int encoding
pub const fn standard() -> Configuration {
    generate()
}

/// Creates the "legacy" default config. This is the default config that was present in bincode 1.0
/// - Little endian
/// - Fixed int length encoding
pub const fn legacy() -> Configuration<LittleEndian, Fixint, NoLimit> {
    generate()
}

Copy link
Collaborator

@srinathsetty srinathsetty Mar 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clarification! I'm not sure there's benefit from being backward compatible with a previous version of Nova. If we are updating to bincode 2.0, I think it makes sense to have the bincode 2.0 format.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is possible to have standard config too, it will require some additional changes in tests too, but it should be feasible. Let me know which one you prefer

use core::marker::PhantomData;
use ff::{Field, PrimeField, PrimeFieldBits};
use flate2::{write::ZlibEncoder, Compression};
use nova_snark::{
frontend::{
num::AllocatedNum, AllocatedBit, ConstraintSystem, LinearCombination, SynthesisError,
Expand Down Expand Up @@ -286,9 +286,8 @@ fn main() {
assert!(res.is_ok());
let compressed_snark = res.unwrap();

let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like we are removing flate2 compression. Is there a reason?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, due to the changes in bincode . If we use bincode::serde::::encode_to_vec() , that function accepts only 2 arguments -> what we are encoding (compressed_snark in this case), and bincode configuration.

If we use bincode::serde::encode_into_writter() , this function accepts 3 arguments -> what we are encoding, "writer", and bincode configuration. However, writer has to implement bincode::Writer trait, and this requirement is not satisfied.

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()
Expand Down
7 changes: 3 additions & 4 deletions examples/hashchain.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand Down Expand Up @@ -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()
Expand Down
7 changes: 3 additions & 4 deletions examples/minroot.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -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()
Expand Down
72 changes: 43 additions & 29 deletions src/digest.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
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<W: Sized + io::Write>(&self, byte_sink: &mut W) -> Result<(), io::Error>;
/// Write the byte representation of Self. Returns a byte vector.
fn to_bytes(&self) -> Result<Vec<u8>, NovaError>;
}

/// Marker trait to be implemented for types that implement `Digestible` and `Serialize`.
/// Their instances will be serialized to bytes then digested.
pub trait SimpleDigestible: Serialize {}

impl<T: SimpleDigestible> Digestible for T {
fn write_bytes<W: Sized + io::Write>(&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<Vec<u8>, NovaError> {
bincode::serde::encode_to_vec(self, legacy()).map_err(|e| NovaError::DigestError {
reason: e.to_string(),
})
}
}

Expand Down Expand Up @@ -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<F, io::Error> {
pub fn digest(&self) -> Result<F, core::fmt::Error> {
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<u8> = 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;
Expand All @@ -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
}
}

Expand All @@ -120,7 +132,7 @@ mod tests {
let oc = OnceCell::new();
oc.set(<E as Engine>::Scalar::ONE).unwrap();

let s2: S<E> = S { i: 42, digest: oc };
let mut s2: S<E> = S { i: 42, digest: oc };

assert_eq!(
DigestComputer::<<E as Engine>::Scalar, _>::new(&s1)
Expand All @@ -143,19 +155,21 @@ mod tests {

#[test]
fn test_digest_impervious_to_serialization() {
let good_s = S::<E>::new(42);

// let's set up a struct with a weird digest field to confuse deserializers
let oc = OnceCell::new();
oc.set(<E as Engine>::Scalar::ONE).unwrap();

let bad_s: S<E> = S { i: 42, digest: oc };
let mut good_s = S::<E>::new(42);
let mut bad_s: S<E> = 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<E> = bincode::serde::decode_from_slice(&naughty_bytes, legacy())
.unwrap()
.0;

let retrieved_s: S<E> = bincode::deserialize(&naughty_bytes).unwrap();
assert_eq!(good_s.digest(), retrieved_s.digest())
}
}
11 changes: 10 additions & 1 deletion src/errors.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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<SynthesisError> for NovaError {
Expand Down
Loading