use std::ops::{Add, Mul};
use ff::Field;
use group::prime::PrimeCurveAffine;
use midnight_curves::{msm::msm_best, CurveExt};
use midnight_proofs::{
plonk::Error,
transcript::{Hashable, Sampleable, Transcript},
};
pub fn ipa_prove<T, C>(
scalars: &[C::Scalar],
bases1: &[C],
bases2: &[C],
res1: &C,
res2: &C,
transcript: &mut T,
) -> Result<(), Error>
where
T: Transcript,
C: CurveExt + Hashable<T::Hash>,
C::Scalar: Sampleable<T::Hash> + Hashable<T::Hash>,
{
assert_eq!(scalars.len(), bases1.len());
assert_eq!(scalars.len(), bases2.len());
assert!(scalars.len().is_power_of_two());
let k = scalars.len().trailing_zeros() as usize;
bases1.iter().try_for_each(|b| transcript.common(b))?;
bases2.iter().try_for_each(|b| transcript.common(b))?;
transcript.common(res1)?;
transcript.common(res2)?;
let r: C::Scalar = transcript.squeeze_challenge();
let bases = fold((C::Scalar::from(1), bases1), (r, bases2));
let mut s = scalars.to_vec();
let mut b = bases.to_vec();
for _ in 0..k {
let half = s.len() / 2;
let l = inner_product::<C>(&s[..half], &b[half..]); let r = inner_product::<C>(&s[half..], &b[..half]);
transcript.write(&l)?;
transcript.write(&r)?;
let uj: C::Scalar = transcript.squeeze_challenge();
let uj_inv = uj.invert().unwrap();
s = fold((uj, &s[..half]), (uj_inv, &s[half..]));
b = fold((uj, &b[half..]), (uj_inv, &b[..half]));
}
debug_assert_eq!(s.len(), 1);
transcript.write(&s[0])?;
Ok(())
}
pub fn ipa_verify<T, C>(
bases1: &[C],
bases2: &[C],
res1: &C,
res2: &C,
transcript: &mut T,
) -> Result<(), Error>
where
T: Transcript,
C: CurveExt + Hashable<T::Hash>,
C::Scalar: Sampleable<T::Hash> + Hashable<T::Hash>,
{
assert_eq!(bases1.len(), bases2.len());
assert!(bases1.len().is_power_of_two());
let k = bases1.len().trailing_zeros() as usize;
bases1.iter().try_for_each(|b| transcript.common(b))?;
bases2.iter().try_for_each(|b| transcript.common(b))?;
transcript.common(res1)?;
transcript.common(res2)?;
let r = transcript.squeeze_challenge();
let mut msm_bases: Vec<C> = vec![];
let mut msm_scalars = vec![];
let mut ujs = vec![];
for _ in 0..k {
let lhs_j = transcript.read()?;
let rhs_j = transcript.read()?;
let uj: C::Scalar = transcript.squeeze_challenge();
let uj_inv = uj.invert().unwrap();
msm_bases.push(lhs_j);
msm_bases.push(rhs_j);
msm_scalars.push(uj.square());
msm_scalars.push(uj_inv.square());
ujs.push((uj, uj_inv));
}
let s: C::ScalarExt = transcript.read()?;
let mut ipa_scalars = vec![-s];
for (uj, uj_inv) in ujs.iter().rev() {
ipa_scalars = [
ipa_scalars.iter().map(|s| *s * uj_inv).collect::<Vec<_>>(),
ipa_scalars.iter().map(|s| *s * uj).collect::<Vec<_>>(),
]
.concat();
}
msm_bases.extend(bases1);
msm_bases.extend(bases2);
msm_scalars.extend(ipa_scalars.clone());
msm_scalars.extend(ipa_scalars.iter().map(|s| *s * r).collect::<Vec<_>>());
msm_bases.push(*res1);
msm_bases.push(*res2);
msm_scalars.push(C::Scalar::from(1));
msm_scalars.push(r);
if (!inner_product::<C>(&msm_scalars, &msm_bases).to_affine().is_identity()).into() {
return Err(Error::Opening);
}
Ok(())
}
fn inner_product<C: CurveExt>(scalars: &[C::ScalarExt], bases: &[C]) -> C {
assert_eq!(scalars.len(), bases.len());
assert!(!scalars.is_empty());
let mut affine_bases = vec![C::AffineExt::default(); bases.len()];
C::batch_normalize(bases, &mut affine_bases);
msm_best(scalars, &affine_bases)
}
fn fold<S, T>((c0, v0): (S, &[T]), (c1, v1): (S, &[T])) -> Vec<T>
where
S: Copy,
T: Copy + Add<Output = T> + Mul<S, Output = T>,
{
assert_eq!(v0.len(), v1.len());
v0.iter().zip(v1.iter()).map(|(v0_i, v1_i)| *v0_i * c0 + *v1_i * c1).collect()
}
#[cfg(test)]
mod tests {
use group::Group;
use midnight_proofs::transcript::CircuitTranscript;
use rand::rngs::OsRng;
use super::*;
type S = midnight_curves::Fq;
type C = midnight_curves::G1Projective;
type H = blake2b_simd::State;
#[test]
fn test_inner_product_argument() {
fn test<const N: usize>() {
let scalars: [S; N] = core::array::from_fn(|_| S::random(OsRng));
let bases1: [C; N] = core::array::from_fn(|_| C::random(OsRng));
let bases2: [C; N] = core::array::from_fn(|_| C::random(OsRng));
let res1 = inner_product::<C>(&scalars, &bases1);
let res2 = inner_product::<C>(&scalars, &bases2);
let mut transcript = CircuitTranscript::<H>::init();
ipa_prove(&scalars, &bases1, &bases2, &res1, &res2, &mut transcript).unwrap();
let mut transcript = CircuitTranscript::<H>::init_from_bytes(&transcript.finalize());
assert!(ipa_verify(&bases1, &bases2, &res1, &res2, &mut transcript).is_ok());
}
test::<2>();
test::<4>();
test::<8>();
test::<16>();
test::<32>();
test::<64>();
test::<1024>();
}
}