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
1 change: 1 addition & 0 deletions provekit/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub use {
r1cs::R1CS,
verifier::Verifier,
whir_r1cs::{IOPattern, WhirConfig, WhirR1CSProof, WhirR1CSScheme},
witness::PublicInputs,
};

#[cfg(test)]
Expand Down
3 changes: 2 additions & 1 deletion provekit/common/src/noir_proof_scheme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use {
crate::{
whir_r1cs::{WhirR1CSProof, WhirR1CSScheme},
witness::{NoirWitnessGenerator, SplitWitnessBuilders},
NoirElement, R1CS,
NoirElement, PublicInputs, R1CS,
},
acir::circuit::Program,
serde::{Deserialize, Serialize},
Expand All @@ -20,6 +20,7 @@ pub struct NoirProofScheme {

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct NoirProof {
pub public_inputs: PublicInputs,
pub whir_r1cs_proof: WhirR1CSProof,
}

Expand Down
6 changes: 5 additions & 1 deletion provekit/common/src/skyscraper/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@ mod pow;
mod sponge;
mod whir;

pub use self::{pow::SkyscraperPoW, sponge::SkyscraperSponge, whir::SkyscraperMerkleConfig};
pub use self::{
pow::SkyscraperPoW,
sponge::SkyscraperSponge,
whir::{SkyscraperCRH, SkyscraperMerkleConfig},
};
1 change: 1 addition & 0 deletions provekit/common/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
mod print_abi;
pub mod serde_ark;
pub mod serde_ark_option;
pub mod serde_ark_vec;
pub mod serde_hex;
pub mod serde_jsonify;
pub mod sumcheck;
Expand Down
87 changes: 87 additions & 0 deletions provekit/common/src/utils/serde_ark_vec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use {
crate::FieldElement,
ark_serialize::{CanonicalDeserialize, CanonicalSerialize},
serde::{
de::{Error as _, SeqAccess, Visitor},
ser::{Error as _, SerializeSeq},
Deserializer, Serializer,
},
std::fmt,
};

pub fn serialize<S>(vec: &Vec<FieldElement>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let is_human_readable = serializer.is_human_readable();
let mut seq = serializer.serialize_seq(Some(vec.len()))?;
for element in vec {
let mut buf = Vec::with_capacity(element.compressed_size());
element
.serialize_compressed(&mut buf)
.map_err(|e| S::Error::custom(format!("Failed to serialize: {e}")))?;

// Write bytes
if is_human_readable {
// ark_serialize doesn't have human-readable serialization. And Serde
// doesn't have good defaults for [u8]. So we implement hexadecimal
// serialization.
let hex = hex::encode(buf);
seq.serialize_element(&hex)?;
} else {
seq.serialize_element(&buf)?;
}
}
seq.end()
}

pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<FieldElement>, D::Error>
where
D: Deserializer<'de>,
{
struct VecVisitor {
is_human_readable: bool,
}

impl<'de> Visitor<'de> for VecVisitor {
type Value = Vec<FieldElement>;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a sequence of field elements")
}

fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut vec = Vec::new();
if self.is_human_readable {
while let Some(hex) = seq.next_element::<String>()? {
let bytes = hex::decode(hex)
.map_err(|e| A::Error::custom(format!("invalid hex: {e}")))?;
let mut reader = &*bytes;
let element = FieldElement::deserialize_compressed(&mut reader)
.map_err(|e| A::Error::custom(format!("deserialize failed: {e}")))?;
if !reader.is_empty() {
return Err(A::Error::custom("while deserializing: trailing bytes"));
}
vec.push(element);
}
} else {
while let Some(bytes) = seq.next_element::<Vec<u8>>()? {
let mut reader = &*bytes;
let element = FieldElement::deserialize_compressed(&mut reader)
.map_err(|e| A::Error::custom(format!("deserialize failed: {e}")))?;
if !reader.is_empty() {
return Err(A::Error::custom("while deserializing: trailing bytes"));
}
vec.push(element);
}
}
Ok(vec)
}
}

let is_human_readable = deserializer.is_human_readable();
deserializer.deserialize_seq(VecVisitor { is_human_readable })
}
10 changes: 10 additions & 0 deletions provekit/common/src/utils/sumcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ pub trait SumcheckIOPattern {
fn add_rand(self, num_rand: usize) -> Self;

fn add_zk_sumcheck_polynomials(self, num_vars: usize) -> Self;

/// Prover sends the hash of the public inputs
/// Verifier sends randomness to construct weights
fn add_public_inputs(self) -> Self;
}

impl<IOPattern> SumcheckIOPattern for IOPattern
Expand All @@ -136,6 +140,12 @@ where
self
}

fn add_public_inputs(mut self) -> Self {
self = self.add_scalars(1, "Public Inputs Hash");
self = self.challenge_scalars(1, "Public Weights Vector Random");
self
}

