diff --git a/fawkes-crypto/src/backend/bellman_groth16/mod.rs b/fawkes-crypto/src/backend/bellman_groth16/mod.rs index cf40a68..5b07852 100644 --- a/fawkes-crypto/src/backend/bellman_groth16/mod.rs +++ b/fawkes-crypto/src/backend/bellman_groth16/mod.rs @@ -1,7 +1,7 @@ use crate::{ circuit::{ cs::{RCS, WitnessCS, CS}, - lc::{Index} + lc::{Index}, gates::{GateSource, Gate, GateIterator, evaluate_gates_memory_size} }, core::signal::Signal, ff_uint::{Num, PrimeField}, @@ -143,8 +143,12 @@ impl Parameters { verifier::VK::from_bellman(&self.0.vk) } - pub fn get_witness_rcs(&self)->RCS> { - WitnessCS::rc_new(self.1 as usize, &self.2, &self.3) + pub fn get_witness_rcs(&self) -> RCS> { + WitnessCS::rc_new(self.1 as usize, GateSource::Compressed(&self.2), &self.3) + } + + pub fn get_witness_rcs_precomputed<'a>(&'a self, data: &'a PrecomputedData) -> RCS> { + WitnessCS::rc_new(self.1 as usize, GateSource::Precomputed(&data.gates), &self.3) } pub fn write(&self, writer: &mut W) -> std::io::Result<()> { @@ -174,4 +178,17 @@ impl Parameters { Ok(Self(e0, e1, e2, e3)) } + pub fn precompute(&self) -> PrecomputedData { + PrecomputedData { + gates: GateIterator::new(&GateSource::Compressed(&self.2)).map(|g| g.gate()).collect() + } + } + + pub fn precompute_memory_size(&self) -> std::io::Result { + evaluate_gates_memory_size::(self.1 as usize, &self.2) + } +} + +pub struct PrecomputedData { + gates: Vec> } diff --git a/fawkes-crypto/src/backend/bellman_groth16/prover.rs b/fawkes-crypto/src/backend/bellman_groth16/prover.rs index 2dc1bce..174b962 100644 --- a/fawkes-crypto/src/backend/bellman_groth16/prover.rs +++ b/fawkes-crypto/src/backend/bellman_groth16/prover.rs @@ -66,7 +66,30 @@ pub fn prove<'a, E: Engine, Pub: Signal>, Sec: Signal (Vec>, Proof) { - let ref rcs = params.get_witness_rcs(); + let rcs = ¶ms.get_witness_rcs(); + prove_internal(params, input_pub, input_sec, circuit, rcs) +} + +#[cfg(feature = "rand_support")] +pub fn prove_precomputed<'a, E: Engine, Pub: Signal>, Sec: Signal>, C: Fn(Pub, Sec)>( + params: &'a Parameters, + input_pub: &Pub::Value, + input_sec: &Sec::Value, + circuit: C, + precomputed: &'a PrecomputedData, +) -> (Vec>, Proof) { + let rcs = ¶ms.get_witness_rcs_precomputed(precomputed); + prove_internal(params, input_pub, input_sec, circuit, rcs) +} + +#[cfg(feature = "rand_support")] +fn prove_internal<'a, E: Engine, Pub: Signal>, Sec: Signal>, C: Fn(Pub, Sec)>( + params: &'a Parameters, + input_pub: &Pub::Value, + input_sec: &Sec::Value, + circuit: C, + rcs: &RCS> +) -> (Vec>, Proof) { let signal_pub = Pub::alloc(rcs, Some(input_pub)); signal_pub.inputize(); let signal_sec = Sec::alloc(rcs, Some(input_sec)); diff --git a/fawkes-crypto/src/circuit/r1cs/cs.rs b/fawkes-crypto/src/circuit/r1cs/cs.rs index 2b8a74a..2382e3e 100644 --- a/fawkes-crypto/src/circuit/r1cs/cs.rs +++ b/fawkes-crypto/src/circuit/r1cs/cs.rs @@ -9,32 +9,22 @@ use crate::{ use std::{cell::RefCell, marker::PhantomData, rc::Rc}; use bit_vec::BitVec; -use byteorder::{ByteOrder, LittleEndian}; pub type RCS = Rc>; -#[cfg(feature="borsh_support")] -use crate::borsh::{BorshSerialize, BorshDeserialize}; +use super::gates::{Gate, self, GateSource, GateWrapper}; -#[derive(Clone, Debug)] -#[cfg_attr(feature = "borsh_support", derive(BorshSerialize, BorshDeserialize))] -pub struct Gate( - pub Vec<(Num, Index)>, - pub Vec<(Num, Index)>, - pub Vec<(Num, Index)>, -); - pub trait CS: Clone { type Fr: PrimeField; type LC: AbstractLC; - type GateIterator: Iterator>; + type GateIterator<'a>: Iterator> where Self: 'a; fn num_gates(&self) -> usize; fn num_input(&self) -> usize; fn num_aux(&self) -> usize; fn get_value(&self, index:Index) -> Option>; - fn get_gate_iterator(&self) -> Self::GateIterator; + fn get_gate_iterator(&self) -> Self::GateIterator<'_>; // a*b === c fn enforce(a: &CNum, b: &CNum, c: &CNum); @@ -100,25 +90,25 @@ pub struct WitnessCS<'a, Fr: PrimeField> { pub values_input: Vec>, pub values_aux: Vec>, pub num_gates: usize, - pub gates_data: &'a[u8], + pub gates: GateSource<'a, Fr>, pub const_tracker: &'a BitVec, pub const_tracker_index: usize } impl<'a, Fr: PrimeField> WitnessCS<'a, Fr> { - pub fn new(num_gates:usize, gates_data: &'a[u8], const_tracker: &'a BitVec) -> Self { + pub fn new(num_gates:usize, gates: GateSource<'a, Fr>, const_tracker: &'a BitVec) -> Self { Self { values_input: vec![Num::ONE], values_aux: vec![], num_gates, - gates_data, + gates, const_tracker, const_tracker_index: 0 } } - pub fn rc_new(num_gates:usize, gates_data: &'a[u8], const_tracker: &'a BitVec) -> RCS { - Rc::new(RefCell::new(Self::new(num_gates, gates_data, const_tracker))) + pub fn rc_new(num_gates:usize, gates: GateSource<'a, Fr>, const_tracker: &'a BitVec) -> RCS { + Rc::new(RefCell::new(Self::new(num_gates, gates, const_tracker))) } } @@ -126,7 +116,7 @@ impl<'a, Fr: PrimeField> WitnessCS<'a, Fr> { impl CS for DebugCS { type Fr = Fr; type LC = LC; - type GateIterator = core::iter::Empty>; + type GateIterator<'a> = core::iter::Empty> where Self: 'a; fn num_gates(&self) -> usize { self.num_gates @@ -143,7 +133,7 @@ impl CS for DebugCS { None } - fn get_gate_iterator(&self) -> Self::GateIterator { + fn get_gate_iterator(&self) -> Self::GateIterator<'_> { std::unimplemented!(); } @@ -180,52 +170,10 @@ impl CS for DebugCS { } - -pub struct GateStreamedIterator(R,PhantomData); - -fn read_u32(r: &mut R) -> std::io::Result { - let mut b = [0; 4]; - r.read_exact(&mut b)?; - Ok(LittleEndian::read_u32(&b)) -} - - -fn read_gate_part(r: &mut R) -> std::io::Result, Index)>> { - let sz = read_u32(r)? as usize; - - let item_size = std::mem::size_of::() + std::mem::size_of::() + std::mem::size_of::(); - let mut buf = vec![0; sz*item_size]; - r.read_exact(&mut buf)?; - let mut buf_ref = &buf[..]; - let mut gate_part = Vec::with_capacity(sz); - for _ in 0..sz { - let a = Num::::deserialize(&mut buf_ref)?; - let b1 = u8::deserialize(&mut buf_ref)?; - let b2 = u32::deserialize(&mut buf_ref)?; - let b = match b1 { - 0 => Index::Input(b2), - 1 => Index::Aux(b2), - _ => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "enum elements overflow")) - }; - gate_part.push((a,b)); - } - Ok(gate_part) -} - -impl Iterator for GateStreamedIterator { - type Item = Gate; - fn next(&mut self) -> Option { - let a = read_gate_part(&mut self.0).ok()?; - let b = read_gate_part(&mut self.0).ok()?; - let c = read_gate_part(&mut self.0).ok()?; - Some(Gate(a,b,c)) - } -} - -impl<'a, Fr: PrimeField> CS for WitnessCS<'a, Fr> { +impl<'a, Fr: PrimeField + 'a> CS for WitnessCS<'a, Fr> { type Fr = Fr; type LC = ZeroLC; - type GateIterator = GateStreamedIterator>; + type GateIterator<'b> = gates::GateIterator<'b, Fr> where Self: 'b; fn num_gates(&self) -> usize { self.num_gates @@ -245,8 +193,8 @@ impl<'a, Fr: PrimeField> CS for WitnessCS<'a, Fr> { } } - fn get_gate_iterator(&self) -> Self::GateIterator { - GateStreamedIterator(brotli::Decompressor::new(self.gates_data, 4096), PhantomData) + fn get_gate_iterator(&self) -> Self::GateIterator<'_> { + gates::GateIterator::new(&self.gates) } fn enforce(_: &CNum, _: &CNum, _: &CNum) { @@ -279,7 +227,7 @@ impl<'a, Fr: PrimeField> CS for WitnessCS<'a, Fr> { impl CS for BuildCS { type Fr = Fr; type LC = LC; - type GateIterator = std::vec::IntoIter>; + type GateIterator<'a> = gates::GateIterator<'a, Fr> where Self: 'a; fn num_gates(&self) -> usize { self.gates.len() @@ -296,8 +244,8 @@ impl CS for BuildCS { None } - fn get_gate_iterator(&self) -> Self::GateIterator { - self.gates.clone().into_iter() + fn get_gate_iterator(&self) -> Self::GateIterator<'_> { + gates::GateIterator::new(&GateSource::Precomputed(&self.gates)) } // a*b === c diff --git a/fawkes-crypto/src/circuit/r1cs/gates.rs b/fawkes-crypto/src/circuit/r1cs/gates.rs new file mode 100644 index 0000000..ea29795 --- /dev/null +++ b/fawkes-crypto/src/circuit/r1cs/gates.rs @@ -0,0 +1,142 @@ +use std::{marker::PhantomData, ops::Deref, io::Read}; + +#[cfg(feature="borsh_support")] +use borsh::{BorshSerialize, BorshDeserialize}; + +use byteorder::{LittleEndian, ByteOrder}; +use ff_uint::{PrimeField, Num}; + +use super::lc::Index; + + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "borsh_support", derive(BorshSerialize, BorshDeserialize))] +pub struct Gate( + pub Vec<(Num, Index)>, + pub Vec<(Num, Index)>, + pub Vec<(Num, Index)>, +); + +#[derive(Clone, Debug)] +pub enum GateSource<'a, Fr: PrimeField> { + Compressed(&'a [u8]), + Precomputed(&'a Vec>) +} + +pub enum GateIterator<'a, Fr: PrimeField> { + Streamed(Box>>), + Precomputed(std::slice::Iter<'a, Gate>) +} + +impl<'a, Fr: PrimeField> GateIterator<'a, Fr> { + pub fn new(source: &GateSource<'a, Fr>) -> Self { + match source { + GateSource::Compressed(bytes) => { + Self::Streamed(Box::new(GateStreamedIterator(brotli::Decompressor::new(bytes, 4096), PhantomData))) + }, + GateSource::Precomputed(vec) => { + Self::Precomputed(vec.iter()) + } + } + } +} + +pub enum GateWrapper<'a, Fr: PrimeField> { + Value(Gate), + Ref(&'a Gate) +} + +impl<'a, Fr: PrimeField> GateWrapper<'a, Fr> { + pub fn gate(self) -> Gate { + match self { + Self::Value(v) => v, + Self::Ref(r) => r.clone() + } + } +} + +impl<'a, Fr: PrimeField> Deref for GateWrapper<'a, Fr> { + type Target = Gate; + + fn deref(&self) -> &Self::Target { + match self { + Self::Value(val) => val, + Self::Ref(reference) => reference + } + } +} + +impl<'a, Fr: PrimeField> Iterator for GateIterator<'a, Fr> { + type Item = GateWrapper<'a, Fr>; + fn next(&mut self) -> Option { + match self { + Self::Streamed(iter) => { + iter.next().map(|g| GateWrapper::Value(g)) + } + Self::Precomputed(iter) => { + iter.next().map(|g| GateWrapper::Ref(g)) + }, + } + } +} + +pub fn evaluate_gates_memory_size( + num_gates: usize, + bytes: &[u8], +) -> std::io::Result { + let r = &mut brotli::Decompressor::new(bytes, 4096); + let mut memory_size = 0; + let item_size = std::mem::size_of::() + std::mem::size_of::() + std::mem::size_of::(); + let gate_size = std::mem::size_of::() + std::mem::size_of::(); + for _ in 0..num_gates { + for _ in 0..3 { + let count = read_u32(r)? as usize; + memory_size += count * gate_size; + + let mut buf = vec![0; count * item_size]; + r.read_exact(&mut buf)?; + } + } + Ok(memory_size) +} + +pub struct GateStreamedIterator(R, PhantomData); + +fn read_u32(r: &mut R) -> std::io::Result { + let mut b = [0; 4]; + r.read_exact(&mut b)?; + Ok(LittleEndian::read_u32(&b)) +} + + +fn read_gate_part(r: &mut R) -> std::io::Result, Index)>> { + let sz = read_u32(r)? as usize; + + let item_size = std::mem::size_of::() + std::mem::size_of::() + std::mem::size_of::(); + let mut buf = vec![0; sz*item_size]; + r.read_exact(&mut buf)?; + let mut buf_ref = &buf[..]; + let mut gate_part = Vec::with_capacity(sz); + for _ in 0..sz { + let a = Num::::deserialize(&mut buf_ref)?; + let b1 = u8::deserialize(&mut buf_ref)?; + let b2 = u32::deserialize(&mut buf_ref)?; + let b = match b1 { + 0 => Index::Input(b2), + 1 => Index::Aux(b2), + _ => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "enum elements overflow")) + }; + gate_part.push((a,b)); + } + Ok(gate_part) +} + +impl Iterator for GateStreamedIterator { + type Item = Gate; + fn next(&mut self) -> Option { + let a = read_gate_part(&mut self.0).ok()?; + let b = read_gate_part(&mut self.0).ok()?; + let c = read_gate_part(&mut self.0).ok()?; + Some(Gate(a,b,c)) + } +} \ No newline at end of file diff --git a/fawkes-crypto/src/circuit/r1cs/mod.rs b/fawkes-crypto/src/circuit/r1cs/mod.rs index 26be863..726ef18 100644 --- a/fawkes-crypto/src/circuit/r1cs/mod.rs +++ b/fawkes-crypto/src/circuit/r1cs/mod.rs @@ -1,4 +1,5 @@ pub mod bool; pub mod cs; pub mod num; -pub mod lc; \ No newline at end of file +pub mod lc; +pub mod gates; \ No newline at end of file diff --git a/fawkes-crypto/tests/circuit_ecc.rs b/fawkes-crypto/tests/circuit_ecc.rs index 45848bd..016d09e 100644 --- a/fawkes-crypto/tests/circuit_ecc.rs +++ b/fawkes-crypto/tests/circuit_ecc.rs @@ -1,5 +1,5 @@ -use fawkes_crypto::{ +use fawkes_crypto_zkbob::{ circuit::{bitify::c_into_bits_le_strict, cs::DebugCS, ecc::*, num::CNum, cs::CS}, native::ecc::*, ff_uint::Num, diff --git a/fawkes-crypto/tests/circuit_poseidon.rs b/fawkes-crypto/tests/circuit_poseidon.rs index 0b56df2..b92cc04 100644 --- a/fawkes-crypto/tests/circuit_poseidon.rs +++ b/fawkes-crypto/tests/circuit_poseidon.rs @@ -1,5 +1,5 @@ -use fawkes_crypto::{ +use fawkes_crypto_zkbob::{ circuit::{cs::{DebugCS, CS}, poseidon::*, num::CNum}, core::{signal::Signal, sizedvec::SizedVec}, engines::bn256::Fr,