Skip to content
Merged
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
26 changes: 26 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,30 @@ jobs:
- name: Run tests
run: cargo test --verbose

no-std-check:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- name: Check no_std compilation
run: |
echo "Checking no_std compilation..."
cargo check --no-default-features --verbose
- name: Check std compilation
run: |
echo "Checking std compilation..."
cargo check --verbose
- name: Check all features
run: |
echo "Checking with all features..."
cargo check --all-features --verbose


full-setup:

Expand All @@ -46,5 +70,7 @@ jobs:

- name: Build (nightly)
run: cargo +${{ matrix.toolchain }} build --all-features --verbose
- name: Build no_std (nightly)
run: cargo +${{ matrix.toolchain }} build --no-default-features --verbose
- name: Run tests (nightly)
run: cargo +${{ matrix.toolchain }} test --all-features --verbose
25 changes: 16 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,25 @@ exclude = [
".gitignore"
]

[features]
default = ["std"]
std = ["thiserror", "rand", "num-bigint/std", "num-traits/std", "sha3/std", "rand_core/std"]

[dependencies]
ff = { version = "0.13", features = ["derive"] }
group = "0.13.0"
num-bigint = "0.4.6"
num-traits = "0.2.19"
rand = "0.8.5"
sha3 = "0.10.8"
subtle = "2.6.1"
thiserror = "1"
keccak = "0.1.5"
zerocopy = "0.8"
zeroize = "1.8.1"
num-bigint = { version = "0.4.6", default-features = false }
num-traits = { version = "0.2.19", default-features = false, features = ["libm"] }
rand = { version = "0.8.5", optional = true }
rand_core = { version = "0.6", default-features = false }
sha3 = { version = "0.10.8", default-features = false }
subtle = { version = "2.6.1", default-features = false }
thiserror = { version = "1", optional = true }
keccak = { version = "0.1.5", default-features = false }
zerocopy = { version = "0.8", default-features = false }
zeroize = { version = "1.8.1", default-features = false, features = ["alloc"] }
hashbrown = { version = "0.15", default-features = false }
ahash = { version = "0.8", default-features = false }

[dev-dependencies]
bls12_381 = "0.8.0"
Expand Down
1 change: 1 addition & 0 deletions src/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
use crate::duplex_sponge::DuplexSpongeInterface;
pub use crate::duplex_sponge::{keccak::KeccakDuplexSponge, shake::ShakeDuplexSponge};
use alloc::vec;
use ff::PrimeField;
use group::prime::PrimeGroup;
use num_bigint::BigUint;
Expand Down
17 changes: 11 additions & 6 deletions src/composition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,13 @@
//! )
//! ```

use alloc::vec::Vec;
use ff::{Field, PrimeField};
use group::prime::PrimeGroup;
#[cfg(feature = "std")]
use rand::{CryptoRng, Rng};
#[cfg(not(feature = "std"))]
use rand_core::{CryptoRng, RngCore as Rng};
use sha3::{Digest, Sha3_256};
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};

