arnaucube
arnaucube/NovaPublic Self-hosted repository
Nova/src/poseidon.rs
src/poseidon.rsRaw
  1//! Poseidon Constants and Poseidon-based RO used in Nova
  2use bellperson::{
  3  gadgets::{
  4    boolean::{AllocatedBit, Boolean},
  5    num::AllocatedNum,
  6  },
  7  ConstraintSystem, SynthesisError,
  8};
  9use ff::{PrimeField, PrimeFieldBits};
 10use generic_array::typenum::{U25, U27, U31};
 11use neptune::{
 12  circuit::poseidon_hash,
 13  poseidon::{Poseidon, PoseidonConstants},
 14};
 15
 16#[cfg(test)]
 17use neptune::Strength;
 18
 19/// All Poseidon Constants that are used in Nova
 20#[derive(Clone)]
 21pub struct NovaPoseidonConstants<F>
 22where
 23  F: PrimeField,
 24{
 25  constants25: PoseidonConstants<F, U25>,
 26  constants27: PoseidonConstants<F, U27>,
 27  constants31: PoseidonConstants<F, U31>,
 28}
 29
 30#[cfg(test)]
 31impl<F> NovaPoseidonConstants<F>
 32where
 33  F: PrimeField,
 34{
 35  /// Generate Poseidon constants for the arities that Nova uses
 36  pub fn new() -> Self {
 37    let constants25 = PoseidonConstants::<F, U25>::new_with_strength(Strength::Strengthened);
 38    let constants27 = PoseidonConstants::<F, U27>::new_with_strength(Strength::Strengthened);
 39    let constants31 = PoseidonConstants::<F, U31>::new_with_strength(Strength::Strengthened);
 40    Self {
 41      constants25,
 42      constants27,
 43      constants31,
 44    }
 45  }
 46}
 47
 48/// A Poseidon-based RO to use outside circuits
 49pub struct PoseidonRO<Scalar>
 50where
 51  Scalar: PrimeField + PrimeFieldBits,
 52{
 53  // Internal State
 54  state: Vec<Scalar>,
 55  // Constants for Poseidon
 56  constants: NovaPoseidonConstants<Scalar>,
 57}
 58
 59impl<Scalar> PoseidonRO<Scalar>
 60where
 61  Scalar: PrimeField + PrimeFieldBits,
 62{
 63  #[allow(dead_code)]
 64  pub fn new(constants: NovaPoseidonConstants<Scalar>) -> Self {
 65    Self {
 66      state: Vec::new(),
 67      constants,
 68    }
 69  }
 70
 71  #[allow(dead_code)]
 72  /// Flush the state of the RO
 73  pub fn flush_state(&mut self) {
 74    self.state = Vec::new();
 75  }
 76
 77  /// Absorb a new number into the state of the oracle
 78  #[allow(dead_code)]
 79  pub fn absorb(&mut self, e: Scalar) {
 80    self.state.push(e);
 81  }
 82
 83  fn hash_inner(&mut self) -> Scalar {
 84    match self.state.len() {
 85      25 => {
 86        Poseidon::<Scalar, U25>::new_with_preimage(&self.state, &self.constants.constants25).hash()
 87      }
 88      27 => {
 89        Poseidon::<Scalar, U27>::new_with_preimage(&self.state, &self.constants.constants27).hash()
 90      }
 91      31 => {
 92        Poseidon::<Scalar, U31>::new_with_preimage(&self.state, &self.constants.constants31).hash()
 93      }
 94      _ => {
 95        panic!("Number of elements in the RO state does not match any of the arities used in Nova")
 96      }
 97    }
 98  }
 99
100  /// Compute a challenge by hashing the current state
101  #[allow(dead_code)]
102  pub fn get_challenge(&mut self) -> Scalar {
103    let hash = self.hash_inner();
104    // Only keep 128 bits
105    let bits = hash.to_le_bits();
106    let mut res = Scalar::zero();
107    let mut coeff = Scalar::one();
108    for bit in bits[0..128].into_iter() {
109      if *bit {
110        res += coeff;
111      }
112      coeff += coeff;
113    }
114    res
115  }
116
117  #[allow(dead_code)]
118  pub fn get_hash(&mut self) -> Scalar {
119    let hash = self.hash_inner();
120    // Only keep 250 bits
121    let bits = hash.to_le_bits();
122    let mut res = Scalar::zero();
123    let mut coeff = Scalar::one();
124    for bit in bits[0..250].into_iter() {
125      if *bit {
126        res += coeff;
127      }
128      coeff += coeff;
129    }
130    res
131  }
132}
133
134/// A Poseidon-based RO gadget to use inside the verifier circuit.
135pub struct PoseidonROGadget<Scalar>
136where
137  Scalar: PrimeField + PrimeFieldBits,
138{
139  // Internal state
140  state: Vec<AllocatedNum<Scalar>>,
141  constants: NovaPoseidonConstants<Scalar>,
142}
143
144impl<Scalar> PoseidonROGadget<Scalar>
145where
146  Scalar: PrimeField + PrimeFieldBits,
147{
148  /// Initialize the internal state and set the poseidon constants
149  #[allow(dead_code)]
150  pub fn new(constants: NovaPoseidonConstants<Scalar>) -> Self {
151    Self {
152      state: Vec::new(),
153      constants,
154    }
155  }
156
157  /// Flush the state of the RO
158  pub fn flush_state(&mut self) {
159    self.state = Vec::new();
160  }
161
162  /// Absorb a new number into the state of the oracle
163  #[allow(dead_code)]
164  pub fn absorb(&mut self, e: AllocatedNum<Scalar>) {
165    self.state.push(e);
166  }
167
168  fn hash_inner<CS>(&mut self, mut cs: CS) -> Result<Vec<AllocatedBit>, SynthesisError>
169  where
170    CS: ConstraintSystem<Scalar>,
171  {
172    let out = match self.state.len() {
173      25 => poseidon_hash(
174        cs.namespace(|| "Poseidon hash"),
175        self.state.clone(),
176        &self.constants.constants25,
177      )?,
178      27 => poseidon_hash(
179        cs.namespace(|| "Poseidon hash"),
180        self.state.clone(),
181        &self.constants.constants27,
182      )?,
183      31 => poseidon_hash(
184        cs.namespace(|| "Poseidon hash"),
185        self.state.clone(),
186        &self.constants.constants31,
187      )?,
188      _ => {
189        panic!("Number of elements in the RO state does not match any of the arities used in Nova")
190      }
191    };
192
193    // return the hash as a vector of bits
194    Ok(
195      out
196        .to_bits_le_strict(cs.namespace(|| "poseidon hash to boolean"))?
197        .iter()
198        .map(|boolean| match boolean {
199          Boolean::Is(ref x) => x.clone(),
200          _ => panic!("Wrong type of input. We should have never reached there"),
201        })
202        .collect(),
203    )
204  }
205
206  /// Compute a challenge by hashing the current state
207  #[allow(dead_code)]
208  pub fn get_challenge<CS>(&mut self, mut cs: CS) -> Result<Vec<AllocatedBit>, SynthesisError>
209  where
210    CS: ConstraintSystem<Scalar>,
211  {
212    let bits = self.hash_inner(cs.namespace(|| "hash"))?;
213    // Only keep 128 bits
214    Ok(bits[..128].into())
215  }
216
217  #[allow(dead_code)]
218  pub fn get_hash<CS>(&mut self, mut cs: CS) -> Result<Vec<AllocatedBit>, SynthesisError>
219  where
220    CS: ConstraintSystem<Scalar>,
221  {
222    let bits = self.hash_inner(cs.namespace(|| "hash"))?;
223    // Only keep 250 bits
224    Ok(bits[..250].into())
225  }
226}
227
228#[cfg(test)]
229mod tests {
230  use super::*;
231  type S = pasta_curves::pallas::Scalar;
232  type G = pasta_curves::pallas::Point;
233  use crate::{bellperson::solver::SatisfyingAssignment, gadgets::utils::le_bits_to_num};
234  use ff::Field;
235  use rand::rngs::OsRng;
236
237  #[test]
238  fn test_poseidon_ro() {
239    // Check that the number computed inside the circuit is equal to the number computed outside the circuit
240    let mut csprng: OsRng = OsRng;
241    let constants = NovaPoseidonConstants::new();
242    let mut ro: PoseidonRO<S> = PoseidonRO::new(constants.clone());
243    let mut ro_gadget: PoseidonROGadget<S> = PoseidonROGadget::new(constants);
244    let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
245    for i in 0..31 {
246      let num = S::random(&mut csprng);
247      ro.absorb(num);
248      let num_gadget =
249        AllocatedNum::alloc(cs.namespace(|| format!("data {}", i)), || Ok(num)).unwrap();
250      let _ = num_gadget
251        .inputize(&mut cs.namespace(|| format!("input {}", i)))
252        .unwrap();
253      ro_gadget.absorb(num_gadget);
254    }
255    let num = ro.get_challenge();
256    let num2_bits = ro_gadget.get_challenge(&mut cs).unwrap();
257    let num2 = le_bits_to_num(&mut cs, num2_bits).unwrap();
258    assert_eq!(num, num2.get_value().unwrap());
259  }
260}