Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experimental support for no_std #344

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 13 additions & 3 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build
- name: Build (std)
uses: actions-rs/toolchain@v1
with:
toolchain: stable
Expand All @@ -38,7 +38,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run tests
- name: Run tests (std)
uses: actions-rs/toolchain@v1
with:
toolchain: stable
Expand All @@ -47,6 +47,15 @@ jobs:
command: test
args: --release --verbose

- name: Run tests (no_std)
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- uses: actions-rs/cargo@v1
with:
command: test
args: --release --no-default-features --verbose

fmt:
runs-on: ubuntu-latest
steps:
Expand All @@ -73,4 +82,5 @@ jobs:
- uses: actions-rs/cargo@v1
with:
command: clippy
args: --all-targets -- -D warnings
#args: --all-targets -- -D warnings // TODO: bring back
args: --all-targets
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ thiserror = "2.0"
group = "0.13.0"
once_cell = "1.18.0"
itertools = "0.13.0"
hashbrown = { version = "0.15.2", features = ["alloc"] }

[target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = { version = "0.2", default-features = false, features = ["js"] }
Expand Down Expand Up @@ -69,5 +70,6 @@ name = "ppsnark"
harness = false

[features]
default = ["halo2curves/asm"]
default = ["halo2curves/asm", "std"]
std = []
flamegraph = ["pprof2/flamegraph", "pprof2/criterion"]
1 change: 1 addition & 0 deletions src/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
alloc_num_equals, alloc_scalar_as_base, alloc_zero, conditionally_select_vec, le_bits_to_num,
},
},
prelude::*,

Check warning on line 19 in src/circuit.rs

View workflow job for this annotation

GitHub Actions / clippy

unused import: `prelude::*`

Check warning on line 19 in src/circuit.rs

View workflow job for this annotation

GitHub Actions / build

unused import: `prelude::*`

Check warning on line 19 in src/circuit.rs

View workflow job for this annotation

GitHub Actions / test

unused import: `prelude::*`
r1cs::{R1CSInstance, RelaxedR1CSInstance},
traits::{
circuit::StepCircuit, commitment::CommitmentTrait, Engine, ROCircuitTrait, ROConstantsCircuit,
Expand Down
92 changes: 35 additions & 57 deletions src/digest.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
use bincode::Options;
use crate::{prelude::*, NovaError, NUM_HASH_BITS};

Check warning on line 1 in src/digest.rs

View workflow job for this annotation

GitHub Actions / clippy

unused import: `prelude::*`

Check warning on line 1 in src/digest.rs

View workflow job for this annotation

GitHub Actions / build

unused import: `prelude::*`

Check warning on line 1 in src/digest.rs

View workflow job for this annotation

GitHub Actions / test

unused import: `prelude::*`
use bincode::{DefaultOptions, Options};
use core::marker::PhantomData;
use ff::PrimeField;
use serde::Serialize;
use sha3::{Digest, Sha3_256};
use std::{io, marker::PhantomData};

use crate::constants::NUM_HASH_BITS;

/// 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>;
fn write_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()
fn write_bytes(&self) -> Result<Vec<u8>, NovaError> {
let config = 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))
// Serialize into a Vec<u8> and return it
config.serialize(self).map_err(|e| NovaError::DigestError {
reason: e.to_string(),
})
}
}

/// Compute the digest of a `Digestible` instance.
pub struct DigestComputer<'a, F: PrimeField, T> {
inner: &'a T,
_phantom: PhantomData<F>,
Expand All @@ -46,15 +46,15 @@
});

// turn the bit vector into a scalar
let mut digest = F::ZERO;
let mut result = F::ZERO;
let mut coeff = F::ONE;
for bit in bv {
if bit {
digest += coeff;
result += coeff;
}
coeff += coeff;
}
digest
result
}

/// Create a new `DigestComputer`
Expand All @@ -66,13 +66,16 @@
}

/// Compute the digest of a `Digestible` instance.
pub fn digest(&self) -> Result<F, io::Error> {
pub fn digest(&self) -> Result<F, core::fmt::Error> {
// Get the serialized bytes
let bytes = self.inner.write_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))
}
}
Expand All @@ -82,46 +85,35 @@
use super::{DigestComputer, SimpleDigestible};
use crate::{provider::PallasEngine, traits::Engine};
use ff::Field;
use once_cell::sync::OnceCell;
use serde::{Deserialize, Serialize};

