sonobe_primitives/transcripts/poseidon/
sponge.rs1use ark_crypto_primitives::sponge::{
4 Absorb, CryptographicSponge, DuplexSpongeMode, FieldBasedCryptographicSponge,
5 constraints::CryptographicSpongeVar,
6 poseidon::{PoseidonConfig, PoseidonSponge, constraints::PoseidonSpongeVar},
7};
8use ark_ff::PrimeField;
9use ark_r1cs_std::fields::{FieldVar, fp::FpVar};
10use ark_relations::gr1cs::{ConstraintSystemRef, SynthesisError};
11use ark_std::mem::transmute_copy;
12
13use crate::transcripts::{AbsorbableVar, Transcript, TranscriptGadget};
14
15impl<F: PrimeField> Transcript<F> for PoseidonSponge<F> {
16 type Config = PoseidonConfig<F>;
17 type Gadget = PoseidonSpongeVar<F>;
18
19 fn new(config: Self::Config) -> Self {
20 Self {
21 state: vec![F::zero(); config.rate + config.capacity],
22 parameters: config,
23 mode: DuplexSpongeMode::Absorbing {
24 next_absorb_index: 0,
25 },
26 }
27 }
28
29 fn add_field_elements(&mut self, input: &[F]) -> &mut Self {
30 struct Hack<I>(I);
31 impl<F> Absorb for Hack<&[F]> {
32 fn to_sponge_bytes(&self, _: &mut Vec<u8>) {
33 unreachable!()
36 }
37
38 fn to_sponge_field_elements<T: PrimeField>(&self, dest: &mut Vec<T>) {
39 dest.extend(unsafe { transmute_copy::<&[F], &[T]>(&self.0) });
43 }
44 }
45 CryptographicSponge::absorb(self, &Hack(input));
46 self
47 }
48
49 fn get_field_elements(&mut self, num_elements: usize) -> Vec<F> {
50 self.squeeze_native_field_elements(num_elements)
51 }
52}
53
54impl<F: PrimeField> TranscriptGadget<F> for PoseidonSpongeVar<F> {
55 type Config = PoseidonConfig<F>;
56 type Widget = PoseidonSponge<F>;
57
58 fn new(config: PoseidonConfig<F>) -> Self
59 where
60 Self: Sized,
61 {
62 Self {
63 cs: ConstraintSystemRef::None,
64 state: vec![FpVar::<F>::zero(); config.rate + config.capacity],
65 parameters: config,
66 mode: DuplexSpongeMode::Absorbing {
67 next_absorb_index: 0,
68 },
69 }
70 }
71
72 fn add<A: AbsorbableVar<F>>(&mut self, input: &A) -> Result<&mut Self, SynthesisError> {
73 let mut result = Vec::new();
74 input.absorb_into(&mut result)?;
75
76 self.absorb(&result)?;
77 Ok(self)
78 }
79
80 fn get_field_elements(&mut self, num_elements: usize) -> Result<Vec<FpVar<F>>, SynthesisError> {
81 self.squeeze_field_elements(num_elements)
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use ark_bn254::{Fr, G1Projective as G1};
88 use ark_crypto_primitives::sponge::poseidon::{PoseidonSponge, constraints::PoseidonSpongeVar};
89 use ark_ff::UniformRand;
90 use ark_grumpkin::Projective as G2;
91 use ark_r1cs_std::{
92 GR1CSVar, alloc::AllocVar, fields::fp::FpVar,
93 groups::curves::short_weierstrass::ProjectiveVar,
94 };
95 use ark_relations::gr1cs::ConstraintSystem;
96 use ark_std::{error::Error, rand::thread_rng, str::FromStr};
97 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
98 use wasm_bindgen_test::wasm_bindgen_test as test;
99
100 use crate::{
101 algebra::group::emulated::EmulatedAffineVar,
102 transcripts::{Transcript, TranscriptGadget, poseidon::poseidon_circom_config},
103 };
104
105 #[test]
107 fn check_against_circom_poseidon() -> Result<(), Box<dyn Error>> {
108 let config = poseidon_circom_config();
109 let mut poseidon_sponge = PoseidonSponge::new(config);
110 let v = vec![1, 2, 3, 4]
111 .into_iter()
112 .map(Fr::from)
113 .collect::<Vec<_>>();
114 poseidon_sponge.add(&v);
115 poseidon_sponge.get_field_elements(1);
116 assert_eq!(
117 poseidon_sponge.state[0],
118 Fr::from_str(
119 "18821383157269793795438455681495246036402687001665670618754263018637548127333"
120 )
121 .unwrap()
122 );
123 Ok(())
124 }
125
126 #[test]
127 fn test_challenge_field_element() -> Result<(), Box<dyn Error>> {
128 let config = poseidon_circom_config();
130 let mut tr = PoseidonSponge::new(config.clone());
131 tr.add(&Fr::from(42_u32));
132 let c = tr.challenge_field_element();
133
134 let cs = ConstraintSystem::new_ref();
136 let mut tr_var = PoseidonSpongeVar::new(config);
137 let v = FpVar::new_witness(cs.clone(), || Ok(Fr::from(42_u32)))?;
138 tr_var.add(&v)?;
139 let c_var = tr_var.challenge_field_element()?;
140
141 assert_eq!(c, c_var.value()?);
144 Ok(())
145 }
146
147 #[test]
148 fn test_challenge_bits() -> Result<(), Box<dyn Error>> {
149 let nbits = 128;
150
151 let config = poseidon_circom_config();
153 let mut tr = PoseidonSponge::new(config.clone());
154 tr.add(&Fr::from(42_u32));
155 let c = tr.challenge_bits(nbits);
156
157 let cs = ConstraintSystem::new_ref();
159 let mut tr_var = PoseidonSpongeVar::new(config);
160 let v = FpVar::new_witness(cs.clone(), || Ok(Fr::from(42_u32)))?;
161 tr_var.add(&v)?;
162 let c_var = tr_var.challenge_bits(nbits)?;
163
164 assert_eq!(c, c_var.value()?);
167 Ok(())
168 }
169
170 #[test]
171 fn test_absorb_canonical_point() -> Result<(), Box<dyn Error>> {
172 let config = poseidon_circom_config();
174 let mut tr = PoseidonSponge::new(config.clone());
175 let rng = &mut thread_rng();
176
177 let p = G2::rand(rng);
178 tr.add(&p);
179 let c = tr.challenge_field_element();
180
181 let cs = ConstraintSystem::new_ref();
183 let mut tr_var = PoseidonSpongeVar::new(config);
184 let p_var = ProjectiveVar::new_witness(cs, || Ok(p))?;
185 tr_var.add(&p_var)?;
186 let c_var = tr_var.challenge_field_element()?;
187
188 assert_eq!(c, c_var.value()?);
191 Ok(())
192 }
193
194 #[test]
195 fn test_absorb_emulated_point() -> Result<(), Box<dyn Error>> {
196 let config = poseidon_circom_config();
198 let mut tr = PoseidonSponge::new(config.clone());
199 let rng = &mut thread_rng();
200
201 let p = G1::rand(rng);
202 tr.add(&p);
203 let c = tr.challenge_field_element();
204
205 let cs = ConstraintSystem::new_ref();
207 let mut tr_var = PoseidonSpongeVar::new(config);
208 let p_var = EmulatedAffineVar::new_witness(cs, || Ok(p))?;
209 tr_var.add(&p_var)?;
210 let c_var = tr_var.challenge_field_element()?;
211
212 assert_eq!(c, c_var.value()?);
215 Ok(())
216 }
217}