|  | 
|  | 1 | +use std::cmp::max; | 
|  | 2 | + | 
|  | 3 | +use crate::errors::ProofVerifyError; | 
|  | 4 | +use crate::r1csproof::R1CSVerifierProof; | 
|  | 5 | +use crate::{ | 
|  | 6 | +  poseidon_transcript::PoseidonTranscript, | 
|  | 7 | +  r1csproof::{R1CSGens, R1CSProof}, | 
|  | 8 | +  transcript::Transcript, | 
|  | 9 | +  InputsAssignment, Instance, VarsAssignment, | 
|  | 10 | +}; | 
|  | 11 | +use ark_crypto_primitives::sponge::poseidon::PoseidonConfig; | 
|  | 12 | +use ark_crypto_primitives::sponge::Absorb; | 
|  | 13 | +use ark_ec::pairing::Pairing; | 
|  | 14 | + | 
|  | 15 | +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; | 
|  | 16 | + | 
|  | 17 | +#[derive(Debug, CanonicalSerialize, CanonicalDeserialize)] | 
|  | 18 | +pub struct TestudoNizk<E: Pairing> { | 
|  | 19 | +  pub r1cs_verifier_proof: R1CSVerifierProof<E>, | 
|  | 20 | +  pub r: (Vec<E::ScalarField>, Vec<E::ScalarField>), | 
|  | 21 | +} | 
|  | 22 | + | 
|  | 23 | +pub struct TestudoNizkGens<E: Pairing> { | 
|  | 24 | +  gens_r1cs_sat: R1CSGens<E>, | 
|  | 25 | +} | 
|  | 26 | + | 
|  | 27 | +impl<E: Pairing> TestudoNizkGens<E> { | 
|  | 28 | +  /// Constructs a new `TestudoNizkGens` given the size of the R1CS statement | 
|  | 29 | +  /// `num_nz_entries` specifies the maximum number of non-zero entries in any of the three R1CS matrices | 
|  | 30 | +  pub fn new( | 
|  | 31 | +    num_cons: usize, | 
|  | 32 | +    num_vars: usize, | 
|  | 33 | +    num_inputs: usize, | 
|  | 34 | +    poseidon: PoseidonConfig<E::ScalarField>, | 
|  | 35 | +  ) -> Self { | 
|  | 36 | +    // ensure num_vars is a power of 2 | 
|  | 37 | +    let num_vars_padded = { | 
|  | 38 | +      let mut num_vars_padded = max(num_vars, num_inputs + 1); | 
|  | 39 | +      if num_vars_padded != num_vars_padded.next_power_of_two() { | 
|  | 40 | +        num_vars_padded = num_vars_padded.next_power_of_two(); | 
|  | 41 | +      } | 
|  | 42 | +      num_vars_padded | 
|  | 43 | +    }; | 
|  | 44 | + | 
|  | 45 | +    let num_cons_padded = { | 
|  | 46 | +      let mut num_cons_padded = num_cons; | 
|  | 47 | + | 
|  | 48 | +      // ensure that num_cons_padded is at least 2 | 
|  | 49 | +      if num_cons_padded == 0 || num_cons_padded == 1 { | 
|  | 50 | +        num_cons_padded = 2; | 
|  | 51 | +      } | 
|  | 52 | + | 
|  | 53 | +      // ensure that num_cons_padded is a power of 2 | 
|  | 54 | +      if num_cons.next_power_of_two() != num_cons { | 
|  | 55 | +        num_cons_padded = num_cons.next_power_of_two(); | 
|  | 56 | +      } | 
|  | 57 | +      num_cons_padded | 
|  | 58 | +    }; | 
|  | 59 | + | 
|  | 60 | +    let gens_r1cs_sat = R1CSGens::new( | 
|  | 61 | +      b"gens_r1cs_sat", | 
|  | 62 | +      num_cons_padded, | 
|  | 63 | +      num_vars_padded, | 
|  | 64 | +      num_inputs, | 
|  | 65 | +      poseidon, | 
|  | 66 | +    ); | 
|  | 67 | +    TestudoNizkGens { gens_r1cs_sat } | 
|  | 68 | +  } | 
|  | 69 | +} | 
|  | 70 | + | 
|  | 71 | +impl<E: Pairing> TestudoNizk<E> | 
|  | 72 | +where | 
|  | 73 | +  E::ScalarField: Absorb, | 
|  | 74 | +{ | 
|  | 75 | +  // Returns the Testudo SNARK proof which has two components: | 
|  | 76 | +  // * proof that the R1CS instance is satisfiable | 
|  | 77 | +  // * proof that the evlauation of matrices A, B and C on (x,y) are correct | 
|  | 78 | +  pub fn prove( | 
|  | 79 | +    inst: &Instance<E::ScalarField>, | 
|  | 80 | +    vars: VarsAssignment<E::ScalarField>, | 
|  | 81 | +    inputs: &InputsAssignment<E::ScalarField>, | 
|  | 82 | +    gens: &TestudoNizkGens<E>, | 
|  | 83 | +    transcript: &mut PoseidonTranscript<E::ScalarField>, | 
|  | 84 | +    poseidon: PoseidonConfig<E::ScalarField>, | 
|  | 85 | +  ) -> Result<TestudoNizk<E>, ProofVerifyError> { | 
|  | 86 | +    transcript.append_bytes(b"", &inst.digest); | 
|  | 87 | + | 
|  | 88 | +    let c: E::ScalarField = transcript.challenge_scalar(b""); | 
|  | 89 | +    transcript.new_from_state(&c); | 
|  | 90 | + | 
|  | 91 | +    // we might need to pad variables | 
|  | 92 | +    let padded_vars = { | 
|  | 93 | +      let num_padded_vars = inst.inst.get_num_vars(); | 
|  | 94 | +      let num_vars = vars.assignment.len(); | 
|  | 95 | +      if num_padded_vars > num_vars { | 
|  | 96 | +        vars.pad(num_padded_vars) | 
|  | 97 | +      } else { | 
|  | 98 | +        vars | 
|  | 99 | +      } | 
|  | 100 | +    }; | 
|  | 101 | + | 
|  | 102 | +    let (r1cs_sat_proof, rx, ry) = R1CSProof::prove( | 
|  | 103 | +      &inst.inst, | 
|  | 104 | +      padded_vars.assignment, | 
|  | 105 | +      &inputs.assignment, | 
|  | 106 | +      &gens.gens_r1cs_sat, | 
|  | 107 | +      transcript, | 
|  | 108 | +    ); | 
|  | 109 | + | 
|  | 110 | +    let inst_evals = inst.inst.evaluate(&rx, &ry); | 
|  | 111 | + | 
|  | 112 | +    transcript.new_from_state(&c); | 
|  | 113 | +    let r1cs_verifier_proof = r1cs_sat_proof | 
|  | 114 | +      .prove_verifier( | 
|  | 115 | +        inst.inst.get_num_vars(), | 
|  | 116 | +        inst.inst.get_num_cons(), | 
|  | 117 | +        &inputs.assignment, | 
|  | 118 | +        &inst_evals, | 
|  | 119 | +        transcript, | 
|  | 120 | +        &gens.gens_r1cs_sat, | 
|  | 121 | +        poseidon, | 
|  | 122 | +      ) | 
|  | 123 | +      .unwrap(); | 
|  | 124 | +    Ok(TestudoNizk { | 
|  | 125 | +      r1cs_verifier_proof, | 
|  | 126 | +      r: (rx, ry), | 
|  | 127 | +    }) | 
|  | 128 | +  } | 
|  | 129 | + | 
|  | 130 | +  // Verifies the Testudo SNARK proof  ensuring the satisfiability of an R1CS | 
|  | 131 | +  // instance | 
|  | 132 | +  pub fn verify( | 
|  | 133 | +    &self, | 
|  | 134 | +    gens: &TestudoNizkGens<E>, | 
|  | 135 | +    inst: &Instance<E::ScalarField>, | 
|  | 136 | +    input: &InputsAssignment<E::ScalarField>, | 
|  | 137 | +    transcript: &mut PoseidonTranscript<E::ScalarField>, | 
|  | 138 | +    _poseidon: PoseidonConfig<E::ScalarField>, | 
|  | 139 | +  ) -> Result<bool, ProofVerifyError> { | 
|  | 140 | +    transcript.append_bytes(b"", &inst.digest); | 
|  | 141 | +    let (claimed_rx, claimed_ry) = &self.r; | 
|  | 142 | +    let inst_evals = inst.inst.evaluate(claimed_rx, claimed_ry); | 
|  | 143 | + | 
|  | 144 | +    let sat_verified = self.r1cs_verifier_proof.verify( | 
|  | 145 | +      &input.assignment, | 
|  | 146 | +      &inst_evals, | 
|  | 147 | +      transcript, | 
|  | 148 | +      &gens.gens_r1cs_sat, | 
|  | 149 | +    )?; | 
|  | 150 | +    assert!(sat_verified == true); | 
|  | 151 | +    Ok(sat_verified) | 
|  | 152 | +  } | 
|  | 153 | +} | 
|  | 154 | + | 
|  | 155 | +#[cfg(test)] | 
|  | 156 | +mod tests { | 
|  | 157 | +  use crate::{ | 
|  | 158 | +    parameters::poseidon_params, | 
|  | 159 | +    poseidon_transcript::PoseidonTranscript, | 
|  | 160 | +    testudo_nizk::{TestudoNizk, TestudoNizkGens}, | 
|  | 161 | +    Instance, | 
|  | 162 | +  }; | 
|  | 163 | + | 
|  | 164 | +  #[test] | 
|  | 165 | +  pub fn check_testudo_nizk() { | 
|  | 166 | +    let num_vars = 256; | 
|  | 167 | +    let num_cons = num_vars; | 
|  | 168 | +    let num_inputs = 10; | 
|  | 169 | + | 
|  | 170 | +    type E = ark_bls12_377::Bls12_377; | 
|  | 171 | + | 
|  | 172 | +    // produce public generators | 
|  | 173 | +    let gens = TestudoNizkGens::<E>::new(num_cons, num_vars, num_inputs, poseidon_params()); | 
|  | 174 | + | 
|  | 175 | +    // produce a synthetic R1CSInstance | 
|  | 176 | +    let (inst, vars, inputs) = Instance::produce_synthetic_r1cs(num_cons, num_vars, num_inputs); | 
|  | 177 | + | 
|  | 178 | +    let params = poseidon_params(); | 
|  | 179 | + | 
|  | 180 | +    // produce a proof | 
|  | 181 | +    let mut prover_transcript = PoseidonTranscript::new(¶ms); | 
|  | 182 | +    let proof = | 
|  | 183 | +      TestudoNizk::prove(&inst, vars, &inputs, &gens, &mut prover_transcript, params).unwrap(); | 
|  | 184 | + | 
|  | 185 | +    // verify the proof | 
|  | 186 | +    let mut verifier_transcript = PoseidonTranscript::new(&poseidon_params()); | 
|  | 187 | +    assert!(proof | 
|  | 188 | +      .verify( | 
|  | 189 | +        &gens, | 
|  | 190 | +        &inst, | 
|  | 191 | +        &inputs, | 
|  | 192 | +        &mut verifier_transcript, | 
|  | 193 | +        poseidon_params() | 
|  | 194 | +      ) | 
|  | 195 | +      .is_ok()); | 
|  | 196 | +  } | 
|  | 197 | +} | 
0 commit comments