#![allow(non_snake_case)]
use core::mem;
use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
use curve25519_dalek::scalar::Scalar;
use curve25519_dalek::traits::VartimeMultiscalarMul;
use merlin::Transcript;
use super::{
ConstraintSystem, LinearCombination, R1CSProof, RandomizableConstraintSystem,
RandomizedConstraintSystem, Variable,
};
use errors::R1CSError;
use generators::{BulletproofGens, PedersenGens};
use transcript::TranscriptProtocol;
pub struct Verifier<'t> {
transcript: &'t mut Transcript,
constraints: Vec<LinearCombination>,
num_vars: usize,
V: Vec<CompressedRistretto>,
deferred_constraints: Vec<Box<Fn(&mut RandomizingVerifier<'t>) -> Result<(), R1CSError>>>,
pending_multiplier: Option<usize>,
}
pub struct RandomizingVerifier<'t> {
verifier: Verifier<'t>,
}
impl<'t> ConstraintSystem for Verifier<'t> {
fn transcript(&mut self) -> &mut Transcript {
self.transcript
}
fn multiply(
&mut self,
mut left: LinearCombination,
mut right: LinearCombination,
) -> (Variable, Variable, Variable) {
let var = self.num_vars;
self.num_vars += 1;
let l_var = Variable::MultiplierLeft(var);
let r_var = Variable::MultiplierRight(var);
let o_var = Variable::MultiplierOutput(var);
left.terms.push((l_var, -Scalar::one()));
right.terms.push((r_var, -Scalar::one()));
self.constrain(left);
self.constrain(right);
(l_var, r_var, o_var)
}
fn allocate(&mut self, _: Option<Scalar>) -> Result<Variable, R1CSError> {
match self.pending_multiplier {
None => {
let i = self.num_vars;
self.num_vars += 1;
self.pending_multiplier = Some(i);
Ok(Variable::MultiplierLeft(i))
}
Some(i) => {
self.pending_multiplier = None;
Ok(Variable::MultiplierRight(i))
}
}
}
fn allocate_multiplier(
&mut self,
_: Option<(Scalar, Scalar)>,
) -> Result<(Variable, Variable, Variable), R1CSError> {
let var = self.num_vars;
self.num_vars += 1;
let l_var = Variable::MultiplierLeft(var);
let r_var = Variable::MultiplierRight(var);
let o_var = Variable::MultiplierOutput(var);
Ok((l_var, r_var, o_var))
}
fn constrain(&mut self, lc: LinearCombination) {
self.constraints.push(lc);
}
}
impl<'t> RandomizableConstraintSystem for Verifier<'t> {
type RandomizedCS = RandomizingVerifier<'t>;
fn specify_randomized_constraints<F>(&mut self, callback: F) -> Result<(), R1CSError>
where
F: 'static + Fn(&mut Self::RandomizedCS) -> Result<(), R1CSError>,
{
self.deferred_constraints.push(Box::new(callback));
Ok(())
}
}
impl<'t> ConstraintSystem for RandomizingVerifier<'t> {
fn transcript(&mut self) -> &mut Transcript {
self.verifier.transcript
}
fn multiply(
&mut self,
left: LinearCombination,
right: LinearCombination,
) -> (Variable, Variable, Variable) {
self.verifier.multiply(left, right)
}
fn allocate(&mut self, assignment: Option<Scalar>) -> Result<Variable, R1CSError> {
self.verifier.allocate(assignment)
}
fn allocate_multiplier(
&mut self,
input_assignments: Option<(Scalar, Scalar)>,
) -> Result<(Variable, Variable, Variable), R1CSError> {
self.verifier.allocate_multiplier(input_assignments)
}
fn constrain(&mut self, lc: LinearCombination) {
self.verifier.constrain(lc)
}
}
impl<'t> RandomizedConstraintSystem for RandomizingVerifier<'t> {
fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar {
self.verifier.transcript.challenge_scalar(label)
}
}
impl<'t> Verifier<'t> {
pub fn new(transcript: &'t mut Transcript) -> Self {
transcript.r1cs_domain_sep();
Verifier {
transcript,
num_vars: 0,
V: Vec::new(),
constraints: Vec::new(),
deferred_constraints: Vec::new(),
pending_multiplier: None,
}
}
pub fn commit(&mut self, commitment: CompressedRistretto) -> Variable {
let i = self.V.len();
self.V.push(commitment);
self.transcript.append_point(b"V", &commitment);
Variable::Committed(i)
}
fn flattened_constraints(
&mut self,
z: &Scalar,
) -> (Vec<Scalar>, Vec<Scalar>, Vec<Scalar>, Vec<Scalar>, Scalar) {
let n = self.num_vars;
let m = self.V.len();
let mut wL = vec![Scalar::zero(); n];
let mut wR = vec![Scalar::zero(); n];
let mut wO = vec![Scalar::zero(); n];
let mut wV = vec![Scalar::zero(); m];
let mut wc = Scalar::zero();
let mut exp_z = *z;
for lc in self.constraints.iter() {
for (var, coeff) in &lc.terms {
match var {
Variable::MultiplierLeft(i) => {
wL[*i] += exp_z * coeff;
}
Variable::MultiplierRight(i) => {
wR[*i] += exp_z * coeff;
}
Variable::MultiplierOutput(i) => {
wO[*i] += exp_z * coeff;
}
Variable::Committed(i) => {
wV[*i] -= exp_z * coeff;
}
Variable::One() => {
wc -= exp_z * coeff;
}
}
}
exp_z *= z;
}
(wL, wR, wO, wV, wc)
}
fn create_randomized_constraints(mut self) -> Result<Self, R1CSError> {
self.pending_multiplier = None;
if self.deferred_constraints.len() == 0 {
self.transcript.r1cs_1phase_domain_sep();
Ok(self)
} else {
self.transcript.r1cs_2phase_domain_sep();
let mut callbacks = mem::replace(&mut self.deferred_constraints, Vec::new());
let mut wrapped_self = RandomizingVerifier { verifier: self };
for callback in callbacks.drain(..) {
callback(&mut wrapped_self)?;
}
Ok(wrapped_self.verifier)
}
}
pub fn verify(
mut self,
proof: &R1CSProof,
pc_gens: &PedersenGens,
bp_gens: &BulletproofGens,
) -> Result<(), R1CSError> {
self.transcript.append_u64(b"m", self.V.len() as u64);
let n1 = self.num_vars;
self.transcript
.validate_and_append_point(b"A_I1", &proof.A_I1)?;
self.transcript
.validate_and_append_point(b"A_O1", &proof.A_O1)?;
self.transcript
.validate_and_append_point(b"S1", &proof.S1)?;
self = self.create_randomized_constraints()?;
let n = self.num_vars;
let n2 = n - n1;
let padded_n = self.num_vars.next_power_of_two();
let pad = padded_n - n;
use inner_product_proof::inner_product;
use std::iter;
use util;
if bp_gens.gens_capacity < padded_n {
return Err(R1CSError::InvalidGeneratorsLength);
}
let gens = bp_gens.share(0);
self.transcript.append_point(b"A_I2", &proof.A_I2);
self.transcript.append_point(b"A_O2", &proof.A_O2);
self.transcript.append_point(b"S2", &proof.S2);
let y = self.transcript.challenge_scalar(b"y");
let z = self.transcript.challenge_scalar(b"z");
self.transcript
.validate_and_append_point(b"T_1", &proof.T_1)?;
self.transcript
.validate_and_append_point(b"T_3", &proof.T_3)?;
self.transcript
.validate_and_append_point(b"T_4", &proof.T_4)?;
self.transcript
.validate_and_append_point(b"T_5", &proof.T_5)?;
self.transcript
.validate_and_append_point(b"T_6", &proof.T_6)?;
let u = self.transcript.challenge_scalar(b"u");
let x = self.transcript.challenge_scalar(b"x");
self.transcript.append_scalar(b"t_x", &proof.t_x);
self.transcript
.append_scalar(b"t_x_blinding", &proof.t_x_blinding);
self.transcript
.append_scalar(b"e_blinding", &proof.e_blinding);
let w = self.transcript.challenge_scalar(b"w");
let (wL, wR, wO, wV, wc) = self.flattened_constraints(&z);
let (u_sq, u_inv_sq, s) = proof
.ipp_proof
.verification_scalars(padded_n, self.transcript)
.map_err(|_| R1CSError::VerificationError)?;
let a = proof.ipp_proof.a;
let b = proof.ipp_proof.b;
let y_inv = y.invert();
let y_inv_vec = util::exp_iter(y_inv)
.take(padded_n)
.collect::<Vec<Scalar>>();
let yneg_wR = wR
.into_iter()
.zip(y_inv_vec.iter())
.map(|(wRi, exp_y_inv)| wRi * exp_y_inv)
.chain(iter::repeat(Scalar::zero()).take(pad))
.collect::<Vec<Scalar>>();
let delta = inner_product(&yneg_wR[0..n], &wL);
let u_for_g = iter::repeat(Scalar::one())
.take(n1)
.chain(iter::repeat(u).take(n2 + pad));
let u_for_h = u_for_g.clone();
let g_scalars = yneg_wR
.iter()
.zip(u_for_g)
.zip(s.iter().take(padded_n))
.map(|((yneg_wRi, u_or_1), s_i)| u_or_1 * (x * yneg_wRi - a * s_i));
let h_scalars = y_inv_vec
.iter()
.zip(u_for_h)
.zip(s.iter().rev().take(padded_n))
.zip(wL.into_iter().chain(iter::repeat(Scalar::zero()).take(pad)))
.zip(wO.into_iter().chain(iter::repeat(Scalar::zero()).take(pad)))
.map(|((((y_inv_i, u_or_1), s_i_inv), wLi), wOi)| {
u_or_1 * (y_inv_i * (x * wLi + wOi - b * s_i_inv) - Scalar::one())
});
use rand::thread_rng;
let mut rng = self.transcript.build_rng().finalize(&mut thread_rng());
let r = Scalar::random(&mut rng);
let xx = x * x;
let rxx = r * xx;
let xxx = x * xx;
let T_scalars = [r * x, rxx * x, rxx * xx, rxx * xxx, rxx * xx * xx];
let T_points = [proof.T_1, proof.T_3, proof.T_4, proof.T_5, proof.T_6];
let mega_check = RistrettoPoint::optional_multiscalar_mul(
iter::once(x)
.chain(iter::once(xx))
.chain(iter::once(xxx))
.chain(iter::once(u * x))
.chain(iter::once(u * xx))
.chain(iter::once(u * xxx))
.chain(wV.iter().map(|wVi| wVi * rxx))
.chain(T_scalars.iter().cloned())
.chain(iter::once(
w * (proof.t_x - a * b) + r * (xx * (wc + delta) - proof.t_x),
))
.chain(iter::once(-proof.e_blinding - r * proof.t_x_blinding))
.chain(g_scalars)
.chain(h_scalars)
.chain(u_sq.iter().cloned())
.chain(u_inv_sq.iter().cloned()),
iter::once(proof.A_I1.decompress())
.chain(iter::once(proof.A_O1.decompress()))
.chain(iter::once(proof.S1.decompress()))
.chain(iter::once(proof.A_I2.decompress()))
.chain(iter::once(proof.A_O2.decompress()))
.chain(iter::once(proof.S2.decompress()))
.chain(self.V.iter().map(|V_i| V_i.decompress()))
.chain(T_points.iter().map(|T_i| T_i.decompress()))
.chain(iter::once(Some(pc_gens.B)))
.chain(iter::once(Some(pc_gens.B_blinding)))
.chain(gens.G(padded_n).map(|&G_i| Some(G_i)))
.chain(gens.H(padded_n).map(|&H_i| Some(H_i)))
.chain(proof.ipp_proof.L_vec.iter().map(|L_i| L_i.decompress()))
.chain(proof.ipp_proof.R_vec.iter().map(|R_i| R_i.decompress())),
)
.ok_or_else(|| R1CSError::VerificationError)?;
use curve25519_dalek::traits::IsIdentity;
if !mega_check.is_identity() {
return Err(R1CSError::VerificationError);
}
Ok(())
}
}