#![allow(non_snake_case)]
use crate::{
Blind, Commitment, CommitmentKey, PCS,
errors::SpartanError,
r1cs::{R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness},
traits::{
Engine,
pcs::{FoldingEngineTrait, PCSEngineTrait},
transcript::TranscriptReprTrait,
},
};
use ff::{Field, PrimeField};
use rayon::prelude::*;
impl<E: Engine> R1CSShape<E> {
pub fn commit_T(
&self,
ck: &CommitmentKey<E>,
U1: &RelaxedR1CSInstance<E>,
W1: &RelaxedR1CSWitness<E>,
U2: &R1CSInstance<E>,
W2: &R1CSWitness<E>,
r_T: &Blind<E>,
) -> Result<(Vec<E::Scalar>, Commitment<E>), SpartanError> {
let n_w = W1.W.len();
if W2.W.len() != n_w {
return Err(SpartanError::InvalidWitnessLength);
}
let n_x = U1.X.len();
if U2.X.len() != n_x {
return Err(SpartanError::InvalidInputLength {
reason: format!(
"commit_T: U1.X.len() ({}) != U2.X.len() ({})",
n_x,
U2.X.len()
),
});
}
let total = n_w + 1 + n_x;
let mut Z = Vec::with_capacity(total);
for i in 0..n_w {
Z.push(W1.W[i] + W2.W[i]);
}
Z.push(U1.u + E::Scalar::ONE);
for i in 0..n_x {
Z.push(U1.X[i] + U2.X[i]);
}
let u = U1.u + E::Scalar::ONE;
let (AZ, BZ, CZ) = self.multiply_vec(&Z)?;
let T: Vec<E::Scalar> = if rayon::current_num_threads() <= 1 {
(0..AZ.len())
.map(|i| AZ[i] * BZ[i] - u * CZ[i] - W1.E[i])
.collect()
} else {
AZ.par_iter()
.zip(BZ.par_iter())
.zip(CZ.par_iter())
.zip(W1.E.par_iter())
.map(|(((az, bz), cz), e)| *az * *bz - u * *cz - *e)
.collect()
};
let t_is_small = T.iter().all(|s| {
let bytes = s.to_repr();
bytes.as_ref()[8..].iter().all(|&b| b == 0)
});
let comm_T = PCS::<E>::commit(ck, &T, r_T, t_is_small)?;
Ok((T, comm_T))
}
}
impl<E: Engine> RelaxedR1CSWitness<E>
where
E::PCS: FoldingEngineTrait<E>,
{
pub fn fold(
&self,
W2: &R1CSWitness<E>,
T: &[E::Scalar],
r_T: &Blind<E>,
r: &E::Scalar,
) -> Result<RelaxedR1CSWitness<E>, SpartanError> {
if self.W.len() != W2.W.len() || self.E.len() != T.len() {
return Err(SpartanError::InvalidWitnessLength);
}
let W = self
.W
.iter()
.zip(&W2.W)
.map(|(w1, w2)| *w1 + *r * *w2)
.collect::<Vec<_>>();
let E_vec = self
.E
.iter()
.zip(T)
.map(|(e1, t)| *e1 + *r * *t)
.collect::<Vec<_>>();
let r_W = <E::PCS as FoldingEngineTrait<E>>::fold_blinds(
&[self.r_W.clone(), W2.r_W.clone()],
&[<E::Scalar as Field>::ONE, *r],
)?;
let r_E = <E::PCS as FoldingEngineTrait<E>>::fold_blinds(
&[self.r_E.clone(), r_T.clone()],
&[<E::Scalar as Field>::ONE, *r],
)?;
Ok(RelaxedR1CSWitness {
W,
r_W,
E: E_vec,
r_E,
})
}
}
impl<E: Engine> RelaxedR1CSInstance<E>
where
E::PCS: FoldingEngineTrait<E>,
{
pub fn fold(
&self,
U2: &R1CSInstance<E>,
comm_T: &Commitment<E>,
r: &E::Scalar,
) -> RelaxedR1CSInstance<E> {
let X = self
.X
.par_iter()
.zip(&U2.X)
.map(|(a, b)| *a + *r * *b)
.collect::<Vec<_>>();
let comm_W = <E::PCS as FoldingEngineTrait<E>>::fold_commitments(
&[self.comm_W.clone(), U2.comm_W.clone()],
&[<E::Scalar as Field>::ONE, *r],
)
.expect("fold commitments");
let comm_E = <E::PCS as FoldingEngineTrait<E>>::fold_commitments(
&[self.comm_E.clone(), comm_T.clone()],
&[<E::Scalar as Field>::ONE, *r],
)
.expect("fold commitments");
RelaxedR1CSInstance {
comm_W,
comm_E,
X,
u: self.u + *r,
}
}
}
impl<E: Engine> TranscriptReprTrait<E::GE> for RelaxedR1CSInstance<E> {
fn to_transcript_bytes(&self) -> Vec<u8> {
[
self.comm_W.to_transcript_bytes(),
self.comm_E.to_transcript_bytes(),
self.u.to_transcript_bytes(),
self.X.as_slice().to_transcript_bytes(),
]
.concat()
}
}
impl<E: Engine> R1CSWitness<E>
where
E::PCS: FoldingEngineTrait<E>,
{
pub fn fold(&self, w2: &R1CSWitness<E>, r_b: &E::Scalar) -> Result<Self, SpartanError> {
if self.W.len() != w2.W.len() {
return Err(SpartanError::InvalidWitnessLength);
}
let new_w = self
.W
.par_iter()
.zip(&w2.W)
.map(|(w1, w2)| *w1 + *r_b * (*w2 - *w1))
.collect::<Vec<_>>();
let r_w = <E::PCS as FoldingEngineTrait<E>>::fold_blinds(
&[self.r_W.clone(), w2.r_W.clone()],
&[<E::Scalar as Field>::ONE - *r_b, *r_b],
)?;
Ok(Self {
W: new_w,
r_W: r_w,
is_small: false, })
}
}