1use crate::halo2_proofs;
4use crate::util::arithmetic::FieldExt;
5use crate::{
6 loader::{
7 halo2::{EcPoint, EccInstructions, Halo2Loader, Scalar},
8 native::{self, NativeLoader},
9 Loader, ScalarLoader,
10 },
11 util::{
12 arithmetic::{fe_to_fe, CurveAffine, PrimeField},
13 hash::{OptimizedPoseidonSpec, Poseidon},
14 transcript::{Transcript, TranscriptRead, TranscriptWrite},
15 Itertools,
16 },
17 Error,
18};
19use halo2_proofs::transcript::EncodedChallenge;
20use std::{
21 io::{self, Read, Write},
22 rc::Rc,
23};
24
25pub trait NativeEncoding<C>: EccInstructions<C>
27where
28 C: CurveAffine,
29{
30 fn encode(
32 &self,
33 ctx: &mut Self::Context,
34 ec_point: &Self::AssignedEcPoint,
35 ) -> Result<Vec<Self::AssignedScalar>, Error>;
36}
37
38#[derive(Clone, Debug)]
40pub enum TranscriptObject<C, L>
41where
42 C: CurveAffine,
43 L: Loader<C>,
44{
45 Scalar(L::LoadedScalar),
47 EcPoint(L::LoadedEcPoint),
49}
50
51#[derive(Debug)]
52pub struct PoseidonTranscript<
56 C,
57 L,
58 S,
59 const T: usize,
60 const RATE: usize,
61 const R_F: usize,
62 const R_P: usize,
63> where
64 C: CurveAffine,
65 L: Loader<C>,
66{
67 loader: L,
68 stream: S,
69 pub loaded_stream: Vec<TranscriptObject<C, L>>,
73 buf: Poseidon<C::Scalar, <L as ScalarLoader<C::Scalar>>::LoadedScalar, T, RATE>,
74}
75
76impl<C, R, EccChip, const T: usize, const RATE: usize, const R_F: usize, const R_P: usize>
77 PoseidonTranscript<C, Rc<Halo2Loader<C, EccChip>>, R, T, RATE, R_F, R_P>
78where
79 C: CurveAffine,
80 R: Read,
81 EccChip: NativeEncoding<C>,
82{
83 pub fn new<const SECURE_MDS: usize>(loader: &Rc<Halo2Loader<C, EccChip>>, stream: R) -> Self
86 where
87 C::Scalar: FieldExt,
88 {
89 let buf = Poseidon::new::<R_F, R_P, SECURE_MDS>(loader);
90 Self { loader: loader.clone(), stream, buf, loaded_stream: vec![] }
91 }
92
93 pub fn from_spec(
95 loader: &Rc<Halo2Loader<C, EccChip>>,
96 stream: R,
97 spec: OptimizedPoseidonSpec<C::Scalar, T, RATE>,
98 ) -> Self {
99 let buf = Poseidon::from_spec(loader, spec);
100 Self { loader: loader.clone(), stream, buf, loaded_stream: vec![] }
101 }
102
103 pub fn new_stream(&mut self, stream: R) {
105 self.buf.clear();
106 self.loaded_stream.clear();
107 self.stream = stream;
108 }
109}
110
111impl<C, R, EccChip, const T: usize, const RATE: usize, const R_F: usize, const R_P: usize>
112 Transcript<C, Rc<Halo2Loader<C, EccChip>>>
113 for PoseidonTranscript<C, Rc<Halo2Loader<C, EccChip>>, R, T, RATE, R_F, R_P>
114where
115 C: CurveAffine,
116 R: Read,
117 EccChip: NativeEncoding<C>,
118{
119 fn loader(&self) -> &Rc<Halo2Loader<C, EccChip>> {
120 &self.loader
121 }
122
123 fn squeeze_challenge(&mut self) -> Scalar<C, EccChip> {
124 self.buf.squeeze()
125 }
126
127 fn common_scalar(&mut self, scalar: &Scalar<C, EccChip>) -> Result<(), Error> {
128 self.buf.update(&[scalar.clone()]);
129 Ok(())
130 }
131
132 fn common_ec_point(&mut self, ec_point: &EcPoint<C, EccChip>) -> Result<(), Error> {
133 let encoded = self
134 .loader
135 .ecc_chip()
136 .encode(&mut self.loader.ctx_mut(), &ec_point.assigned())
137 .map(|encoded| {
138 encoded
139 .into_iter()
140 .map(|encoded| self.loader.scalar_from_assigned(encoded))
141 .collect_vec()
142 })
143 .map_err(|_| {
144 Error::Transcript(
145 io::ErrorKind::Other,
146 "Failed to encode elliptic curve point into native field elements".to_string(),
147 )
148 })?;
149 self.buf.update(&encoded);
150 Ok(())
151 }
152}
153
154impl<C, R, EccChip, const T: usize, const RATE: usize, const R_F: usize, const R_P: usize>
155 TranscriptRead<C, Rc<Halo2Loader<C, EccChip>>>
156 for PoseidonTranscript<C, Rc<Halo2Loader<C, EccChip>>, R, T, RATE, R_F, R_P>
157where
158 C: CurveAffine,
159 R: Read,
160 EccChip: NativeEncoding<C>,
161{
162 fn read_scalar(&mut self) -> Result<Scalar<C, EccChip>, Error> {
163 let scalar = {
164 let mut data = <C::Scalar as PrimeField>::Repr::default();
165 self.stream.read_exact(data.as_mut()).unwrap();
166 C::Scalar::from_repr(data).unwrap()
167 };
168 let scalar = self.loader.assign_scalar(scalar);
169 self.loaded_stream.push(TranscriptObject::Scalar(scalar.clone()));
170 self.common_scalar(&scalar)?;
171 Ok(scalar)
172 }
173
174 fn read_ec_point(&mut self) -> Result<EcPoint<C, EccChip>, Error> {
175 let ec_point = {
176 let mut compressed = C::Repr::default();
177 self.stream.read_exact(compressed.as_mut()).unwrap();
178 C::from_bytes(&compressed).unwrap()
179 };
180 let ec_point = self.loader.assign_ec_point(ec_point);
181 self.loaded_stream.push(TranscriptObject::EcPoint(ec_point.clone()));
182 self.common_ec_point(&ec_point)?;
183 Ok(ec_point)
184 }
185}
186
187impl<C: CurveAffine, S, const T: usize, const RATE: usize, const R_F: usize, const R_P: usize>
188 PoseidonTranscript<C, NativeLoader, S, T, RATE, R_F, R_P>
189{
190 pub fn new<const SECURE_MDS: usize>(stream: S) -> Self
193 where
194 C::Scalar: FieldExt,
195 {
196 Self {
197 loader: NativeLoader,
198 stream,
199 buf: Poseidon::new::<R_F, R_P, SECURE_MDS>(&NativeLoader),
200 loaded_stream: vec![],
201 }
202 }
203
204 pub fn from_spec(stream: S, spec: OptimizedPoseidonSpec<C::Scalar, T, RATE>) -> Self {
206 Self {
207 loader: NativeLoader,
208 stream,
209 buf: Poseidon::from_spec(&NativeLoader, spec),
210 loaded_stream: vec![],
211 }
212 }
213
214 pub fn new_stream(&mut self, stream: S) {
216 self.buf.clear();
217 self.loaded_stream.clear();
218 self.stream = stream;
219 }
220}
221
222impl<C: CurveAffine, const T: usize, const RATE: usize, const R_F: usize, const R_P: usize>
223 PoseidonTranscript<C, NativeLoader, Vec<u8>, T, RATE, R_F, R_P>
224{
225 pub fn clear(&mut self) {
227 self.buf.clear();
228 self.loaded_stream.clear();
229 self.stream.clear();
230 }
231}
232
233impl<C: CurveAffine, S, const T: usize, const RATE: usize, const R_F: usize, const R_P: usize>
234 Transcript<C, NativeLoader> for PoseidonTranscript<C, NativeLoader, S, T, RATE, R_F, R_P>
235{
236 fn loader(&self) -> &NativeLoader {
237 &native::LOADER
238 }
239
240 fn squeeze_challenge(&mut self) -> C::Scalar {
241 self.buf.squeeze()
242 }
243
244 fn common_scalar(&mut self, scalar: &C::Scalar) -> Result<(), Error> {
245 self.buf.update(&[*scalar]);
246 Ok(())
247 }
248
249 fn common_ec_point(&mut self, ec_point: &C) -> Result<(), Error> {
250 let encoded: Vec<_> = Option::from(ec_point.coordinates().map(|coordinates| {
251 [coordinates.x(), coordinates.y()].into_iter().cloned().map(fe_to_fe).collect_vec()
252 }))
253 .ok_or_else(|| {
254 Error::Transcript(
255 io::ErrorKind::Other,
256 "Invalid elliptic curve point encoding in proof".to_string(),
257 )
258 })?;
259 self.buf.update(&encoded);
260 Ok(())
261 }
262}
263
264impl<C, R, const T: usize, const RATE: usize, const R_F: usize, const R_P: usize>
265 TranscriptRead<C, NativeLoader> for PoseidonTranscript<C, NativeLoader, R, T, RATE, R_F, R_P>
266where
267 C: CurveAffine,
268 R: Read,
269{
270 fn read_scalar(&mut self) -> Result<C::Scalar, Error> {
271 let mut data = <C::Scalar as PrimeField>::Repr::default();
272 self.stream
273 .read_exact(data.as_mut())
274 .map_err(|err| Error::Transcript(err.kind(), err.to_string()))?;
275 let scalar = C::Scalar::from_repr_vartime(data).ok_or_else(|| {
276 Error::Transcript(io::ErrorKind::Other, "Invalid scalar encoding in proof".to_string())
277 })?;
278 self.loaded_stream.push(TranscriptObject::Scalar(scalar));
279 self.common_scalar(&scalar)?;
280 Ok(scalar)
281 }
282
283 fn read_ec_point(&mut self) -> Result<C, Error> {
284 let mut data = C::Repr::default();
285 self.stream
286 .read_exact(data.as_mut())
287 .map_err(|err| Error::Transcript(err.kind(), err.to_string()))?;
288 let ec_point = Option::<C>::from(C::from_bytes(&data)).ok_or_else(|| {
289 Error::Transcript(
290 io::ErrorKind::Other,
291 "Invalid elliptic curve point encoding in proof".to_string(),
292 )
293 })?;
294 self.loaded_stream.push(TranscriptObject::EcPoint(ec_point));
295 self.common_ec_point(&ec_point)?;
296 Ok(ec_point)
297 }
298}
299
300impl<C, W, const T: usize, const RATE: usize, const R_F: usize, const R_P: usize>
301 PoseidonTranscript<C, NativeLoader, W, T, RATE, R_F, R_P>
302where
303 C: CurveAffine,
304 W: Write,
305{
306 pub fn stream_mut(&mut self) -> &mut W {
308 &mut self.stream
309 }
310
311 pub fn finalize(self) -> W {
313 self.stream
314 }
315}
316
317impl<C, W, const T: usize, const RATE: usize, const R_F: usize, const R_P: usize> TranscriptWrite<C>
318 for PoseidonTranscript<C, NativeLoader, W, T, RATE, R_F, R_P>
319where
320 C: CurveAffine,
321 W: Write,
322{
323 fn write_scalar(&mut self, scalar: C::Scalar) -> Result<(), Error> {
324 self.common_scalar(&scalar)?;
325 let data = scalar.to_repr();
326 self.stream_mut().write_all(data.as_ref()).map_err(|err| {
327 Error::Transcript(err.kind(), "Failed to write scalar to transcript".to_string())
328 })
329 }
330
331 fn write_ec_point(&mut self, ec_point: C) -> Result<(), Error> {
332 self.common_ec_point(&ec_point)?;
333 let data = ec_point.to_bytes();
334 self.stream_mut().write_all(data.as_ref()).map_err(|err| {
335 Error::Transcript(
336 err.kind(),
337 "Failed to write elliptic curve to transcript".to_string(),
338 )
339 })
340 }
341}
342
343#[derive(Debug)]
347pub struct ChallengeScalar<C: CurveAffine>(C::Scalar);
348
349impl<C: CurveAffine> EncodedChallenge<C> for ChallengeScalar<C> {
350 type Input = C::Scalar;
351
352 fn new(challenge_input: &C::Scalar) -> Self {
353 ChallengeScalar(*challenge_input)
354 }
355
356 fn get_scalar(&self) -> C::Scalar {
357 self.0
358 }
359}
360
361impl<C: CurveAffine, S, const T: usize, const RATE: usize, const R_F: usize, const R_P: usize>
362 halo2_proofs::transcript::Transcript<C, ChallengeScalar<C>>
363 for PoseidonTranscript<C, NativeLoader, S, T, RATE, R_F, R_P>
364{
365 fn squeeze_challenge(&mut self) -> ChallengeScalar<C> {
366 ChallengeScalar::new(&Transcript::squeeze_challenge(self))
367 }
368
369 fn common_point(&mut self, ec_point: C) -> io::Result<()> {
370 match Transcript::common_ec_point(self, &ec_point) {
371 Err(Error::Transcript(kind, msg)) => Err(io::Error::new(kind, msg)),
372 Err(_) => unreachable!(),
373 _ => Ok(()),
374 }
375 }
376
377 fn common_scalar(&mut self, scalar: C::Scalar) -> io::Result<()> {
378 match Transcript::common_scalar(self, &scalar) {
379 Err(Error::Transcript(kind, msg)) => Err(io::Error::new(kind, msg)),
380 Err(_) => unreachable!(),
381 _ => Ok(()),
382 }
383 }
384}
385
386impl<C, R, const T: usize, const RATE: usize, const R_F: usize, const R_P: usize>
387 halo2_proofs::transcript::TranscriptRead<C, ChallengeScalar<C>>
388 for PoseidonTranscript<C, NativeLoader, R, T, RATE, R_F, R_P>
389where
390 C: CurveAffine,
391 R: Read,
392{
393 fn read_point(&mut self) -> io::Result<C> {
394 match TranscriptRead::read_ec_point(self) {
395 Err(Error::Transcript(kind, msg)) => Err(io::Error::new(kind, msg)),
396 Err(_) => unreachable!(),
397 Ok(value) => Ok(value),
398 }
399 }
400
401 fn read_scalar(&mut self) -> io::Result<C::Scalar> {
402 match TranscriptRead::read_scalar(self) {
403 Err(Error::Transcript(kind, msg)) => Err(io::Error::new(kind, msg)),
404 Err(_) => unreachable!(),
405 Ok(value) => Ok(value),
406 }
407 }
408}
409
410impl<C, R, const T: usize, const RATE: usize, const R_F: usize, const R_P: usize>
411 halo2_proofs::transcript::TranscriptReadBuffer<R, C, ChallengeScalar<C>>
412 for PoseidonTranscript<C, NativeLoader, R, T, RATE, R_F, R_P>
413where
414 C: CurveAffine,
415 C::Scalar: FieldExt,
416 R: Read,
417{
418 fn init(reader: R) -> Self {
419 Self::new::<0>(reader)
420 }
421}
422
423impl<C, W, const T: usize, const RATE: usize, const R_F: usize, const R_P: usize>
424 halo2_proofs::transcript::TranscriptWrite<C, ChallengeScalar<C>>
425 for PoseidonTranscript<C, NativeLoader, W, T, RATE, R_F, R_P>
426where
427 C: CurveAffine,
428 W: Write,
429{
430 fn write_point(&mut self, ec_point: C) -> io::Result<()> {
431 halo2_proofs::transcript::Transcript::<C, ChallengeScalar<C>>::common_point(
432 self, ec_point,
433 )?;
434 let data = ec_point.to_bytes();
435 self.stream_mut().write_all(data.as_ref())
436 }
437
438 fn write_scalar(&mut self, scalar: C::Scalar) -> io::Result<()> {
439 halo2_proofs::transcript::Transcript::<C, ChallengeScalar<C>>::common_scalar(self, scalar)?;
440 let data = scalar.to_repr();
441 self.stream_mut().write_all(data.as_ref())
442 }
443}
444
445impl<C, W, const T: usize, const RATE: usize, const R_F: usize, const R_P: usize>
446 halo2_proofs::transcript::TranscriptWriterBuffer<W, C, ChallengeScalar<C>>
447 for PoseidonTranscript<C, NativeLoader, W, T, RATE, R_F, R_P>
448where
449 C: CurveAffine,
450 C::Scalar: FieldExt,
451 W: Write,
452{
453 fn init(writer: W) -> Self {
454 Self::new::<0>(writer)
455 }
456
457 fn finalize(self) -> W {
458 self.finalize()
459 }
460}
461
462mod halo2_lib {
463 use crate::system::halo2::transcript::halo2::NativeEncoding;
464 use halo2_base::utils::{BigPrimeField, CurveAffineExt};
465 use halo2_ecc::ecc::BaseFieldEccChip;
466
467 impl<C: CurveAffineExt> NativeEncoding<C> for BaseFieldEccChip<'_, C>
468 where
469 C::Scalar: BigPrimeField,
470 C::Base: BigPrimeField,
471 {
472 fn encode(
473 &self,
474 _: &mut Self::Context,
475 ec_point: &Self::AssignedEcPoint,
476 ) -> Result<Vec<Self::AssignedScalar>, crate::Error> {
477 Ok(vec![*ec_point.x().native(), *ec_point.y().native()])
478 }
479 }
480}