#[cfg(not(feature = "cuda-supraseal"))]
mod native;
#[cfg(feature = "cuda-supraseal")]
mod supraseal;
use std::fmt;
use bellpepper_core::{
Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable,
};
use ec_gpu_gen::multiexp_cpu::DensityTracker;
use ff::{Field, PrimeField};
use pairing::MultiMillerLoop;
use rand_core::RngCore;
#[cfg(not(feature = "cuda-supraseal"))]
use self::native as prover;
#[cfg(feature = "cuda-supraseal")]
use self::supraseal as prover;
use super::{ParameterSource, Proof};
use crate::{gpu::GpuName, lc};
struct ProvingAssignment<Scalar: PrimeField> {
a_aux_density: DensityTracker,
b_input_density: DensityTracker,
b_aux_density: DensityTracker,
a: Vec<Scalar>,
b: Vec<Scalar>,
c: Vec<Scalar>,
input_assignment: Vec<Scalar>,
aux_assignment: Vec<Scalar>,
}
impl<Scalar: PrimeField> fmt::Debug for ProvingAssignment<Scalar> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("ProvingAssignment")
.field("a_aux_density", &self.a_aux_density)
.field("b_input_density", &self.b_input_density)
.field("b_aux_density", &self.b_aux_density)
.field(
"a",
&self
.a
.iter()
.map(|v| format!("Fr({:?})", v))
.collect::<Vec<_>>(),
)
.field(
"b",
&self
.b
.iter()
.map(|v| format!("Fr({:?})", v))
.collect::<Vec<_>>(),
)
.field(
"c",
&self
.c
.iter()
.map(|v| format!("Fr({:?})", v))
.collect::<Vec<_>>(),
)
.field("input_assignment", &self.input_assignment)
.field("aux_assignment", &self.aux_assignment)
.finish()
}
}
impl<Scalar: PrimeField> PartialEq for ProvingAssignment<Scalar> {
fn eq(&self, other: &ProvingAssignment<Scalar>) -> bool {
self.a_aux_density == other.a_aux_density
&& self.b_input_density == other.b_input_density
&& self.b_aux_density == other.b_aux_density
&& self.a == other.a
&& self.b == other.b
&& self.c == other.c
&& self.input_assignment == other.input_assignment
&& self.aux_assignment == other.aux_assignment
}
}
impl<Scalar: PrimeField> ConstraintSystem<Scalar> for ProvingAssignment<Scalar> {
type Root = Self;
fn new() -> Self {
Self {
a_aux_density: DensityTracker::new(),
b_input_density: DensityTracker::new(),
b_aux_density: DensityTracker::new(),
a: vec![],
b: vec![],
c: vec![],
input_assignment: vec![],
aux_assignment: vec![],
}
}
fn alloc<F, A, AR>(&mut self, _: A, f: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<Scalar, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
self.aux_assignment.push(f()?);
self.a_aux_density.add_element();
self.b_aux_density.add_element();
Ok(Variable(Index::Aux(self.aux_assignment.len() - 1)))
}
fn alloc_input<F, A, AR>(&mut self, _: A, f: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<Scalar, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
self.input_assignment.push(f()?);
self.b_input_density.add_element();
Ok(Variable(Index::Input(self.input_assignment.len() - 1)))
}
fn enforce<A, AR, LA, LB, LC>(&mut self, _: A, a: LA, b: LB, c: LC)
where
A: FnOnce() -> AR,
AR: Into<String>,
LA: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
LB: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
LC: FnOnce(LinearCombination<Scalar>) -> LinearCombination<Scalar>,
{
let a = a(LinearCombination::zero());
let b = b(LinearCombination::zero());
let c = c(LinearCombination::zero());
let input_assignment = &self.input_assignment;
let aux_assignment = &self.aux_assignment;
let a_aux_density = &mut self.a_aux_density;
let b_input_density = &mut self.b_input_density;
let b_aux_density = &mut self.b_aux_density;
let a_res = lc::eval_with_trackers(
&a,
None,
Some(a_aux_density),
input_assignment,
aux_assignment,
);
let b_res = lc::eval_with_trackers(
&b,
Some(b_input_density),
Some(b_aux_density),
input_assignment,
aux_assignment,
);
let c_res = c.eval(input_assignment, aux_assignment);
self.a.push(a_res);
self.b.push(b_res);
self.c.push(c_res);
}
fn push_namespace<NR, N>(&mut self, _: N)
where
NR: Into<String>,
N: FnOnce() -> NR,
{
}
fn pop_namespace(&mut self) {
}
fn get_root(&mut self) -> &mut Self::Root {
self
}
fn is_extensible() -> bool {
true
}
fn extend(&mut self, other: &Self) {
self.a_aux_density.extend(&other.a_aux_density, false);
self.b_input_density.extend(&other.b_input_density, true);
self.b_aux_density.extend(&other.b_aux_density, false);
self.a.extend(&other.a);
self.b.extend(&other.b);
self.c.extend(&other.c);
self.input_assignment
.extend(&other.input_assignment[1..]);
self.aux_assignment.extend(&other.aux_assignment);
}
}
pub(super) fn create_random_proof_batch_priority<E, C, R, P: ParameterSource<E>>(
circuits: Vec<C>,
params: P,
rng: &mut R,
priority: bool,
) -> Result<Vec<Proof<E>>, SynthesisError>
where
E: MultiMillerLoop,
C: Circuit<E::Fr> + Send,
R: RngCore,
E::Fr: GpuName,
E::G1Affine: GpuName,
E::G2Affine: GpuName,
{
let r_s = (0..circuits.len())
.map(|_| E::Fr::random(&mut *rng))
.collect();
let s_s = (0..circuits.len())
.map(|_| E::Fr::random(&mut *rng))
.collect();
create_proof_batch_priority::<E, C, P>(circuits, params, r_s, s_s, priority)
}
pub(super) fn create_proof_batch_priority<E, C, P: ParameterSource<E>>(
circuits: Vec<C>,
params: P,
r_s: Vec<E::Fr>,
s_s: Vec<E::Fr>,
priority: bool,
) -> Result<Vec<Proof<E>>, SynthesisError>
where
E: MultiMillerLoop,
C: Circuit<E::Fr> + Send,
E::Fr: GpuName,
E::G1Affine: GpuName,
E::G2Affine: GpuName,
{
prover::create_proof_batch_priority_inner(circuits, params, Some((r_s, s_s)), priority)
}
#[cfg(test)]
mod tests {
use super::*;
use blstrs::Scalar as Fr;
use rand::Rng;
use rand_core::SeedableRng;
use rand_xorshift::XorShiftRng;
#[test]
fn test_proving_assignment_extend() {
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06,
0xbc, 0xe5,
]);
for k in &[2, 4, 8] {
for j in &[10, 20, 50] {
let count: usize = k * j;
let mut full_assignment = ProvingAssignment::<Fr>::new();
full_assignment
.alloc_input(|| "one", || Ok(<Fr as Field>::ONE))
.unwrap();
let mut partial_assignments = Vec::with_capacity(count / k);
for i in 0..count {
if i % k == 0 {
let mut p = ProvingAssignment::new();
p.alloc_input(|| "one", || Ok(<Fr as Field>::ONE)).unwrap();
partial_assignments.push(p)
}
let index: usize = i / k;
let partial_assignment = &mut partial_assignments[index];
if rng.r#gen() {
let el = Fr::random(&mut rng);
full_assignment
.alloc(|| format!("alloc:{},{}", i, k), || Ok(el))
.unwrap();
partial_assignment
.alloc(|| format!("alloc:{},{}", i, k), || Ok(el))
.unwrap();
}
if rng.r#gen() {
let el = Fr::random(&mut rng);
full_assignment
.alloc_input(|| format!("alloc_input:{},{}", i, k), || Ok(el))
.unwrap();
partial_assignment
.alloc_input(|| format!("alloc_input:{},{}", i, k), || Ok(el))
.unwrap();
}
}
let mut combined = ProvingAssignment::new();
combined
.alloc_input(|| "one", || Ok(<Fr as Field>::ONE))
.unwrap();
for assignment in partial_assignments.iter() {
combined.extend(assignment);
}
assert_eq!(combined, full_assignment);
}
}
}
}