Skip to main content

sigma_compiler_core/sigma/
codegen.rs

1//! A module for generating the code that uses the `sigma-proofs` crate API.
2//!
3//! If that crate gets its own macro interface, it can use this module
4//! directly.
5
6use 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/// Names and types of fields that might end up in a generated struct
14#[derive(Clone)]
15pub enum StructField {
16    Scalar(Ident),
17    VecScalar(Ident),
18    Point(Ident),
19    VecPoint(Ident),
20}
21
22impl StructField {
23    /// Extract the [`struct@Ident`] from the [`StructField`]
24    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/// A list of StructField items
34#[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    /// Output a ToTokens of code to dump the contents of the fields to
78    /// the `std::fmt::Formatter` with the given `fmt_id`
79    pub fn dump(&self, fmt_id: &Ident) -> impl ToTokens {
80        // Sort the field ids
81        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            // It's not a big deal if writes fail here, so we use "ok()"
86            // to ignore the `Result`
87            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    /// Output a ToTokens of the fields as they would appear in a struct
119    /// definition
120    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    /// Output a ToTokens of the list of fields
138    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
157/// The main struct to handle code generation using the `sigma-proofs` API.
158pub 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    /// Find a prefix that does not appear at the beginning of any
168    /// variable name in `vars`
169    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        // The compiler complains if this isn't here, but it will only
184        // get hit if vars contains at least usize::MAX entries, which
185        // isn't going to happen.
186        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    /// Generate the code for the `protocol` and `protocol_witness`
205    /// functions that create the `ComposedRelation` and `ComposedWitness`
206    /// structs, respectively, given a slice of [`Expr`]s that will be
207    /// bundled into a single `LinearRelation`.  The `protocol` code
208    /// must evaluate to a `Result<ComposedRelation>` and the `protocol_witness`
209    /// code must evaluate to a `Result<ComposedWitness>`.
210    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            // Record any vector variables we encountered in this
228            // expression
229            let mut vec_param_vars: HashSet<Ident> = HashSet::new();
230            let mut vec_witness_vars: HashSet<Ident> = HashSet::new();
231
232            // Ensure the `Expr` is of a type we recognize.  In
233            // particular, it must be an assignment (left = right) where
234            // the expression on the left is an arithmetic expression
235            // that evaluates to a public Point, and the expression on
236            // the right is an arithmetic expression that evaluates to a
237            // Point.  It is allowed for neither or both Points to be
238            // vector variables.
239            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    /// Generate the code for the `protocol` and `protocol_witness`
471    /// functions that create the `Protocol` and `ComposedWitness`
472    /// structs, respectively, given a [`StatementTree`] describing the
473    /// statements to be proven.  The output components are the code for
474    /// the `protocol` and `protocol_witness` functions, respectively.
475    /// The `protocol` code must evaluate to a `Result<Protocol>` and
476    /// the `protocol_witness` code must evaluate to a
477    /// `Result<ComposedWitness>`.
478    fn proto_witness_codegen(&self, statement: &StatementTree) -> (TokenStream, TokenStream) {
479        match statement {
480            // The StatementTree has no statements (it's just the single
481            // leaf "true")
482            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            // The StatementTree is a single statement.  Generate a
491            // single LinearRelation from it.
492            StatementTree::Leaf(leafexpr) => {
493                self.linear_relation_codegen(std::slice::from_ref(&leafexpr))
494            }
495            // The StatementTree is an And.  Separate out the leaf
496            // statements, and generate a single LinearRelation from
497            // them.  Then if there are non-leaf nodes as well, And them
498            // together.
499            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    /// Generate the code that uses the `sigma-proofs` API to prove and
573    /// verify the statements in the [`CodeGen`].
574    ///
575    /// `emit_prover` and `emit_verifier` are as in
576    /// [`sigma_compiler_core`](super::super::sigma_compiler_core).
577    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        // Flatten nested "And"s into single "And"s
588        self.statements.flatten_ands();
589
590        let mut pub_instance_fields = StructFieldList::default();
591        pub_instance_fields.push_vars(self.vars, true);
592
593        // Generate the public instance struct definition
594        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                                // It's not a big deal if writes fail
605                                // here, so we use "ok()" to ignore the
606                                // `Result`
607                                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                                // It's not a big deal if writes fail
615                                // here, so we use "ok()" to ignore the
616                                // `Result`
617                                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        // Generate the witness struct definition
648        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        // Generate the function that creates the sigma-proofs Protocol
663        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        // Generate the function that creates the sigma-proofs ComposedWitness
676        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        // Generate the prove function
690        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        // Generate the verify function
718        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        // Output the generated module for this protocol
742        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}