627 lines
18 KiB
C++
627 lines
18 KiB
C++
|
|
#include <gtest/gtest.h>
|
||
|
|
|
||
|
|
#include <cstdint>
|
||
|
|
#include <stdexcept>
|
||
|
|
#include <vector>
|
||
|
|
|
||
|
|
#include "dpf/beaver.hpp"
|
||
|
|
#include "dpf/buffered_prg.hpp"
|
||
|
|
#include "dpf/modint.hpp"
|
||
|
|
|
||
|
|
namespace
|
||
|
|
{
|
||
|
|
|
||
|
|
struct Counter
|
||
|
|
{
|
||
|
|
int draws = 0;
|
||
|
|
|
||
|
|
std::uint64_t operator()()
|
||
|
|
{
|
||
|
|
++draws;
|
||
|
|
return 0x9e3779b97f4a7c15ull * static_cast<std::uint64_t>(draws);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
template <typename Ring>
|
||
|
|
struct Seq
|
||
|
|
{
|
||
|
|
unsigned n = 1;
|
||
|
|
|
||
|
|
Ring operator()()
|
||
|
|
{
|
||
|
|
return Ring{static_cast<typename Ring::integral_type>(n++ * 17u + 3u)};
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
template <typename Ring>
|
||
|
|
struct XorSeq
|
||
|
|
{
|
||
|
|
unsigned n = 1;
|
||
|
|
|
||
|
|
Ring operator()()
|
||
|
|
{
|
||
|
|
using u = typename Ring::value_type;
|
||
|
|
return Ring{static_cast<u>(n++ * 0x9e3779b9u)};
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
using u64 = std::uint64_t;
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
TEST(Beaver, ProductTwo)
|
||
|
|
{
|
||
|
|
dpf::beavers::session<u64> s;
|
||
|
|
auto x = s.input();
|
||
|
|
auto y = s.input();
|
||
|
|
auto z = s(x * y);
|
||
|
|
Counter rng;
|
||
|
|
s.sample(rng);
|
||
|
|
EXPECT_EQ(rng.draws, 5); // two input blinds * 2 draws + one product share
|
||
|
|
EXPECT_EQ(s.monomial_count(), 1u);
|
||
|
|
EXPECT_EQ(s.round_of(z), 1);
|
||
|
|
EXPECT_EQ(s.monomial({{x, 1u}, {y, 1u}}).open(),
|
||
|
|
s.lambda(x).open() * s.lambda(y).open());
|
||
|
|
|
||
|
|
s.bind(x, u64{7}, rng);
|
||
|
|
s.bind(y, u64{9}, rng);
|
||
|
|
s.evaluate();
|
||
|
|
EXPECT_EQ(s.open(z), 63u);
|
||
|
|
EXPECT_EQ(s.delta(x), u64{7} + s.lambda(x).open());
|
||
|
|
EXPECT_EQ(dpf::reconstruct(s.value(z).party0(), s.value(z).party1()), 63u);
|
||
|
|
EXPECT_NE(s.value(z).p0, s.open(z));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(Beaver, ProductThreeAndSquare)
|
||
|
|
{
|
||
|
|
dpf::beavers::session<u64> s;
|
||
|
|
auto a = s.input();
|
||
|
|
auto b = s.input();
|
||
|
|
auto c = s.input();
|
||
|
|
auto p = s.product(a, b, c);
|
||
|
|
auto sq = s(b * b);
|
||
|
|
Counter rng;
|
||
|
|
s.sample(rng);
|
||
|
|
// wires a,b,c,p,sq = 5 * 2, monomials of a*b*c are the three pairs + abc
|
||
|
|
// (singletons live on the wires), plus λb²
|
||
|
|
EXPECT_EQ(s.monomial_count(), 5u);
|
||
|
|
EXPECT_EQ(s.round_of(p), 1);
|
||
|
|
EXPECT_EQ(s.round_of(sq), 1);
|
||
|
|
s.bind(a, u64{2}, rng);
|
||
|
|
s.bind(b, u64{3}, rng);
|
||
|
|
s.bind(c, u64{5}, rng);
|
||
|
|
s.evaluate();
|
||
|
|
EXPECT_EQ(s.open(p), 30u);
|
||
|
|
EXPECT_EQ(s.open(sq), 9u);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(Beaver, MulSquareIsOneRound)
|
||
|
|
{
|
||
|
|
dpf::beavers::session<u64> s;
|
||
|
|
auto a = s.input();
|
||
|
|
auto x = s.input();
|
||
|
|
auto z = s(a * x * x);
|
||
|
|
Counter rng;
|
||
|
|
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.
|
||
|
|
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(),
|
||
|
|
s.lambda(x).open() * s.lambda(x).open());
|
||
|
|
EXPECT_EQ(s.monomial({{a, 1u}, {x, 2u}}).open(),
|
||
|
|
s.lambda(a).open() * s.lambda(x).open() * s.lambda(x).open());
|
||
|
|
s.bind(a, u64{4}, rng);
|
||
|
|
s.bind(x, u64{5}, rng);
|
||
|
|
s.evaluate();
|
||
|
|
EXPECT_EQ(s.open(z), 100u);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(Beaver, ChainedSquareUsesTwoRounds)
|
||
|
|
{
|
||
|
|
dpf::beavers::session<u64> s;
|
||
|
|
auto a = s.input();
|
||
|
|
auto x = s.input();
|
||
|
|
auto x2 = s(x * x);
|
||
|
|
auto z = s(a * x2);
|
||
|
|
EXPECT_EQ(s.round_of(x2), 1);
|
||
|
|
EXPECT_EQ(s.round_of(z), 2);
|
||
|
|
EXPECT_EQ(s.monomial_count(), 2u);
|
||
|
|
Counter rng;
|
||
|
|
s.sample(rng);
|
||
|
|
s.bind(a, u64{4}, rng);
|
||
|
|
s.bind(x, u64{5}, rng);
|
||
|
|
s.evaluate();
|
||
|
|
EXPECT_EQ(s.open(x2), 25u);
|
||
|
|
EXPECT_EQ(s.open(z), 100u);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(Beaver, DotAggregatesCrossTerm)
|
||
|
|
{
|
||
|
|
dpf::beavers::session<u64> s;
|
||
|
|
auto x0 = s.input();
|
||
|
|
auto x1 = s.input();
|
||
|
|
auto x2 = s.input();
|
||
|
|
auto y0 = s.input();
|
||
|
|
auto y1 = s.input();
|
||
|
|
auto y2 = s.input();
|
||
|
|
auto z = s.dot({x0, x1, x2}, {y0, y1, y2});
|
||
|
|
Counter rng;
|
||
|
|
s.sample(rng);
|
||
|
|
EXPECT_EQ(s.monomial_count(), 0u);
|
||
|
|
EXPECT_EQ(s.round_of(z), 1);
|
||
|
|
// 6 input blinds, two draws each, plus one aggregated cross draw
|
||
|
|
EXPECT_EQ(rng.draws, 13);
|
||
|
|
auto cross = s.lambda(x0).open() * s.lambda(y0).open()
|
||
|
|
+ s.lambda(x1).open() * s.lambda(y1).open()
|
||
|
|
+ s.lambda(x2).open() * s.lambda(y2).open();
|
||
|
|
EXPECT_EQ(s.dot_cross(z).open(), cross);
|
||
|
|
s.bind(x0, u64{1}, rng);
|
||
|
|
s.bind(x1, u64{2}, rng);
|
||
|
|
s.bind(x2, u64{3}, rng);
|
||
|
|
s.bind(y0, u64{4}, rng);
|
||
|
|
s.bind(y1, u64{5}, rng);
|
||
|
|
s.bind(y2, u64{6}, rng);
|
||
|
|
s.evaluate();
|
||
|
|
EXPECT_EQ(s.open(z), 32u);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(Beaver, DotReusesAPair)
|
||
|
|
{
|
||
|
|
dpf::beavers::session<u64> s;
|
||
|
|
auto a = s.input();
|
||
|
|
auto b = s.input();
|
||
|
|
auto z = s.dot({a, a}, {b, b});
|
||
|
|
Counter rng;
|
||
|
|
s.sample(rng);
|
||
|
|
EXPECT_EQ(s.monomial_count(), 0u);
|
||
|
|
s.bind(a, u64{3}, rng);
|
||
|
|
s.bind(b, u64{4}, rng);
|
||
|
|
s.evaluate();
|
||
|
|
EXPECT_EQ(s.open(z), 24u);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(Beaver, ScaleSharesScalarBlind)
|
||
|
|
{
|
||
|
|
dpf::beavers::session<u64> s;
|
||
|
|
auto scalar = s.input();
|
||
|
|
auto v0 = s.input();
|
||
|
|
auto v1 = s.input();
|
||
|
|
auto v2 = s.input();
|
||
|
|
auto v3 = s.input();
|
||
|
|
auto z = s.scale(scalar, {v0, v1, v2, v3});
|
||
|
|
Counter rng;
|
||
|
|
s.sample(rng);
|
||
|
|
EXPECT_EQ(z.size(), 4u);
|
||
|
|
EXPECT_EQ(s.monomial_count(), 4u);
|
||
|
|
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);
|
||
|
|
s.bind(scalar, u64{9}, rng);
|
||
|
|
s.bind(v0, u64{0}, rng);
|
||
|
|
s.bind(v1, u64{1}, rng);
|
||
|
|
s.bind(v2, u64{0}, rng);
|
||
|
|
s.bind(v3, u64{0}, rng);
|
||
|
|
s.evaluate();
|
||
|
|
EXPECT_EQ(s.open(z[0]), 0u);
|
||
|
|
EXPECT_EQ(s.open(z[1]), 9u);
|
||
|
|
EXPECT_EQ(s.open(z[2]), 0u);
|
||
|
|
EXPECT_EQ(s.open(z[3]), 0u);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(Beaver, ScaleDedupsARepeatedLane)
|
||
|
|
{
|
||
|
|
dpf::beavers::session<u64> s;
|
||
|
|
auto scalar = s.input();
|
||
|
|
auto v = s.input();
|
||
|
|
auto z = s.scale(scalar, {v, v});
|
||
|
|
Counter rng;
|
||
|
|
s.sample(rng);
|
||
|
|
EXPECT_EQ(s.monomial_count(), 1u);
|
||
|
|
EXPECT_EQ(rng.draws, 5);
|
||
|
|
s.bind(scalar, u64{6}, rng);
|
||
|
|
s.bind(v, u64{7}, rng);
|
||
|
|
s.evaluate();
|
||
|
|
EXPECT_EQ(s.open(z[0]), 42u);
|
||
|
|
EXPECT_EQ(s.open(z[1]), 42u);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(Beaver, BitMulAndMux)
|
||
|
|
{
|
||
|
|
dpf::beavers::session<u64> s;
|
||
|
|
auto b = s.bit();
|
||
|
|
auto x = s.input();
|
||
|
|
auto y = s.input();
|
||
|
|
auto prod = s.bit_mul(b, x);
|
||
|
|
auto picked = s.mux(b, x, y);
|
||
|
|
Counter rng;
|
||
|
|
s.sample(rng);
|
||
|
|
s.bind(b, u64{1}, rng);
|
||
|
|
s.bind(x, u64{42}, rng);
|
||
|
|
s.bind(y, u64{7}, rng);
|
||
|
|
s.evaluate();
|
||
|
|
EXPECT_EQ(s.open(prod), 42u);
|
||
|
|
EXPECT_EQ(s.open(picked), 42u);
|
||
|
|
EXPECT_EQ(s.round_of(prod), 1);
|
||
|
|
EXPECT_EQ(s.round_of(picked), 1);
|
||
|
|
|
||
|
|
dpf::beavers::session<u64> t;
|
||
|
|
auto b0 = t.bit();
|
||
|
|
auto x0 = t.input();
|
||
|
|
auto y0 = t.input();
|
||
|
|
auto prod0 = t.bit_mul(b0, x0);
|
||
|
|
auto picked0 = t.mux(b0, x0, y0);
|
||
|
|
t.sample(rng);
|
||
|
|
t.bind(b0, u64{0}, rng);
|
||
|
|
t.bind(x0, u64{42}, rng);
|
||
|
|
t.bind(y0, u64{7}, rng);
|
||
|
|
t.evaluate();
|
||
|
|
EXPECT_EQ(t.open(prod0), 0u);
|
||
|
|
EXPECT_EQ(t.open(picked0), 7u);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(Beaver, BatchOneRoundSharesOperands)
|
||
|
|
{
|
||
|
|
dpf::beavers::session<u64> s;
|
||
|
|
auto x = s.input();
|
||
|
|
auto y = s.input();
|
||
|
|
auto z = s.input();
|
||
|
|
auto xy = s(x * y);
|
||
|
|
auto yz = s(y * z);
|
||
|
|
auto xx = s(x * x);
|
||
|
|
EXPECT_EQ(s.monomial_count(), 3u);
|
||
|
|
EXPECT_EQ(s.round_of(xy), 1);
|
||
|
|
EXPECT_EQ(s.round_of(yz), 1);
|
||
|
|
EXPECT_EQ(s.round_of(xx), 1);
|
||
|
|
Counter rng;
|
||
|
|
s.sample(rng);
|
||
|
|
EXPECT_EQ(rng.draws, 9);
|
||
|
|
s.bind(x, u64{2}, rng);
|
||
|
|
s.bind(y, u64{3}, rng);
|
||
|
|
s.bind(z, u64{5}, rng);
|
||
|
|
s.evaluate();
|
||
|
|
EXPECT_EQ(s.open(xy), 6u);
|
||
|
|
EXPECT_EQ(s.open(yz), 15u);
|
||
|
|
EXPECT_EQ(s.open(xx), 4u);
|
||
|
|
// y's blind was opened once and serves both products
|
||
|
|
EXPECT_EQ(s.delta(y), u64{3} + s.lambda(y).open());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(Beaver, BlindSurvivesALaterRound)
|
||
|
|
{
|
||
|
|
dpf::beavers::session<u64> s;
|
||
|
|
auto x = s.input();
|
||
|
|
auto y = s.input();
|
||
|
|
auto xy = s(x * y);
|
||
|
|
s.pin(xy);
|
||
|
|
Counter rng;
|
||
|
|
s.sample(rng);
|
||
|
|
auto lx = s.lambda(x);
|
||
|
|
int draws = rng.draws;
|
||
|
|
s.sample(rng);
|
||
|
|
EXPECT_EQ(rng.draws, draws);
|
||
|
|
EXPECT_EQ(s.lambda(x), lx);
|
||
|
|
s.bind(x, u64{4}, rng);
|
||
|
|
s.bind(y, u64{5}, rng);
|
||
|
|
s.evaluate();
|
||
|
|
EXPECT_EQ(s.open(xy), 20u);
|
||
|
|
auto lxy = s.lambda(xy);
|
||
|
|
draws = rng.draws;
|
||
|
|
|
||
|
|
// Next batch: z is new, xy already has a blind. Depth is 2.
|
||
|
|
auto z = s.input();
|
||
|
|
auto xyz = s(xy * z);
|
||
|
|
EXPECT_EQ(s.round_of(xyz), 2);
|
||
|
|
s.sample(rng);
|
||
|
|
EXPECT_EQ(s.lambda(x), lx);
|
||
|
|
EXPECT_EQ(s.lambda(xy), lxy);
|
||
|
|
EXPECT_EQ(s.monomial_count(), 2u);
|
||
|
|
// z's blind (2 draws) and one new product λ_xy λ_z
|
||
|
|
EXPECT_EQ(rng.draws, draws + 3);
|
||
|
|
s.bind(z, u64{6}, rng);
|
||
|
|
s.evaluate();
|
||
|
|
EXPECT_EQ(s.open(xy), 20u);
|
||
|
|
EXPECT_EQ(s.open(xyz), 120u);
|
||
|
|
EXPECT_EQ(s.lambda(x), lx);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(Beaver, XorWrapperAndMux)
|
||
|
|
{
|
||
|
|
using W = dpf::xor_wrapper<std::uint32_t>;
|
||
|
|
dpf::beavers::session<W> s;
|
||
|
|
auto x = s.input();
|
||
|
|
auto y = s.input();
|
||
|
|
auto bit = s.bit();
|
||
|
|
auto prod = s(x * y);
|
||
|
|
auto sq = s(x * x);
|
||
|
|
auto picked = s.mux(bit, x, y);
|
||
|
|
XorSeq<W> rng;
|
||
|
|
s.sample(rng);
|
||
|
|
s.bind(x, W{0b1100u}, rng);
|
||
|
|
s.bind(y, W{0b1010u}, rng);
|
||
|
|
s.bind(bit, dpf::beavers::ring_traits<W>::one(), rng);
|
||
|
|
s.evaluate();
|
||
|
|
EXPECT_EQ(s.open(prod), W{0b1000u});
|
||
|
|
EXPECT_EQ(s.open(sq), W{0b1100u});
|
||
|
|
EXPECT_EQ(s.open(picked), W{0b1100u});
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(Beaver, ModintProduct)
|
||
|
|
{
|
||
|
|
using M = dpf::modint<17>;
|
||
|
|
dpf::beavers::session<M> s;
|
||
|
|
auto x = s.input();
|
||
|
|
auto y = s.input();
|
||
|
|
auto z = s.mul_square(x, y);
|
||
|
|
Seq<M> rng;
|
||
|
|
s.sample(rng);
|
||
|
|
s.bind(x, M{6}, rng);
|
||
|
|
s.bind(y, M{5}, rng);
|
||
|
|
s.evaluate();
|
||
|
|
EXPECT_EQ(static_cast<M::integral_type>(s.open(z)),
|
||
|
|
static_cast<M::integral_type>(M{6} * M{5} * M{5}));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(Beaver, OneShotTriplesMatchProducts)
|
||
|
|
{
|
||
|
|
Counter rng;
|
||
|
|
auto p2 = dpf::beavers::sample_beaver2<u64>(rng);
|
||
|
|
EXPECT_EQ(p2.ab.open(), p2.a.open() * p2.b.open());
|
||
|
|
|
||
|
|
auto p3 = dpf::beavers::sample_beaver3<u64>(rng);
|
||
|
|
EXPECT_EQ(p3.ab.open(), p3.a.open() * p3.b.open());
|
||
|
|
EXPECT_EQ(p3.ac.open(), p3.a.open() * p3.c.open());
|
||
|
|
EXPECT_EQ(p3.bc.open(), p3.b.open() * p3.c.open());
|
||
|
|
EXPECT_EQ(p3.abc.open(), p3.a.open() * p3.b.open() * p3.c.open());
|
||
|
|
|
||
|
|
auto sq = dpf::beavers::sample_square<u64>(rng);
|
||
|
|
EXPECT_EQ(sq.x2.open(), sq.x.open() * sq.x.open());
|
||
|
|
|
||
|
|
auto ax = dpf::beavers::sample_mul_square<u64>(rng);
|
||
|
|
EXPECT_EQ(ax.x2.open(), ax.x.open() * ax.x.open());
|
||
|
|
EXPECT_EQ(ax.ax.open(), ax.a.open() * ax.x.open());
|
||
|
|
EXPECT_EQ(ax.ax2.open(), ax.a.open() * ax.x.open() * ax.x.open());
|
||
|
|
|
||
|
|
auto dot = dpf::beavers::sample_dot<u64>(3, rng);
|
||
|
|
u64 cross = 0;
|
||
|
|
for (std::size_t i = 0; i < 3; ++i)
|
||
|
|
cross += dot.x[i].open() * dot.y[i].open();
|
||
|
|
EXPECT_EQ(dot.cross.open(), cross);
|
||
|
|
|
||
|
|
auto sc = dpf::beavers::sample_scale<u64>(3, rng);
|
||
|
|
for (std::size_t i = 0; i < 3; ++i)
|
||
|
|
EXPECT_EQ(sc.cross[i].open(), sc.scalar.open() * sc.lanes[i].open());
|
||
|
|
|
||
|
|
auto bm = dpf::beavers::sample_bit_mul<u64>(rng);
|
||
|
|
EXPECT_EQ(bm.product.open(), bm.bit.open() * bm.scalar.open());
|
||
|
|
|
||
|
|
auto mx = dpf::beavers::sample_mux<u64>(rng);
|
||
|
|
EXPECT_EQ(mx.bit_when1.open(), mx.bit.open() * mx.when1.open());
|
||
|
|
EXPECT_EQ(mx.bit_when0.open(), mx.bit.open() * mx.when0.open());
|
||
|
|
|
||
|
|
auto fresh = dpf::beavers::sample_fresh<4, u64>(rng);
|
||
|
|
EXPECT_EQ(fresh.subset[(1u << 4) - 2].open(),
|
||
|
|
fresh.in[0].open() * fresh.in[1].open() * fresh.in[2].open()
|
||
|
|
* fresh.in[3].open());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(Beaver, DefaultSamplerOpens)
|
||
|
|
{
|
||
|
|
auto t = dpf::beavers::sample_beaver2<u64>();
|
||
|
|
EXPECT_EQ(t.ab.open(), t.a.open() * t.b.open());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BufferedPrg, SeedReplaysLanesInAnyOrder)
|
||
|
|
{
|
||
|
|
using block = dpf::prg::aes128::block_type;
|
||
|
|
block seed = simde_mm_set_epi64x(0x1111, 0x2222);
|
||
|
|
dpf::beavers::oracle<u64> first(seed, 4);
|
||
|
|
dpf::beavers::oracle<u64> second(seed, 4);
|
||
|
|
u64 late = first.blind(1, 10);
|
||
|
|
u64 early = first.blind(0, 0);
|
||
|
|
EXPECT_EQ(second.blind(1, 10), late);
|
||
|
|
EXPECT_EQ(second.blind(0, 0), early);
|
||
|
|
EXPECT_EQ(first.mask(0, 10), second.mask(0, 10));
|
||
|
|
EXPECT_NE(first.blind(0, 0), first.blind(1, 0));
|
||
|
|
|
||
|
|
dpf::randomness::aes_buffered_prg<u64, u64> streamed(seed, 8);
|
||
|
|
u64 s0 = streamed.get<0>();
|
||
|
|
u64 s1 = streamed.get<1>();
|
||
|
|
dpf::randomness::aes_buffered_prg<u64, u64> replay(seed, 8);
|
||
|
|
EXPECT_EQ(replay.at<0>(0), s0);
|
||
|
|
EXPECT_EQ(replay.at<1>(0), s1);
|
||
|
|
EXPECT_EQ(replay.get<0>(), s0);
|
||
|
|
|
||
|
|
dpf::randomness::buffered_prg<dpf::prg::dummy, u64> dummy_stream(seed, 4);
|
||
|
|
u64 d0 = dummy_stream.get<0>();
|
||
|
|
dpf::randomness::buffered_prg<dpf::prg::dummy, u64> dummy_replay(seed, 4);
|
||
|
|
EXPECT_EQ(dummy_replay.at<0>(0), d0);
|
||
|
|
EXPECT_EQ(dummy_replay.at<0>(2), d0);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(Beaver, OracleCopiesStayOnOneLanePerBlind)
|
||
|
|
{
|
||
|
|
using block = dpf::prg::aes128::block_type;
|
||
|
|
block seed = simde_mm_set_epi64x(0x9, 0x9);
|
||
|
|
dpf::beavers::oracle<u64> src(seed, 4);
|
||
|
|
dpf::beavers::beaver2<u64> bulk[32];
|
||
|
|
dpf::beavers::fill_beaver2(src, 0, bulk, 32);
|
||
|
|
|
||
|
|
dpf::beavers::oracle<u64> again(seed, 4);
|
||
|
|
auto at31 = dpf::beavers::beaver2_at(again, 31);
|
||
|
|
auto at4 = dpf::beavers::beaver2_at(src, 4);
|
||
|
|
EXPECT_EQ(bulk[31].a, at31.a);
|
||
|
|
EXPECT_EQ(bulk[31].b, at31.b);
|
||
|
|
EXPECT_EQ(bulk[31].ab, at31.ab);
|
||
|
|
EXPECT_EQ(bulk[31].out, at31.out);
|
||
|
|
EXPECT_EQ(bulk[4].a, at4.a);
|
||
|
|
EXPECT_EQ(bulk[4].ab, at4.ab);
|
||
|
|
EXPECT_EQ(bulk[0].ab.open(), bulk[0].a.open() * bulk[0].b.open());
|
||
|
|
EXPECT_EQ(bulk[31].ab.open(), bulk[31].a.open() * bulk[31].b.open());
|
||
|
|
|
||
|
|
dpf::beavers::session<u64> formula;
|
||
|
|
auto x = formula.input();
|
||
|
|
auto y = formula.input();
|
||
|
|
auto z = formula(x * y);
|
||
|
|
auto m = formula.material_at(src, 7);
|
||
|
|
auto one = dpf::beavers::beaver2_at(src, 7);
|
||
|
|
EXPECT_EQ(m.lambda[0], one.a);
|
||
|
|
EXPECT_EQ(m.lambda[1], one.b);
|
||
|
|
EXPECT_EQ(m.lambda[2], one.out);
|
||
|
|
EXPECT_EQ(m.bundles[0].open(), one.ab.open());
|
||
|
|
EXPECT_EQ(m.lambda[0], bulk[7].a);
|
||
|
|
|
||
|
|
formula.sample_from(src, 7);
|
||
|
|
formula.bind(x, u64{6});
|
||
|
|
formula.bind(y, u64{7});
|
||
|
|
formula.evaluate();
|
||
|
|
EXPECT_EQ(formula.open(z), 42u);
|
||
|
|
EXPECT_EQ(formula.lambda(x), one.a);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(Beaver, GrottoAppendixEPreprocessing)
|
||
|
|
{
|
||
|
|
dpf::beavers::session<u64> linear;
|
||
|
|
auto x = linear.input();
|
||
|
|
auto sgn = linear.input();
|
||
|
|
auto a0 = linear.input();
|
||
|
|
auto a1 = linear.input();
|
||
|
|
auto lin = linear(sgn * (a1 * x + a0));
|
||
|
|
(void)lin;
|
||
|
|
// Four masks plus four fused products (ePrint 2023/108, Table 3).
|
||
|
|
EXPECT_EQ(linear.preprocessing_count(), 8u);
|
||
|
|
|
||
|
|
dpf::beavers::session<u64> quad;
|
||
|
|
auto x2 = quad.input();
|
||
|
|
auto s2 = quad.input();
|
||
|
|
auto b0 = quad.input();
|
||
|
|
auto b1 = quad.input();
|
||
|
|
auto b2 = quad.input();
|
||
|
|
auto q = quad(s2 * (b2 * pow(x2, 2) + b1 * x2 + b0));
|
||
|
|
(void)q;
|
||
|
|
EXPECT_EQ(quad.preprocessing_count(), 13u);
|
||
|
|
|
||
|
|
dpf::beavers::session<u64> cube;
|
||
|
|
auto x3 = cube.input();
|
||
|
|
auto s3 = cube.input();
|
||
|
|
auto c0 = cube.input();
|
||
|
|
auto c1 = cube.input();
|
||
|
|
auto c2 = cube.input();
|
||
|
|
auto c3 = cube.input();
|
||
|
|
auto y = cube(s3 * (c3 * pow(x3, 3) + c2 * pow(x3, 2) + c1 * x3 + c0));
|
||
|
|
EXPECT_EQ(cube.preprocessing_count(), 18u);
|
||
|
|
|
||
|
|
Counter rng;
|
||
|
|
cube.sample(rng);
|
||
|
|
cube.bind(x3, u64{2}, rng);
|
||
|
|
cube.bind(s3, u64{3}, rng);
|
||
|
|
cube.bind(c0, u64{4}, rng);
|
||
|
|
cube.bind(c1, u64{5}, rng);
|
||
|
|
cube.bind(c2, u64{6}, rng);
|
||
|
|
cube.bind(c3, u64{7}, rng);
|
||
|
|
cube.evaluate();
|
||
|
|
EXPECT_EQ(cube.open(y), 3u * (7u * 8u + 6u * 4u + 5u * 2u + 4u));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(Beaver, MultivariatePolynomialsSharePowers)
|
||
|
|
{
|
||
|
|
dpf::beavers::session<u64> s;
|
||
|
|
auto x = s.input();
|
||
|
|
auto y = s.input();
|
||
|
|
auto z = s.input();
|
||
|
|
auto sgn = s.input();
|
||
|
|
auto p = s(u64{2} + u64{3} * x + u64{4} * y + u64{5} * x * y
|
||
|
|
+ u64{6} * pow(x, 2) + pow(x, 2) * y + monomial(u64{9}, x, y, z));
|
||
|
|
EXPECT_EQ(s.round_of(p), 1);
|
||
|
|
// Fused buckets: fewer shares than one subset product per monomial.
|
||
|
|
EXPECT_EQ(s.monomial_count(), 5u);
|
||
|
|
auto q = s(sgn * (x * y + pow(x, 2)));
|
||
|
|
EXPECT_EQ(s.round_of(q), 1);
|
||
|
|
// Sign crosses are new; λx² and λx λy are not sampled again.
|
||
|
|
EXPECT_EQ(s.monomial_count(), 10u);
|
||
|
|
Counter rng;
|
||
|
|
s.sample(rng);
|
||
|
|
s.bind(x, u64{2}, rng);
|
||
|
|
s.bind(y, u64{3}, rng);
|
||
|
|
s.bind(z, u64{4}, rng);
|
||
|
|
s.bind(sgn, u64{5}, rng);
|
||
|
|
s.evaluate();
|
||
|
|
EXPECT_EQ(s.open(p),
|
||
|
|
2u + 3u * 2u + 4u * 3u + 5u * 2u * 3u + 6u * 4u + 4u * 3u + 9u * 2u * 3u * 4u);
|
||
|
|
EXPECT_EQ(s.open(q), 5u * (2u * 3u + 4u));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(Beaver, PolynomialsSharePowers)
|
||
|
|
{
|
||
|
|
dpf::beavers::session<u64> s;
|
||
|
|
auto x = s.input();
|
||
|
|
auto quad = s(u64{1} + u64{2} * x + u64{3} * pow(x, 2));
|
||
|
|
auto cube = s.horner(x, {u64{4}, u64{0}, u64{5}, u64{6}});
|
||
|
|
EXPECT_EQ(s.round_of(quad), 1);
|
||
|
|
EXPECT_EQ(s.round_of(cube), 1);
|
||
|
|
// λx² is shared. The cubic also needs λx³, and one fused bucket.
|
||
|
|
EXPECT_EQ(s.monomial_count(), 3u);
|
||
|
|
Counter rng;
|
||
|
|
s.sample(rng);
|
||
|
|
s.bind(x, u64{3}, rng);
|
||
|
|
s.evaluate();
|
||
|
|
EXPECT_EQ(s.open(quad), 1u + 2u * 3u + 3u * 9u);
|
||
|
|
EXPECT_EQ(s.open(cube), 4u + 5u * 9u + 6u * 27u);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(Beaver, SignMultipliesThePolynomialInOneRound)
|
||
|
|
{
|
||
|
|
dpf::beavers::session<u64> s;
|
||
|
|
auto sgn = s.input();
|
||
|
|
auto x = s.input();
|
||
|
|
auto y = s(sgn * (u64{1} + x + pow(x, 2)));
|
||
|
|
auto z = s.horner(sgn, x, {u64{1}, u64{1}, u64{1}});
|
||
|
|
EXPECT_EQ(s.round_of(y), 1);
|
||
|
|
EXPECT_EQ(s.round_of(z), 1);
|
||
|
|
// λx², λsgn λx, λsgn λx². The two polynomials share them.
|
||
|
|
EXPECT_EQ(s.monomial_count(), 3u);
|
||
|
|
Counter rng;
|
||
|
|
s.sample(rng);
|
||
|
|
s.bind(sgn, u64{2}, rng);
|
||
|
|
s.bind(x, u64{3}, rng);
|
||
|
|
s.evaluate();
|
||
|
|
EXPECT_EQ(s.open(y), 2u * (1u + 3u + 9u));
|
||
|
|
EXPECT_EQ(s.open(z), s.open(y));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(Beaver, LikeTermsCollapse)
|
||
|
|
{
|
||
|
|
dpf::beavers::session<u64> s;
|
||
|
|
auto x = s.input();
|
||
|
|
auto y = s.input();
|
||
|
|
auto p = s(x * y + y * x);
|
||
|
|
auto q = s(u64{5} * pow(x, 2) + u64{3} * x * x);
|
||
|
|
EXPECT_EQ(s.monomial_count(), 2u);
|
||
|
|
Counter rng;
|
||
|
|
s.sample(rng);
|
||
|
|
s.bind(x, u64{4}, rng);
|
||
|
|
s.bind(y, u64{6}, rng);
|
||
|
|
s.evaluate();
|
||
|
|
EXPECT_EQ(s.open(p), 2u * 4u * 6u);
|
||
|
|
EXPECT_EQ(s.open(q), 8u * 16u);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(Beaver, RejectsBadUse)
|
||
|
|
{
|
||
|
|
dpf::beavers::session<u64> a;
|
||
|
|
dpf::beavers::session<u64> b;
|
||
|
|
auto x = a.input();
|
||
|
|
auto y = b.input();
|
||
|
|
EXPECT_THROW((void)[&] { return a.product(x, y); }(), std::invalid_argument);
|
||
|
|
EXPECT_THROW((void)[&] { return x * y; }(), std::invalid_argument);
|
||
|
|
auto bit = a.bit();
|
||
|
|
EXPECT_THROW(a.bind(bit, u64{2}), std::invalid_argument);
|
||
|
|
auto z = a.product(x, x);
|
||
|
|
EXPECT_THROW(a.evaluate(), std::logic_error);
|
||
|
|
a.sample();
|
||
|
|
EXPECT_THROW(a.evaluate(), std::logic_error);
|
||
|
|
EXPECT_THROW((void)[&] { return a.bit_mul(x, x); }(), std::invalid_argument);
|
||
|
|
(void)z;
|
||
|
|
}
|