Document the new DPF surfaces in one command set, and test the field, half-tree, and multipoint edges.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
0d8a5a8131
commit
0dff6df8ed
250 changed files with 12199 additions and 1981 deletions
|
|
@ -103,6 +103,7 @@ TEST(Beaver, ProductTwo)
|
|||
Counter rng;
|
||||
s.sample(rng);
|
||||
EXPECT_EQ(rng.draws, 5); // two input blinds * 2 draws + one product share
|
||||
EXPECT_EQ(s.preprocessing_count(), 3u);
|
||||
EXPECT_EQ(s.monomial_count(), 1u);
|
||||
EXPECT_EQ(s.round_of(z), 1);
|
||||
EXPECT_EQ(s.monomial({{x, 1u}, {y, 1u}}).open(),
|
||||
|
|
@ -150,7 +151,8 @@ TEST(Beaver, MulSquareIsOneRound)
|
|||
s.sample(rng);
|
||||
EXPECT_EQ(s.round_of(z), 1);
|
||||
EXPECT_EQ(s.wire_count(), 3u);
|
||||
// λx², λa λx, λa λx². One blind for both x factors.
|
||||
// λx², λa λx, λa λx². One blind for both x factors. The output is unpinned.
|
||||
EXPECT_EQ(s.preprocessing_count(), 5u);
|
||||
EXPECT_EQ(s.monomial_count(), 3u);
|
||||
EXPECT_EQ(rng.draws, 7); // two input blinds * 2 + three product shares
|
||||
EXPECT_EQ(s.monomial({{x, 2u}}).open(),
|
||||
|
|
@ -194,9 +196,10 @@ TEST(Beaver, DotAggregatesCrossTerm)
|
|||
auto z = s.dot({x0, x1, x2}, {y0, y1, y2});
|
||||
Counter rng;
|
||||
s.sample(rng);
|
||||
EXPECT_EQ(s.monomial_count(), 0u);
|
||||
EXPECT_EQ(s.monomial_count(), 1u);
|
||||
EXPECT_EQ(s.round_of(z), 1);
|
||||
// 6 input blinds, two draws each, plus one aggregated cross draw
|
||||
// Six input blinds and one fused cross. Three separate products would be three crosses.
|
||||
EXPECT_EQ(s.preprocessing_count(), 7u);
|
||||
EXPECT_EQ(rng.draws, 13);
|
||||
auto cross = s.lambda(x0).open() * s.lambda(y0).open()
|
||||
+ s.lambda(x1).open() * s.lambda(y1).open()
|
||||
|
|
@ -220,13 +223,427 @@ TEST(Beaver, DotReusesAPair)
|
|||
auto z = s.dot({a, a}, {b, b});
|
||||
Counter rng;
|
||||
s.sample(rng);
|
||||
EXPECT_EQ(s.monomial_count(), 0u);
|
||||
EXPECT_EQ(s.monomial_count(), 1u);
|
||||
EXPECT_EQ(s.preprocessing_count(), 3u);
|
||||
EXPECT_EQ(rng.draws, 5);
|
||||
EXPECT_EQ(s.dot_cross(z).open(),
|
||||
u64{2} * s.lambda(a).open() * s.lambda(b).open());
|
||||
s.bind(a, u64{3}, rng);
|
||||
s.bind(b, u64{4}, rng);
|
||||
s.evaluate();
|
||||
EXPECT_EQ(s.open(z), 24u);
|
||||
}
|
||||
|
||||
TEST(Beaver, InnerProductMatchesTheSumAndIsOneCross)
|
||||
{
|
||||
const u64 x[] = {0, 2, 5};
|
||||
const u64 y[] = {4, 0, 7};
|
||||
u64 expect = 0;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
expect += x[i] * y[i];
|
||||
|
||||
session64 fused;
|
||||
std::vector<wire64> fx;
|
||||
std::vector<wire64> fy;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
fx.push_back(fused.input());
|
||||
fy.push_back(fused.input());
|
||||
}
|
||||
auto dot = fused.dot(fx, fy);
|
||||
auto as_sum = fused(fx[0] * fy[0] + fx[1] * fy[1] + fx[2] * fy[2]);
|
||||
EXPECT_EQ(fused.round_of(dot), 1);
|
||||
EXPECT_EQ(fused.round_of(as_sum), 1);
|
||||
EXPECT_EQ(fused.preprocessing_count(), 7u);
|
||||
EXPECT_EQ(fused.monomial_count(), 1u);
|
||||
|
||||
session64 separate;
|
||||
std::vector<wire64> sx;
|
||||
std::vector<wire64> sy;
|
||||
std::vector<wire64> prods;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
sx.push_back(separate.input());
|
||||
sy.push_back(separate.input());
|
||||
}
|
||||
for (int i = 0; i < 3; ++i)
|
||||
prods.push_back(separate(sx[i] * sy[i]));
|
||||
auto summed = separate(prods[0] + prods[1] + prods[2]);
|
||||
EXPECT_EQ(separate.preprocessing_count(), 9u);
|
||||
EXPECT_LT(fused.preprocessing_count(), separate.preprocessing_count());
|
||||
|
||||
Counter rf;
|
||||
Counter rs;
|
||||
fused.sample(rf);
|
||||
separate.sample(rs);
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
fused.bind(fx[i], x[i], rf);
|
||||
fused.bind(fy[i], y[i], rf);
|
||||
separate.bind(sx[i], x[i], rs);
|
||||
separate.bind(sy[i], y[i], rs);
|
||||
}
|
||||
fused.evaluate();
|
||||
separate.evaluate();
|
||||
EXPECT_EQ(fused.open(dot), expect);
|
||||
EXPECT_EQ(fused.open(as_sum), expect);
|
||||
EXPECT_EQ(separate.open(summed), expect);
|
||||
u64 cross = 0;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
cross += fused.lambda(fx[i]).open() * fused.lambda(fy[i]).open();
|
||||
EXPECT_EQ(fused.dot_cross(dot).open(), cross);
|
||||
|
||||
const auto monos = fused.monomial_count();
|
||||
const auto prep = fused.preprocessing_count();
|
||||
auto again = fused.dot(fx, fy);
|
||||
EXPECT_EQ(fused.monomial_count(), monos);
|
||||
EXPECT_EQ(fused.preprocessing_count(), prep);
|
||||
fused.sample(rf);
|
||||
fused.evaluate();
|
||||
EXPECT_EQ(fused.open(again), expect);
|
||||
}
|
||||
|
||||
TEST(Beaver, InnerProductPeelsASharedFactor)
|
||||
{
|
||||
session64 s;
|
||||
auto a = s.input();
|
||||
auto x = s.input();
|
||||
auto y = s.input();
|
||||
auto z = s.input();
|
||||
auto dotted = s.dot({a, a, a}, {x, y, z});
|
||||
EXPECT_EQ(s.round_of(dotted), 2);
|
||||
EXPECT_EQ(s.preprocessing_count(), 3u);
|
||||
|
||||
session64 hand;
|
||||
auto ha = hand.input();
|
||||
auto hx = hand.input();
|
||||
auto hy = hand.input();
|
||||
auto hz = hand.input();
|
||||
auto written = hand(ha * (hx + hy + hz));
|
||||
EXPECT_EQ(hand.round_of(written), 2);
|
||||
EXPECT_EQ(hand.preprocessing_count(), s.preprocessing_count());
|
||||
EXPECT_LT(s.preprocessing_count(), 7u);
|
||||
|
||||
Counter rng;
|
||||
s.sample(rng);
|
||||
s.bind(a, u64{3}, rng);
|
||||
s.bind(x, u64{4}, rng);
|
||||
s.bind(y, u64{5}, rng);
|
||||
s.bind(z, u64{6}, rng);
|
||||
s.evaluate();
|
||||
EXPECT_EQ(s.open(dotted), 3u * (4u + 5u + 6u));
|
||||
}
|
||||
|
||||
TEST(Beaver, InnerProductOfSquaresIsOneShare)
|
||||
{
|
||||
auto got = run_poly(2, [](session64 & s, const std::vector<wire64> & in) {
|
||||
return s.dot({in[0], in[1]}, {in[0], in[1]});
|
||||
}, {6, 7});
|
||||
EXPECT_EQ(got.value, 36u + 49u);
|
||||
EXPECT_EQ(got.rounds, 1);
|
||||
EXPECT_EQ(got.prep, 3u);
|
||||
EXPECT_EQ(got.monos, 1u);
|
||||
}
|
||||
|
||||
TEST(Beaver, ScaledInnerProductUsesThePolynomialSchedule)
|
||||
{
|
||||
session64 automatic;
|
||||
auto sgn = automatic.input();
|
||||
auto x0 = automatic.input();
|
||||
auto x1 = automatic.input();
|
||||
auto y0 = automatic.input();
|
||||
auto y1 = automatic.input();
|
||||
auto y = automatic(sgn * dpf::beavers::dot({x0, x1}, {y0, y1}));
|
||||
|
||||
session64 hand;
|
||||
auto hs = hand.input();
|
||||
auto hx0 = hand.input();
|
||||
auto hx1 = hand.input();
|
||||
auto hy0 = hand.input();
|
||||
auto hy1 = hand.input();
|
||||
auto inner = hand.dot({hx0, hx1}, {hy0, hy1});
|
||||
auto outer = hand(hs * inner);
|
||||
EXPECT_EQ(automatic.preprocessing_count(), hand.preprocessing_count());
|
||||
EXPECT_EQ(automatic.round_of(y), hand.round_of(outer));
|
||||
EXPECT_LT(automatic.preprocessing_count(), 9u);
|
||||
|
||||
const u64 sv = 3, a = 2, b = 5, c = 4, d = 6;
|
||||
Counter ra;
|
||||
Counter rh;
|
||||
automatic.sample(ra);
|
||||
hand.sample(rh);
|
||||
automatic.bind(sgn, sv, ra);
|
||||
automatic.bind(x0, a, ra);
|
||||
automatic.bind(x1, b, ra);
|
||||
automatic.bind(y0, c, ra);
|
||||
automatic.bind(y1, d, ra);
|
||||
hand.bind(hs, sv, rh);
|
||||
hand.bind(hx0, a, rh);
|
||||
hand.bind(hx1, b, rh);
|
||||
hand.bind(hy0, c, rh);
|
||||
hand.bind(hy1, d, rh);
|
||||
automatic.evaluate();
|
||||
hand.evaluate();
|
||||
const u64 expect = sv * (a * c + b * d);
|
||||
EXPECT_EQ(automatic.open(y), expect);
|
||||
EXPECT_EQ(hand.open(outer), expect);
|
||||
|
||||
session64 scaled;
|
||||
auto sx0 = scaled.input();
|
||||
auto sx1 = scaled.input();
|
||||
auto sy0 = scaled.input();
|
||||
auto sy1 = scaled.input();
|
||||
auto plain = scaled.dot({sx0, sx1}, {sy0, sy1});
|
||||
auto triple = scaled(u64{3} * dpf::beavers::dot({sx0, sx1}, {sy0, sy1}));
|
||||
EXPECT_EQ(scaled.preprocessing_count(), 5u);
|
||||
EXPECT_EQ(scaled.monomial_count(), 1u);
|
||||
(void)plain;
|
||||
Counter rs;
|
||||
scaled.sample(rs);
|
||||
scaled.bind(sx0, a, rs);
|
||||
scaled.bind(sx1, b, rs);
|
||||
scaled.bind(sy0, c, rs);
|
||||
scaled.bind(sy1, d, rs);
|
||||
scaled.evaluate();
|
||||
EXPECT_EQ(scaled.open(triple), 3u * (a * c + b * d));
|
||||
EXPECT_EQ(scaled.open(plain), a * c + b * d);
|
||||
}
|
||||
|
||||
TEST(Beaver, InnerProductsOnModintAndXor)
|
||||
{
|
||||
using M = dpf::modint<17>;
|
||||
dpf::beavers::session<M> ms;
|
||||
auto mx0 = ms.input();
|
||||
auto mx1 = ms.input();
|
||||
auto my0 = ms.input();
|
||||
auto my1 = ms.input();
|
||||
auto my = ms.dot({mx0, mx1}, {my0, my1});
|
||||
EXPECT_EQ(ms.preprocessing_count(), 5u);
|
||||
EXPECT_EQ(ms.monomial_count(), 1u);
|
||||
Seq<M> mr;
|
||||
ms.sample(mr);
|
||||
ms.bind(mx0, M{10}, mr);
|
||||
ms.bind(mx1, M{9}, mr);
|
||||
ms.bind(my0, M{8}, mr);
|
||||
ms.bind(my1, M{7}, mr);
|
||||
ms.evaluate();
|
||||
EXPECT_EQ(ms.open(my), M{10} * M{8} + M{9} * M{7});
|
||||
|
||||
using W = dpf::xor_wrapper<std::uint32_t>;
|
||||
dpf::beavers::session<W> xs;
|
||||
auto xx = xs.input();
|
||||
auto xy = xs.input();
|
||||
auto yx = xs.input();
|
||||
auto yy = xs.input();
|
||||
auto dot = xs.dot({xx, xy}, {yx, yy});
|
||||
EXPECT_EQ(xs.preprocessing_count(), 5u);
|
||||
XorSeq<W> xr;
|
||||
xs.sample(xr);
|
||||
const W a{0b11110000u};
|
||||
const W b{0b11001100u};
|
||||
const W c{0b10101010u};
|
||||
const W d{0b11111111u};
|
||||
xs.bind(xx, a, xr);
|
||||
xs.bind(xy, b, xr);
|
||||
xs.bind(yx, c, xr);
|
||||
xs.bind(yy, d, xr);
|
||||
xs.evaluate();
|
||||
EXPECT_EQ(xs.open(dot), a * c + b * d);
|
||||
}
|
||||
|
||||
TEST(Beaver, InnerProductOfEarlierProductsIsALaterRound)
|
||||
{
|
||||
session64 s;
|
||||
auto a = s.input();
|
||||
auto b = s.input();
|
||||
auto c = s.input();
|
||||
auto d = s.input();
|
||||
auto ab = s(a * b);
|
||||
auto cd = s(c * d);
|
||||
s.pin(ab);
|
||||
s.pin(cd);
|
||||
Counter rng;
|
||||
s.sample(rng);
|
||||
auto lab = s.lambda(ab);
|
||||
s.bind(a, u64{2}, rng);
|
||||
s.bind(b, u64{3}, rng);
|
||||
s.bind(c, u64{4}, rng);
|
||||
s.bind(d, u64{5}, rng);
|
||||
auto both = s.dot({ab, cd}, {ab, cd});
|
||||
EXPECT_EQ(s.round_of(both), 2);
|
||||
const auto draws = rng.draws;
|
||||
s.sample(rng);
|
||||
EXPECT_EQ(s.lambda(ab), lab);
|
||||
EXPECT_EQ(rng.draws, draws + 1);
|
||||
s.evaluate();
|
||||
EXPECT_EQ(s.open(ab), 6u);
|
||||
EXPECT_EQ(s.open(cd), 20u);
|
||||
EXPECT_EQ(s.open(both), 6u * 6u + 20u * 20u);
|
||||
}
|
||||
|
||||
TEST(Beaver, TriplesUseNoExtraShares)
|
||||
{
|
||||
{
|
||||
session64 s;
|
||||
auto a = s.input();
|
||||
auto b = s.input();
|
||||
auto ab = s(a * b);
|
||||
s.pin(ab);
|
||||
EXPECT_EQ(s.preprocessing_count(), 4u);
|
||||
Counter rng;
|
||||
s.sample(rng);
|
||||
s.bind(a, u64{6}, rng);
|
||||
s.bind(b, u64{7}, rng);
|
||||
s.evaluate();
|
||||
EXPECT_EQ(s.open(ab), 42u);
|
||||
EXPECT_EQ(s.monomial({{a, 1u}, {b, 1u}}).open(),
|
||||
s.lambda(a).open() * s.lambda(b).open());
|
||||
}
|
||||
{
|
||||
session64 s;
|
||||
auto a = s.input();
|
||||
auto b = s.input();
|
||||
auto c = s.input();
|
||||
auto abc = s(a * b * c);
|
||||
s.pin(abc);
|
||||
EXPECT_EQ(s.preprocessing_count(), 8u);
|
||||
Counter rng;
|
||||
s.sample(rng);
|
||||
s.bind(a, u64{2}, rng);
|
||||
s.bind(b, u64{3}, rng);
|
||||
s.bind(c, u64{5}, rng);
|
||||
s.evaluate();
|
||||
EXPECT_EQ(s.open(abc), 30u);
|
||||
EXPECT_EQ(s.monomial({{a, 1u}, {b, 1u}}).open(),
|
||||
s.lambda(a).open() * s.lambda(b).open());
|
||||
EXPECT_EQ(s.monomial({{a, 1u}, {b, 1u}, {c, 1u}}).open(),
|
||||
s.lambda(a).open() * s.lambda(b).open() * s.lambda(c).open());
|
||||
}
|
||||
{
|
||||
session64 s;
|
||||
auto x = s.input();
|
||||
auto x2 = s(x * x);
|
||||
s.pin(x2);
|
||||
EXPECT_EQ(s.preprocessing_count(), 3u);
|
||||
Counter rng;
|
||||
s.sample(rng);
|
||||
s.bind(x, u64{9}, rng);
|
||||
s.evaluate();
|
||||
EXPECT_EQ(s.open(x2), 81u);
|
||||
}
|
||||
{
|
||||
session64 s;
|
||||
auto a = s.input();
|
||||
auto x = s.input();
|
||||
auto ax2 = s(a * x * x);
|
||||
s.pin(ax2);
|
||||
EXPECT_EQ(s.preprocessing_count(), 6u);
|
||||
Counter rng;
|
||||
s.sample(rng);
|
||||
s.bind(a, u64{4}, rng);
|
||||
s.bind(x, u64{3}, rng);
|
||||
s.evaluate();
|
||||
EXPECT_EQ(s.open(ax2), 36u);
|
||||
EXPECT_EQ(s.monomial({{a, 1u}, {x, 2u}}).open(),
|
||||
s.lambda(a).open() * s.lambda(x).open() * s.lambda(x).open());
|
||||
}
|
||||
{
|
||||
session64 s;
|
||||
std::vector<wire64> x;
|
||||
std::vector<wire64> y;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
x.push_back(s.input());
|
||||
y.push_back(s.input());
|
||||
}
|
||||
auto unpinned = s.dot(x, y);
|
||||
EXPECT_EQ(s.preprocessing_count(), 7u);
|
||||
s.pin(unpinned);
|
||||
EXPECT_EQ(s.preprocessing_count(), 8u);
|
||||
Counter rng;
|
||||
s.sample(rng);
|
||||
const u64 xv[] = {1, 2, 3};
|
||||
const u64 yv[] = {4, 5, 6};
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
s.bind(x[i], xv[i], rng);
|
||||
s.bind(y[i], yv[i], rng);
|
||||
}
|
||||
s.evaluate();
|
||||
EXPECT_EQ(s.open(unpinned), 32u);
|
||||
}
|
||||
{
|
||||
session64 s;
|
||||
auto scalar = s.input();
|
||||
auto v0 = s.input();
|
||||
auto v1 = s.input();
|
||||
auto v2 = s.input();
|
||||
auto z = s.scale(scalar, {v0, v1, v2});
|
||||
EXPECT_EQ(s.preprocessing_count(), 7u);
|
||||
for (auto out : z)
|
||||
s.pin(out);
|
||||
EXPECT_EQ(s.preprocessing_count(), 10u);
|
||||
Counter rng;
|
||||
s.sample(rng);
|
||||
s.bind(scalar, u64{3}, rng);
|
||||
s.bind(v0, u64{4}, rng);
|
||||
s.bind(v1, u64{5}, rng);
|
||||
s.bind(v2, u64{0}, rng);
|
||||
s.evaluate();
|
||||
EXPECT_EQ(s.open(z[0]), 12u);
|
||||
EXPECT_EQ(s.open(z[1]), 15u);
|
||||
EXPECT_EQ(s.open(z[2]), 0u);
|
||||
}
|
||||
{
|
||||
session64 s;
|
||||
auto bit = s.bit();
|
||||
auto scalar = s.input();
|
||||
auto out = s.bit_mul(bit, scalar);
|
||||
s.pin(out);
|
||||
EXPECT_EQ(s.preprocessing_count(), 4u);
|
||||
Counter rng;
|
||||
s.sample(rng);
|
||||
s.bind(bit, u64{1}, rng);
|
||||
s.bind(scalar, u64{19}, rng);
|
||||
s.evaluate();
|
||||
EXPECT_EQ(s.open(out), 19u);
|
||||
}
|
||||
{
|
||||
session64 s;
|
||||
auto bit = s.bit();
|
||||
auto when1 = s.input();
|
||||
auto when0 = s.input();
|
||||
auto out = s.mux(bit, when1, when0);
|
||||
s.pin(out);
|
||||
EXPECT_EQ(s.preprocessing_count(), 6u);
|
||||
Counter rng;
|
||||
s.sample(rng);
|
||||
s.bind(bit, u64{0}, rng);
|
||||
s.bind(when1, u64{8}, rng);
|
||||
s.bind(when0, u64{9}, rng);
|
||||
s.evaluate();
|
||||
EXPECT_EQ(s.open(out), 9u);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Beaver, InnerProductRejectsABadShape)
|
||||
{
|
||||
session64 s;
|
||||
auto x = s.input();
|
||||
auto y = s.input();
|
||||
auto z = s.input();
|
||||
EXPECT_THROW((void)[&] {
|
||||
return s.dot(std::initializer_list<wire64>{}, std::initializer_list<wire64>{});
|
||||
}(), std::invalid_argument);
|
||||
EXPECT_THROW((void)[&] { return s.dot({x}, {y, z}); }(), std::invalid_argument);
|
||||
session64 other;
|
||||
auto w = other.input();
|
||||
EXPECT_THROW((void)[&] { return s.dot({x}, {w}); }(), std::invalid_argument);
|
||||
auto dotted = s.dot({x}, {y});
|
||||
EXPECT_THROW((void)[&] { return s.dot_cross(dotted); }(), std::logic_error);
|
||||
EXPECT_THROW((void)[&] { return s.dot_cross(x); }(), std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST(Beaver, ScaleSharesScalarBlind)
|
||||
{
|
||||
dpf::beavers::session<u64> s;
|
||||
|
|
@ -240,6 +657,7 @@ TEST(Beaver, ScaleSharesScalarBlind)
|
|||
s.sample(rng);
|
||||
EXPECT_EQ(z.size(), 4u);
|
||||
EXPECT_EQ(s.monomial_count(), 4u);
|
||||
EXPECT_EQ(s.preprocessing_count(), 9u);
|
||||
EXPECT_EQ(s.round_of(z[0]), 1);
|
||||
// 1 scalar + 4 lanes + 4 outputs = 9 wires * 2, plus 4 cross terms
|
||||
EXPECT_EQ(rng.draws, 14);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue