Browse Source

Allow Ptables to be public or private

Private Ptables are more expensive for the prover but cheaper for the
verifier, and public Ptables are the reverse.
Ian Goldberg 4 years ago
parent
commit
881144454e
2 changed files with 165 additions and 26 deletions
  1. 145 19
      ecgadget.hpp
  2. 20 7
      varscalarmul.cpp

+ 145 - 19
ecgadget.hpp

@@ -661,6 +661,9 @@ public:
   }
 };
 
+template<typename FieldT>
+class ec_scalarmul_gadget;
+
 // Compute A + s*P as (outx, outy) for an accumulator A, a precomputed
 // addition table Ptable for a variable point P, and s given as a bit
 // vector.  The _caller_ is responsible for proving that the elements of
@@ -669,17 +672,27 @@ public:
 // subtracted from the accumulator A.  The addition table is a variable
 // array of length 2*numbits (where numbits is the length of svec) such
 // that Ptable[2*i] and Ptable[2*i+1] are the (x,y) coordinates of
-// 2^i * P + C.
+// 2^i * P + C.  Set Ptable_set_constraints to true (exactly once
+// in the event the same Ptable is reused in the same circuit) if
+// the Ptable is part of the private input.  Set Ptable_fill_values
+// to true exactly once per Ptable (again, in case it it reused in the
+// same circuit).
 template<typename FieldT>
 class ec_scalarmul_vec_accum_gadget : public gadget<FieldT> {
 private:
   FieldT Cx, Cy;
   pb_variable_array<FieldT> accumx, accumy;
+  pb_variable_array<FieldT> twoiPx, twoiPy;
+  std::vector<ec_constant_add_gadget<FieldT> > cadders;
+  std::vector<ec_add_gadget<FieldT> > adders;
   std::vector<ec_2_1constant_add_gadget<FieldT> > twoadders;
 public:
   const pb_variable<FieldT> outx, outy;
   const pb_variable<FieldT> Ax, Ay;
-  const pb_variable_array<FieldT> svec, Ptable;
+  const pb_variable_array<FieldT> svec;
+  const pb_variable<FieldT> Px, Py;
+  const pb_variable_array<FieldT> Ptable;
+  bool Ptable_set_constraints, Ptable_fill_values;
 
   ec_scalarmul_vec_accum_gadget(protoboard<FieldT> &pb,
               const pb_variable<FieldT> &outx,
@@ -687,16 +700,59 @@ public:
               const pb_variable<FieldT> &Ax,
               const pb_variable<FieldT> &Ay,
               const pb_variable_array<FieldT> &svec,
+              const pb_variable<FieldT> &Px,
+              const pb_variable<FieldT> &Py,
               const pb_variable_array<FieldT> &Ptable,
+              bool Ptable_set_constraints,
+              bool Ptable_fill_values,
               FieldT &AXSx, FieldT &AXSy) :
     gadget<FieldT>(pb, "ec_scalarmul_vec_accum_gadget"),
     // Precomputed coordinates of C
     Cx(2),
     Cy("4950745124018817972378217179409499695353526031437053848725554590521829916331"),
-    outx(outx), outy(outy), Ax(Ax), Ay(Ay), svec(svec), Ptable(Ptable)
+    outx(outx), outy(outy), Ax(Ax), Ay(Ay), svec(svec),
+    Px(Px), Py(Py), Ptable(Ptable),
+    Ptable_set_constraints(Ptable_set_constraints),
+    Ptable_fill_values(Ptable_fill_values)
   {
     size_t numbits = svec.size();
     assert(Ptable.size() == 2*numbits);
+
+    if (Ptable_set_constraints) {
+        // Create the adders to fill the Ptable with the correct values.
+        // Ptable[2*i] and Ptable[2*i+1] are the (x,y) coordinates of
+        // 2^i * P + C.
+        if (numbits > 0) {
+            // Add P and C to get Ptable[0,1] = P+C
+            cadders.emplace_back(this->pb, Ptable[0], Ptable[1],
+                    Px, Py, Cx, Cy);
+        }
+        if (numbits > 1) {
+            // Add P and P+C to get Ptable[2,3] = 2*P+C
+            adders.emplace_back(this->pb, Ptable[2], Ptable[3],
+                    Px, Py, Ptable[0], Ptable[1]);
+        }
+        if (numbits > 2) {
+            twoiPx.allocate(this->pb, numbits-2, "twoiPx");
+            twoiPy.allocate(this->pb, numbits-2, "twoiPy");
+        }
+        for (size_t i = 2; i < numbits; ++i) {
+            // Invariant: twoiP[i] = 2^{i+1} * P
+
+            // Compute 2^{i-1}*P = (2^{i-1}*P + C) - C
+            cadders.emplace_back(this->pb,
+                    twoiPx[i-2], twoiPy[i-2],
+                    Ptable[2*(i-1)], Ptable[2*(i-1)+1],
+                    Cx, -Cy);
+
+            // Compute 2^{i}*P + C = (2^{i-1}*P + C) + (2^{i-1}*P)
+            adders.emplace_back(this->pb,
+                    Ptable[2*i], Ptable[2*i+1],
+                    Ptable[2*i-2], Ptable[2*i-1],
+                    twoiPx[i-2], twoiPy[i-2]);
+        }
+    }
+
     accumx.allocate(this->pb, numbits-1, "accumx");
     accumy.allocate(this->pb, numbits-1, "accumy");
 
@@ -717,6 +773,14 @@ public:
 
   void generate_r1cs_constraints()
   {
+    if (Ptable_set_constraints) {
+        for (auto&& gadget : cadders) {
+            gadget.generate_r1cs_constraints();
+        }
+        for (auto&& gadget : adders) {
+            gadget.generate_r1cs_constraints();
+        }
+    }
     for (auto&& gadget : twoadders) {
         gadget.generate_r1cs_constraints();
     }
@@ -724,6 +788,24 @@ public:
 
   void generate_r1cs_witness()
   {
+    if (Ptable_set_constraints) {
+        // We also have to satisfy the constraints we set
+        size_t numbits = Ptable.size() / 2;
+
+        if (numbits > 0) {
+            cadders[0].generate_r1cs_witness();
+        }
+        if (numbits > 1) {
+            adders[0].generate_r1cs_witness();
+        }
+        for (size_t i = 2; i < numbits; ++i) {
+            cadders[i-1].generate_r1cs_witness();
+            adders[i-1].generate_r1cs_witness();
+        }
+    } else if (Ptable_fill_values) {
+        // We can just compute the Ptable values manually
+        ec_scalarmul_gadget<FieldT>::compute_Ptable(this->pb, Ptable, Px, Py);
+    }
     for (auto&& gadget : twoadders) {
         gadget.generate_r1cs_witness();
     }
@@ -738,7 +820,11 @@ public:
 // subtracted from the accumulator A.  The addition table is a variable
 // array of length 2*numbits (where numbits is the length of the FieldT
 // size) such that Ptable[2*i] and Ptable[2*i+1] are the (x,y)
-// coordinates of 2^i * P + C.
+// coordinates of 2^i * P + C.  Set Ptable_set_constraints to true
+// (exactly once in the event the same Ptable is reused in the same
+// circuit) if the Ptable is part of the private input.  Set
+// Ptable_fill_values to true exactly once per Ptable (again, in case it
+// it reused in the same circuit).
 template<typename FieldT>
 class ec_scalarmul_accum_gadget : public gadget<FieldT> {
 private:
@@ -750,7 +836,9 @@ public:
   const pb_variable<FieldT> outx, outy;
   const pb_variable<FieldT> Ax, Ay;
   const pb_variable<FieldT> s;
+  const pb_variable<FieldT> Px, Py;
   const pb_variable_array<FieldT> Ptable;
+  bool Ptable_set_constraints, Ptable_fill_values;
 
   ec_scalarmul_accum_gadget(protoboard<FieldT> &pb,
               const pb_variable<FieldT> &outx,
@@ -758,10 +846,17 @@ public:
               const pb_variable<FieldT> &Ax,
               const pb_variable<FieldT> &Ay,
               const pb_variable<FieldT> &s,
+              const pb_variable<FieldT> &Px,
+              const pb_variable<FieldT> &Py,
               const pb_variable_array<FieldT> &Ptable,
+              bool Ptable_set_constraints,
+              bool Ptable_fill_values,
               FieldT &AXSx, FieldT &AXSy) :
     gadget<FieldT>(pb, "ec_scalarmul_accum_gadget"),
-    outx(outx), outy(outy), Ax(Ax), Ay(Ay), s(s), Ptable(Ptable)
+    outx(outx), outy(outy), Ax(Ax), Ay(Ay), s(s),
+    Px(Px), Py(Py), Ptable(Ptable),
+    Ptable_set_constraints(Ptable_set_constraints),
+    Ptable_fill_values(Ptable_fill_values)
   {
     // Allocate variables to protoboard
     // The strings (like "x") are only for debugging purposes
@@ -769,7 +864,9 @@ public:
     size_t numbits = FieldT::num_bits;
     svec.allocate(this->pb, numbits, "svec");
     packers.emplace_back(this->pb, svec, s);
-    vecgadget.emplace_back(this->pb, outx, outy, Ax, Ay, svec, Ptable, AXSx, AXSy);
+    vecgadget.emplace_back(this->pb, outx, outy, Ax, Ay, svec,
+        Px, Py, Ptable, Ptable_set_constraints, Ptable_fill_values,
+        AXSx, AXSy);
   }
 
   void generate_r1cs_constraints()
@@ -790,7 +887,11 @@ public:
 // responsible for proving that the elements of svec are bits.
 // The addition table is a variable array of length 2*numbits (where
 // numbits is the length of svec) such that Ptable[2*i] and
-// Ptable[2*i+1] are the (x,y) coordinates of 2^i * P + C.
+// Ptable[2*i+1] are the (x,y) coordinates of 2^i * P + C.  Set
+// Ptable_set_constraints to true (exactly once in the event the same
+// Ptable is reused in the same circuit) if the Ptable is part of the
+// private input.  Set Ptable_fill_values to true exactly once per
+// Ptable (again, in case it it reused in the same circuit).
 template<typename FieldT>
 class ec_scalarmul_vec_gadget : public gadget<FieldT> {
 private:
@@ -801,20 +902,29 @@ private:
 public:
   const pb_variable<FieldT> outx, outy;
   const pb_variable_array<FieldT> svec;
+  const pb_variable<FieldT> Px, Py;
   const pb_variable_array<FieldT> Ptable;
+  bool Ptable_set_constraints, Ptable_fill_values;
 
   ec_scalarmul_vec_gadget(protoboard<FieldT> &pb,
               const pb_variable<FieldT> &outx,
               const pb_variable<FieldT> &outy,
               const pb_variable_array<FieldT> &svec,
-              const pb_variable_array<FieldT> &Ptable) :
+              const pb_variable<FieldT> &Px,
+              const pb_variable<FieldT> &Py,
+              const pb_variable_array<FieldT> &Ptable,
+              bool Ptable_set_constraints,
+              bool Ptable_fill_values) :
     gadget<FieldT>(pb, "ec_scalarmul_vec_gadget"),
     // Precomputed coordinates of C and A
     Cx(2),
     Cy("4950745124018817972378217179409499695353526031437053848725554590521829916331"),
     Ax("7536839002660211356286040193441766649532044555061394833845553337792579131020"),
     Ay("11391058648720923807988142436733355540810929560298907319389650598553246451302"),
-    outx(outx), outy(outy), svec(svec), Ptable(Ptable)
+    outx(outx), outy(outy), svec(svec),
+    Px(Px), Py(Py), Ptable(Ptable),
+    Ptable_set_constraints(Ptable_set_constraints),
+    Ptable_fill_values(Ptable_fill_values)
   {
     AXSx = Ax;
     AXSy = Ay;
@@ -823,7 +933,9 @@ public:
     accoutx.allocate(this->pb, "accoutx");
     accouty.allocate(this->pb, "accouty");
 
-    scalarmuls.emplace_back(pb, accoutx, accouty, accinx, acciny, svec, Ptable, AXSx, AXSy);
+    scalarmuls.emplace_back(pb, accoutx, accouty, accinx, acciny, svec,
+        Px, Py, Ptable, Ptable_set_constraints, Ptable_fill_values,
+        AXSx, AXSy);
     adders.emplace_back(pb, outx, outy, accoutx, accouty, AXSx, -AXSy);
   }
 
@@ -848,7 +960,11 @@ public:
 // for a variable point P, and s given as a field element.  The addition
 // table is a variable array of length 2*numbits (where numbits is the
 // length of the FieldT size) such that Ptable[2*i] and Ptable[2*i+1]
-// are the (x,y) coordinates of 2^i * P + C.
+// are the (x,y) coordinates of 2^i * P + C.  Set Ptable_set_constraints
+// to true (exactly once in the event the same Ptable is reused in the
+// same circuit) if the Ptable is part of the private input.  Set
+// Ptable_fill_values to true exactly once per Ptable (again, in case it
+// it reused in the same circuit).
 template<typename FieldT>
 class ec_scalarmul_gadget : public gadget<FieldT> {
 private:
@@ -859,15 +975,24 @@ private:
 public:
   const pb_variable<FieldT> outx, outy;
   const pb_variable<FieldT> s;
+  const pb_variable<FieldT> Px, Py;
   const pb_variable_array<FieldT> Ptable;
+  bool Ptable_set_constraints, Ptable_fill_values;
 
   ec_scalarmul_gadget(protoboard<FieldT> &pb,
               const pb_variable<FieldT> &outx,
               const pb_variable<FieldT> &outy,
               const pb_variable<FieldT> &s,
-              const pb_variable_array<FieldT> &Ptable) :
+              const pb_variable<FieldT> &Px,
+              const pb_variable<FieldT> &Py,
+              const pb_variable_array<FieldT> &Ptable,
+              bool Ptable_set_constraints,
+              bool Ptable_fill_values) :
     gadget<FieldT>(pb, "ec_scalarmul_gadget"),
-    outx(outx), outy(outy), s(s), Ptable(Ptable)
+    outx(outx), outy(outy), s(s),
+    Px(Px), Py(Py), Ptable(Ptable),
+    Ptable_set_constraints(Ptable_set_constraints),
+    Ptable_fill_values(Ptable_fill_values)
   {
     // Allocate variables to protoboard
     // The strings (like "x") are only for debugging purposes
@@ -875,7 +1000,8 @@ public:
     size_t numbits = FieldT::num_bits;
     svec.allocate(this->pb, numbits, "svec");
     packers.emplace_back(this->pb, svec, s);
-    vecgadget.emplace_back(this->pb, outx, outy, svec, Ptable);
+    vecgadget.emplace_back(this->pb, outx, outy, svec,
+        Px, Py, Ptable, Ptable_set_constraints, Ptable_fill_values);
   }
 
   void generate_r1cs_constraints()
@@ -894,9 +1020,9 @@ public:
   // of length 2*numbits such that Ptable[2*i] and Ptable[2*i+1] are the
   // (x,y) coordinates of 2^i * P + C.
   static void compute_Ptable(protoboard<FieldT> &pb,
-                pb_variable_array<FieldT> &Ptable,
-                const FieldT &Px,
-                const FieldT &Py)
+                const pb_variable_array<FieldT> &Ptable,
+                const pb_variable<FieldT> &Px,
+                const pb_variable<FieldT> &Py)
   {
     const FieldT Cx(2);
     const FieldT Cy("4950745124018817972378217179409499695353526031437053848725554590521829916331");
@@ -904,8 +1030,8 @@ public:
     assert(Ptable.size() % 2 == 0);
     size_t numbits = Ptable.size() / 2;
 
-    FieldT twoiPx = Px;
-    FieldT twoiPy = Py;
+    FieldT twoiPx = pb.val(Px);
+    FieldT twoiPy = pb.val(Py);
 
     for (size_t i = 0; i < numbits; ++i) {
         // Invariant: (twoiPx, twoiPy) = 2^i * P

+ 20 - 7
varscalarmul.cpp

@@ -8,8 +8,15 @@
 using namespace libsnark;
 using namespace std;
 
-int main()
+int main(int argc, char **argv)
 {
+  // Should the Ptable be private or public (arg of "1" = public, "0" = private)
+  bool Ptable_is_private = true;
+
+  if (argc > 1 && atoi(argv[1]) > 0) {
+    Ptable_is_private = false;
+  }
+
   // Initialize the curve parameters
 
   default_r1cs_gg_ppzksnark_pp::init_public_params();
@@ -24,28 +31,32 @@ int main()
 
   protoboard<FieldT> pb;
   pb_variable<FieldT> outx, outy;
+  pb_variable<FieldT> Px, Py;
   pb_variable<FieldT> s;
   pb_variable_array<FieldT> Ptable;
 
-  // A variable base point P
-  const FieldT Px = FieldT("1095194319010475832867263440470707690447963461907735667341232728633587089702");
-  const FieldT Py = FieldT("9185463202887631101218413269806857706246311016297504828581985913021301344974");
 
   // Allocate variables
 
   size_t numbits = FieldT::num_bits;
   outx.allocate(pb, "outx");
   outy.allocate(pb, "outy");
+  Px.allocate(pb, "Px");
+  Py.allocate(pb, "Py");
   Ptable.allocate(pb, 2*numbits, "Ptable");
   s.allocate(pb, "s");
 
   // This sets up the protoboard variables so that the first n of them
   // represent the public input and the rest is private input
 
-  pb.set_input_sizes(2+2*numbits);
+  if (Ptable_is_private) {
+      pb.set_input_sizes(4);
+  } else {
+      pb.set_input_sizes(4+2*numbits);
+  }
 
   // Initialize the gadget
-  ec_scalarmul_gadget<FieldT> sm(pb, outx, outy, s, Ptable);
+  ec_scalarmul_gadget<FieldT> sm(pb, outx, outy, s, Px, Py, Ptable, Ptable_is_private, true);
   sm.generate_r1cs_constraints();
 
   const r1cs_constraint_system<FieldT> constraint_system = pb.get_constraint_system();
@@ -57,9 +68,11 @@ int main()
   cout << "Prover" << endl;
   
   pb.val(s) = FieldT::random_element();
+  // A variable base point P
+  pb.val(Px) = FieldT("1095194319010475832867263440470707690447963461907735667341232728633587089702");
+  pb.val(Py) = FieldT("9185463202887631101218413269806857706246311016297504828581985913021301344974");
   cout << "Computing " << pb.val(s) << "*G" << endl;
 
-  ec_scalarmul_gadget<FieldT>::compute_Ptable(pb, Ptable, Px, Py);
   sm.generate_r1cs_witness();
 
   const r1cs_gg_ppzksnark_proof<default_r1cs_gg_ppzksnark_pp> proof = r1cs_gg_ppzksnark_prover<default_r1cs_gg_ppzksnark_pp>(keypair.pk, pb.primary_input(), pb.auxiliary_input());