|
@@ -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
|