1use super::sigma::codegen::{StructField, StructFieldList};
5use super::syntax::*;
6use proc_macro2::TokenStream;
7use quote::{format_ident, quote};
8#[cfg(test)]
9use syn::parse_quote;
10use syn::Ident;
11
12pub struct CodeGen {
21 proto_name: Ident,
24 group_name: Ident,
27 vars: TaggedVarDict,
30 unique_prefix: String,
33 sent_instance: StructFieldList,
38 prove_code: TokenStream,
40 verify_code: TokenStream,
42 verify_pre_instance_code: TokenStream,
46}
47
48impl CodeGen {
49 fn unique_prefix(vars: &TaggedVarDict) -> String {
52 'outer: for tag in 0usize.. {
53 let try_prefix = if tag == 0 {
54 "gen__".to_string()
55 } else {
56 format!("gen{}__", tag)
57 };
58 for v in vars.keys() {
59 if v.starts_with(&try_prefix) {
60 continue 'outer;
61 }
62 }
63 return try_prefix;
64 }
65 String::new()
69 }
70
71 pub fn new(spec: &SigmaCompSpec) -> Self {
74 Self {
75 proto_name: spec.proto_name.clone(),
76 group_name: spec.group_name.clone(),
77 vars: spec.vars.clone(),
78 unique_prefix: Self::unique_prefix(&spec.vars),
79 sent_instance: StructFieldList::default(),
80 prove_code: quote! {},
81 verify_code: quote! {},
82 verify_pre_instance_code: quote! {},
83 }
84 }
85
86 #[cfg(test)]
87 pub fn new_empty() -> Self {
89 Self {
90 proto_name: parse_quote! { proto },
91 group_name: parse_quote! { G },
92 vars: TaggedVarDict::default(),
93 unique_prefix: "gen__".into(),
94 sent_instance: StructFieldList::default(),
95 prove_code: quote! {},
96 verify_code: quote! {},
97 verify_pre_instance_code: quote! {},
98 }
99 }
100
101 pub fn gen_scalar(
109 &self,
110 vars: &mut TaggedVarDict,
111 base: &Ident,
112 is_rand: bool,
113 is_vec: bool,
114 ) -> Ident {
115 let id = format_ident!("{}{}", self.unique_prefix, base);
116 vars.insert(
117 id.to_string(),
118 TaggedIdent::Scalar(TaggedScalar {
119 id: id.clone(),
120 is_pub: false,
121 is_rand,
122 is_vec,
123 }),
124 );
125 id
126 }
127
128 pub fn gen_point(
142 &mut self,
143 vars: &mut TaggedVarDict,
144 base: &Ident,
145 is_vec: bool,
146 send_to_verifier: bool,
147 ) -> Ident {
148 let id = format_ident!("{}{}", self.unique_prefix, base);
149 vars.insert(
150 id.to_string(),
151 TaggedIdent::Point(TaggedPoint {
152 id: id.clone(),
153 is_cind: false,
154 is_const: false,
155 is_vec,
156 }),
157 );
158 if send_to_verifier {
159 if is_vec {
160 self.sent_instance.push_vecpoint(&id);
161 } else {
162 self.sent_instance.push_point(&id);
163 }
164 }
165 id
166 }
167
168 pub fn gen_ident(&self, base: &Ident) -> Ident {
170 format_ident!("{}{}", self.unique_prefix, base)
171 }
172
173 pub fn prove_append(&mut self, code: TokenStream) {
175 let prove_code = &self.prove_code;
176 self.prove_code = quote! {
177 #prove_code
178 #code
179 };
180 }
181
182 pub fn verify_append(&mut self, code: TokenStream) {
184 let verify_code = &self.verify_code;
185 self.verify_code = quote! {
186 #verify_code
187 #code
188 };
189 }
190
191 pub fn verify_pre_instance_append(&mut self, code: TokenStream) {
194 let verify_pre_instance_code = &self.verify_pre_instance_code;
195 self.verify_pre_instance_code = quote! {
196 #verify_pre_instance_code
197 #code
198 };
199 }
200
201 pub fn prove_verify_append(&mut self, code: TokenStream) {
204 let prove_code = &self.prove_code;
205 self.prove_code = quote! {
206 #prove_code
207 #code
208 };
209 let verify_code = &self.verify_code;
210 self.verify_code = quote! {
211 #verify_code
212 #code
213 };
214 }
215
216 pub fn prove_verify_pre_instance_append(&mut self, code: TokenStream) {
220 let prove_code = &self.prove_code;
221 self.prove_code = quote! {
222 #prove_code
223 #code
224 };
225 let verify_pre_instance_code = &self.verify_pre_instance_code;
226 self.verify_pre_instance_code = quote! {
227 #verify_pre_instance_code
228 #code
229 };
230 }
231
232 pub fn code_strings(&self) -> (String, String, String) {
237 (
238 self.prove_code.to_string(),
239 self.verify_code.to_string(),
240 self.verify_pre_instance_code.to_string(),
241 )
242 }
243
244 pub fn generate(
249 &self,
250 spec: &mut SigmaCompSpec,
251 emit_prover: bool,
252 emit_verifier: bool,
253 ) -> TokenStream {
254 let proto_name = &self.proto_name;
255 let group_name = &self.group_name;
256
257 let group_types = quote! {
258 use super::group;
259 pub type Scalar = <super::#group_name as group::Group>::Scalar;
260 pub type Point = super::#group_name;
261 };
262
263 let vardict = taggedvardict_to_vardict(&self.vars);
266 let sigma_proofs_vardict = taggedvardict_to_vardict(&spec.vars);
271
272 let mut sigma_proofs_codegen = super::sigma::codegen::CodeGen::new(
274 format_ident!("sigma"),
275 format_ident!("Point"),
276 &sigma_proofs_vardict,
277 &mut spec.statements,
278 );
279 let sigma_proofs_code = sigma_proofs_codegen.generate(emit_prover, emit_verifier);
280
281 let mut pub_instance_fields = StructFieldList::default();
282 pub_instance_fields.push_vars(&vardict, true);
283 let mut witness_fields = StructFieldList::default();
284 witness_fields.push_vars(&vardict, false);
285
286 let mut sigma_proofs_instance_fields = StructFieldList::default();
287 sigma_proofs_instance_fields.push_vars(&sigma_proofs_vardict, true);
288 let mut sigma_proofs_witness_fields = StructFieldList::default();
289 sigma_proofs_witness_fields.push_vars(&sigma_proofs_vardict, false);
290
291 let instance_def = {
293 let decls = pub_instance_fields.field_decls();
294 #[cfg(feature = "dump")]
295 let dump_impl = {
296 let dump_chunks = pub_instance_fields.dump(&format_ident!("fmt"));
297 quote! {
298 impl Instance {
299 fn dump_scalar(s: &Scalar, fmt: &mut std::fmt::Formatter<'_>) {
300 let bytes: &[u8] = &s.to_repr();
301 for b in bytes.iter().rev() {
302 write!(fmt, "{:02x}", b).ok();
306 }
307 }
308
309 fn dump_point(p: &Point, fmt: &mut std::fmt::Formatter<'_>) {
310 let bytes: &[u8] = &p.to_bytes();
311 for b in bytes.iter().rev() {
312 write!(fmt, "{:02x}", b).ok();
316 }
317 }
318 }
319
320 impl std::fmt::Debug for Instance {
321 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
322 #dump_chunks
323 Ok(())
324 }
325 }
326 }
327 };
328 #[cfg(not(feature = "dump"))]
329 let dump_impl = {
330 quote! {}
331 };
332 quote! {
333 #[derive(Clone)]
334 pub struct Instance {
335 #decls
336 }
337
338 #dump_impl
339 }
340 };
341
342 let witness_def = if emit_prover {
344 let decls = witness_fields.field_decls();
345 quote! {
346 #[derive(Clone)]
347 pub struct Witness {
348 #decls
349 }
350 }
351 } else {
352 quote! {}
353 };
354
355 let prove_func = if emit_prover {
357 let instance_ids = pub_instance_fields.field_list();
358 let witness_ids = witness_fields.field_list();
359 let sigma_proofs_instance_ids = sigma_proofs_instance_fields.field_list();
360 let sigma_proofs_witness_ids = sigma_proofs_witness_fields.field_list();
361 let prove_code = &self.prove_code;
362 let codegen_instance_var = format_ident!("{}sigma_instance", self.unique_prefix);
363 let codegen_witness_var = format_ident!("{}sigma_witness", self.unique_prefix);
364 let instance_var = format_ident!("{}instance", self.unique_prefix);
365 let witness_var = format_ident!("{}witness", self.unique_prefix);
366 let rng_var = format_ident!("{}rng", self.unique_prefix);
367 let proof_var = format_ident!("{}proof", self.unique_prefix);
368 let sid_var = format_ident!("{}session_id", self.unique_prefix);
369 let sent_instance_code = {
370 let chunks = self.sent_instance.fields.iter().map(|sf| match sf {
371 StructField::Point(id) => quote! {
372 #proof_var.extend_from_slice(
373 <Point as group::GroupEncoding>::to_bytes(&#codegen_instance_var.#id)
374 .as_ref()
375 );
376 },
377 StructField::VecPoint(id) => quote! {
378 for point in &#codegen_instance_var.#id {
379 #proof_var.extend_from_slice(
380 <Point as group::GroupEncoding>::to_bytes(point).as_ref()
381 );
382 }
383 },
384 _ => quote! {},
385 });
386 quote! { #(#chunks)* }
387 };
388
389 let dumper = if cfg!(feature = "dump") {
390 quote! {
391 sigma_compiler::dumper::dump(
392 &format!("{} sigma_compiler prover instance = {{\n{:?}}}\n",
393 stringify!(#proto_name), #instance_var));
394 }
395 } else {
396 quote! {}
397 };
398
399 let sigma_dumper = if cfg!(feature = "dump") {
400 quote! {
401 sigma_compiler::dumper::dump(
402 &format!("{} sigma prover instance = {{\n{:?}}}\n",
403 stringify!(#proto_name), #codegen_instance_var));
404 }
405 } else {
406 quote! {}
407 };
408
409 quote! {
410 pub fn prove(
411 #instance_var: &Instance,
412 #witness_var: &Witness,
413 #sid_var: &[u8],
414 #rng_var: &mut (impl CryptoRng + RngCore),
415 ) -> Result<Vec<u8>, SigmaError> {
416 #dumper
417 let Instance { #instance_ids } = #instance_var.clone();
418 let Witness { #witness_ids } = #witness_var.clone();
419 #prove_code
420 let mut #proof_var = Vec::<u8>::new();
421 let #codegen_instance_var = sigma::Instance {
422 #sigma_proofs_instance_ids
423 };
424 let #codegen_witness_var = sigma::Witness {
425 #sigma_proofs_witness_ids
426 };
427 #sent_instance_code
428 #sigma_dumper
429 #proof_var.extend(
430 sigma::prove(
431 &#codegen_instance_var,
432 &#codegen_witness_var,
433 #sid_var,
434 #rng_var,
435 )?
436 );
437 Ok(#proof_var)
438 }
439 }
440 } else {
441 quote! {}
442 };
443
444 let verify_func = if emit_verifier {
446 let instance_ids = pub_instance_fields.field_list();
447 let sigma_proofs_instance_ids = sigma_proofs_instance_fields.field_list();
448 let verify_pre_instance_code = &self.verify_pre_instance_code;
449 let verify_code = &self.verify_code;
450 let codegen_instance_var = format_ident!("{}sigma_instance", self.unique_prefix);
451 let element_len_var = format_ident!("{}element_len", self.unique_prefix);
452 let offset_var = format_ident!("{}proof_offset", self.unique_prefix);
453 let instance_var = format_ident!("{}instance", self.unique_prefix);
454 let proof_var = format_ident!("{}proof", self.unique_prefix);
455 let sid_var = format_ident!("{}session_id", self.unique_prefix);
456 let sent_instance_code = {
457 let element_len_code = if self.sent_instance.fields.is_empty() {
458 quote! {}
459 } else {
460 quote! {
461 let #element_len_var =
462 <Point as group::GroupEncoding>::Repr::default().as_ref().len();
463 }
464 };
465
466 let chunks = self.sent_instance.fields.iter().map(|sf| match sf {
467 StructField::Point(id) => quote! {
468 let #id: Point = {
469 let end = #offset_var + #element_len_var;
470 if #proof_var.len() < end {
471 return Err(SigmaError::VerificationFailure);
472 }
473 let mut repr = <Point as group::GroupEncoding>::Repr::default();
474 repr.as_mut()
475 .copy_from_slice(&#proof_var[#offset_var..end]);
476 #offset_var = end;
477 Option::<Point>::from(
478 <Point as group::GroupEncoding>::from_bytes(&repr)
479 )
480 .ok_or(SigmaError::VerificationFailure)?
481 };
482 },
483 StructField::VecPoint(id) => quote! {
484 {
485 let expected_len = #id.len();
486 let mut points = Vec::with_capacity(expected_len);
487 for _ in 0..expected_len {
488 let end = #offset_var + #element_len_var;
489 if #proof_var.len() < end {
490 return Err(SigmaError::VerificationFailure);
491 }
492 let mut repr =
493 <Point as group::GroupEncoding>::Repr::default();
494 repr.as_mut()
495 .copy_from_slice(&#proof_var[#offset_var..end]);
496 #offset_var = end;
497 let point = Option::<Point>::from(
498 <Point as group::GroupEncoding>::from_bytes(&repr)
499 )
500 .ok_or(SigmaError::VerificationFailure)?;
501 points.push(point);
502 }
503 #id = points;
504 }
505 },
506 _ => quote! {},
507 });
508
509 quote! {
510 let mut #offset_var = 0usize;
511 #element_len_code
512 #(#chunks)*
513 }
514 };
515
516 let dumper = if cfg!(feature = "dump") {
517 quote! {
518 sigma_compiler::dumper::dump(
519 &format!("{} sigma_compiler verifier instance = {{\n{:?}}}\n",
520 stringify!(#proto_name), #instance_var));
521 }
522 } else {
523 quote! {}
524 };
525
526 let sigma_dumper = if cfg!(feature = "dump") {
527 quote! {
528 sigma_compiler::dumper::dump(
529 &format!("{} sigma verifier instance = {{\n{:?}}}\n",
530 stringify!(#proto_name), #codegen_instance_var));
531 }
532 } else {
533 quote! {}
534 };
535
536 quote! {
537 pub fn verify(
538 #instance_var: &Instance,
539 #proof_var: &[u8],
540 #sid_var: &[u8],
541 ) -> Result<(), SigmaError> {
542 #dumper
543 let Instance { #instance_ids } = #instance_var.clone();
544 #verify_pre_instance_code
545 #sent_instance_code
546 #verify_code
547 let #codegen_instance_var = sigma::Instance {
548 #sigma_proofs_instance_ids
549 };
550 #sigma_dumper
551 sigma::verify(
552 &#codegen_instance_var,
553 &#proof_var[#offset_var..],
554 #sid_var,
555 )
556 }
557 }
558 } else {
559 quote! {}
560 };
561
562 let dump_use = if cfg!(feature = "dump") {
564 quote! {
565 use group::GroupEncoding;
566 }
567 } else {
568 quote! {}
569 };
570 quote! {
571 #[allow(non_snake_case)]
572 pub mod #proto_name {
573 use super::sigma_compiler;
574 use sigma_compiler::group::Group;
575 use sigma_compiler::group::ff::{Field, PrimeField};
576 use sigma_compiler::group::ff::derive::subtle::ConditionallySelectable;
577 use sigma_compiler::rand::{CryptoRng, RngCore};
578 use sigma_compiler::sigma_proofs;
579 use sigma_compiler::sigma_proofs::errors::Error as SigmaError;
580 use sigma_compiler::vecutils::*;
581 use std::ops::Neg;
582 #dump_use
583
584 #group_types
585
586 #sigma_proofs_code
587
588 #instance_def
589 #witness_def
590 #prove_func
591 #verify_func
592 }
593 }
594 }
595}