Expand Down Expand Up @@ -162,7 +167,7 @@ impl<G: PrimeGroup + ConstantTimeEq> ComposedRelation<G> {
fn prover_commit_simple(
protocol: &CanonicalLinearRelation<G>,
witness: &<CanonicalLinearRelation<G> as SigmaProtocol>::Witness,
rng: &mut (impl rand::Rng + rand::CryptoRng),
rng: &mut (impl Rng + CryptoRng),
) -> Result<(ComposedCommitment<G>, ComposedProverState<G>), Error> {
protocol.prover_commit(witness, rng).map(|(c, s)| {
(
Expand All @@ -185,7 +190,7 @@ impl<G: PrimeGroup + ConstantTimeEq> ComposedRelation<G> {
fn prover_commit_and(
protocols: &[ComposedRelation<G>],
witnesses: &[ComposedWitness<G>],
rng: &mut (impl rand::Rng + rand::CryptoRng),
rng: &mut (impl Rng + CryptoRng),
) -> Result<(ComposedCommitment<G>, ComposedProverState<G>), Error> {
if protocols.len() != witnesses.len() {
return Err(Error::InvalidInstanceWitnessPair);
Expand Down Expand Up @@ -227,7 +232,7 @@ impl<G: PrimeGroup + ConstantTimeEq> ComposedRelation<G> {
fn prover_commit_or(
instances: &[ComposedRelation<G>],
witnesses: &[ComposedWitness<G>],
rng: &mut (impl rand::Rng + rand::CryptoRng),
rng: &mut (impl Rng + CryptoRng),
) -> Result<(ComposedCommitment<G>, ComposedProverState<G>), Error> {
if instances.len() != witnesses.len() {
return Err(Error::InvalidInstanceWitnessPair);
Expand Down Expand Up @@ -360,7 +365,7 @@ impl<G: PrimeGroup + ConstantTimeEq> SigmaProtocol for ComposedRelation<G> {
fn prover_commit(
&self,
witness: &Self::Witness,
rng: &mut (impl rand::Rng + rand::CryptoRng),
rng: &mut (impl Rng + CryptoRng),
) -> Result<(Self::Commitment, Self::ProverState), Error> {
match (self, witness) {
(ComposedRelation::Simple(p), ComposedWitness::Simple(w)) => {
Expand Down Expand Up @@ -634,7 +639,7 @@ impl<G: PrimeGroup + ConstantTimeEq> SigmaProtocolSimulator for ComposedRelation
}
}

fn simulate_response<R: rand::Rng + rand::CryptoRng>(&self, rng: &mut R) -> Self::Response {
fn simulate_response<R: Rng + CryptoRng>(&self, rng: &mut R) -> Self::Response {
match self {
ComposedRelation::Simple(p) => ComposedResponse::Simple(p.simulate_response(rng)),
ComposedRelation::And(ps) => {
Expand All @@ -654,7 +659,7 @@ impl<G: PrimeGroup + ConstantTimeEq> SigmaProtocolSimulator for ComposedRelation
}
}

fn simulate_transcript<R: rand::Rng + rand::CryptoRng>(
fn simulate_transcript<R: Rng + CryptoRng>(
&self,
rng: &mut R,
) -> Result<(Self::Commitment, Self::Challenge, Self::Response), Error> {
Expand Down
1 change: 1 addition & 0 deletions src/duplex_sponge/keccak.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//! It is designed to match test vectors from the original Sage implementation.

use crate::duplex_sponge::DuplexSpongeInterface;
use alloc::vec::Vec;
use zerocopy::IntoBytes;

const RATE: usize = 136;
Expand Down
2 changes: 2 additions & 0 deletions src/duplex_sponge/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
//! a generic interface for cryptographic sponge functions that support
//! duplex operations: alternating absorb and squeeze phases.
use alloc::vec::Vec;

pub mod keccak;
pub mod shake;

Expand Down
2 changes: 2 additions & 0 deletions src/duplex_sponge/shake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
//! This module implements a duplex sponge construction using SHAKE128.
use crate::duplex_sponge::DuplexSpongeInterface;
use alloc::vec;
use alloc::vec::Vec;
use sha3::digest::{ExtendableOutput, Update};
use sha3::Shake128;

Expand Down
42 changes: 36 additions & 6 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@
//! - Mismatched parameter lengths (e.g., during batch verification),
//! - Access to unassigned group variables in constraint systems.

use alloc::string::String;
#[cfg(not(feature = "std"))]
use core::fmt;

/// Represents an invalid instance error.
#[derive(Debug, thiserror::Error)]
#[error("Invalid instance: {message}")]
#[derive(Debug)]
#[cfg_attr(feature = "std", derive(thiserror::Error))]
#[cfg_attr(feature = "std", error("Invalid instance: {message}"))]
pub struct InvalidInstance {
/// The error message describing what's invalid about the instance.
pub message: String,
Expand All @@ -35,22 +40,47 @@ impl From<InvalidInstance> for Error {
///
/// This may occur during proof generation, response computation, or verification.
#[non_exhaustive]
#[derive(Debug, thiserror::Error)]
#[derive(Debug)]
#[cfg_attr(feature = "std", derive(thiserror::Error))]
pub enum Error {
/// The proof is invalid: verification failed.
#[error("Verification failed.")]
#[cfg_attr(feature = "std", error("Verification failed."))]
VerificationFailure,
/// Indicates an invalid statement/witness pair
#[error("Invalid instance/witness pair.")]
#[cfg_attr(feature = "std", error("Invalid instance/witness pair."))]
InvalidInstanceWitnessPair,
/// Uninitialized group element variable.
#[error("Uninitialized group element variable: {var_debug}")]
#[cfg_attr(
feature = "std",
error("Uninitialized group element variable: {var_debug}")
)]
UnassignedGroupVar {
/// Debug representation of the unassigned variable.
var_debug: String,
},
}

// Manual Display implementation for no_std compatibility
#[cfg(not(feature = "std"))]
impl fmt::Display for InvalidInstance {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Invalid instance: {}", self.message)
}
}

#[cfg(not(feature = "std"))]
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::VerificationFailure => write!(f, "Verification failed."),
Error::InvalidInstanceWitnessPair => write!(f, "Invalid instance/witness pair."),
Error::UnassignedGroupVar { var_debug } => {
write!(f, "Uninitialized group element variable: {}", var_debug)
}
}
}
}

pub type Result<T> = core::result::Result<T, Error>;

/// Construct an `Ok` value of type `Result<T, sigma_rs::errors::Error>`.
Expand Down
4 changes: 4 additions & 0 deletions src/fiat_shamir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@
use crate::errors::Error;
use crate::traits::SigmaProtocol;
use crate::{codec::Codec, traits::SigmaProtocolSimulator};
use alloc::vec::Vec;

#[cfg(feature = "std")]
use rand::{CryptoRng, RngCore};
#[cfg(not(feature = "std"))]
use rand_core::{CryptoRng, RngCore};

type Transcript<P> = (
<P as SigmaProtocol>::Commitment,
Expand Down
4 changes: 3 additions & 1 deletion src/group/msm.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use alloc::vec;
use alloc::vec::Vec;
use ff::PrimeField;
use group::prime::PrimeGroup;

Expand Down Expand Up @@ -82,7 +84,7 @@ fn msm_internal<G: PrimeGroup>(bases: &[G], scalars: &[G::Scalar]) -> G {
window_buckets.push((window, vec![G::identity(); buckets_num]));
}

for (scalar, base) in scalars.into_iter().zip(bases) {
for (scalar, base) in scalars.iter().zip(bases) {
Copy link

Copilot AI Sep 9, 2025

Choose a reason for hiding this comment

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

This change from scalars.into_iter() to scalars.iter() changes the semantic behavior. The original code consumed the scalars iterator, but now it borrows it, which may cause issues if the function signature expects to take ownership of scalars.

Copilot uses AI. Check for mistakes.
for (w, bucket) in window_buckets.iter_mut() {
let scalar_repr = scalar.to_repr();
let scalar_bytes = scalar_repr.as_ref();
Expand Down
1 change: 1 addition & 0 deletions src/group/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//! This module provides functions to convert group elements and scalars to and from
//! byte representations using canonical encodings.
use alloc::vec::Vec;
use ff::PrimeField;
use group::prime::PrimeGroup;

Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,14 @@
//! Σ-rs is designed to be modular, extensible, and easy to integrate into different
//! groups, protocols depending on sigma protocols, and other proof systems.
#![cfg_attr(not(feature = "std"), no_std)]
#![allow(non_snake_case)]
#![doc(html_logo_url = "https://mmaker.github.io/sigma-rs/")]
#![deny(unused_variables)]
#![deny(unused_mut)]

extern crate alloc;

pub mod codec;
pub mod composition;
pub mod errors;
Expand Down
19 changes: 19 additions & 0 deletions src/linear_relation/canonical.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
#[cfg(not(feature = "std"))]
use ahash::RandomState;
use alloc::boxed::Box;
use alloc::format;
use alloc::vec::Vec;
use core::iter;
use core::marker::PhantomData;
#[cfg(not(feature = "std"))]
use hashbrown::HashMap;
#[cfg(feature = "std")]
use std::collections::HashMap;

use ff::Field;
Expand Down Expand Up @@ -35,7 +43,11 @@ pub struct CanonicalLinearRelation<G: PrimeGroup> {
/// The cache is essentially a mapping (GroupVar, Scalar) => GroupVar, which maps the original
/// weighted group vars to a new assignment, such that if a pair appears more than once, it will
/// map to the same group variable in the canonical linear relation.
#[cfg(feature = "std")]
type WeightedGroupCache<G> = HashMap<GroupVar<G>, Vec<(<G as group::Group>::Scalar, GroupVar<G>)>>;
#[cfg(not(feature = "std"))]
type WeightedGroupCache<G> =
HashMap<GroupVar<G>, Vec<(<G as group::Group>::Scalar, GroupVar<G>)>, RandomState>;

impl<G: PrimeGroup> CanonicalLinearRelation<G> {
/// Create a new empty canonical linear relation.
Expand Down Expand Up @@ -177,7 +189,11 @@ impl<G: PrimeGroup> CanonicalLinearRelation<G> {
// However, relations built using TryFrom<LinearRelation> are NOT guaranteed to lead
// to the same ordering of elements across versions of this library.
// Changes to LinearRelation may have unpredictable effects on how this label is built.
#[cfg(feature = "std")]
let mut group_repr_mapping: HashMap<Box<[u8]>, u32> = HashMap::new();
#[cfg(not(feature = "std"))]
let mut group_repr_mapping: HashMap<Box<[u8]>, u32, RandomState> =
HashMap::with_hasher(RandomState::new());
let mut group_elements_ordered = Vec::new();

// Helper function to get or create index for a group element representation
Expand Down Expand Up @@ -425,7 +441,10 @@ impl<G: PrimeGroup> TryFrom<&LinearRelation<G>> for CanonicalLinearRelation<G> {
canonical.num_scalars = relation.linear_map.num_scalars;

// Cache for deduplicating weighted group elements
#[cfg(feature = "std")]
let mut weighted_group_cache = HashMap::new();
#[cfg(not(feature = "std"))]
let mut weighted_group_cache = HashMap::with_hasher(RandomState::new());

// Process each constraint using the modular helper method
for (lhs, rhs) in iter::zip(&relation.image, &relation.linear_map.linear_combinations) {
Expand Down
2 changes: 2 additions & 0 deletions src/linear_relation/convert.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use alloc::vec;
use alloc::vec::Vec;
use ff::Field;
use group::Group;

Expand Down
2 changes: 2 additions & 0 deletions src/linear_relation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
//! - [`LinearMap`]: a collection of linear combinations acting on group elements.
//! - [`LinearRelation`]: a higher-level structure managing linear maps and their associated images.

use alloc::format;
use alloc::vec::Vec;
use core::iter;
use core::marker::PhantomData;

Expand Down
2 changes: 2 additions & 0 deletions src/linear_relation/ops.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use alloc::vec;
use alloc::vec::Vec;
use core::ops::{Add, Mul, Neg, Sub};
use ff::Field;
use group::Group;
Expand Down
4 changes: 4 additions & 0 deletions src/schnorr_protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@ use crate::group::serialization::{
};
use crate::linear_relation::CanonicalLinearRelation;
use crate::traits::{SigmaProtocol, SigmaProtocolSimulator};
use alloc::vec::Vec;

use ff::Field;
use group::prime::PrimeGroup;
#[cfg(feature = "std")]
use rand::{CryptoRng, Rng, RngCore};
#[cfg(not(feature = "std"))]
use rand_core::{CryptoRng, RngCore, RngCore as Rng};

impl<G: PrimeGroup> SigmaProtocol for CanonicalLinearRelation<G> {
type Commitment = Vec<G>;
Expand Down
Loading
Loading