Просмотр исходного кода

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 6 лет назад
Родитель
Сommit
881144454e
2 измененных файлов с 165 добавлено и 26 удалено
  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());