mirror of
https://github.com/arnaucube/Nova.git
synced 2026-01-11 00:21:29 +01:00
make step_circuit mandatory; add support for longer hashes (#30)
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
Reference in New Issue
Block a user