Bläddra i källkod

Finish range proofs

Ian Goldberg 4 månader sedan
förälder
incheckning
d8d24b7ac9

+ 2 - 0
sigma_compiler_core/src/lib.rs

@@ -56,5 +56,7 @@ pub fn sigma_compiler_core(
     });
     */
 
+    spec.statements.dump();
+
     codegen.generate(spec, emit_prover, emit_verifier)
 }

+ 81 - 7
sigma_compiler_core/src/rangeproof.rs

@@ -285,10 +285,10 @@ pub fn transform(
     // A HashSet of the unique random Scalars in the macro input
     let mut randoms = unique_random_scalars(vars, st);
 
-    // Gather mutable references to all Exprs in the leaves of the
+    // Gather mutable references to all of the leaves of the
     // StatementTree.  Note that this ignores the combiner structure in
     // the StatementTree, but that's fine.
-    let mut leaves = st.leaves_mut();
+    let mut leaves = st.leaves_st_mut();
 
     // A list of the computationally independent (non-vector) Points in
     // the macro input.  There must be at least two of them in order to
@@ -316,21 +316,28 @@ pub fn transform(
     // variable to the parsed commitment.
     let pedersens: HashMap<Ident, PedersenAssignment> = leaves
         .iter()
-        .filter_map(|leafexpr| {
+        .filter_map(|leaf| {
             // See if we recognize this leaf expression as a
             // PedersenAssignment, and if so, make a pair mapping its
             // variable to the PedersenAssignment.  (The "collect()"
             // will turn the list of pairs into a HashMap.)
-            recognize_pedersen_assignment(vars, &randoms, &vardict, leafexpr)
-                .map(|ped_assign| (ped_assign.var(), ped_assign))
+            if let StatementTree::Leaf(leafexpr) = leaf {
+                recognize_pedersen_assignment(vars, &randoms, &vardict, leafexpr)
+                    .map(|ped_assign| (ped_assign.var(), ped_assign))
+            } else {
+                None
+            }
         })
         .collect();
 
     // Count how many range statements we've seen
     let mut range_stmt_index = 0usize;
 
-    for leafexpr in leaves.iter_mut() {
+    for leaf in leaves.iter_mut() {
         // For each leaf expression, see if it looks like a range statement
+        let StatementTree::Leaf(leafexpr) = leaf else {
+            continue;
+        };
         let Some(range_stmt) = parse(vars, &vardict, leafexpr) else {
             continue;
         };
@@ -389,6 +396,8 @@ pub fn transform(
                 let #ped_assign_expr;
             });
 
+            basic_statements.push(ped_assign_expr);
+
             ped_assign
         };
 
@@ -523,6 +532,12 @@ pub fn transform(
             true, // is_rand
             true, // is_vec
         );
+        let bitrandsq_var = codegen.gen_scalar(
+            vars,
+            &format_ident!("range{}_{}_bitrandsq", range_stmt_index, range_id),
+            true, // is_rand
+            true, // is_vec
+        );
         let firstbitcomm_var = codegen.gen_point(
             vars,
             &format_ident!("range{}_{}_firstbitC", range_stmt_index, range_id),
@@ -541,6 +556,12 @@ pub fn transform(
             true,  // is_rand
             false, // is_vec
         );
+        let firstbitrandsq_var = codegen.gen_scalar(
+            vars,
+            &format_ident!("range{}_{}_firstbitrandsq", range_stmt_index, range_id),
+            true,  // is_rand
+            false, // is_vec
+        );
 
         // Update vardict and randoms with the new vars
         vardict = taggedvardict_to_vardict(vars);
@@ -557,6 +578,15 @@ pub fn transform(
         });
         // The prover code
         codegen.prove_append(quote! {
+            // The main strategy is to prove that each commitment is to
+            // a bit (0 or 1), and we do this by showing that the
+            // committed value equals its own square.  That is, we show
+            // that C = b*A + r*B and also that C = b*C + s*B.  If both
+            // of those are true (and A and B are computationally
+            // independent), then C = b*(b*A + r*B) + s*B = b^2*A +
+            // (r*b+s)*B, so b=b^2 and r=r*b+s.  Therefore either b=0
+            // and s=r or b=1 and s=0.
+
             // Map the bit representation to a vector of Scalar(0) and
             // Scalar(1), but skip the first bit, as described above.
             let #bits_var: Vec<Scalar> =
@@ -569,11 +599,21 @@ pub fn transform(
                         *b,
                     ))
                     .collect();
-            // Choose randomizers for the commitments randomly
+            // Choose randomizers r for the commitments randomly
             let #bitrand_var: Vec<Scalar> =
                 (0..(#nbits_var-1))
                     .map(|_| Scalar::random(rng))
                     .collect();
+            // The randomizers s for the commitments to the squares are
+            // chosen as above: s=r if b=0 and s=0 if b=1.
+            let #bitrandsq_var: Vec<Scalar> =
+                (0..(#nbits_var-1))
+                    .map(|i| Scalar::conditional_select(
+                        &#bitrand_var[i],
+                        &Scalar::ZERO,
+                        #bitrep_var[i+1],
+                    ))
+                    .collect();
             // Compute the commitments
             let #bitcomm_var: Vec<Point> =
                 (0..(#nbits_var-1))
@@ -596,6 +636,12 @@ pub fn transform(
                 #firstbitrand_var -=
                     #bitrand_var[i] * #bitrep_scalars_var[i+1];
             }
+            // And the randomization for the first square is as above
+            let #firstbitrandsq_var = Scalar::conditional_select(
+                    &#firstbitrand_var,
+                    &Scalar::ZERO,
+                    #bitrep_var[0],
+                );
             // Compute the first bit commitment
             let #firstbitcomm_var =
                 #firstbit_var * #commit_generator +
@@ -610,6 +656,34 @@ pub fn transform(
                     #bitcomm_var[i] * #bitrep_scalars_var[i+1];
             }
         });
+
+        basic_statements.push(parse_quote! {
+            #bitcomm_var = #bits_var * #commit_generator
+                + #bitrand_var * #rand_generator
+        });
+        basic_statements.push(parse_quote! {
+            #bitcomm_var = #bits_var * #bitcomm_var
+                + #bitrandsq_var * #rand_generator
+        });
+        basic_statements.push(parse_quote! {
+            #firstbitcomm_var = #firstbit_var * #commit_generator
+                + #firstbitrand_var * #rand_generator
+        });
+        basic_statements.push(parse_quote! {
+            #firstbitcomm_var = #firstbit_var * #bitcomm_var
+                + #firstbitrandsq_var * #rand_generator
+        });
+
+        // Now replace the range statement with an And of the
+        // basic_statements
+        let range_st = StatementTree::And(
+            basic_statements
+                .into_iter()
+                .map(StatementTree::Leaf)
+                .collect(),
+        );
+
+        **leaf = range_st;
     }
 
     Ok(())

+ 51 - 4
sigma_compiler_core/src/sigma/combiners.rs

@@ -2,6 +2,7 @@
 //! combined with `AND`, `OR`, and `THRESH`.
 
 use super::types::*;
+use quote::quote;
 use std::collections::HashMap;
 use syn::parse::Result;
 use syn::visit::Visit;
@@ -155,8 +156,8 @@ impl StatementTree {
         Ok(StatementTree::And(children?))
     }
 
-    /// Return a vector of references to all of the leaves in the
-    /// [`StatementTree`]
+    /// Return a vector of references to all of the leaf expressions in
+    /// the [`StatementTree`]
     pub fn leaves(&self) -> Vec<&Expr> {
         match self {
             StatementTree::Leaf(ref e) => vec![e],
@@ -169,8 +170,8 @@ impl StatementTree {
         }
     }
 
-    /// Return a vector of mutable references to all of the leaves in
-    /// the [`StatementTree`]
+    /// Return a vector of mutable references to all of the leaf
+    /// expressions in the [`StatementTree`]
     pub fn leaves_mut(&mut self) -> Vec<&mut Expr> {
         match self {
             StatementTree::Leaf(ref mut e) => vec![e],
@@ -183,6 +184,20 @@ impl StatementTree {
         }
     }
 
+    /// Return a vector of mutable references to all of the leaves in
+    /// the [`StatementTree`]
+    pub fn leaves_st_mut(&mut self) -> Vec<&mut StatementTree> {
+        match self {
+            StatementTree::Leaf(_) => vec![self],
+            StatementTree::And(v) | StatementTree::Or(v) | StatementTree::Thresh(_, v) => v
+                .iter_mut()
+                .fold(Vec::<&mut StatementTree>::new(), |mut b, st| {
+                    b.extend(st.leaves_st_mut());
+                    b
+                }),
+        }
+    }
+
     #[cfg(not(doctest))]
     /// Verify whether the [`StatementTree`] satisfies the disjunction
     /// invariant.
@@ -391,6 +406,38 @@ impl StatementTree {
             }
         }
     }
+
+    fn dump_int(&self, depth: usize) {
+        match self {
+            StatementTree::Leaf(e) => {
+                println!(
+                    "{:1$}{2}",
+                    "",
+                    depth * 2,
+                    quote! { #e }.to_string().replace('\n', " ")
+                )
+            }
+            StatementTree::And(v) => {
+                println!("{:1$}And (", "", depth * 2);
+                v.iter().for_each(|n| n.dump_int(depth + 1));
+                println!("{:1$})", "", depth * 2);
+            }
+            StatementTree::Or(v) => {
+                println!("{:1$}Or (", "", depth * 2);
+                v.iter().for_each(|n| n.dump_int(depth + 1));
+                println!("{:1$})", "", depth * 2);
+            }
+            StatementTree::Thresh(thresh, v) => {
+                println!("{:1$}Thresh ({2}", "", depth * 2, thresh);
+                v.iter().for_each(|n| n.dump_int(depth + 1));
+                println!("{:1$})", "", depth * 2);
+            }
+        }
+    }
+
+    pub fn dump(&self) {
+        self.dump_int(0);
+    }
 }
 
 #[cfg(test)]