Browse Source

make step_circuit mandatory; add support for longer hashes (#30)

main
Srinath Setty 2 years ago
committed by GitHub
parent
commit
9a0f5604d6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 159 additions and 179 deletions
  1. +102
    -163
      src/circuit.rs
  2. +57
    -16
      src/poseidon.rs

+ 102
- 163
src/circuit.rs

@ -104,7 +104,7 @@ where
{ {
params: NIFSVerifierCircuitParams, params: NIFSVerifierCircuitParams,
inputs: Option<NIFSVerifierCircuitInputs<G>>, inputs: Option<NIFSVerifierCircuitInputs<G>>,
step_circuit: Option<SC>, // The function that is applied for each step. may be None.
step_circuit: SC, // The function that is applied for each step
poseidon_constants: NovaPoseidonConstants<G::Base>, poseidon_constants: NovaPoseidonConstants<G::Base>,
} }
@ -119,7 +119,7 @@ where
pub fn new( pub fn new(
params: NIFSVerifierCircuitParams, params: NIFSVerifierCircuitParams,
inputs: Option<NIFSVerifierCircuitInputs<G>>, inputs: Option<NIFSVerifierCircuitInputs<G>>,
step_circuit: Option<SC>,
step_circuit: SC,
poseidon_constants: NovaPoseidonConstants<G::Base>, poseidon_constants: NovaPoseidonConstants<G::Base>,
) -> Self ) -> Self
where where
@ -289,7 +289,7 @@ where
// U2' = default if i == 0, otherwise NIFS.V(pp, u_new, U, T) // U2' = default if i == 0, otherwise NIFS.V(pp, u_new, U, T)
/***********************************************************************/ /***********************************************************************/
//Allocate 0 and 1
// Allocate 0 and 1
let zero = alloc_zero(cs.namespace(|| "zero"))?; let zero = alloc_zero(cs.namespace(|| "zero"))?;
// Hack: We just do this because the number of inputs must be even!! // Hack: We just do this because the number of inputs must be even!!
zero.inputize(cs.namespace(|| "allocate zero as input"))?; zero.inputize(cs.namespace(|| "allocate zero as input"))?;
@ -477,182 +477,114 @@ where
|lc| lc + next_i.get_variable() - CS::one() - i.get_variable(), |lc| lc + next_i.get_variable() - CS::one() - i.get_variable(),
); );
if self.step_circuit.is_some() {
/***********************************************************************/
//Allocate z0
/***********************************************************************/
/***********************************************************************/
// Allocate z0
/***********************************************************************/
let z_0 = AllocatedNum::alloc(cs.namespace(|| "z0"), || Ok(self.inputs.get()?.z0))?;
let z_0 = AllocatedNum::alloc(cs.namespace(|| "z0"), || Ok(self.inputs.get()?.z0))?;
/***********************************************************************/
//Allocate zi
/***********************************************************************/
/***********************************************************************/
// Allocate zi
/***********************************************************************/
let z_i = AllocatedNum::alloc(cs.namespace(|| "zi"), || Ok(self.inputs.get()?.zi))?;
let z_i = AllocatedNum::alloc(cs.namespace(|| "zi"), || Ok(self.inputs.get()?.zi))?;
/***********************************************************************/
//Check that if i == 0, z0 = zi, that is (i == 0) AND (z0 != zi) = false
/***********************************************************************/
/***********************************************************************/
//Check that if i == 0, z0 = zi, that is (i == 0) AND (z0 != zi) = false
/***********************************************************************/
let z0_is_zi = Boolean::from(alloc_num_equals(
cs.namespace(|| "z0 = zi"),
z_0.clone(),
z_i.clone(),
)?);
let z0_is_zi = Boolean::from(alloc_num_equals(
cs.namespace(|| "z0 = zi"),
z_0.clone(),
z_i.clone(),
)?);
cs.enforce(
|| "i == 0 and z0 != zi = false",
|_| base_case.lc(CS::one(), G::Base::one()),
|_| z0_is_zi.not().lc(CS::one(), G::Base::one()),
|lc| lc,
);
cs.enforce(
|| "i == 0 and z0 != zi = false",
|_| base_case.lc(CS::one(), G::Base::one()),
|_| z0_is_zi.not().lc(CS::one(), G::Base::one()),
|lc| lc,
);
/***********************************************************************/
// Check that h1 = Hash(params, u2,i,z0,zi)
/***********************************************************************/
let mut h1_hash: PoseidonROGadget<G::Base> =
PoseidonROGadget::new(self.poseidon_constants.clone());
h1_hash.absorb(params.clone());
h1_hash.absorb(W_r.x);
h1_hash.absorb(W_r.y);
h1_hash.absorb(W_r.is_infinity);
h1_hash.absorb(E_r.x);
h1_hash.absorb(E_r.y);
h1_hash.absorb(E_r.is_infinity);
h1_hash.absorb(u_r.clone());
// absorb each of the limbs of X_r[0]
for limb in Xr0_bn.into_iter() {
h1_hash.absorb(limb);
}
/***********************************************************************/
// Check that h1 = Hash(params, u2,i,z0,zi)
/***********************************************************************/
// absorb each of the limbs of X_r[1]
for limb in Xr1_bn.into_iter() {
h1_hash.absorb(limb);
}
let mut h1_hash: PoseidonROGadget<G::Base> =
PoseidonROGadget::new(self.poseidon_constants.clone());
h1_hash.absorb(i.clone());
h1_hash.absorb(z_0.clone());
h1_hash.absorb(z_i.clone());
h1_hash.absorb(params.clone());
h1_hash.absorb(W_r.x);
h1_hash.absorb(W_r.y);
h1_hash.absorb(W_r.is_infinity);
h1_hash.absorb(E_r.x);
h1_hash.absorb(E_r.y);
h1_hash.absorb(E_r.is_infinity);
h1_hash.absorb(u_r.clone());
let hash_bits = h1_hash.get_challenge(cs.namespace(|| "Input hash"))?;
let hash = le_bits_to_num(cs.namespace(|| "bits to hash"), hash_bits)?;
// absorb each of the limbs of X_r[0]
for limb in Xr0_bn.into_iter() {
h1_hash.absorb(limb);
}
cs.enforce(
|| "check h1",
|lc| lc,
|lc| lc,
|lc| lc + h1.get_variable() - hash.get_variable(),
);
// absorb each of the limbs of X_r[1]
for limb in Xr1_bn.into_iter() {
h1_hash.absorb(limb);
}
/***********************************************************************/
// Compute z_{i+1}
/***********************************************************************/
let z_next = self
.step_circuit
.unwrap()
.synthesize(&mut cs.namespace(|| "F"), z_i)?;
/***********************************************************************/
// Compute the new hash H(params, u2_new, i+1, z0, z_{i+1})
/***********************************************************************/
h1_hash.flush_state();
h1_hash.absorb(params);
h1_hash.absorb(W_new.x.clone());
h1_hash.absorb(W_new.y.clone());
h1_hash.absorb(W_new.is_infinity);
h1_hash.absorb(E_new.x.clone());
h1_hash.absorb(E_new.y.clone());
h1_hash.absorb(E_new.is_infinity);
h1_hash.absorb(u_new);
// absorb each of the limbs of X_r_new[0]
for limb in Xr0_new_bn.into_iter() {
h1_hash.absorb(limb);
}
h1_hash.absorb(i.clone());
h1_hash.absorb(z_0.clone());
h1_hash.absorb(z_i.clone());
// absorb each of the limbs of X_r_new[1]
for limb in Xr1_new_bn.into_iter() {
h1_hash.absorb(limb);
}
let hash_bits = h1_hash.get_challenge(cs.namespace(|| "Input hash"))?; // TODO: use get_hash method
let hash = le_bits_to_num(cs.namespace(|| "bits to hash"), hash_bits)?;
h1_hash.absorb(next_i.clone());
h1_hash.absorb(z_0);
h1_hash.absorb(z_next);
let h1_new_bits = h1_hash.get_challenge(cs.namespace(|| "h1_new bits"))?;
let h1_new = le_bits_to_num(cs.namespace(|| "h1_new"), h1_new_bits)?;
let _ = h1_new.inputize(cs.namespace(|| "output h1_new"))?;
} else {
/***********************************************************************/
// Check that h1 = Hash(params, u2, i)
/***********************************************************************/
let mut h1_hash: PoseidonROGadget<G::Base> = PoseidonROGadget::new(self.poseidon_constants);
h1_hash.absorb(params.clone());
h1_hash.absorb(W_r.x);
h1_hash.absorb(W_r.y);
h1_hash.absorb(W_r.is_infinity);
h1_hash.absorb(E_r.x);
h1_hash.absorb(E_r.y);
h1_hash.absorb(E_r.is_infinity);
h1_hash.absorb(u_r);
h1_hash.absorb(i.clone());
// absorb each of the limbs of X_r[0]
for limb in Xr0_bn.into_iter() {
h1_hash.absorb(limb);
}
// absorb each of the limbs of X_r[1]
for limb in Xr1_bn.into_iter() {
h1_hash.absorb(limb);
}
cs.enforce(
|| "check h1",
|lc| lc,
|lc| lc,
|lc| lc + h1.get_variable() - hash.get_variable(),
);
let hash_bits = h1_hash.get_challenge(cs.namespace(|| "Input hash"))?;
let hash = le_bits_to_num(cs.namespace(|| "bits to hash"), hash_bits)?;
/***********************************************************************/
// Compute z_{i+1}
/***********************************************************************/
cs.enforce(
|| "check h1",
|lc| lc,
|lc| lc,
|lc| lc + h1.get_variable() - hash.get_variable(),
);
let z_next = self
.step_circuit
.synthesize(&mut cs.namespace(|| "F"), z_i)?;
/***********************************************************************/
// Compute the new hash H(params, u2')
/***********************************************************************/
h1_hash.flush_state();
h1_hash.absorb(params);
h1_hash.absorb(W_new.x.clone());
h1_hash.absorb(W_new.y.clone());
h1_hash.absorb(W_new.is_infinity);
h1_hash.absorb(E_new.x.clone());
h1_hash.absorb(E_new.y.clone());
h1_hash.absorb(E_new.is_infinity);
h1_hash.absorb(u_new);
h1_hash.absorb(next_i.clone());
// absorb each of the limbs of X_r_new[0]
for limb in Xr0_new_bn.into_iter() {
h1_hash.absorb(limb);
}
/***********************************************************************/
// Compute the new hash H(params, u2_new, i+1, z0, z_{i+1})
/***********************************************************************/
// absorb each of the limbs of X_r_new[1]
for limb in Xr1_new_bn.into_iter() {
h1_hash.absorb(limb);
}
h1_hash.flush_state();
h1_hash.absorb(params);
h1_hash.absorb(W_new.x.clone());
h1_hash.absorb(W_new.y.clone());
h1_hash.absorb(W_new.is_infinity);
h1_hash.absorb(E_new.x.clone());
h1_hash.absorb(E_new.y.clone());
h1_hash.absorb(E_new.is_infinity);
h1_hash.absorb(u_new);
// absorb each of the limbs of X_r_new[0]
for limb in Xr0_new_bn.into_iter() {
h1_hash.absorb(limb);
}
let h1_new_bits = h1_hash.get_challenge(cs.namespace(|| "h1_new bits"))?;
let h1_new = le_bits_to_num(cs.namespace(|| "h1_new"), h1_new_bits)?;
let _ = h1_new.inputize(cs.namespace(|| "output h1_new"))?;
// absorb each of the limbs of X_r_new[1]
for limb in Xr1_new_bn.into_iter() {
h1_hash.absorb(limb);
} }
h1_hash.absorb(next_i.clone());
h1_hash.absorb(z_0);
h1_hash.absorb(z_next);
let h1_new_bits = h1_hash.get_challenge(cs.namespace(|| "h1_new bits"))?; // TODO: use get_hash method
let h1_new = le_bits_to_num(cs.namespace(|| "h1_new"), h1_new_bits)?;
let _ = h1_new.inputize(cs.namespace(|| "output h1_new"))?;
Ok(()) Ok(())
} }
} }
@ -700,9 +632,9 @@ mod tests {
NIFSVerifierCircuit::new( NIFSVerifierCircuit::new(
params.clone(), params.clone(),
None, None,
Some(TestCircuit {
TestCircuit {
_p: Default::default(), _p: Default::default(),
}),
},
poseidon_constants1.clone(), poseidon_constants1.clone(),
); );
// First create the shape // First create the shape
@ -718,7 +650,14 @@ mod tests {
let poseidon_constants2: NovaPoseidonConstants<<G1 as Group>::Base> = let poseidon_constants2: NovaPoseidonConstants<<G1 as Group>::Base> =
NovaPoseidonConstants::new(); NovaPoseidonConstants::new();
let circuit2: NIFSVerifierCircuit<G1, TestCircuit<<G1 as Group>::Base>> = let circuit2: NIFSVerifierCircuit<G1, TestCircuit<<G1 as Group>::Base>> =
NIFSVerifierCircuit::new(params.clone(), None, None, poseidon_constants2);
NIFSVerifierCircuit::new(
params.clone(),
None,
TestCircuit {
_p: Default::default(),
},
poseidon_constants2,
);
// First create the shape // First create the shape
let mut cs: ShapeCS<G2> = ShapeCS::new(); let mut cs: ShapeCS<G2> = ShapeCS::new();
let _ = circuit2.synthesize(&mut cs); let _ = circuit2.synthesize(&mut cs);
@ -755,9 +694,9 @@ mod tests {
NIFSVerifierCircuit::new( NIFSVerifierCircuit::new(
params, params,
Some(inputs), Some(inputs),
Some(TestCircuit {
TestCircuit {
_p: Default::default(), _p: Default::default(),
}),
},
poseidon_constants1, poseidon_constants1,
); );
let _ = circuit.synthesize(&mut cs); let _ = circuit.synthesize(&mut cs);

+ 57
- 16
src/poseidon.rs

@ -80,10 +80,8 @@ where
self.state.push(e); self.state.push(e);
} }
/// Compute a challenge by hashing the current state
#[allow(dead_code)]
pub fn get_challenge(&mut self) -> Scalar {
let hash = match self.state.len() {
fn hash_inner(&mut self) -> Scalar {
match self.state.len() {
25 => { 25 => {
Poseidon::<Scalar, U25>::new_with_preimage(&self.state, &self.constants.constants25).hash() Poseidon::<Scalar, U25>::new_with_preimage(&self.state, &self.constants.constants25).hash()
} }
@ -96,7 +94,13 @@ where
_ => { _ => {
panic!("Number of elements in the RO state does not match any of the arities used in Nova") panic!("Number of elements in the RO state does not match any of the arities used in Nova")
} }
};
}
}
/// Compute a challenge by hashing the current state
#[allow(dead_code)]
pub fn get_challenge(&mut self) -> Scalar {
let hash = self.hash_inner();
// Only keep 128 bits // Only keep 128 bits
let bits = hash.to_le_bits(); let bits = hash.to_le_bits();
let mut res = Scalar::zero(); let mut res = Scalar::zero();
@ -109,6 +113,22 @@ where
} }
res res
} }
#[allow(dead_code)]
pub fn get_hash(&mut self) -> Scalar {
let hash = self.hash_inner();
// Only keep 250 bits
let bits = hash.to_le_bits();
let mut res = Scalar::zero();
let mut coeff = Scalar::one();
for bit in bits[0..250].into_iter() {
if *bit {
res += coeff;
}
coeff += coeff;
}
res
}
} }
/// A Poseidon-based RO gadget to use inside the verifier circuit. /// A Poseidon-based RO gadget to use inside the verifier circuit.
@ -145,9 +165,7 @@ where
self.state.push(e); self.state.push(e);
} }
/// Compute a challenge by hashing the current state
#[allow(dead_code)]
pub fn get_challenge<CS>(&mut self, mut cs: CS) -> Result<Vec<AllocatedBit>, SynthesisError>
fn hash_inner<CS>(&mut self, mut cs: CS) -> Result<Vec<AllocatedBit>, SynthesisError>
where where
CS: ConstraintSystem<Scalar>, CS: ConstraintSystem<Scalar>,
{ {
@ -171,17 +189,40 @@ where
panic!("Number of elements in the RO state does not match any of the arities used in Nova") panic!("Number of elements in the RO state does not match any of the arities used in Nova")
} }
}; };
// return the hash as a vector of bits
Ok(
out
.to_bits_le_strict(cs.namespace(|| "poseidon hash to boolean"))?
.iter()
.map(|boolean| match boolean {
Boolean::Is(ref x) => x.clone(),
_ => panic!("Wrong type of input. We should have never reached there"),
})
.collect(),
)
}
/// Compute a challenge by hashing the current state
#[allow(dead_code)]
pub fn get_challenge<CS>(&mut self, mut cs: CS) -> Result<Vec<AllocatedBit>, SynthesisError>
where
CS: ConstraintSystem<Scalar>,
{
let bits = self.hash_inner(cs.namespace(|| "hash"))?;
// Only keep 128 bits // Only keep 128 bits
let bits: Vec<AllocatedBit> = out
.to_bits_le_strict(cs.namespace(|| "poseidon hash to boolean"))?
.iter()
.map(|boolean| match boolean {
Boolean::Is(ref x) => x.clone(),
_ => panic!("Wrong type of input. We should have never reached there"),
})
.collect();
Ok(bits[..128].into()) Ok(bits[..128].into())
} }
#[allow(dead_code)]
pub fn get_hash<CS>(&mut self, mut cs: CS) -> Result<Vec<AllocatedBit>, SynthesisError>
where
CS: ConstraintSystem<Scalar>,
{
let bits = self.hash_inner(cs.namespace(|| "hash"))?;
// Only keep 250 bits
Ok(bits[..250].into())
}
} }
#[cfg(test)] #[cfg(test)]

Loading…
Cancel
Save