arnaucube
arnaucube/sonobePublic Self-hosted repository
sonobe/src/transcript/poseidon.rs
src/transcript/poseidon.rsRaw
  1use ark_crypto_primitives::sponge::{
  2    constraints::CryptographicSpongeVar,
  3    poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig, PoseidonSponge},
  4    Absorb, CryptographicSponge,
  5};
  6use ark_ec::{AffineRepr, CurveGroup, Group};
  7use ark_ff::{BigInteger, Field, PrimeField};
  8use ark_r1cs_std::fields::fp::FpVar;
  9use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
 10
 11use crate::transcript::Transcript;
 12
 13/// PoseidonTranscript implements the Transcript trait using the Poseidon hash
 14pub struct PoseidonTranscript<C: CurveGroup>
 15where
 16    <C as Group>::ScalarField: Absorb,
 17{
 18    sponge: PoseidonSponge<C::ScalarField>,
 19}
 20
 21impl<C: CurveGroup> Transcript<C> for PoseidonTranscript<C>
 22where
 23    <C as Group>::ScalarField: Absorb,
 24{
 25    type TranscriptConfig = PoseidonConfig<C::ScalarField>;
 26
 27    fn new(poseidon_config: &Self::TranscriptConfig) -> Self {
 28        let sponge = PoseidonSponge::<C::ScalarField>::new(poseidon_config);
 29        Self { sponge }
 30    }
 31    fn absorb(&mut self, v: &C::ScalarField) {
 32        self.sponge.absorb(&v);
 33    }
 34    fn absorb_vec(&mut self, v: &[C::ScalarField]) {
 35        self.sponge.absorb(&v);
 36    }
 37    fn absorb_point(&mut self, p: &C) {
 38        self.sponge.absorb(&prepare_point(p));
 39    }
 40    fn get_challenge(&mut self) -> C::ScalarField {
 41        let c = self.sponge.squeeze_field_elements(1);
 42        self.sponge.absorb(&c[0]);
 43        c[0]
 44    }
 45    fn get_challenges(&mut self, n: usize) -> Vec<C::ScalarField> {
 46        let c = self.sponge.squeeze_field_elements(n);
 47        self.sponge.absorb(&c);
 48        c
 49    }
 50}
 51
 52// Returns the point coordinates in Fr, so it can be absrobed by the transcript. It does not work
 53// over bytes in order to have a logic that can be reproduced in-circuit.
 54fn prepare_point<C: CurveGroup>(p: &C) -> Vec<C::ScalarField> {
 55    let binding = p.into_affine();
 56    let p_coords = &binding.xy().unwrap();
 57    let x_bi = p_coords
 58        .0
 59        .to_base_prime_field_elements()
 60        .next()
 61        .expect("a")
 62        .into_bigint();
 63    let y_bi = p_coords
 64        .1
 65        .to_base_prime_field_elements()
 66        .next()
 67        .expect("a")
 68        .into_bigint();
 69    vec![
 70        C::ScalarField::from_le_bytes_mod_order(x_bi.to_bytes_le().as_ref()),
 71        C::ScalarField::from_le_bytes_mod_order(y_bi.to_bytes_le().as_ref()),
 72    ]
 73}
 74
 75/// PoseidonTranscriptVar implements the gadget compatible with PoseidonTranscript
 76pub struct PoseidonTranscriptVar<F: PrimeField> {
 77    sponge: PoseidonSpongeVar<F>,
 78}
 79impl<F: PrimeField> PoseidonTranscriptVar<F> {
 80    pub fn new(cs: ConstraintSystemRef<F>, poseidon_config: &PoseidonConfig<F>) -> Self {
 81        let sponge = PoseidonSpongeVar::<F>::new(cs, poseidon_config);
 82        Self { sponge }
 83    }
 84    pub fn absorb(&mut self, v: FpVar<F>) -> Result<(), SynthesisError> {
 85        self.sponge.absorb(&v)
 86    }
 87    pub fn absorb_vec(&mut self, v: &[FpVar<F>]) -> Result<(), SynthesisError> {
 88        self.sponge.absorb(&v)
 89    }
 90    pub fn get_challenge(&mut self) -> Result<FpVar<F>, SynthesisError> {
 91        let c = self.sponge.squeeze_field_elements(1)?;
 92        self.sponge.absorb(&c[0])?;
 93        Ok(c[0].clone())
 94    }
 95    pub fn get_challenges(&mut self, n: usize) -> Result<Vec<FpVar<F>>, SynthesisError> {
 96        let c = self.sponge.squeeze_field_elements(n)?;
 97        self.sponge.absorb(&c)?;
 98        Ok(c)
 99    }
100}
101
102#[cfg(test)]
103pub mod tests {
104    use super::*;
105    use ark_bls12_377::{Fr, G1Projective};
106    use ark_crypto_primitives::sponge::poseidon::find_poseidon_ark_and_mds;
107    use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar, R1CSVar};
108    use ark_relations::r1cs::ConstraintSystem;
109
110    /// WARNING the method poseidon_test_config is for tests only
111    #[cfg(test)]
112    pub fn poseidon_test_config<F: PrimeField>() -> PoseidonConfig<F> {
113        let full_rounds = 8;
114        let partial_rounds = 31;
115        let alpha = 5;
116        let rate = 2;
117
118        let (ark, mds) = find_poseidon_ark_and_mds::<F>(
119            F::MODULUS_BIT_SIZE as u64,
120            rate,
121            full_rounds,
122            partial_rounds,
123            0,
124        );
125
126        PoseidonConfig::new(
127            full_rounds as usize,
128            partial_rounds as usize,
129            alpha,
130            mds,
131            ark,
132            rate,
133            1,
134        )
135    }
136
137    #[test]
138    fn test_transcript_and_transcriptvar() {
139        // use 'native' transcript
140        let config = poseidon_test_config::<Fr>();
141        let mut tr = PoseidonTranscript::<G1Projective>::new(&config);
142        tr.absorb(&Fr::from(42_u32));
143        let c = tr.get_challenge();
144
145        // use 'gadget' transcript
146        let cs = ConstraintSystem::<Fr>::new_ref();
147        let mut tr_var = PoseidonTranscriptVar::<Fr>::new(cs.clone(), &config);
148        let v = FpVar::<Fr>::new_witness(cs.clone(), || Ok(Fr::from(42_u32))).unwrap();
149        tr_var.absorb(v).unwrap();
150        let c_var = tr_var.get_challenge().unwrap();
151
152        // assert that native & gadget transcripts return the same challenge
153        assert_eq!(c, c_var.value().unwrap());
154    }
155}