1use super::combiners::StatementTree;
7use super::types::{expr_type_tokens_id_closure, AExprType, VarDict};
8use proc_macro2::TokenStream;
9use quote::{format_ident, quote, ToTokens};
10use std::collections::HashSet;
11use syn::{Expr, Ident};
12
13#[derive(Clone)]
15pub enum StructField {
16 Scalar(Ident),
17 VecScalar(Ident),
18 Point(Ident),
19 VecPoint(Ident),
20}
21
22impl StructField {
23 pub fn ident(&self) -> Ident {
25 match self {
26 Self::Scalar(id) | Self::VecScalar(id) | Self::Point(id) | Self::VecPoint(id) => {
27 id.clone()
28 }
29 }
30 }
31}
32
33#[derive(Default)]
35pub struct StructFieldList {
36 pub fields: Vec<StructField>,
37}
38
39impl StructFieldList {
40 pub fn push_scalar(&mut self, s: &Ident) {
41 self.fields.push(StructField::Scalar(s.clone()));
42 }
43 pub fn push_vecscalar(&mut self, s: &Ident) {
44 self.fields.push(StructField::VecScalar(s.clone()));
45 }
46 pub fn push_point(&mut self, s: &Ident) {
47 self.fields.push(StructField::Point(s.clone()));
48 }
49 pub fn push_vecpoint(&mut self, s: &Ident) {
50 self.fields.push(StructField::VecPoint(s.clone()));
51 }
52 pub fn push_vars(&mut self, vars: &VarDict, for_instance: bool) {
53 for (id, ti) in vars.iter() {
54 match ti {
55 AExprType::Scalar { is_pub, is_vec, .. } => {
56 if *is_pub == for_instance {
57 if *is_vec {
58 self.push_vecscalar(&format_ident!("{}", id))
59 } else {
60 self.push_scalar(&format_ident!("{}", id))
61 }
62 }
63 }
64 AExprType::Point { is_vec, .. } => {
65 if for_instance {
66 if *is_vec {
67 self.push_vecpoint(&format_ident!("{}", id))
68 } else {
69 self.push_point(&format_ident!("{}", id))
70 }
71 }
72 }
73 }
74 }
75 }
76 #[cfg(feature = "dump")]
77 pub fn dump(&self, fmt_id: &Ident) -> impl ToTokens {
80 let mut fields = self.fields.clone();
82 fields.sort_by_key(|f| f.ident());
83
84 let dump_chunks = fields.iter().map(|f| match f {
85 StructField::Scalar(id) => quote! {
88 write!(#fmt_id, " {}: ", stringify!(#id)).ok();
89 Instance::dump_scalar(&self.#id, #fmt_id);
90 write!(#fmt_id, ",\n").ok();
91 },
92 StructField::VecScalar(id) => quote! {
93 write!(#fmt_id, " {}: [\n", stringify!(#id)).ok();
94 for s in self.#id.iter() {
95 write!(#fmt_id, " ").ok();
96 Instance::dump_scalar(s, #fmt_id);
97 write!(#fmt_id, ",\n").ok();
98 }
99 write!(#fmt_id, " ],\n").ok();
100 },
101 StructField::Point(id) => quote! {
102 write!(#fmt_id, " {}: ", stringify!(#id)).ok();
103 Instance::dump_point(&self.#id, #fmt_id);
104 write!(#fmt_id, ",\n").ok();
105 },
106 StructField::VecPoint(id) => quote! {
107 write!(#fmt_id, " {}: [\n", stringify!(#id)).ok();
108 for p in self.#id.iter() {
109 write!(#fmt_id, " ").ok();
110 Instance::dump_point(p, #fmt_id);
111 write!(#fmt_id, ",\n").ok();
112 }
113 write!(#fmt_id, " ],\n").ok();
114 },
115 });
116 quote! { #(#dump_chunks)* }
117 }
118 pub fn field_decls(&self) -> impl ToTokens {
121 let decls = self.fields.iter().map(|f| match f {
122 StructField::Scalar(id) => quote! {
123 pub #id: Scalar,
124 },
125 StructField::VecScalar(id) => quote! {
126 pub #id: Vec<Scalar>,
127 },
128 StructField::Point(id) => quote! {
129 pub #id: Point,
130 },
131 StructField::VecPoint(id) => quote! {
132 pub #id: Vec<Point>,
133 },
134 });
135 quote! { #(#decls)* }
136 }
137 pub fn field_list(&self) -> impl ToTokens {
139 let field_ids = self.fields.iter().map(|f| match f {
140 StructField::Scalar(id) => quote! {
141 #id,
142 },
143 StructField::VecScalar(id) => quote! {
144 #id,
145 },
146 StructField::Point(id) => quote! {
147 #id,
148 },
149 StructField::VecPoint(id) => quote! {
150 #id,
151 },
152 });
153 quote! { #(#field_ids)* }
154 }
155}
156
157pub struct CodeGen<'a> {
159 proto_name: Ident,
160 group_name: Ident,
161 vars: &'a VarDict,
162 unique_prefix: String,
163 statements: &'a mut StatementTree,
164}
165
166impl<'a> CodeGen<'a> {
167 fn unique_prefix(vars: &VarDict) -> String {
170 'outer: for tag in 0usize.. {
171 let try_prefix = if tag == 0 {
172 "sigma__".to_string()
173 } else {
174 format!("sigma{}__", tag)
175 };
176 for v in vars.keys() {
177 if v.starts_with(&try_prefix) {
178 continue 'outer;
179 }
180 }
181 return try_prefix;
182 }
183 String::new()
187 }
188
189 pub fn new(
190 proto_name: Ident,
191 group_name: Ident,
192 vars: &'a VarDict,
193 statements: &'a mut StatementTree,
194 ) -> Self {
195 Self {
196 proto_name,
197 group_name,
198 vars,
199 unique_prefix: Self::unique_prefix(vars),
200 statements,
201 }
202 }
203
204 fn linear_relation_codegen(&self, exprs: &[&Expr]) -> (TokenStream, TokenStream) {
211 let instance_var = format_ident!("{}instance", self.unique_prefix);
212 let lr_var = format_ident!("{}lr", self.unique_prefix);
213 let mut allocated_vars: HashSet<Ident> = HashSet::new();
214 let mut param_vec_code = quote! {};
215 let mut witness_vec_code = quote! {};
216 let mut witness_code = quote! {};
217 let mut scalar_allocs = quote! {};
218 let mut element_allocs = quote! {};
219 let mut eq_code = quote! {};
220 let mut element_assigns = quote! {};
221
222 for (i, expr) in exprs.iter().enumerate() {
223 let eq_id = format_ident!("{}eq{}", self.unique_prefix, i + 1);
224 let vec_index_var = format_ident!("{}i", self.unique_prefix);
225 let vec_len_var = format_ident!("{}veclen{}", self.unique_prefix, i + 1);
226
227 let mut vec_param_vars: HashSet<Ident> = HashSet::new();
230 let mut vec_witness_vars: HashSet<Ident> = HashSet::new();
231
232 let Expr::Assign(syn::ExprAssign { left, right, .. }) = expr else {
240 let expr_str = quote! { #expr }.to_string();
241 panic!("Unrecognized expression: {expr_str}");
242 };
243 let (left_type, left_tokens) =
244 expr_type_tokens_id_closure(self.vars, left, &mut |id, id_type| match id_type {
245 AExprType::Scalar { is_pub: false, .. } => {
246 panic!("Left side of = contains a private Scalar");
247 }
248 AExprType::Scalar {
249 is_vec: false,
250 is_pub: true,
251 ..
252 }
253 | AExprType::Point { is_vec: false, .. } => Ok(quote! {#instance_var.#id}),
254 AExprType::Scalar {
255 is_vec: true,
256 is_pub: true,
257 ..
258 }
259 | AExprType::Point { is_vec: true, .. } => {
260 vec_param_vars.insert(id.clone());
261 Ok(quote! {#instance_var.#id})
262 }
263 })
264 .unwrap();
265 let AExprType::Point {
266 is_pub: true,
267 is_vec: left_is_vec,
268 } = left_type
269 else {
270 let expr_str = quote! { #expr }.to_string();
271 panic!("Left side of = does not evaluate to a public point: {expr_str}");
272 };
273 let Ok((right_type, right_tokens)) =
274 expr_type_tokens_id_closure(self.vars, right, &mut |id, id_type| match id_type {
275 AExprType::Scalar {
276 is_vec: false,
277 is_pub: false,
278 ..
279 } => {
280 if allocated_vars.insert(id.clone()) {
281 scalar_allocs = quote! {
282 #scalar_allocs
283 let #id = #lr_var.allocate_scalar();
284 };
285 witness_code = quote! {
286 #witness_code
287 witnessvec.push(witness.#id);
288 };
289 }
290 Ok(quote! {#id})
291 }
292 AExprType::Scalar {
293 is_vec: false,
294 is_pub: true,
295 ..
296 } => Ok(quote! {#instance_var.#id}),
297 AExprType::Scalar {
298 is_vec: true,
299 is_pub: false,
300 ..
301 } => {
302 vec_witness_vars.insert(id.clone());
303 if allocated_vars.insert(id.clone()) {
304 scalar_allocs = quote! {
305 #scalar_allocs
306 let #id = (0..#vec_len_var)
307 .map(|i| #lr_var.allocate_scalar())
308 .collect::<Vec<_>>();
309 };
310 witness_code = quote! {
311 #witness_code
312 witnessvec.extend(witness.#id.clone());
313 };
314 }
315 Ok(quote! { #id })
316 }
317 AExprType::Scalar {
318 is_vec: true,
319 is_pub: true,
320 ..
321 } => {
322 vec_param_vars.insert(id.clone());
323 Ok(quote! {#instance_var.#id})
324 }
325 AExprType::Point { is_vec: false, .. } => {
326 if allocated_vars.insert(id.clone()) {
327 element_allocs = quote! {
328 #element_allocs
329 let #id = #lr_var.allocate_element();
330 };
331 element_assigns = quote! {
332 #element_assigns
333 #lr_var.set_element(#id, #instance_var.#id);
334 };
335 }
336 Ok(quote! {#id})
337 }
338 AExprType::Point { is_vec: true, .. } => {
339 vec_param_vars.insert(id.clone());
340 if allocated_vars.insert(id.clone()) {
341 element_allocs = quote! {
342 #element_allocs
343 let #id = (0..#vec_len_var)
344 .map(|#vec_index_var| #lr_var.allocate_element())
345 .collect::<Vec<_>>();
346 };
347 element_assigns = quote! {
348 #element_assigns
349 for #vec_index_var in 0..#vec_len_var {
350 #lr_var.set_element(
351 #id[#vec_index_var],
352 #instance_var.#id[#vec_index_var],
353 );
354 }
355 };
356 }
357 Ok(quote! { #id })
358 }
359 })
360 else {
361 let expr_str = quote! { #expr }.to_string();
362 panic!("Right side of = is not a valid arithmetic expression: {expr_str}");
363 };
364 let AExprType::Point {
365 is_vec: right_is_vec,
366 ..
367 } = right_type
368 else {
369 let expr_str = quote! { #expr }.to_string();
370 panic!("Right side of = does not evaluate to a Point: {expr_str}");
371 };
372 if left_is_vec != right_is_vec {
373 let expr_str = quote! { #expr }.to_string();
374 panic!("Only one side of = is a vector expression: {expr_str}");
375 }
376 let vec_param_varvec = Vec::from_iter(vec_param_vars);
377 let vec_witness_varvec = Vec::from_iter(vec_witness_vars);
378
379 if !vec_param_varvec.is_empty() {
380 let firstvar = &vec_param_varvec[0];
381 param_vec_code = quote! {
382 #param_vec_code
383 let #vec_len_var = #instance_var.#firstvar.len();
384 };
385 for thisvar in vec_param_varvec.iter().skip(1) {
386 param_vec_code = quote! {
387 #param_vec_code
388 if #vec_len_var != #instance_var.#thisvar.len() {
389 eprintln!(
390 "Instance variables {} and {} must have the same length",
391 stringify!(#firstvar),
392 stringify!(#thisvar),
393 );
394 return Err(SigmaError::VerificationFailure);
395 }
396 };
397 }
398 if !vec_witness_varvec.is_empty() {
399 witness_vec_code = quote! {
400 #witness_vec_code
401 let #vec_len_var = instance.#firstvar.len();
402 };
403 }
404 for witvar in vec_witness_varvec {
405 witness_vec_code = quote! {
406 #witness_vec_code
407 if #vec_len_var != witness.#witvar.len() {
408 eprintln!(
409 "Instance variables {} and {} must have the same length",
410 stringify!(#firstvar),
411 stringify!(#witvar),
412 );
413 return Err(SigmaError::VerificationFailure);
414 }
415 }
416 }
417 };
418 if right_is_vec {
419 eq_code = quote! {
420 #eq_code
421 let #eq_id = (#right_tokens)
422 .iter()
423 .cloned()
424 .map(|lr| #lr_var.allocate_eq(lr))
425 .collect::<Vec<_>>();
426 };
427 element_assigns = quote! {
428 #element_assigns
429 (#left_tokens)
430 .iter()
431 .zip(#eq_id.iter())
432 .for_each(|(l,eq)| #lr_var.set_element(*eq, *l));
433 };
434 } else {
435 eq_code = quote! {
436 #eq_code
437 let #eq_id = #lr_var.allocate_eq(#right_tokens);
438 };
439 element_assigns = quote! {
440 #element_assigns
441 #lr_var.set_element(#eq_id, #left_tokens);
442 }
443 }
444 }
445
446 (
447 quote! {
448 {
449 let mut #lr_var = LinearRelation::<Point>::new();
450 #param_vec_code
451 #scalar_allocs
452 #element_allocs
453 #eq_code
454 #element_assigns
455
456 SigmaOk(ComposedRelation::try_from(#lr_var).unwrap())
457 }
458 },
459 quote! {
460 {
461 #witness_vec_code
462 let mut witnessvec = Vec::new();
463 #witness_code
464 SigmaOk(ComposedWitness::Simple(witnessvec))
465 }
466 },
467 )
468 }
469
470 fn proto_witness_codegen(&self, statement: &StatementTree) -> (TokenStream, TokenStream) {
479 match statement {
480 StatementTree::Leaf(_) if statement.is_leaf_true() => (
483 quote! {
484 Ok(ComposedRelation::try_from(LinearRelation::<Point>::new()).unwrap())
485 },
486 quote! {
487 Ok(ComposedWitness::Simple(vec![]))
488 },
489 ),
490 StatementTree::Leaf(leafexpr) => {
493 self.linear_relation_codegen(std::slice::from_ref(&leafexpr))
494 }
495 StatementTree::And(stvec) => {
500 let mut leaves: Vec<&Expr> = Vec::new();
501 let mut others: Vec<&StatementTree> = Vec::new();
502 for st in stvec {
503 match st {
504 StatementTree::Leaf(le) => leaves.push(le),
505 _ => others.push(st),
506 }
507 }
508 let (proto_code, witness_code) = self.linear_relation_codegen(&leaves);
509 if others.is_empty() {
510 (proto_code, witness_code)
511 } else {
512 let (others_proto, others_witness): (Vec<TokenStream>, Vec<TokenStream>) =
513 others
514 .iter()
515 .map(|st| self.proto_witness_codegen(st))
516 .unzip();
517 (
518 quote! {
519 SigmaOk(ComposedRelation::and([
520 #proto_code?,
521 #(#others_proto?,)*
522 ]))
523 },
524 quote! {
525 SigmaOk(ComposedWitness::and([
526 #witness_code?,
527 #(#others_witness?,)*
528 ]))
529 },
530 )
531 }
532 }
533 StatementTree::Or(stvec) => {
534 let (proto, witness): (Vec<TokenStream>, Vec<TokenStream>) = stvec
535 .iter()
536 .map(|st| self.proto_witness_codegen(st))
537 .unzip();
538 (
539 quote! {
540 SigmaOk(ComposedRelation::or([
541 #(#proto?,)*
542 ]))
543 },
544 quote! {
545 SigmaOk(ComposedWitness::or([
546 #(#witness?,)*
547 ]))
548 },
549 )
550 }
551 StatementTree::Thresh(thresh, stvec) => {
552 let (proto, witness): (Vec<TokenStream>, Vec<TokenStream>) = stvec
553 .iter()
554 .map(|st| self.proto_witness_codegen(st))
555 .unzip();
556 (
557 quote! {
558 SigmaOk(ComposedRelation::threshold(#thresh, [
559 #(#proto?,)*
560 ]))
561 },
562 quote! {
563 SigmaOk(ComposedWitness::threshold([
564 #(#witness?,)*
565 ]))
566 },
567 )
568 }
569 }
570 }
571
572 pub fn generate(&mut self, emit_prover: bool, emit_verifier: bool) -> TokenStream {
578 let proto_name = &self.proto_name;
579 let group_name = &self.group_name;
580
581 let group_types = quote! {
582 use super::group;
583 pub type Scalar = <super::#group_name as group::Group>::Scalar;
584 pub type Point = super::#group_name;
585 };
586
587 self.statements.flatten_ands();
589
590 let mut pub_instance_fields = StructFieldList::default();
591 pub_instance_fields.push_vars(self.vars, true);
592
593 let instance_def = {
595 let decls = pub_instance_fields.field_decls();
596 #[cfg(feature = "dump")]
597 let dump_impl = {
598 let dump_chunks = pub_instance_fields.dump(&format_ident!("fmt"));
599 quote! {
600 impl Instance {
601 fn dump_scalar(s: &Scalar, fmt: &mut std::fmt::Formatter<'_>) {
602 let bytes: &[u8] = &s.to_repr();
603 for b in bytes.iter().rev() {
604 write!(fmt, "{:02x}", b).ok();
608 }
609 }
610
611 fn dump_point(p: &Point, fmt: &mut std::fmt::Formatter<'_>) {
612 let bytes: &[u8] = &p.to_bytes();
613 for b in bytes.iter().rev() {
614 write!(fmt, "{:02x}", b).ok();
618 }
619 }
620 }
621
622 impl std::fmt::Debug for Instance {
623 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
624 #dump_chunks
625 Ok(())
626 }
627 }
628 }
629 };
630 #[cfg(not(feature = "dump"))]
631 let dump_impl = {
632 quote! {}
633 };
634 quote! {
635 #[derive(Clone)]
636 pub struct Instance {
637 #decls
638 }
639
640 #dump_impl
641 }
642 };
643
644 let mut witness_fields = StructFieldList::default();
645 witness_fields.push_vars(self.vars, false);
646
647 let witness_def = if emit_prover {
649 let decls = witness_fields.field_decls();
650 quote! {
651 #[derive(Clone)]
652 pub struct Witness {
653 #decls
654 }
655 }
656 } else {
657 quote! {}
658 };
659
660 let (protocol_code, witness_code) = self.proto_witness_codegen(self.statements);
661
662 let protocol_func = {
664 let instance_var = format_ident!("{}instance", self.unique_prefix);
665
666 quote! {
667 fn protocol(
668 #instance_var: &Instance,
669 ) -> SigmaResult<ComposedRelation<Point>> {
670 #protocol_code
671 }
672 }
673 };
674
675 let witness_func = if emit_prover {
677 quote! {
678 fn protocol_witness(
679 instance: &Instance,
680 witness: &Witness,
681 ) -> SigmaResult<ComposedWitness<Point>> {
682 #witness_code
683 }
684 }
685 } else {
686 quote! {}
687 };
688
689 let prove_func = if emit_prover {
691 let instance_var = format_ident!("{}instance", self.unique_prefix);
692 let witness_var = format_ident!("{}witness", self.unique_prefix);
693 let session_id_var = format_ident!("{}session_id", self.unique_prefix);
694 let rng_var = format_ident!("{}rng", self.unique_prefix);
695 let proto_var = format_ident!("{}proto", self.unique_prefix);
696 let proto_witness_var = format_ident!("{}proto_witness", self.unique_prefix);
697 let nizk_var = format_ident!("{}nizk", self.unique_prefix);
698
699 quote! {
700 pub fn prove(
701 #instance_var: &Instance,
702 #witness_var: &Witness,
703 #session_id_var: &[u8],
704 #rng_var: &mut (impl CryptoRng + RngCore),
705 ) -> SigmaResult<Vec<u8>> {
706 let #proto_var = protocol(#instance_var)?;
707 let #proto_witness_var = protocol_witness(#instance_var, #witness_var)?;
708 let #nizk_var = #proto_var.into_nizk(#session_id_var);
709
710 #nizk_var.prove_compact(&#proto_witness_var, #rng_var)
711 }
712 }
713 } else {
714 quote! {}
715 };
716
717 let verify_func = if emit_verifier {
719 let instance_var = format_ident!("{}instance", self.unique_prefix);
720 let proof_var = format_ident!("{}proof", self.unique_prefix);
721 let session_id_var = format_ident!("{}session_id", self.unique_prefix);
722 let proto_var = format_ident!("{}proto", self.unique_prefix);
723 let nizk_var = format_ident!("{}nizk", self.unique_prefix);
724
725 quote! {
726 pub fn verify(
727 #instance_var: &Instance,
728 #proof_var: &[u8],
729 #session_id_var: &[u8],
730 ) -> SigmaResult<()> {
731 let #proto_var = protocol(#instance_var)?;
732 let #nizk_var = #proto_var.into_nizk(#session_id_var);
733
734 #nizk_var.verify_compact(#proof_var)
735 }
736 }
737 } else {
738 quote! {}
739 };
740
741 let dump_use = if cfg!(feature = "dump") {
743 quote! {
744 use group::GroupEncoding;
745 }
746 } else {
747 quote! {}
748 };
749 quote! {
750 #[allow(non_snake_case)]
751 pub mod #proto_name {
752 use super::sigma_compiler;
753 use sigma_compiler::sigma_proofs;
754 use sigma_compiler::group::ff::PrimeField;
755 use sigma_compiler::rand::{CryptoRng, RngCore};
756 use sigma_compiler::subtle::CtOption;
757 use sigma_compiler::vecutils::*;
758 use sigma_proofs::{
759 composition::{ComposedRelation, ComposedWitness},
760 errors::Error as SigmaError,
761 errors::Ok as SigmaOk,
762 errors::Result as SigmaResult,
763 LinearRelation, Nizk,
764 };
765 use std::ops::Neg;
766 #dump_use
767
768 #group_types
769 #instance_def
770 #witness_def
771
772 #protocol_func
773 #witness_func
774 #prove_func
775 #verify_func
776 }
777 }
778 }
779}