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

This commit is contained in:
Srinath Setty
2022-04-15 16:06:26 -07:00
committed by GitHub
parent 866717a8f2
commit 9a0f5604d6
2 changed files with 168 additions and 188 deletions

View File

@@ -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,15 +477,14 @@ 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))?;
@@ -537,7 +536,7 @@ where
h1_hash.absorb(z_0.clone()); h1_hash.absorb(z_0.clone());
h1_hash.absorb(z_i.clone()); h1_hash.absorb(z_i.clone());
let hash_bits = h1_hash.get_challenge(cs.namespace(|| "Input hash"))?; 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)?; let hash = le_bits_to_num(cs.namespace(|| "bits to hash"), hash_bits)?;
cs.enforce( cs.enforce(
@@ -553,7 +552,6 @@ where
let z_next = self let z_next = self
.step_circuit .step_circuit
.unwrap()
.synthesize(&mut cs.namespace(|| "F"), z_i)?; .synthesize(&mut cs.namespace(|| "F"), z_i)?;
/***********************************************************************/ /***********************************************************************/
@@ -583,75 +581,9 @@ where
h1_hash.absorb(next_i.clone()); h1_hash.absorb(next_i.clone());
h1_hash.absorb(z_0); h1_hash.absorb(z_0);
h1_hash.absorb(z_next); h1_hash.absorb(z_next);
let h1_new_bits = h1_hash.get_challenge(cs.namespace(|| "h1_new bits"))?; 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 = le_bits_to_num(cs.namespace(|| "h1_new"), h1_new_bits)?;
let _ = h1_new.inputize(cs.namespace(|| "output h1_new"))?; 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);
}
let hash_bits = h1_hash.get_challenge(cs.namespace(|| "Input hash"))?;
let hash = le_bits_to_num(cs.namespace(|| "bits to hash"), hash_bits)?;
cs.enforce(
|| "check h1",
|lc| lc,
|lc| lc,
|lc| lc + h1.get_variable() - hash.get_variable(),
);
/***********************************************************************/
// 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);
}
// absorb each of the limbs of X_r_new[1]
for limb in Xr1_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"))?;
}
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);

View File

@@ -80,10 +80,8 @@ where
self.state.push(e); self.state.push(e);
} }
/// Compute a challenge by hashing the current state fn hash_inner(&mut self) -> Scalar {
#[allow(dead_code)] match self.state.len() {
pub fn get_challenge(&mut self) -> Scalar {
let hash = 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 fn hash_inner<CS>(&mut self, mut cs: CS) -> Result<Vec<AllocatedBit>, SynthesisError>
#[allow(dead_code)]
pub fn get_challenge<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")
} }
}; };
// Only keep 128 bits
let bits: Vec<AllocatedBit> = out // return the hash as a vector of bits
Ok(
out
.to_bits_le_strict(cs.namespace(|| "poseidon hash to boolean"))? .to_bits_le_strict(cs.namespace(|| "poseidon hash to boolean"))?
.iter() .iter()
.map(|boolean| match boolean { .map(|boolean| match boolean {
Boolean::Is(ref x) => x.clone(), Boolean::Is(ref x) => x.clone(),
_ => panic!("Wrong type of input. We should have never reached there"), _ => panic!("Wrong type of input. We should have never reached there"),
}) })
.collect(); .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
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)]