Browse Source

Convert commitments and randomness of one LinScalar to that of another LinScalar of the same variable

Ian Goldberg 4 months ago
parent
commit
d9ca5c733f

+ 15 - 0
sigma_compiler_core/src/codegen.rs

@@ -193,6 +193,21 @@ impl CodeGen {
         };
     }
 
+    /// Append some code to both the generated `prove` and `verify`
+    /// functions
+    pub fn prove_verify_append(&mut self, code: TokenStream) {
+        let prove_code = &self.prove_code;
+        self.prove_code = quote! {
+            #prove_code
+            #code
+        };
+        let verify_code = &self.verify_code;
+        self.verify_code = quote! {
+            #verify_code
+            #code
+        };
+    }
+
     /// Generate the code to be output by this macro.
     ///
     /// `emit_prover` and `emit_verifier` are as in

+ 284 - 3
sigma_compiler_core/src/rangeproof.rs

@@ -25,10 +25,11 @@ use super::pedersen::{
     LinScalar, PedersenAssignment,
 };
 use super::sigma::combiners::*;
-use super::sigma::types::VarDict;
+use super::sigma::types::{const_i128_tokens, expr_type_tokens, VarDict};
 use super::syntax::taggedvardict_to_vardict;
 use super::transform::paren_if_needed;
 use super::{TaggedIdent, TaggedPoint, TaggedVarDict};
+use proc_macro2::TokenStream;
 use quote::{format_ident, quote};
 use std::collections::HashMap;
 use syn::{parse_quote, Error, Expr, Ident, Result};
@@ -189,6 +190,89 @@ fn parse(vars: &TaggedVarDict, vardict: &VarDict, expr: &Expr) -> Option<RangeSt
     None
 }
 
