#![allow(non_snake_case)]
use crate::{
constants::NUM_CHALLENGE_BITS,
errors::NovaError,
gadgets::utils::{base_as_scalar, scalar_as_base},
r1cs::{R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness},
traits::{AbsorbInROTrait, Engine, ROConstants, ROTrait},
Commitment, CommitmentKey,
};
use ff::Field;
use rand_core::OsRng;
use serde::{Deserialize, Serialize};
#[allow(clippy::upper_case_acronyms)]
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(bound = "")]
pub struct NIFS<E: Engine> {
pub(crate) comm_T: Commitment<E>,
}
impl<E: Engine> NIFS<E> {
pub fn prove(
ck: &CommitmentKey<E>,
ro_consts: &ROConstants<E>,
pp_digest: &E::Scalar,
S: &R1CSShape<E>,
U1: &RelaxedR1CSInstance<E>,
W1: &RelaxedR1CSWitness<E>,
U2: &R1CSInstance<E>,
W2: &R1CSWitness<E>,
) -> Result<(NIFS<E>, (RelaxedR1CSInstance<E>, RelaxedR1CSWitness<E>)), NovaError> {
let mut ro = E::RO::new(ro_consts.clone());
ro.absorb(scalar_as_base::<E>(*pp_digest));
U2.absorb_in_ro(&mut ro);
let r_T = E::Scalar::random(&mut OsRng);
let (T, comm_T) = S.commit_T(ck, U1, W1, U2, W2, &r_T)?;
comm_T.absorb_in_ro(&mut ro);
let r = base_as_scalar::<E>(ro.squeeze(NUM_CHALLENGE_BITS, false));
let U = U1.fold(U2, &comm_T, &r);
let W = W1.fold(W2, &T, &r_T, &r)?;
Ok((Self { comm_T }, (U, W)))
}
pub fn verify(
&self,
ro_consts: &ROConstants<E>,
pp_digest: &E::Scalar,
U1: &RelaxedR1CSInstance<E>,
U2: &R1CSInstance<E>,
) -> Result<RelaxedR1CSInstance<E>, NovaError> {
let mut ro = E::RO::new(ro_consts.clone());
ro.absorb(scalar_as_base::<E>(*pp_digest));
U2.absorb_in_ro(&mut ro);
self.comm_T.absorb_in_ro(&mut ro);
let r = ro.squeeze(NUM_CHALLENGE_BITS, false);
let U = U1.fold(U2, &self.comm_T, &base_as_scalar::<E>(r));
Ok(U)
}
}
#[allow(clippy::upper_case_acronyms)]
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(bound = "")]
pub struct NIFSRelaxed<E: Engine> {
pub(crate) comm_T: Commitment<E>,
}
impl<E: Engine> NIFSRelaxed<E> {
pub fn prove(
ck: &CommitmentKey<E>,
ro_consts: &ROConstants<E>,
vk: &E::Scalar,
S: &R1CSShape<E>,
U1: &RelaxedR1CSInstance<E>,
W1: &RelaxedR1CSWitness<E>,
U2: &RelaxedR1CSInstance<E>,
W2: &RelaxedR1CSWitness<E>,
) -> Result<
(
NIFSRelaxed<E>,
(RelaxedR1CSInstance<E>, RelaxedR1CSWitness<E>),
),
NovaError,
> {
let mut ro = E::RO::new(ro_consts.clone());
ro.absorb(scalar_as_base::<E>(*vk));
U1.absorb_in_ro(&mut ro);
U2.absorb_in_ro(&mut ro);
let r_T = E::Scalar::random(&mut OsRng);
let (T, comm_T) = S.commit_T_relaxed(ck, U1, W1, U2, W2, &r_T)?;
comm_T.absorb_in_ro(&mut ro);
let r = ro.squeeze(NUM_CHALLENGE_BITS, false);
let U = U1.fold_relaxed(U2, &comm_T, &base_as_scalar::<E>(r));
let W = W1.fold_relaxed(W2, &T, &r_T, &base_as_scalar::<E>(r))?;
Ok((Self { comm_T }, (U, W)))
}
pub fn verify(
&self,
ro_consts: &ROConstants<E>,
pp_digest: &E::Scalar,
U1: &RelaxedR1CSInstance<E>,
U2: &RelaxedR1CSInstance<E>,
) -> Result<RelaxedR1CSInstance<E>, NovaError> {
let mut ro = E::RO::new(ro_consts.clone());
ro.absorb(scalar_as_base::<E>(*pp_digest));
U1.absorb_in_ro(&mut ro);
U2.absorb_in_ro(&mut ro);
self.comm_T.absorb_in_ro(&mut ro);
let r = ro.squeeze(NUM_CHALLENGE_BITS, false);
let U = U1.fold_relaxed(U2, &self.comm_T, &base_as_scalar::<E>(r));
Ok(U)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
frontend::{
num::AllocatedNum,
r1cs::{NovaShape, NovaWitness},
solver::SatisfyingAssignment,
test_shape_cs::TestShapeCS,
ConstraintSystem, SynthesisError,
},
provider::{Bn256EngineKZG, PallasEngine, Secp256k1Engine},
r1cs::SparseMatrix,
traits::{commitment::CommitmentEngineTrait, snark::default_ck_hint, Engine, ROConstants},
};
use ff::{Field, PrimeField};
use rand::rngs::OsRng;
fn synthesize_tiny_r1cs_bellpepper<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
cs: &mut CS,
x_val: Option<Scalar>,
) -> Result<(), SynthesisError> {
let x = AllocatedNum::alloc_infallible(cs.namespace(|| "x"), || x_val.unwrap());
let _ = x.inputize(cs.namespace(|| "x is input"));
let x_sq = x.square(cs.namespace(|| "x_sq"))?;
let x_cu = x_sq.mul(cs.namespace(|| "x_cu"), &x)?;
let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
Ok(x_cu.get_value().unwrap() + x.get_value().unwrap() + Scalar::from(5u64))
})?;
let _ = y.inputize(cs.namespace(|| "y is output"));
cs.enforce(
|| "y = x^3 + x + 5",
|lc| {
lc + x_cu.get_variable()
+ x.get_variable()
+ CS::one()
+ CS::one()
+ CS::one()
+ CS::one()
+ CS::one()
},
|lc| lc + CS::one(),
|lc| lc + y.get_variable(),
);
Ok(())
}
fn test_tiny_r1cs_bellpepper_with<E: Engine>() {
let mut cs: TestShapeCS<E> = TestShapeCS::new();
let _ = synthesize_tiny_r1cs_bellpepper(&mut cs, None);
let shape = cs.r1cs_shape().unwrap();
let ck = R1CSShape::commitment_key(&[&shape], &[&*default_ck_hint()]).unwrap();
let ro_consts = ROConstants::<E>::default();
let mut cs = SatisfyingAssignment::<E>::new();
let _ = synthesize_tiny_r1cs_bellpepper(&mut cs, Some(E::Scalar::from(5)));
let (U1, W1) = cs.r1cs_instance_and_witness(&shape, &ck).unwrap();
assert!(shape.is_sat(&ck, &U1, &W1).is_ok());
let mut cs = SatisfyingAssignment::<E>::new();
let _ = synthesize_tiny_r1cs_bellpepper(&mut cs, Some(E::Scalar::from(135)));
let (U2, W2) = cs.r1cs_instance_and_witness(&shape, &ck).unwrap();
assert!(shape.is_sat(&ck, &U2, &W2).is_ok());
execute_sequence(
&ck,
&ro_consts,
&<E as Engine>::Scalar::ZERO,
&shape,
&U1,
&W1,
&U2,
&W2,
);
}
#[test]
fn test_tiny_r1cs_bellpepper() {
test_tiny_r1cs_bellpepper_with::<PallasEngine>();
test_tiny_r1cs_bellpepper_with::<Bn256EngineKZG>();
test_tiny_r1cs_bellpepper_with::<Secp256k1Engine>();
}
fn execute_sequence<E: Engine>(
ck: &CommitmentKey<E>,
ro_consts: &ROConstants<E>,
pp_digest: &<E as Engine>::Scalar,
shape: &R1CSShape<E>,
U1: &R1CSInstance<E>,
W1: &R1CSWitness<E>,
U2: &R1CSInstance<E>,
W2: &R1CSWitness<E>,
) {
let mut running_W = RelaxedR1CSWitness::default(shape);
let mut running_U = RelaxedR1CSInstance::default(ck, shape);
let res = NIFS::prove(
ck, ro_consts, pp_digest, shape, &running_U, &running_W, U1, W1,
);
assert!(res.is_ok());
let (nifs, (_U, W)) = res.unwrap();
let res = nifs.verify(ro_consts, pp_digest, &running_U, U1);
assert!(res.is_ok());
let U = res.unwrap();
assert_eq!(U, _U);
running_W = W;
running_U = U;
let res = NIFS::prove(
ck, ro_consts, pp_digest, shape, &running_U, &running_W, U2, W2,
);
assert!(res.is_ok());
let (nifs, (_U, W)) = res.unwrap();
let res = nifs.verify(ro_consts, pp_digest, &running_U, U2);
assert!(res.is_ok());
let U = res.unwrap();
assert_eq!(U, _U);
running_W = W;
running_U = U;
assert!(shape.is_sat_relaxed(ck, &running_U, &running_W).is_ok());
}
fn execute_sequence_relaxed<E: Engine>(
ck: &CommitmentKey<E>,
ro_consts: &ROConstants<E>,
pp_digest: &<E as Engine>::Scalar,
shape: &R1CSShape<E>,
U1: &RelaxedR1CSInstance<E>,
W1: &RelaxedR1CSWitness<E>,
U2: &RelaxedR1CSInstance<E>,
W2: &RelaxedR1CSWitness<E>,
) -> (RelaxedR1CSInstance<E>, RelaxedR1CSWitness<E>) {
let mut running_W = RelaxedR1CSWitness::default(shape);
let mut running_U = RelaxedR1CSInstance::default(ck, shape);
let res = NIFSRelaxed::prove(
ck, ro_consts, pp_digest, shape, &running_U, &running_W, U1, W1,
);
assert!(res.is_ok());
let (nifs, (_U, W)) = res.unwrap();
let res = nifs.verify(ro_consts, pp_digest, &running_U, U1);
assert!(res.is_ok());
let U = res.unwrap();
assert_eq!(U, _U);
running_W = W;
running_U = U;
let res = NIFSRelaxed::prove(
ck, ro_consts, pp_digest, shape, &running_U, &running_W, U2, W2,
);
assert!(res.is_ok());
let (nifs, (_U, W)) = res.unwrap();
let res = nifs.verify(ro_consts, pp_digest, &running_U, U2);
assert!(res.is_ok());
let U = res.unwrap();
assert_eq!(U, _U);
running_W = W;
running_U = U;
assert!(shape.is_sat_relaxed(ck, &running_U, &running_W).is_ok());
(running_U, running_W)
}
fn test_tiny_r1cs_relaxed_derandomize_with<E: Engine>() {
let (ck, S, final_U, final_W) = test_tiny_r1cs_relaxed_with::<E>();
assert!(S.is_sat_relaxed(&ck, &final_U, &final_W).is_ok());
let dk = E::CE::derand_key(&ck);
let (derandom_final_W, final_blind_W, final_blind_E) = final_W.derandomize();
let derandom_final_U = final_U.derandomize(&dk, &final_blind_W, &final_blind_E);
assert!(S
.is_sat_relaxed(&ck, &derandom_final_U, &derandom_final_W)
.is_ok());
}
#[test]
fn test_tiny_r1cs_relaxed_derandomize() {
test_tiny_r1cs_relaxed_derandomize_with::<PallasEngine>();
test_tiny_r1cs_relaxed_derandomize_with::<Bn256EngineKZG>();
test_tiny_r1cs_relaxed_derandomize_with::<Secp256k1Engine>();
}
fn test_tiny_r1cs_relaxed_with<E: Engine>() -> (
CommitmentKey<E>,
R1CSShape<E>,
RelaxedR1CSInstance<E>,
RelaxedR1CSWitness<E>,
) {
let one = <E::Scalar as Field>::ONE;
let (num_cons, num_vars, num_io, A, B, C) = {
let num_cons = 4;
let num_vars = 3;
let num_io = 2;
let mut A: Vec<(usize, usize, E::Scalar)> = Vec::new();
let mut B: Vec<(usize, usize, E::Scalar)> = Vec::new();
let mut C: Vec<(usize, usize, E::Scalar)> = Vec::new();
A.push((0, num_vars + 1, one));
B.push((0, num_vars + 1, one));
C.push((0, 0, one));
A.push((1, 0, one));
B.push((1, num_vars + 1, one));
C.push((1, 1, one));
A.push((2, 1, one));
A.push((2, num_vars + 1, one));
B.push((2, num_vars, one));
C.push((2, 2, one));
A.push((3, 2, one));
A.push((3, num_vars, one + one + one + one + one));
B.push((3, num_vars, one));
C.push((3, num_vars + 2, one));
(num_cons, num_vars, num_io, A, B, C)
};
let rows = num_cons;
let num_inputs = num_io + 1;
let cols = num_vars + num_inputs;
let S = {
let res = R1CSShape::new(
num_cons,
num_vars,
num_inputs - 1,
SparseMatrix::new(&A, rows, cols),
SparseMatrix::new(&B, rows, cols),
SparseMatrix::new(&C, rows, cols),
);
assert!(res.is_ok());
res.unwrap()
};
let ck = R1CSShape::commitment_key(&[&S], &[&*default_ck_hint()]).unwrap();
let ro_consts = ROConstants::<E>::default();
let rand_inst_witness_generator =
|ck: &CommitmentKey<E>, I: &E::Scalar| -> (E::Scalar, R1CSInstance<E>, R1CSWitness<E>) {
let i0 = *I;
let (O, vars, X) = {
let z0 = i0 * i0; let z1 = i0 * z0; let z2 = z1 + i0; let i1 = z2 + one + one + one + one + one;
let W = vec![z0, z1, z2];
let X = vec![i0, i1];
(i1, W, X)
};
let W = {
let res = R1CSWitness::new(&S, &vars);
assert!(res.is_ok());
res.unwrap()
};
let U = {
let comm_W = W.commit(ck);
let res = R1CSInstance::new(&S, &comm_W, &X);
assert!(res.is_ok());
res.unwrap()
};
assert!(S.is_sat(ck, &U, &W).is_ok());
(O, U, W)
};
let mut csprng: OsRng = OsRng;
let I = E::Scalar::random(&mut csprng); let (_O, U1, W1) = rand_inst_witness_generator(&ck, &I);
let (U2, W2) = S.sample_random_instance_witness(&ck).unwrap();
println!("INSTANCE {:#?}", U1.clone());
let (final_U, final_W) = execute_sequence_relaxed(
&ck,
&ro_consts,
&<E as Engine>::Scalar::ZERO,
&S,
&RelaxedR1CSInstance::from_r1cs_instance(&ck, &S, &U1),
&RelaxedR1CSWitness::from_r1cs_witness(&S, &W1),
&U2,
&W2,
);
(ck, S, final_U, final_W)
}
#[test]
fn test_tiny_r1cs_relaxed() {
test_tiny_r1cs_relaxed_with::<PallasEngine>();
test_tiny_r1cs_relaxed_with::<Bn256EngineKZG>();
test_tiny_r1cs_relaxed_with::<Secp256k1Engine>();
}
fn test_tiny_r1cs_with<E: Engine>() {
let one = <E::Scalar as Field>::ONE;
let (num_cons, num_vars, num_io, A, B, C) = {
let num_cons = 4;
let num_vars = 3;
let num_io = 2;
let mut A: Vec<(usize, usize, E::Scalar)> = Vec::new();
let mut B: Vec<(usize, usize, E::Scalar)> = Vec::new();
let mut C: Vec<(usize, usize, E::Scalar)> = Vec::new();
A.push((0, num_vars + 1, one));
B.push((0, num_vars + 1, one));
C.push((0, 0, one));
A.push((1, 0, one));
B.push((1, num_vars + 1, one));
C.push((1, 1, one));
A.push((2, 1, one));
A.push((2, num_vars + 1, one));
B.push((2, num_vars, one));
C.push((2, 2, one));
A.push((3, 2, one));
A.push((3, num_vars, one + one + one + one + one));
B.push((3, num_vars, one));
C.push((3, num_vars + 2, one));
(num_cons, num_vars, num_io, A, B, C)
};
let rows = num_cons;
let num_inputs = num_io + 1;
let cols = num_vars + num_inputs;
let S = {
let res = R1CSShape::new(
num_cons,
num_vars,
num_inputs - 1,
SparseMatrix::new(&A, rows, cols),
SparseMatrix::new(&B, rows, cols),
SparseMatrix::new(&C, rows, cols),
);
assert!(res.is_ok());
res.unwrap()
};
let ck = R1CSShape::commitment_key(&[&S], &[&*default_ck_hint()]).unwrap();
let ro_consts = ROConstants::<E>::default();
let rand_inst_witness_generator =
|ck: &CommitmentKey<E>, I: &E::Scalar| -> (E::Scalar, R1CSInstance<E>, R1CSWitness<E>) {
let i0 = *I;
let (O, vars, X) = {
let z0 = i0 * i0; let z1 = i0 * z0; let z2 = z1 + i0; let i1 = z2 + one + one + one + one + one;
let W = vec![z0, z1, z2];
let X = vec![i0, i1];
(i1, W, X)
};
let W = {
let res = R1CSWitness::new(&S, &vars);
assert!(res.is_ok());
res.unwrap()
};
let U = {
let comm_W = W.commit(ck);
let res = R1CSInstance::new(&S, &comm_W, &X);
assert!(res.is_ok());
res.unwrap()
};
assert!(S.is_sat(ck, &U, &W).is_ok());
(O, U, W)
};
let mut csprng: OsRng = OsRng;
let I = E::Scalar::random(&mut csprng); let (O, U1, W1) = rand_inst_witness_generator(&ck, &I);
let (_O, U2, W2) = rand_inst_witness_generator(&ck, &O);
execute_sequence(
&ck,
&ro_consts,
&<E as Engine>::Scalar::ZERO,
&S,
&U1,
&W1,
&U2,
&W2,
);
}
#[test]
fn test_tiny_r1cs() {
test_tiny_r1cs_with::<PallasEngine>();
test_tiny_r1cs_with::<Bn256EngineKZG>();
test_tiny_r1cs_with::<Secp256k1Engine>();
}
}