|
@@ -182,70 +182,55 @@ public:
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-// Add nothing or the constant EC point P to the variable EC point
|
|
|
-// (inx,iny) to yield (outx,outy). The input point must not be the
|
|
|
-// point at infinity. The input bit do_add controls whether the
|
|
|
-// addition is done.
|
|
|
+// Add constant EC point P0 or the constant EC point P1 to the variable
|
|
|
+// EC point (inx,iny) to yield (outx,outy). The input point must not be
|
|
|
+// the point at infinity. The input bit which controls which addition
|
|
|
+// is done.
|
|
|
template<typename FieldT>
|
|
|
-class ec_conditional_constant_add_gadget : public gadget<FieldT> {
|
|
|
+class ec_2_constant_add_gadget : public gadget<FieldT> {
|
|
|
private:
|
|
|
pb_variable<FieldT> sumx, sumy;
|
|
|
- std::vector<ec_constant_add_gadget<FieldT> > adder;
|
|
|
+ pb_linear_combination<FieldT> addx, addy;
|
|
|
+ std::vector<ec_add_gadget<FieldT> > adder;
|
|
|
public:
|
|
|
const pb_variable<FieldT> outx, outy;
|
|
|
const pb_linear_combination<FieldT> inx, iny;
|
|
|
- const pb_variable<FieldT> do_add;
|
|
|
- const FieldT Px, Py;
|
|
|
+ const pb_variable<FieldT> which;
|
|
|
+ const FieldT P0x, P0y, P1x, P1y;
|
|
|
|
|
|
- ec_conditional_constant_add_gadget(protoboard<FieldT> &pb,
|
|
|
+ ec_2_constant_add_gadget(protoboard<FieldT> &pb,
|
|
|
const pb_variable<FieldT> &outx,
|
|
|
const pb_variable<FieldT> &outy,
|
|
|
const pb_linear_combination<FieldT> &inx,
|
|
|
const pb_linear_combination<FieldT> &iny,
|
|
|
- const pb_variable<FieldT> &do_add,
|
|
|
- const FieldT &Px, const FieldT &Py) :
|
|
|
- gadget<FieldT>(pb, "ec_conditional_constant_add_gadget"),
|
|
|
- outx(outx), outy(outy), inx(inx), iny(iny), do_add(do_add),
|
|
|
- Px(Px), Py(Py)
|
|
|
+ const pb_variable<FieldT> &which,
|
|
|
+ const FieldT &P0x, const FieldT &P0y,
|
|
|
+ const FieldT &P1x, const FieldT &P1y) :
|
|
|
+ gadget<FieldT>(pb, "ec_2_constant_add_gadget"),
|
|
|
+ outx(outx), outy(outy), inx(inx), iny(iny), which(which),
|
|
|
+ P0x(P0x), P0y(P0y), P1x(P1x), P1y(P1y)
|
|
|
{
|
|
|
// Allocate variables to protoboard
|
|
|
- // The strings (like "x") are only for debugging purposes
|
|
|
|
|
|
- sumx.allocate(this->pb, "sumx");
|
|
|
- sumy.allocate(this->pb, "sumy");
|
|
|
- adder.emplace_back(this->pb, sumx, sumy, inx, iny, Px, Py);
|
|
|
+ addx.assign(pb, which * (P1x-P0x) + P0x);
|
|
|
+ addy.assign(pb, which * (P1y-P0y) + P0y);
|
|
|
+ adder.emplace_back(this->pb, outx, outy, inx, iny, addx, addy);
|
|
|
}
|
|
|
|
|
|
void generate_r1cs_constraints()
|
|
|
{
|
|
|
- // Strategy: we always do the addition, but if do_add = 0, we throw
|
|
|
- // it away later.
|
|
|
-
|
|
|
adder[0].generate_r1cs_constraints();
|
|
|
-
|
|
|
- // Now we want to conditionally move the sum. We want that
|
|
|
- // outx = do_add ? sumx : inx
|
|
|
- // outy = do_add ? sumy : iny
|
|
|
-
|
|
|
- // so we compute
|
|
|
- // outx = inx + (sumx - inx) * do_add
|
|
|
- // outy = iny + (sumy - iny) * do_add
|
|
|
-
|
|
|
- this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(sumx - inx, do_add, outx - inx));
|
|
|
- this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(sumy - iny, do_add, outy - iny));
|
|
|
-
|
|
|
}
|
|
|
|
|
|
void generate_r1cs_witness()
|
|
|
{
|
|
|
+ addx.evaluate(this->pb);
|
|
|
+ addy.evaluate(this->pb);
|
|
|
adder[0].generate_r1cs_witness();
|
|
|
-
|
|
|
- bool move = this->pb.val(do_add) != FieldT(0);
|
|
|
- this->pb.val(outx) = move ? this->pb.val(sumx) : this->pb.lc_val(inx);
|
|
|
- this->pb.val(outy) = move ? this->pb.val(sumy) : this->pb.lc_val(iny);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+#if 0
|
|
|
// Add nothing, or one of the constant EC points P1, P2, or P3 to the EC
|
|
|
// point (inx,iny) to yield (outx,outy). The input point must not be
|
|
|
// the point at infinity. The two input bits add1 and add2 control what
|
|
@@ -333,155 +318,125 @@ public:
|
|
|
this->pb.val(outy) = (a1 || a2) ? this->pb.val(sumy) : this->pb.lc_val(iny);
|
|
|
}
|
|
|
};
|
|
|
-
|
|
|
-// Compute a*P as (outx, outy) for a given constant point P, given a
|
|
|
-// as a bit vector. The _caller_ is responsible for proving that the
|
|
|
-// elements of avec are bits.
|
|
|
+#endif
|
|
|
+
|
|
|
+// Compute A + s*P as (outx, outy) for an accumulator A, a given
|
|
|
+// constant point P, and s given as a bit vector. The _caller_ is
|
|
|
+// responsible for proving that the elements of svec are bits. The
|
|
|
+// (constant) accumulator excess (AXS) will be updated; when all the
|
|
|
+// computations are complete, AXS should be subtracted from the
|
|
|
+// accumulator A.
|
|
|
template<typename FieldT>
|
|
|
class ec_constant_scalarmul_vec_gadget : public gadget<FieldT> {
|
|
|
private:
|
|
|
- FieldT Cx, Cy, CPx, CPy;
|
|
|
+ FieldT Cx, Cy;
|
|
|
pb_variable_array<FieldT> accumx, accumy;
|
|
|
- std::vector<ec_add_P123_gadget<FieldT> > conddoubleadders;
|
|
|
- std::vector<ec_conditional_constant_add_gadget<FieldT> > condsingleadders;
|
|
|
- std::vector<ec_constant_add_gadget<FieldT> > singleadders;
|
|
|
+ std::vector<ec_2_constant_add_gadget<FieldT> > twoadders;
|
|
|
public:
|
|
|
const pb_variable<FieldT> outx, outy;
|
|
|
- const pb_variable_array<FieldT> avec;
|
|
|
+ const pb_variable<FieldT> Ax, Ay;
|
|
|
+ const pb_variable_array<FieldT> svec;
|
|
|
const FieldT Px, Py;
|
|
|
|
|
|
// Strategy: We compute (as compile-time constants) (powers of 2)
|
|
|
- // times P, and then conditionally add them into an accumulator.
|
|
|
- // Because our adder cannot handle the point at infinity O, we start
|
|
|
- // the accumulator with a value of C, whose discrete log with respect
|
|
|
- // to P should be unknown, so that we won't encounter O along the way.
|
|
|
- // (Also, a should not be 0 or the group order.) We actually start
|
|
|
- // the accumulator with either C or C+P depending on avec[0], so we
|
|
|
- // get the first conditional add "for free". Then we use the
|
|
|
- // ec_add_P123_gadget to do the conditional adds two at a time (at a
|
|
|
- // cost of 6 constraints per pair, as opposed to 5 for a single
|
|
|
- // conditional add). If the length of avec is even, then there will
|
|
|
- // be one left over, and we do a single conditional add for that one.
|
|
|
- // Finally, we add the public point -C.
|
|
|
+ // times P. Based on each bit of s, we add one of the constant points
|
|
|
+ // C or (2^i * P) + C to the accumulator, and regardless of s, add C
|
|
|
+ // to the excess.
|
|
|
|
|
|
ec_constant_scalarmul_vec_gadget(protoboard<FieldT> &pb,
|
|
|
const pb_variable<FieldT> &outx,
|
|
|
const pb_variable<FieldT> &outy,
|
|
|
- const pb_variable_array<FieldT> &avec,
|
|
|
- const FieldT &Px, const FieldT &Py) :
|
|
|
+ const pb_variable<FieldT> &Ax,
|
|
|
+ const pb_variable<FieldT> &Ay,
|
|
|
+ const pb_variable_array<FieldT> &svec,
|
|
|
+ const FieldT &Px, const FieldT &Py,
|
|
|
+ FieldT &AXSx, FieldT &AXSy) :
|
|
|
gadget<FieldT>(pb, "ec_constant_scalarmul_vec_gadget"),
|
|
|
// Precomputed coordinates of C
|
|
|
Cx(2),
|
|
|
Cy("4950745124018817972378217179409499695353526031437053848725554590521829916331"),
|
|
|
- outx(outx), outy(outy), avec(avec), Px(Px), Py(Py)
|
|
|
+ outx(outx), outy(outy), Ax(Ax), Ay(Ay), svec(svec), Px(Px), Py(Py)
|
|
|
{
|
|
|
- size_t numbits = avec.size();
|
|
|
- accumx.allocate(this->pb, numbits/2+1, "accumx");
|
|
|
- accumy.allocate(this->pb, numbits/2+1, "accumy");
|
|
|
-
|
|
|
- ec_add_points(CPx, CPy, Cx, Cy, Px, Py);
|
|
|
+ size_t numbits = svec.size();
|
|
|
+ accumx.allocate(this->pb, numbits-1, "accumx");
|
|
|
+ accumy.allocate(this->pb, numbits-1, "accumy");
|
|
|
|
|
|
- FieldT twoiPx, twoiPy, twoi1Px, twoi1Py, twoi3Px, twoi3Py;
|
|
|
- size_t i = 1;
|
|
|
-
|
|
|
- ec_double_point(twoiPx, twoiPy, Px, Py);
|
|
|
+ FieldT twoiPx = Px, twoiPy = Py;
|
|
|
+ size_t i = 0;
|
|
|
|
|
|
while(i < numbits) {
|
|
|
- // Invariants: i is odd, and twoiP = 2^i * P
|
|
|
- // Compute twoi1P = 2^{i+1} * P = 2 * twoiP and
|
|
|
- // twoi3P = 2^i * 3 * P = 3 * twoiP
|
|
|
- ec_double_point(twoi1Px, twoi1Py, twoiPx, twoiPy);
|
|
|
- ec_add_points(twoi3Px, twoi3Py, twoi1Px, twoi1Py, twoiPx, twoiPy);
|
|
|
-
|
|
|
- if (i == numbits-1) {
|
|
|
- // There's only one bit of avec left; use a single conditional
|
|
|
- // add.
|
|
|
- condsingleadders.emplace_back(this->pb,
|
|
|
- accumx[(i+1)/2], accumy[(i+1)/2],
|
|
|
- accumx[(i-1)/2], accumy[(i-1)/2],
|
|
|
- avec[i],
|
|
|
- twoiPx, twoiPy);
|
|
|
- } else {
|
|
|
- conddoubleadders.emplace_back(this->pb,
|
|
|
- accumx[(i+1)/2], accumy[(i+1)/2],
|
|
|
- accumx[(i-1)/2], accumy[(i-1)/2],
|
|
|
- avec[i], avec[i+1],
|
|
|
- twoiPx, twoiPy, twoi1Px, twoi1Py, twoi3Px, twoi3Py);
|
|
|
- }
|
|
|
-
|
|
|
- ec_double_point(twoiPx, twoiPy, twoi1Px, twoi1Py);
|
|
|
- i += 2;
|
|
|
+ // Invariant: twoiP = 2^i * P
|
|
|
+ FieldT twoiPCx, twoiPCy;
|
|
|
+ ec_add_points(twoiPCx, twoiPCy, twoiPx, twoiPy, Cx, Cy);
|
|
|
+
|
|
|
+ twoadders.emplace_back(this->pb,
|
|
|
+ (i == numbits-1 ? outx : accumx[i]),
|
|
|
+ (i == numbits-1 ? outy : accumy[i]),
|
|
|
+ (i == 0 ? Ax : accumx[i-1]),
|
|
|
+ (i == 0 ? Ay : accumy[i-1]),
|
|
|
+ svec[i], Cx, Cy, twoiPCx, twoiPCy);
|
|
|
+
|
|
|
+ FieldT newtwoiPx, newtwoiPy, newAXSx, newAXSy;
|
|
|
+ ec_double_point(newtwoiPx, newtwoiPy, twoiPx, twoiPy);
|
|
|
+ twoiPx = newtwoiPx;
|
|
|
+ twoiPy = newtwoiPy;
|
|
|
+ i += 1;
|
|
|
+ ec_add_points(newAXSx, newAXSy, AXSx, AXSy, Cx, Cy);
|
|
|
+ AXSx = newAXSx;
|
|
|
+ AXSy = newAXSy;
|
|
|
}
|
|
|
-
|
|
|
- // If numbits is even, the output so far is in accum[(numbits)/2].
|
|
|
- // If numbits is odd, it is in accum[(numbits-1)/2]. So in either
|
|
|
- // case, it is in accum[numbits/2].
|
|
|
- singleadders.emplace_back(this->pb,
|
|
|
- outx, outy, accumx[numbits/2], accumy[numbits/2],
|
|
|
- Cx, -Cy);
|
|
|
}
|
|
|
|
|
|
void generate_r1cs_constraints()
|
|
|
{
|
|
|
- this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(Cx + (CPx-Cx) * avec[0], 1, accumx[0]));
|
|
|
- this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(Cy + (CPy-Cy) * avec[0], 1, accumy[0]));
|
|
|
-
|
|
|
- for (auto&& gadget : conddoubleadders) {
|
|
|
- gadget.generate_r1cs_constraints();
|
|
|
- }
|
|
|
- for (auto&& gadget : condsingleadders) {
|
|
|
- gadget.generate_r1cs_constraints();
|
|
|
- }
|
|
|
- for (auto&& gadget : singleadders) {
|
|
|
+ for (auto&& gadget : twoadders) {
|
|
|
gadget.generate_r1cs_constraints();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void generate_r1cs_witness()
|
|
|
{
|
|
|
- this->pb.val(accumx[0]) = Cx + (CPx-Cx) * this->pb.val(avec[0]);
|
|
|
- this->pb.val(accumy[0]) = Cy + (CPy-Cy) * this->pb.val(avec[0]);
|
|
|
-
|
|
|
- for (auto&& gadget : conddoubleadders) {
|
|
|
- gadget.generate_r1cs_witness();
|
|
|
- }
|
|
|
- for (auto&& gadget : condsingleadders) {
|
|
|
- gadget.generate_r1cs_witness();
|
|
|
- }
|
|
|
- for (auto&& gadget : singleadders) {
|
|
|
+ for (auto&& gadget : twoadders) {
|
|
|
gadget.generate_r1cs_witness();
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-// Compute a*P as (outx, outy) for a given constant point P, given a
|
|
|
-// as a field element.
|
|
|
+// Compute A + s*P as (outx, outy) for an accumulator A, a given
|
|
|
+// constant point P, and s given as a field element. The (constant)
|
|
|
+// accumulator excess (AXS) will be updated; when all the computations
|
|
|
+// are complete, AXS should be subtracted from the accumulator A.
|
|
|
template<typename FieldT>
|
|
|
class ec_constant_scalarmul_gadget : public gadget<FieldT> {
|
|
|
private:
|
|
|
- pb_variable_array<FieldT> avec;
|
|
|
+ pb_variable_array<FieldT> svec;
|
|
|
std::vector<packing_gadget<FieldT> > packers;
|
|
|
std::vector<ec_constant_scalarmul_vec_gadget<FieldT> > vecgadget;
|
|
|
|
|
|
public:
|
|
|
- const pb_variable<FieldT> outx, outy, a;
|
|
|
+ const pb_variable<FieldT> outx, outy;
|
|
|
+ const pb_variable<FieldT> Ax, Ay;
|
|
|
+ const pb_variable<FieldT> s;
|
|
|
const FieldT Px, Py;
|
|
|
|
|
|
ec_constant_scalarmul_gadget(protoboard<FieldT> &pb,
|
|
|
const pb_variable<FieldT> &outx,
|
|
|
const pb_variable<FieldT> &outy,
|
|
|
- const pb_variable<FieldT> &a,
|
|
|
- const FieldT &Px, const FieldT &Py) :
|
|
|
+ const pb_variable<FieldT> &Ax,
|
|
|
+ const pb_variable<FieldT> &Ay,
|
|
|
+ const pb_variable<FieldT> &s,
|
|
|
+ const FieldT &Px, const FieldT &Py,
|
|
|
+ FieldT &AXSx, FieldT &AXSy) :
|
|
|
gadget<FieldT>(pb, "ec_constant_scalarmul_gadget"),
|
|
|
- outx(outx), outy(outy), a(a), Px(Px), Py(Py)
|
|
|
+ outx(outx), outy(outy), Ax(Ax), Ay(Ay), s(s), Px(Px), Py(Py)
|
|
|
{
|
|
|
// Allocate variables to protoboard
|
|
|
// The strings (like "x") are only for debugging purposes
|
|
|
|
|
|
size_t numbits = FieldT::num_bits;
|
|
|
- avec.allocate(this->pb, numbits, "avec");
|
|
|
- packers.emplace_back(this->pb, avec, a);
|
|
|
- vecgadget.emplace_back(this->pb, outx, outy, avec, Px, Py);
|
|
|
+ svec.allocate(this->pb, numbits, "svec");
|
|
|
+ packers.emplace_back(this->pb, svec, s);
|
|
|
+ vecgadget.emplace_back(this->pb, outx, outy, Ax, Ay, svec, Px, Py, AXSx, AXSy);
|
|
|
}
|
|
|
|
|
|
void generate_r1cs_constraints()
|
|
@@ -501,10 +456,10 @@ public:
|
|
|
template<typename FieldT>
|
|
|
class ec_pedersen_gadget : public gadget<FieldT> {
|
|
|
private:
|
|
|
- pb_variable<FieldT> aoutx, aouty, boutx, bouty;
|
|
|
+ pb_variable<FieldT> accinx, acciny, accmidx, accmidy, accoutx, accouty;
|
|
|
std::vector<ec_constant_scalarmul_gadget<FieldT> > mulgadgets;
|
|
|
- std::vector<ec_add_gadget<FieldT> > addgadget;
|
|
|
- const FieldT Gx, Gy, Hx, Hy;
|
|
|
+ std::vector<ec_constant_add_gadget<FieldT> > addgadget;
|
|
|
+ const FieldT Gx, Gy, Hx, Hy, Ax, Ay;
|
|
|
|
|
|
public:
|
|
|
const pb_variable<FieldT> outx, outy, a, b;
|
|
@@ -516,26 +471,41 @@ public:
|
|
|
const pb_variable<FieldT> &b) :
|
|
|
gadget<FieldT>(pb, "ec_pedersen_gadget"),
|
|
|
outx(outx), outy(outy), a(a), b(b),
|
|
|
- // Precomputed coordinates of G and H
|
|
|
+ // Precomputed coordinates of G, H, and A
|
|
|
Gx(0),
|
|
|
Gy("11977228949870389393715360594190192321220966033310912010610740966317727761886"),
|
|
|
Hx(1),
|
|
|
- Hy("21803877843449984883423225223478944275188924769286999517937427649571474907279")
|
|
|
+ Hy("21803877843449984883423225223478944275188924769286999517937427649571474907279"),
|
|
|
+ Ax("7536839002660211356286040193441766649532044555061394833845553337792579131020"),
|
|
|
+ Ay("11391058648720923807988142436733355540810929560298907319389650598553246451302")
|
|
|
{
|
|
|
// Allocate variables to protoboard
|
|
|
// The strings (like "x") are only for debugging purposes
|
|
|
|
|
|
- aoutx.allocate(this->pb, "aoutx");
|
|
|
- aouty.allocate(this->pb, "aouty");
|
|
|
- boutx.allocate(this->pb, "boutx");
|
|
|
- bouty.allocate(this->pb, "bouty");
|
|
|
- mulgadgets.emplace_back(this->pb, aoutx, aouty, a, Gx, Gy);
|
|
|
- mulgadgets.emplace_back(this->pb, boutx, bouty, b, Hx, Hy);
|
|
|
- addgadget.emplace_back(this->pb, outx, outy, aoutx, aouty, boutx, bouty);
|
|
|
+ accinx.allocate(this->pb, "accinx");
|
|
|
+ acciny.allocate(this->pb, "acciny");
|
|
|
+ accmidx.allocate(this->pb, "accmidx");
|
|
|
+ accmidy.allocate(this->pb, "accmidy");
|
|
|
+ accoutx.allocate(this->pb, "accoutx");
|
|
|
+ accouty.allocate(this->pb, "accouty");
|
|
|
+
|
|
|
+ // Initialize the accumulator
|
|
|
+ FieldT AXSx = Ax;
|
|
|
+ FieldT AXSy = Ay;
|
|
|
+ this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(accinx, 1, Ax));
|
|
|
+ this->pb.add_r1cs_constraint(r1cs_constraint<FieldT>(acciny, 1, Ay));
|
|
|
+
|
|
|
+ // Initialize the gadgets
|
|
|
+ mulgadgets.emplace_back(this->pb, accmidx, accmidy, accinx, acciny, a, Gx, Gy, AXSx, AXSy);
|
|
|
+ mulgadgets.emplace_back(this->pb, accoutx, accouty, accmidx, accmidy, b, Hx, Hy, AXSx, AXSy);
|
|
|
+ // Subtract the accumulator excess to get the result
|
|
|
+ addgadget.emplace_back(this->pb, outx, outy, accoutx, accouty, AXSx, -AXSy);
|
|
|
}
|
|
|
|
|
|
void generate_r1cs_constraints()
|
|
|
{
|
|
|
+ this->pb.val(accinx) = Ax;
|
|
|
+ this->pb.val(acciny) = Ay;
|
|
|
mulgadgets[0].generate_r1cs_constraints();
|
|
|
mulgadgets[1].generate_r1cs_constraints();
|
|
|
addgadget[0].generate_r1cs_constraints();
|