+/// Output code to convert a commitment given by a
+/// [`PedersenAssignment`] into one for a different [`LinScalar`] of the
+/// same variable.
+fn convert_commitment(
+    output_commitment: &Ident,
+    ped_assign: &PedersenAssignment,
+    new_linscalar: &LinScalar,
+    vardict: &VarDict,
+) -> Result<TokenStream> {
+    let orig_commitment = &ped_assign.id;
+    let ped_assign_linscalar = &ped_assign.pedersen.var_term.coeff;
+    let generator = &ped_assign.pedersen.var_term.id;
+    let mut generated_code = quote! { #orig_commitment };
+    // Subtract the pub_scalar_expr in ped_assign_linscalar (if present)
+    // times the generator
+    if let Some(ref pse) = ped_assign_linscalar.pub_scalar_expr {
+        let ppse_tokens = expr_type_tokens(vardict, &paren_if_needed(pse.clone()))?.1;
+        generated_code = quote! {
+            ( #generated_code - #ppse_tokens * #generator )
+        };
+    }
+    // Divide by the coeff in ped_assign_linscalar, if present (noting
+    // it also cannot be 0, so will have an inverse)
+    if ped_assign_linscalar.coeff != 1 {
+        let coeff_tokens = const_i128_tokens(ped_assign_linscalar.coeff);
+        generated_code = quote! {
+            #coeff_tokens.inverse().unwrap() * #generated_code
+        };
+    }
+    // Now multiply by the coeff in new_linscalar, if present
+    if new_linscalar.coeff != 1 {
+        let coeff_tokens = const_i128_tokens(new_linscalar.coeff);
+        generated_code = quote! {
+            #coeff_tokens * #generated_code
+        };
+    }
+    // And add the pub_scalar_expr in new_linscalar (if present) times
+    // the generator
+    if let Some(ref pse) = new_linscalar.pub_scalar_expr {
+        let ppse_tokens = expr_type_tokens(vardict, &paren_if_needed(pse.clone()))?.1;
+        generated_code = quote! {
+            #generated_code + #ppse_tokens * #generator
+        };
+    }
+
+    Ok(quote! { let #output_commitment = #generated_code; })
+}
+
+/// Output code to convert the randomness given by a
+/// [`PedersenAssignment`] into that resulting from the conversion in
+/// [`convert_commitment`].
+fn convert_randomness(
+    output_randomness: &Ident,
+    ped_assign: &PedersenAssignment,
+    new_linscalar: &LinScalar,
+    vardict: &VarDict,
+) -> Result<TokenStream> {
+    let ped_assign_linscalar = &ped_assign.pedersen.var_term.coeff;
+    // Start with the LinScalar in ped_assign.pedersen.rand_term
+    let mut generated_code = expr_type_tokens(
+        vardict,
+        &paren_if_needed(ped_assign.pedersen.rand_term.coeff.to_expr()),
+    )?
+    .1;
+    // Divide by the coeff in ped_assign_linscalar, if present (noting
+    // it also cannot be 0, so will have an inverse)
+    if ped_assign_linscalar.coeff != 1 {
+        let coeff_tokens = const_i128_tokens(ped_assign_linscalar.coeff);
+        generated_code = quote! {
+            #coeff_tokens.inverse().unwrap() * #generated_code
+        };
+    }
+    // Now multiply by the coeff in new_linscalar, if present
+    if new_linscalar.coeff != 1 {
+        let coeff_tokens = const_i128_tokens(new_linscalar.coeff);
+        generated_code = quote! {
+            #coeff_tokens * #generated_code
+        };
+    }
+
+    Ok(quote! { let #output_randomness = #generated_code; })
+}
+
 /// Look for, and transform, range statements specified in the
 /// [`StatementTree`] into basic statements about linear combinations of
 /// `Point`s.
@@ -282,13 +366,13 @@ pub fn transform(
             // random Scalar.
             let commitment_var = codegen.gen_point(
                 vars,
-                &format_ident!("range{}_{}_C", range_stmt_index, range_id),
+                &format_ident!("range{}_{}_genC", range_stmt_index, range_id),
                 false,
                 true,
             );
             let rand_var = codegen.gen_scalar(
                 vars,
-                &format_ident!("range{}_{}_r", range_stmt_index, range_id),
+                &format_ident!("range{}_{}_genr", range_stmt_index, range_id),
                 true,
                 false,
             );
@@ -310,6 +394,48 @@ pub fn transform(
 
             ped_assign
         };
+
+        // At this point, we have a Pedersen commitment for some linear
+        // function of range_id (given by
+        // ped_assign.pedersen.var_term.coeff), using some linear
+        // function of rand_var (given by
+        // ped_assign.pedersen.rand_term.coeff) as the randomness.  But
+        // what we need is a Pedersen commitment for a possibly
+        // different linear function of range_id (given by
+        // range_stmt.expr).  So we output runtime code for both the
+        // prover and the verifier that converts the commitment, and
+        // code for just the prover that converts the randomness.
+
+        // Make a new runtime variable to hold the converted commitment
+        let commitment_var = codegen.gen_point(
+            vars,
+            &format_ident!("range{}_{}_C", range_stmt_index, range_id),
+            false,
+            false,
+        );
+        let rand_var = codegen.gen_scalar(
+            vars,
+            &format_ident!("range{}_{}_r", range_stmt_index, range_id),
+            true,
+            false,
+        );
+
+        // Update vardict and randoms with the new vars
+        vardict = taggedvardict_to_vardict(vars);
+        randoms.insert(rand_var.to_string());
+
+        codegen.prove_verify_append(convert_commitment(
+            &commitment_var,
+            &ped_assign,
+            &range_stmt.expr,
+            &vardict,
+        )?);
+        codegen.prove_append(convert_randomness(
+            &rand_var,
+            &ped_assign,
+            &range_stmt.expr,
+            &vardict,
+        )?);
     }
 
     Ok(())
@@ -319,6 +445,7 @@ pub fn transform(
 mod tests {
     use super::super::syntax::taggedvardict_from_strs;
     use super::*;
+    use std::collections::HashSet;
 
     fn parse_tester(vars: (&[&str], &[&str]), expr: Expr, expect: Option<RangeStatement>) {
         let taggedvardict = taggedvardict_from_strs(vars);
@@ -481,4 +608,158 @@ mod tests {
             }),
         );
     }
+
+    fn convert_commitment_randomness_tester(
+        vars: (&[&str], &[&str]),
+        randoms: &[&str],
+        ped_assign_expr: Expr,
+        lin_scalar_expr: Expr,
+        expect_commitment: TokenStream,
+        expect_randomness: TokenStream,
+    ) {
+        let taggedvardict = taggedvardict_from_strs(vars);
+        let vardict = taggedvardict_to_vardict(&taggedvardict);
+        let mut randoms_hash = HashSet::new();
+        for r in randoms {
+            randoms_hash.insert(r.to_string());
+        }
+        let output_commitment = format_ident! { "out" };
+        let output_randomness = format_ident! { "out_rand" };
+        let ped_assign = recognize_pedersen_assignment(
+            &taggedvardict,
+            &randoms_hash,
+            &vardict,
+            &ped_assign_expr,
+        )
+        .unwrap();
+        let lin_scalar = recognize_linscalar(&taggedvardict, &vardict, &lin_scalar_expr).unwrap();
+        assert_eq!(
+            convert_commitment(&output_commitment, &ped_assign, &lin_scalar, &vardict)
+                .unwrap()
+                .to_string(),
+            expect_commitment.to_string()
+        );
+        assert_eq!(
+            convert_randomness(&output_randomness, &ped_assign, &lin_scalar, &vardict)
+                .unwrap()
+                .to_string(),
+            expect_randomness.to_string()
+        );
+    }
+
+    #[test]
+    fn convert_commitment_randomness_test() {
+        let vars = (
+            [
+                "x", "y", "z", "pub a", "pub b", "pub c", "rand r", "rand s", "rand t",
+            ]
+            .as_slice(),
+            ["C", "cind A", "cind B"].as_slice(),
+        );
+        let randoms = ["r", "s", "t"].as_slice();
+
+        convert_commitment_randomness_tester(
+            vars,
+            &randoms,
+            parse_quote! { C = x*A + r*B },
+            parse_quote! { x },
+            quote! { let out = C; },
+            quote! { let out_rand = r; },
+        );
+
+        convert_commitment_randomness_tester(
+            vars,
+            &randoms,
+            parse_quote! { C = x*A + r*B },
+            parse_quote! { 2 * x },
+            quote! { let out = Scalar::from_u128(2u128) * C; },
+            quote! { let out_rand = Scalar::from_u128(2u128) * r; },
+        );
+
+        convert_commitment_randomness_tester(
+            vars,
+            &randoms,
+            parse_quote! { C = x*A + r*B },
+            parse_quote! { 2 * x + 12 },
+            quote! { let out = Scalar::from_u128(2u128) * C +
+            Scalar::from_u128(12u128) * A; },
+            quote! { let out_rand = Scalar::from_u128(2u128) * r; },
+        );
+
+        convert_commitment_randomness_tester(
+            vars,
+            &randoms,
+            parse_quote! { C = x*A + r*B },
+            parse_quote! { 2 * x + 12 + a },
+            quote! { let out = Scalar::from_u128(2u128) * C +
+            (Scalar::from_u128(12u128) + a) * A; },
+            quote! { let out_rand = Scalar::from_u128(2u128) * r; },
+        );
+
+        convert_commitment_randomness_tester(
+            vars,
+            &randoms,
+            parse_quote! { C = 3*x*A + r*B },
+            parse_quote! { 2 * x + 12 + a },
+            quote! { let out = Scalar::from_u128(2u128) *
+            Scalar::from_u128(3u128).inverse().unwrap() * C +
+            (Scalar::from_u128(12u128) + a) * A; },
+            quote! { let out_rand = Scalar::from_u128(2u128) *
+            Scalar::from_u128(3u128).inverse().unwrap() * r; },
+        );
+
+        convert_commitment_randomness_tester(
+            vars,
+            &randoms,
+            parse_quote! { C = -3*x*A + r*B },
+            parse_quote! { 2 * x + 12 + a },
+            quote! { let out = Scalar::from_u128(2u128) *
+            Scalar::from_u128(3u128).neg().inverse().unwrap() * C +
+            (Scalar::from_u128(12u128) + a) * A; },
+            quote! { let out_rand = Scalar::from_u128(2u128) *
+            Scalar::from_u128(3u128).neg().inverse().unwrap() * r; },
+        );
+
+        convert_commitment_randomness_tester(
+            vars,
+            &randoms,
+            parse_quote! { C = (-3*x+4+b)*A + r*B },
+            parse_quote! { 2 * x + 12 + a },
+            quote! { let out = Scalar::from_u128(2u128) *
+            Scalar::from_u128(3u128).neg().inverse().unwrap() *
+            (C - (Scalar::from_u128(4u128) + b) * A) +
+            (Scalar::from_u128(12u128) + a) * A; },
+            quote! { let out_rand = Scalar::from_u128(2u128) *
+            Scalar::from_u128(3u128).neg().inverse().unwrap() * r; },
+        );
+
+        convert_commitment_randomness_tester(
+            vars,
+            &randoms,
+            parse_quote! { C = (-3*x+4+b)*A + 2*r*B },
+            parse_quote! { 2 * x + 12 + a },
+            quote! { let out = Scalar::from_u128(2u128) *
+            Scalar::from_u128(3u128).neg().inverse().unwrap() *
+            (C - (Scalar::from_u128(4u128) + b) * A) +
+            (Scalar::from_u128(12u128) + a) * A; },
+            quote! { let out_rand = Scalar::from_u128(2u128) *
+            Scalar::from_u128(3u128).neg().inverse().unwrap() *
+            (Scalar::from_u128(2u128) * r); },
+        );
+
+        convert_commitment_randomness_tester(
+            vars,
+            &randoms,
+            parse_quote! { C = (-3*x+4+b)*A + (2*r+c-3)*B },
+            parse_quote! { 2 * x + 12 + a },
+            quote! { let out = Scalar::from_u128(2u128) *
+            Scalar::from_u128(3u128).neg().inverse().unwrap() *
+            (C - (Scalar::from_u128(4u128) + b) * A) +
+            (Scalar::from_u128(12u128) + a) * A; },
+            quote! { let out_rand = Scalar::from_u128(2u128) *
+            Scalar::from_u128(3u128).neg().inverse().unwrap() *
+            (Scalar::from_u128(2u128) * r +
+            (c + (Scalar::from_u128(3u128).neg()))); },
+        );
+    }
 }

+ 1 - 1
sigma_compiler_core/src/sigma/types.rs

@@ -114,7 +114,7 @@ pub fn vardict_from_strs(strs: &[(&str, &str)]) -> VarDict {
 /// Given an [`i128`] value, output a [`TokenStream`] representing a
 /// valid Rust expression that evaluates to a `Scalar` having that
 /// value.
-fn const_i128_tokens(val: i128) -> TokenStream {
+pub fn const_i128_tokens(val: i128) -> TokenStream {
     let uval = val.unsigned_abs();
     if val >= 0 {
         quote! { Scalar::from_u128(#uval) }