type E = PallasEngine;

#[derive(Serialize, Deserialize)]
struct S<E: Engine> {
i: usize,
#[serde(skip, default = "OnceCell::new")]
digest: OnceCell<E::Scalar>,
#[serde(skip)]
digest: Option<E::Scalar>, // Use Option instead of OnceCell
}

impl<E: Engine> SimpleDigestible for S<E> {}

impl<E: Engine> S<E> {
fn new(i: usize) -> Self {
S {
i,
digest: OnceCell::new(),
}
S { i, digest: None }
}

fn digest(&self) -> E::Scalar {
self
.digest
.get_or_try_init(|| DigestComputer::new(self).digest())
.cloned()
.unwrap()
fn digest(&mut self) -> E::Scalar {
let digest: E::Scalar = DigestComputer::new(self).digest().unwrap();
*self.digest.get_or_insert(digest)
}
}

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

// let's set up a struct with a weird digest field to make sure the digest computation does not depend of it
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>::new(42);
s2.digest = Some(<E as Engine>::Scalar::ONE); // Set manually for test

assert_eq!(
DigestComputer::<<E as Engine>::Scalar, _>::new(&s1)
Expand All @@ -131,32 +123,18 @@
.digest()
.unwrap()
);

// note: because of the semantics of `OnceCell::get_or_try_init`, the above
// equality will not result in `s1.digest() == s2.digest`
assert_ne!(
s2.digest(),
DigestComputer::<<E as Engine>::Scalar, _>::new(&s2)
.digest()
.unwrap()
);
}

#[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 mut good_s = S::<E>::new(42);
let mut bad_s = S::<E>::new(42);
bad_s.digest = Some(<E as Engine>::Scalar::ONE); // Set manually for test

let 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 retrieved_s: S<E> = bincode::deserialize(&naughty_bytes).unwrap();
let mut retrieved_s: S<E> = bincode::deserialize(&naughty_bytes).unwrap();
assert_eq!(good_s.digest(), retrieved_s.digest())
}
}
10 changes: 6 additions & 4 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
//! This module defines errors returned by the library.
use crate::{frontend::SynthesisError, prelude::*};

Check warning on line 2 in src/errors.rs

View workflow job for this annotation

GitHub Actions / clippy

unused import: `prelude::*`

Check warning on line 2 in src/errors.rs

View workflow job for this annotation

GitHub Actions / build

unused import: `prelude::*`

Check warning on line 2 in src/errors.rs

View workflow job for this annotation

GitHub Actions / test

unused import: `prelude::*`
use core::fmt::Debug;
use thiserror::Error;

use crate::frontend::SynthesisError;

/// Errors returned by Nova
#[derive(Clone, Debug, Eq, PartialEq, Error)]
#[non_exhaustive]
Expand Down Expand Up @@ -64,8 +63,11 @@
reason: String,
},
/// returned when there is an error creating a digest
#[error("DigestError")]
DigestError,
#[error("DigestError: {reason}")]
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,
Expand Down
10 changes: 3 additions & 7 deletions src/frontend/constraint_system.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use std::{io, marker::PhantomData};

use ff::PrimeField;

use super::lc::{Index, LinearCombination, Variable};
use crate::prelude::*;

Check warning on line 2 in src/frontend/constraint_system.rs

View workflow job for this annotation

GitHub Actions / clippy

unused import: `crate::prelude::*`

Check warning on line 2 in src/frontend/constraint_system.rs

View workflow job for this annotation

GitHub Actions / build

unused import: `crate::prelude::*`

Check warning on line 2 in src/frontend/constraint_system.rs

View workflow job for this annotation

GitHub Actions / test

