Skip to main content

sigma_compiler_core/
codegen.rs

1//! A module for generating the code produced by this macro.  This code
2//! will interact with the underlying `sigma` macro.
3
4use 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
12/// The main struct to handle code generation for this macro.
13///
14/// Initialize a [`CodeGen`] with the [`SigmaCompSpec`] you get by
15/// parsing the macro input.  Pass it to the various transformations and
16/// statement handlers, which will both update the code it will
17/// generate, and modify the [`SigmaCompSpec`].  Then at the end, call
18/// [`CodeGen::generate`] with the modified [`SigmaCompSpec`] to generate the
19/// code output by this macro.
20pub struct CodeGen {
21    /// The protocol name specified in the `sigma_compiler` macro
22    /// invocation
23    proto_name: Ident,
24    /// The group name specified in the `sigma_compiler` macro
25    /// invocation
26    group_name: Ident,
27    /// The variables that were explicitly listed in the
28    /// `sigma_compiler` macro invocation
29    vars: TaggedVarDict,
30    /// A prefix that does not appear at the beginning of any variable
31    /// name in `vars`
32    unique_prefix: String,
33    /// Variables (not necessarily appearing in `vars`, since they may
34    /// be generated by the sigma_compiler itself) that the prover needs
35    /// to send to the verifier along with the proof.  These could
36    /// include commitments to bits in range proofs, for example.
37    sent_instance: StructFieldList,
38    /// Extra code that will be emitted in the `prove` function
39    prove_code: TokenStream,
40    /// Extra code that will be emitted in the `verify` function
41    verify_code: TokenStream,
42    /// Extra code that will be emitted in the `verify` function before
43    /// the `sent_instance` are deserialized.  This is where the verifier
44    /// sets the lengths of vector variables in the `sent_instance`.
45    verify_pre_instance_code: TokenStream,
46}
47
48impl CodeGen {
49    /// Find a prefix that does not appear at the beginning of any
50    /// variable name in `vars`
51    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        // The compiler complains if this isn't here, but it will only
66        // get hit if vars contains at least usize::MAX entries, which
67        // isn't going to happen.
68        String::new()
69    }
70
71    /// Create a new [`CodeGen`] given the [`SigmaCompSpec`] you get by
72    /// parsing the macro input.
73    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    /// Create an empty [`CodeGen`].  Primarily useful in testing.
88    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    /// Create a new generated private Scalar variable to put in the
102    /// Witness.
103    ///
104    /// If you call this, you should also call
105    /// [`prove_append`](Self::prove_append) with code like `quote!{ let
106    /// #id = ... }` where `id` is the [`struct@Ident`] returned from
107    /// this function.
108    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    /// Create a new public Point variable to put in the Instance,
129    /// optionally marking it as needing to be sent from the prover to
130    /// the verifier along with the proof.
131    ///
132    /// If you call this function, you should also call
133    /// [`prove_append`](Self::prove_append) with code like `quote!{ let
134    /// #id = ... }` where `id` is the [`struct@Ident`] returned from
135    /// this function.  If `is_vec` is `true`, then you should also call
136    /// [`verify_pre_instance_append`](Self::verify_pre_instance_append)
137    /// with code like `quote!{ let mut #id = Vec::<Point>::new();
138    /// #id.resize(#len, Point::default()); }` where `len` is the number
139    /// of elements you expect to have in the vector (computed at
140    /// runtime, perhaps based on the values of public parameters).
141    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    /// Create a new identifier, using the unique prefix
169    pub fn gen_ident(&self, base: &Ident) -> Ident {
170        format_ident!("{}{}", self.unique_prefix, base)
171    }
172
173    /// Append some code to the generated `prove` function
174    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    /// Append some code to the generated `verify` function
183    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    /// Append some code to the generated `verify` function to be run
192    /// before the `sent_instance` are deserialized
193    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    /// Append some code to both the generated `prove` and `verify`
202    /// functions
203    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    /// Append some code to both the generated `prove` and `verify`
217    /// functions, the latter to be run before the `sent_instance` are
218    /// deserialized
219    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    /// Extract (as [`String`]s) the code inserted by
233    /// [`prove_append`](Self::prove_append),
234    /// [`verify_append`](Self::verify_append), and
235    /// [`verify_pre_instance_append`](Self::verify_pre_instance_append).
236    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    /// Generate the code to be output by this macro.
245    ///
246    /// `emit_prover` and `emit_verifier` are as in
247    /// [`sigma_compiler_core`](super::sigma_compiler_core).
248    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        // vardict contains the variables that were defined in the macro
264        // call to [`sigma_compiler`]
265        let vardict = taggedvardict_to_vardict(&self.vars);
266        // sigma_proofs_vardict contains the variables that we are passing
267        // to sigma_proofs.  We may have removed some via substitution, and
268        // we may have added some when compiling statements like range
269        // assertions into underlying linear combination assertions.
270        let sigma_proofs_vardict = taggedvardict_to_vardict(&spec.vars);
271
272        // Generate the code that uses the underlying sigma_proofs API
273        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        // Generate the public instance struct definition
292        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                                // It's not a big deal if writes fail
303                                // here, so we use "ok()" to ignore the
304                                // `Result`
305                                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                                // It's not a big deal if writes fail
313                                // here, so we use "ok()" to ignore the
314                                // `Result`
315                                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        // Generate the witness struct definition
343        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        // Generate the prove function
356        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(sigma_proofs::serialization::serialize_elements(
373                            std::slice::from_ref(&#codegen_instance_var.#id)
374                        ));
375                    },
376                    StructField::VecPoint(id) => quote! {
377                        #proof_var.extend(sigma_proofs::serialization::serialize_elements(
378                            &#codegen_instance_var.#id
379                        ));
380                    },
381                    _ => quote! {},
382                });
383                quote! { #(#chunks)* }
384            };
385
386            let dumper = if cfg!(feature = "dump") {
387                quote! {
388                    sigma_compiler::dumper::dump(
389                        &format!("{} sigma_compiler prover instance = {{\n{:?}}}\n",
390                            stringify!(#proto_name), #instance_var));
391                }
392            } else {
393                quote! {}
394            };
395
396            let sigma_dumper = if cfg!(feature = "dump") {
397                quote! {
398                    sigma_compiler::dumper::dump(
399                        &format!("{} sigma prover instance = {{\n{:?}}}\n",
400                            stringify!(#proto_name), #codegen_instance_var));
401                }
402            } else {
403                quote! {}
404            };
405
406            quote! {
407                pub fn prove(
408                    #instance_var: &Instance,
409                    #witness_var: &Witness,
410                    #sid_var: &[u8],
411                    #rng_var: &mut (impl CryptoRng + RngCore),
412                ) -> Result<Vec<u8>, SigmaError> {
413                    #dumper
414                    let Instance { #instance_ids } = #instance_var.clone();
415                    let Witness { #witness_ids } = #witness_var.clone();
416                    #prove_code
417                    let mut #proof_var = Vec::<u8>::new();
418                    let #codegen_instance_var = sigma::Instance {
419                        #sigma_proofs_instance_ids
420                    };
421                    let #codegen_witness_var = sigma::Witness {
422                        #sigma_proofs_witness_ids
423                    };
424                    #sent_instance_code
425                    #sigma_dumper
426                    #proof_var.extend(
427                        sigma::prove(
428                            &#codegen_instance_var,
429                            &#codegen_witness_var,
430                            #sid_var,
431                            #rng_var,
432                        )?
433                    );
434                    Ok(#proof_var)
435                }
436            }
437        } else {
438            quote! {}
439        };
440
441        // Generate the verify function
442        let verify_func = if emit_verifier {
443            let instance_ids = pub_instance_fields.field_list();
444            let sigma_proofs_instance_ids = sigma_proofs_instance_fields.field_list();
445            let verify_pre_instance_code = &self.verify_pre_instance_code;
446            let verify_code = &self.verify_code;
447            let codegen_instance_var = format_ident!("{}sigma_instance", self.unique_prefix);
448            let element_len_var = format_ident!("{}element_len", self.unique_prefix);
449            let offset_var = format_ident!("{}proof_offset", self.unique_prefix);
450            let instance_var = format_ident!("{}instance", self.unique_prefix);
451            let proof_var = format_ident!("{}proof", self.unique_prefix);
452            let sid_var = format_ident!("{}session_id", self.unique_prefix);
453            let sent_instance_code = {
454                let element_len_code = if self.sent_instance.fields.is_empty() {
455                    quote! {}
456                } else {
457                    quote! {
458                        let #element_len_var =
459                            <Point as group::GroupEncoding>::Repr::default().as_ref().len();
460                    }
461                };
462
463                let chunks = self.sent_instance.fields.iter().map(|sf| match sf {
464                    StructField::Point(id) => quote! {
465                        let #id: Point = sigma_proofs::serialization::deserialize_elements(
466                                &#proof_var[#offset_var..],
467                                1,
468                            ).ok_or(SigmaError::VerificationFailure)?[0];
469                        #offset_var += #element_len_var;
470                    },
471                    StructField::VecPoint(id) => quote! {
472                        #id = sigma_proofs::serialization::deserialize_elements(
473                                &#proof_var[#offset_var..],
474                                #id.len(),
475                            ).ok_or(SigmaError::VerificationFailure)?;
476                        #offset_var += #element_len_var * #id.len();
477                    },
478                    _ => quote! {},
479                });
480
481                quote! {
482                    let mut #offset_var = 0usize;
483                    #element_len_code
484                    #(#chunks)*
485                }
486            };
487
488            let dumper = if cfg!(feature = "dump") {
489                quote! {
490                    sigma_compiler::dumper::dump(
491                        &format!("{} sigma_compiler verifier instance = {{\n{:?}}}\n",
492                            stringify!(#proto_name), #instance_var));
493                }
494            } else {
495                quote! {}
496            };
497
498            let sigma_dumper = if cfg!(feature = "dump") {
499                quote! {
500                    sigma_compiler::dumper::dump(
501                        &format!("{} sigma verifier instance = {{\n{:?}}}\n",
502                            stringify!(#proto_name), #codegen_instance_var));
503                }
504            } else {
505                quote! {}
506            };
507
508            quote! {
509                pub fn verify(
510                    #instance_var: &Instance,
511                    #proof_var: &[u8],
512                    #sid_var: &[u8],
513                ) -> Result<(), SigmaError> {
514                    #dumper
515                    let Instance { #instance_ids } = #instance_var.clone();
516                    #verify_pre_instance_code
517                    #sent_instance_code
518                    #verify_code
519                    let #codegen_instance_var = sigma::Instance {
520                        #sigma_proofs_instance_ids
521                    };
522                    #sigma_dumper
523                    sigma::verify(
524                        &#codegen_instance_var,
525                        &#proof_var[#offset_var..],
526                        #sid_var,
527                    )
528                }
529            }
530        } else {
531            quote! {}
532        };
533
534        // Output the generated module for this protocol
535        let dump_use = if cfg!(feature = "dump") {
536            quote! {
537                use group::GroupEncoding;
538            }
539        } else {
540            quote! {}
541        };
542        quote! {
543            #[allow(non_snake_case)]
544            pub mod #proto_name {
545                use super::sigma_compiler;
546                use sigma_compiler::group::Group;
547                use sigma_compiler::group::ff::{Field, PrimeField};
548                use sigma_compiler::group::ff::derive::subtle::ConditionallySelectable;
549                use sigma_compiler::rand::{CryptoRng, RngCore};
550                use sigma_compiler::sigma_proofs;
551                use sigma_compiler::sigma_proofs::errors::Error as SigmaError;
552                use sigma_compiler::vecutils::*;
553                use std::ops::Neg;
554                #dump_use
555
556                #group_types
557
558                #sigma_proofs_code
559
560                #instance_def
561                #witness_def
562                #prove_func
563                #verify_func
564            }
565        }
566    }
567}