fn add_rand(self, num_rand: usize) -> Self {
self.challenge_scalars(num_rand, "rand")
}
Expand Down
10 changes: 8 additions & 2 deletions provekit/common/src/whir_r1cs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub struct WhirR1CSScheme {
pub m_0: usize,
pub a_num_terms: usize,
pub num_challenges: usize,
pub has_public_inputs: bool,
pub whir_witness: WhirConfig,
pub whir_for_hiding_spartan: WhirConfig,
}
Expand All @@ -34,10 +35,11 @@ impl WhirR1CSScheme {
if self.num_challenges > 0 {
// Compute total constraints: OOD + statement
// OOD: 2 witnesses × committment_ood_samples each
// Statement: 2 statements × 3 constraints each = 6
// Statement: statement_1 has 3 constraints + 1 public weights constraint = 4,
// statement_2 has 3 constraints = 3, total = 7
let num_witnesses = 2;
let num_ood_constraints = num_witnesses * self.whir_witness.committment_ood_samples;
let num_statement_constraints = 6; // 2 statements × 3 constraints
let num_statement_constraints = if self.has_public_inputs { 7 } else { 6 };
let num_constraints_total = num_ood_constraints + num_statement_constraints;

io = io
Expand All @@ -48,8 +50,10 @@ impl WhirR1CSScheme {
.commit_statement(&self.whir_for_hiding_spartan)
.add_zk_sumcheck_polynomials(self.m_0)
.add_whir_proof(&self.whir_for_hiding_spartan)
.add_public_inputs()
.hint("claimed_evaluations_1")
.hint("claimed_evaluations_2")
.hint("public_weights_evaluations")
.add_whir_batch_proof(&self.whir_witness, num_witnesses, num_constraints_total);
} else {
io = io
Expand All @@ -58,7 +62,9 @@ impl WhirR1CSScheme {
.commit_statement(&self.whir_for_hiding_spartan)
.add_zk_sumcheck_polynomials(self.m_0)
.add_whir_proof(&self.whir_for_hiding_spartan)
.add_public_inputs()
.hint("claimed_evaluations")
.hint("public_weights_evaluations")
.add_whir_proof(&self.whir_witness);
}

Expand Down
49 changes: 47 additions & 2 deletions provekit/common/src/witness/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,20 @@ mod witness_generator;
mod witness_io_pattern;

use {
crate::{utils::serde_ark, FieldElement},
crate::{
skyscraper::SkyscraperCRH,
utils::{serde_ark, serde_ark_vec},
FieldElement,
},
ark_crypto_primitives::crh::CRHScheme,
ark_ff::One,
serde::{Deserialize, Serialize},
};
pub use {
binops::{BINOP_ATOMIC_BITS, BINOP_BITS, NUM_DIGITS},
digits::{decompose_into_digits, DigitalDecompositionWitnesses},
ram::{SpiceMemoryOperation, SpiceWitnesses},
scheduling::{Layer, LayerType, LayeredWitnessBuilders, SplitWitnessBuilders},
scheduling::{Layer, LayerType, LayeredWitnessBuilders, SplitError, SplitWitnessBuilders},
witness_builder::{
ConstantTerm, ProductLinearTerm, SumTerm, WitnessBuilder, WitnessCoefficient,
},
Expand All @@ -40,3 +45,43 @@ impl ConstantOrR1CSWitness {
}
}
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PublicInputs(#[serde(with = "serde_ark_vec")] pub Vec<FieldElement>);

impl PublicInputs {
pub fn new() -> Self {
Self(Vec::new())
}

pub fn from_vec(vec: Vec<FieldElement>) -> Self {
Self(vec)
}

pub fn len(&self) -> usize {
self.0.len()
}

Comment thread
ashpect marked this conversation as resolved.
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}

pub fn hash(&self) -> FieldElement {
match self.0.len() {
0 => FieldElement::from(0u64),
Comment thread
ashpect marked this conversation as resolved.
1 => {
// For single element, hash it with zero to ensure it gets properly hashed
Comment thread
ashpect marked this conversation as resolved.
let padded = vec![self.0[0], FieldElement::from(0u64)];
SkyscraperCRH::evaluate(&(), &padded[..]).expect("hash should succeed")
}
_ => SkyscraperCRH::evaluate(&(), &self.0[..])
.expect("hash should succeed for multiple inputs"),
}
}
}
Comment thread
ashpect marked this conversation as resolved.

impl Default for PublicInputs {
fn default() -> Self {
Self::new()
}
}
6 changes: 4 additions & 2 deletions provekit/common/src/witness/scheduling/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ mod scheduler;
mod splitter;

pub use {
dependency::DependencyInfo, remapper::WitnessIndexRemapper, scheduler::LayerScheduler,
splitter::WitnessSplitter,
dependency::DependencyInfo,
remapper::WitnessIndexRemapper,
scheduler::LayerScheduler,
splitter::{SplitError, WitnessSplitter},
};

/// Type of operations contained in a layer.
Expand Down
Loading
Loading