unused import: `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
Expand Down Expand Up @@ -33,9 +32,6 @@
/// 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,
Expand Down
6 changes: 4 additions & 2 deletions src/frontend/gadgets/boolean.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
//! Gadgets for allocating bits in the circuit and performing boolean logic.

use crate::{
frontend::{ConstraintSystem, LinearCombination, SynthesisError, Variable},
prelude::*,

Check warning on line 5 in src/frontend/gadgets/boolean.rs

View workflow job for this annotation

GitHub Actions / clippy

unused import: `prelude::*`

Check warning on line 5 in src/frontend/gadgets/boolean.rs

View workflow job for this annotation

GitHub Actions / build

unused import: `prelude::*`

Check warning on line 5 in src/frontend/gadgets/boolean.rs

View workflow job for this annotation

GitHub Actions / test

unused import: `prelude::*`
};
use ff::{PrimeField, PrimeFieldBits};

use crate::frontend::{ConstraintSystem, LinearCombination, SynthesisError, Variable};

/// Represents a variable in the constraint system which is guaranteed
/// to be either zero or one.
#[derive(Debug, Clone)]
Expand Down
1 change: 0 additions & 1 deletion src/frontend/gadgets/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//! Self-contained sub-circuit implementations for various primitives.

use super::SynthesisError;
pub mod boolean;
mod multieq;
Expand Down
6 changes: 4 additions & 2 deletions src/frontend/gadgets/multieq.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::{
frontend::{ConstraintSystem, LinearCombination, SynthesisError, Variable},
prelude::*,

Check warning on line 3 in src/frontend/gadgets/multieq.rs

View workflow job for this annotation

GitHub Actions / clippy

unused import: `prelude::*`

Check warning on line 3 in src/frontend/gadgets/multieq.rs

View workflow job for this annotation

GitHub Actions / build

unused import: `prelude::*`

Check warning on line 3 in src/frontend/gadgets/multieq.rs

View workflow job for this annotation

GitHub Actions / test

unused import: `prelude::*`
};
use ff::PrimeField;

use crate::frontend::{ConstraintSystem, LinearCombination, SynthesisError, Variable};

#[derive(Debug)]
pub struct MultiEq<Scalar: PrimeField, CS: ConstraintSystem<Scalar>> {
cs: CS,
Expand Down
11 changes: 7 additions & 4 deletions src/frontend/gadgets/num.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
//! Gadgets representing numbers in the scalar field of the underlying curve.

use crate::{
frontend::{
gadgets::boolean::{self, AllocatedBit, Boolean},
{ConstraintSystem, LinearCombination, SynthesisError, Variable},
},
prelude::*,

Check warning on line 8 in src/frontend/gadgets/num.rs

View workflow job for this annotation

GitHub Actions / clippy

unused import: `prelude::*`

Check warning on line 8 in src/frontend/gadgets/num.rs

View workflow job for this annotation

GitHub Actions / build

unused import: `prelude::*`

Check warning on line 8 in src/frontend/gadgets/num.rs

View workflow job for this annotation

GitHub Actions / test

unused import: `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<Scalar: PrimeField> {
Expand Down
8 changes: 5 additions & 3 deletions src/frontend/gadgets/sha256.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@

#![allow(clippy::many_single_char_names)]

use ff::PrimeField;

use super::{boolean::Boolean, multieq::MultiEq, uint32::UInt32};
use crate::frontend::{ConstraintSystem, SynthesisError};
use crate::{
frontend::{ConstraintSystem, SynthesisError},
prelude::*,

Check warning on line 11 in src/frontend/gadgets/sha256.rs

View workflow job for this annotation

GitHub Actions / clippy

unused import: `prelude::*`

Check warning on line 11 in src/frontend/gadgets/sha256.rs

View workflow job for this annotation

GitHub Actions / build

unused import: `prelude::*`

Check warning on line 11 in src/frontend/gadgets/sha256.rs

View workflow job for this annotation

GitHub Actions / test

unused import: `prelude::*`
};
use ff::PrimeField;

#[allow(clippy::unreadable_literal)]
const ROUND_CONSTANTS: [u32; 64] = [
Expand Down
9 changes: 5 additions & 4 deletions src/frontend/gadgets/uint32.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
//! 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},
prelude::*,
};
use ff::PrimeField;

/// Represents an interpretation of 32 `Boolean` objects as an
/// unsigned integer.
Expand Down
4 changes: 2 additions & 2 deletions src/frontend/lc.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::ops::{Add, Sub};

use crate::prelude::*;
use core::ops::{Add, Sub};
use ff::PrimeField;
use serde::{Deserialize, Serialize};

Expand Down
1 change: 1 addition & 0 deletions src/frontend/shape_cs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use crate::{
frontend::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable},
prelude::*,
traits::Engine,
};
use ff::PrimeField;
Expand Down
Loading
Loading