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:
Ryan Henry 2026-09-24 23:18:10 -06:00
parent 0d8a5a8131
commit 0dff6df8ed
250 changed files with 12199 additions and 1981 deletions

View file

@ -0,0 +1,370 @@
#include <gtest/gtest.h>
#include "dpf.hpp"
#include <cstdint>
#include <cstring>
namespace
{
simde__m128i g_roots[16];
int g_ri = 0;
simde__m128i take_root() { return g_roots[g_ri++]; }
struct Pad
{
uint64_t n = 1;
simde__m128i block()
{
auto v = simde_mm_set_epi64x(static_cast<long long>(n),
static_cast<long long>(n * 9 + 3));
n += 2;
return v;
}
uint8_t bit() { return static_cast<uint8_t>(n++ & 1u); }
};
void reset_roots()
{
g_ri = 0;
for (int i = 0; i < 16; ++i)
g_roots[i] = simde_mm_set_epi64x(0x2222 * (i + 1), 0xBEEF0000u + i * 13);
}
template <typename T>
T bare(const T & v)
{
return v;
}
template <typename T, std::size_t Party, dpf::sharing Scheme>
T bare(const dpf::secret_share<T, Party, Scheme> & s)
{
return s.raw();
}
template <typename A, typename B>
auto recon(const A & a, const B & b)
{
using T = decltype(bare(a));
return static_cast<T>(bare(a) - bare(b));
}
template <typename Key, typename In>
auto ev(const Key & key, In x)
{
return bare(*dpf::eval_point(key, x));
}
} // namespace
TEST(ArithPayload, DsXorIndexMatchesDealer)
{
using in_t = std::uint8_t;
using out_t = std::uint32_t;
const in_t alpha = 0x2a;
const in_t x0 = 0x55;
const in_t x1 = static_cast<in_t>(alpha ^ x0);
const out_t beta = 0x01020304;
const out_t y0 = 0x00010002;
const out_t y1 = static_cast<out_t>(beta - y0);
reset_roots();
auto dealer = dpf::make_dpf(alpha, dpf::root_sampler_t<dpf::prg::aes128>{take_root},
beta);
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), Pad> rng{take_root, Pad{}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(dpf::arith_output, x0, x1, rng, y0, y1);
using key_t = std::decay_t<decltype(dealer.first)>;
EXPECT_EQ(std::memcmp(&dealer.first.root(), &ds.first.root(),
sizeof(simde__m128i)), 0);
EXPECT_EQ(std::memcmp(&dealer.second.root(), &ds.second.root(),
sizeof(simde__m128i)), 0);
for (std::size_t i = 0; i < key_t::depth; ++i)
{
EXPECT_EQ(std::memcmp(&dealer.first.correction_word(i),
&ds.first.correction_word(i), sizeof(simde__m128i)), 0)
<< "cw " << i;
EXPECT_EQ(dealer.first.correction_advice(i),
ds.first.correction_advice(i))
<< "advice " << i;
}
EXPECT_EQ(std::memcmp(&dealer.first.leaf(), &ds.first.leaf(),
sizeof(dealer.first.leaf())), 0);
for (int i = 0; i < 256; ++i)
{
const in_t q = static_cast<in_t>(i);
EXPECT_EQ(recon(ev(ds.first, q), ev(ds.second, q)),
q == alpha ? beta : out_t{})
<< i;
EXPECT_EQ(ev(ds.first, q), ev(dealer.first, q)) << "p0 " << i;
EXPECT_EQ(ev(ds.second, q), ev(dealer.second, q)) << "p1 " << i;
}
}
TEST(ArithPayload, DsArithIndexMatchesDealer)
{
using in_t = std::uint8_t;
using out_t = std::uint16_t;
const in_t alpha = 0xc0;
const in_t x0 = 0x40;
const in_t x1 = static_cast<in_t>(alpha - x0);
const out_t beta = 9;
const out_t y0 = 3;
const out_t y1 = 6;
reset_roots();
auto dealer = dpf::make_dpf(alpha, dpf::root_sampler_t<dpf::prg::aes128>{take_root},
beta);
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), Pad> rng{take_root, Pad{}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(dpf::arith_input, dpf::arith_output,
x0, x1, rng, y0, y1);
for (int i = 0; i < 256; ++i)
{
const in_t q = static_cast<in_t>(i);
EXPECT_EQ(recon(ev(ds.first, q), ev(ds.second, q)),
q == alpha ? beta : out_t{})
<< i;
EXPECT_EQ(ev(ds.first, q), ev(dealer.first, q)) << "p0 " << i;
EXPECT_EQ(ev(ds.second, q), ev(dealer.second, q)) << "p1 " << i;
}
}
TEST(ArithPayload, GenevalXorSharedBeta)
{
using in_t = std::uint8_t;
using out_t = std::uint16_t;
const in_t alpha = 0x33;
const in_t x0 = 0x0f;
const in_t x1 = static_cast<in_t>(alpha ^ x0);
const out_t beta = 0x77;
const out_t y0 = 0x10;
const out_t y1 = static_cast<out_t>(beta - y0);
reset_roots();
auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t<dpf::prg::aes128>{take_root},
beta);
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), Pad> g_rng{take_root, Pad{}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto g = dpf::geneval_point(dpf::arith_output, x0, x1, alpha, g_rng, y0, y1);
EXPECT_TRUE(g.leaf_live);
ASSERT_EQ(g.party0.size(), 1u);
EXPECT_EQ(recon(g.party0[0], g.party1[0]), beta);
EXPECT_EQ(g.party0[0], ev(keys.first, alpha));
EXPECT_EQ(g.party1[0], ev(keys.second, alpha));
}
TEST(ArithPayload, GenevalArithSharedBeta)
{
using in_t = std::uint8_t;
using out_t = std::uint16_t;
const in_t alpha = 0x90;
const in_t x0 = 0x20;
const in_t x1 = static_cast<in_t>(alpha - x0);
const out_t beta = 3;
const out_t y0 = 1;
const out_t y1 = 2;
reset_roots();
auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t<dpf::prg::aes128>{take_root},
beta);
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), Pad> g_rng{take_root, Pad{}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto g = dpf::geneval_point(dpf::arith_input, dpf::arith_output, x0, x1,
alpha, g_rng, y0, y1);
EXPECT_TRUE(g.leaf_live);
ASSERT_EQ(g.party0.size(), 1u);
EXPECT_EQ(recon(g.party0[0], g.party1[0]), beta);
EXPECT_EQ(g.party0[0], ev(keys.first, alpha));
EXPECT_EQ(g.party1[0], ev(keys.second, alpha));
}
TEST(ArithPayload, XorWrapperSharesMatchDealer)
{
using in_t = std::uint8_t;
using out_t = dpf::xor_wrapper<std::uint32_t>;
const in_t alpha = 0x11;
const in_t x0 = 0x55;
const in_t x1 = static_cast<in_t>(alpha ^ x0);
const out_t beta{0x0a0b0c0du};
const out_t y0{0x01020304u};
const out_t y1 = beta ^ y0;
reset_roots();
auto dealer = dpf::make_dpf(alpha, dpf::root_sampler_t<dpf::prg::aes128>{take_root},
beta);
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), Pad> rng{take_root, Pad{}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(dpf::arith_output, x0, x1, rng, y0, y1);
for (int i = 0; i < 256; ++i)
{
const in_t q = static_cast<in_t>(i);
const auto got = dpf::reconstruct(*dpf::eval_point(ds.first, q),
*dpf::eval_point(ds.second, q));
const auto expect = dpf::reconstruct(*dpf::eval_point(dealer.first, q),
*dpf::eval_point(dealer.second, q));
EXPECT_EQ(got, expect) << i;
EXPECT_EQ(got, q == alpha ? beta : out_t{}) << i;
}
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), Pad> g_rng{take_root, Pad{}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto g = dpf::geneval_point(dpf::arith_output, x0, x1, alpha, g_rng, y0, y1);
EXPECT_TRUE(g.leaf_live);
ASSERT_EQ(g.party0.size(), 1u);
EXPECT_EQ(g.party0[0] ^ g.party1[0], beta);
}
TEST(ArithPayload, MultiBlockUint256MatchesDealer)
{
using in_t = std::uint8_t;
using out_t = uint256_t;
const in_t alpha = 0x2a;
const in_t x0 = 0x0f;
const in_t x1 = static_cast<in_t>(alpha ^ x0);
const out_t beta = (out_t{1} << 200) + out_t{0xabcdefu};
const out_t y0 = (out_t{1} << 180) + out_t{0x1111u};
const out_t y1 = beta - y0;
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
EXPECT_GT((dpf::block_length_of_leaf_v<out_t, simde__m128i>), 1u);
HEDLEY_PRAGMA(GCC diagnostic pop)
reset_roots();
auto dealer = dpf::make_dpf(alpha, dpf::root_sampler_t<dpf::prg::aes128>{take_root},
beta);
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), Pad> rng{take_root, Pad{}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(dpf::arith_output, x0, x1, rng, y0, y1);
EXPECT_EQ(std::memcmp(&dealer.first.leaf(), &ds.first.leaf(),
sizeof(dealer.first.leaf())), 0);
for (int i = 0; i < 256; i += 17)
{
const in_t q = static_cast<in_t>(i);
EXPECT_EQ(recon(ev(ds.first, q), ev(ds.second, q)),
q == alpha ? beta : out_t{})
<< i;
EXPECT_EQ(ev(ds.first, q), ev(dealer.first, q)) << "p0 " << i;
EXPECT_EQ(ev(ds.second, q), ev(dealer.second, q)) << "p1 " << i;
}
}
TEST(ArithPayload, IncrementalMultiSlotArithBeta)
{
using in_t = std::uint8_t;
const in_t alpha = 0x44;
const in_t x0 = 0x12;
const in_t x1 = static_cast<in_t>(alpha ^ x0);
const std::uint16_t b0 = 0x1111;
const std::uint16_t b1 = 0x2222;
const std::uint16_t y00 = 0x0100;
const std::uint16_t y01 = static_cast<std::uint16_t>(b0 - y00);
const std::uint16_t y10 = 0x0003;
const std::uint16_t y11 = static_cast<std::uint16_t>(b1 - y10);
reset_roots();
auto dealer = dpf::make_dpf(alpha, dpf::root_sampler_t<dpf::prg::aes128>{take_root},
dpf::at<8>(b0, b1));
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), Pad> rng{take_root, Pad{}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(dpf::arith_output, x0, x1, rng,
dpf::at<8>(dpf::arith_beta<std::uint16_t>{y00, y01},
dpf::arith_beta<std::uint16_t>{y10, y11}));
auto r0 = [&](auto & k0, auto & k1, in_t q) {
return recon(*dpf::eval_point(dpf::out<0, 8>, k0, q),
*dpf::eval_point(dpf::out<0, 8>, k1, q));
};
auto r1 = [&](auto & k0, auto & k1, in_t q) {
return recon(*dpf::eval_point(dpf::out<1, 8>, k0, q),
*dpf::eval_point(dpf::out<1, 8>, k1, q));
};
for (int i = 0; i < 256; i += 13)
{
const in_t q = static_cast<in_t>(i);
EXPECT_EQ(r0(ds.first, ds.second, q), r0(dealer.first, dealer.second, q))
<< "s0 " << i;
EXPECT_EQ(r1(ds.first, ds.second, q), r1(dealer.first, dealer.second, q))
<< "s1 " << i;
EXPECT_EQ(r0(ds.first, ds.second, q),
q == alpha ? b0 : std::uint16_t{0})
<< i;
EXPECT_EQ(r1(ds.first, ds.second, q),
q == alpha ? b1 : std::uint16_t{0})
<< i;
}
}
TEST(ArithPayload, MixedArithBetaAndWildcard)
{
using in_t = std::uint8_t;
const in_t alpha = 0x70;
const in_t x0 = 0x01;
const in_t x1 = static_cast<in_t>(alpha ^ x0);
const std::uint8_t beta = 9;
const std::uint8_t y0 = 2;
const std::uint8_t y1 = 7;
reset_roots();
auto dealer = dpf::make_dpf(alpha, dpf::root_sampler_t<dpf::prg::aes128>{take_root},
dpf::at<8>(beta, dpf::wildcard_value<std::uint8_t>{}));
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), Pad> rng{take_root, Pad{}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(dpf::arith_output, x0, x1, rng,
dpf::at<8>(dpf::arith_beta<std::uint8_t>{y0, y1},
dpf::wildcard_value<std::uint8_t>{}));
using key_t = std::decay_t<decltype(ds.first)>;
static_assert(dpf::is_wildcard_v<typename key_t::template output_type_t<1>>);
for (int i = 0; i < 256; i += 19)
{
const in_t q = static_cast<in_t>(i);
const auto got = recon(*dpf::eval_point(dpf::out<0, 8>, ds.first, q),
*dpf::eval_point(dpf::out<0, 8>, ds.second, q));
const auto expect =
recon(*dpf::eval_point(dpf::out<0, 8>, dealer.first, q),
*dpf::eval_point(dpf::out<0, 8>, dealer.second, q));
EXPECT_EQ(got, expect) << i;
EXPECT_EQ(got, q == alpha ? beta : std::uint8_t{0}) << i;
}
}

View file

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

View file

@ -1,6 +1,7 @@
#include <gtest/gtest.h>
#include "dpf.hpp"
#include "dpf/blocked_dcf.hpp"
#include "dpf/json.hpp"
#include "grotto/offset_horner.hpp"
#include "grotto/prefix_parity.hpp"
@ -238,7 +239,10 @@ TEST(BlockedDcf, DealerMatchesDoernerShelat)
dpf::root_sampler_t<dpf::prg::aes128>{take_root},
dpf::block_width<4>(dpf::lt(uint64_t{15}, uint64_t{2})));
reset_tape();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng,
dpf::block_width<4>(dpf::lt(uint64_t{15}, uint64_t{2})));
@ -336,7 +340,10 @@ TEST(BlockedDcf, GenevalOpensCheckpointWords)
const uint8_t x0 = 1;
const uint8_t x1 = static_cast<uint8_t>(alpha ^ x0);
reset_tape();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
std::array<uint8_t, 3> ends{{0, 20, 21}};
auto g = dpf::geneval_cmp(x0, x1, ends.begin(), ends.end(), rng,
dpf::block_width<4>(dpf::lt(uint64_t{5})));
@ -350,6 +357,85 @@ TEST(BlockedDcf, GenevalOpensCheckpointWords)
EXPECT_EQ((g.party0[2] + g.party1[2]) & g.mask, 0u);
}
TEST(BlockedDcf, UnevenScheduleMatchesDenseComparison)
{
const uint16_t alpha = 0x0B4C;
auto dense = dpf::make_dpf(alpha, dpf::lt(uint64_t{9}, uint64_t{2}));
auto blocked = dpf::make_dpf(alpha,
dpf::block_width<4>(dpf::lt(uint64_t{9}, uint64_t{2})));
using KT = std::decay_t<decltype(blocked.first)>;
EXPECT_EQ(KT::cmp_h, 14u);
EXPECT_EQ(KT::cmp_block, 4u);
EXPECT_NE(KT::cmp_h % KT::cmp_block, 0u);
EXPECT_EQ(blocked.first.value_cw().size(), KT::cmp_checkpoints);
using sched = dpf::detail::blocked::schedule<KT::cmp_h, KT::cmp_block>;
ASSERT_GE(sched::count, 2u);
bool uneven = false;
const auto first_step = sched::depths[1] - sched::depths[0];
for (std::size_t i = 1; i < sched::count; ++i)
{
if (sched::depths[i] - sched::depths[i - 1] != first_step)
uneven = true;
}
EXPECT_TRUE(uneven);
for (uint32_t x = 0; x < 65536u; ++x)
{
const auto q = static_cast<uint16_t>(x);
ASSERT_EQ(recon_cmp(blocked.first, blocked.second, q),
recon_cmp(dense.first, dense.second, q))
<< x;
}
}
TEST(BlockedDcf, WideCheckpointFrontierMatchesDense)
{
const uint32_t alpha = 0x01020304u;
// `cmp_q` is 2, so `lt_at<19>` checkpoints at height 17. One block of 17
// parks a root sibling 16 levels above that checkpoint.
auto dense = dpf::make_dpf(alpha, dpf::lt_at<19>(uint64_t{5}, uint64_t{1}));
auto blocked = dpf::make_dpf(alpha,
dpf::block_width<17>(dpf::lt_at<19>(uint64_t{5}, uint64_t{1})));
using KT = std::decay_t<decltype(blocked.first)>;
EXPECT_GE(KT::cmp_h, 17u);
EXPECT_EQ(KT::cmp_checkpoints, 1u);
const uint32_t pts[] = {
0u, 1u, alpha - 1u, alpha, alpha + 1u, 0x80000000u, 0xffffffffu
};
for (uint32_t x : pts)
{
EXPECT_EQ(recon_cmp(blocked.first, blocked.second, x),
recon_cmp(dense.first, dense.second, x))
<< std::hex << x;
}
const uint32_t from = 100, to = 101;
auto buf0 = dpf::make_output_buffer(dpf::cmp, blocked.first, from, to);
auto buf1 = dpf::make_output_buffer(dpf::cmp, blocked.second, from, to);
dpf::eval_interval(dpf::cmp, blocked.first, from, to, buf0);
dpf::eval_interval(dpf::cmp, blocked.second, from, to, buf1);
EXPECT_EQ(recon(buf0[0], buf1[0]) & blocked.first.cmp().mask,
recon_cmp(blocked.first, blocked.second, from));
EXPECT_EQ(recon(buf0[1], buf1[1]) & blocked.first.cmp().mask,
recon_cmp(blocked.first, blocked.second, to));
}
TEST(BlockedDcf, PathRecipesStayOnThePerLevelChannel)
{
const uint8_t alpha = 0x3C;
EXPECT_THROW(dpf::make_dpf(alpha, dpf::block_width<4>(dpf::lcp(uint64_t{1}))),
std::invalid_argument);
EXPECT_THROW(dpf::make_dpf(alpha, dpf::block_width<4>(dpf::break_bit(uint64_t{3}))),
std::invalid_argument);
EXPECT_THROW(dpf::make_dpf(alpha,
dpf::block_width<4>(dpf::prefix_with_length<4>(uint64_t{1}))),
std::invalid_argument);
EXPECT_THROW(dpf::make_dpf(alpha, dpf::block_width<2>(dpf::path_paint(
[](std::size_t matched, uint64_t, bool) {
return static_cast<uint64_t>(matched);
}))),
std::invalid_argument);
}
TEST(BlockedDcf, GrottoPrefixSegmentAndHorner)
{
const uint16_t alpha = 1000;

View file

@ -0,0 +1,64 @@
#include <gtest/gtest.h>
#include "dpf.hpp"
#include <cstdint>
#include <limits>
#include <random>
TEST(ConstrainedCmp, LemmaAdjacentPairs)
{
// |x0 − x1| = 1 ⇒ local_ccmp = 1{x0 < x1}.
EXPECT_EQ(dpf::local_ccmp(0, 1), 1u);
EXPECT_EQ(dpf::local_ccmp(1, 0), 0u);
EXPECT_EQ(dpf::local_ccmp(2, 3), 1u);
EXPECT_EQ(dpf::local_ccmp(3, 2), 0u);
EXPECT_EQ(dpf::local_ccmp(100, 101), 1u);
EXPECT_EQ(dpf::local_ccmp(101, 100), 0u);
// Control-bit pairs used by arith leaf open (t0 ⊕ t1 = 1).
EXPECT_EQ(dpf::local_ccmp(0, 1), 1u); // g = t1
EXPECT_EQ(dpf::local_ccmp(1, 0), 0u); // g = t1
}
TEST(ConstrainedCmp, RandomAdjacentBatch)
{
std::mt19937_64 rng{0xC0FFEEULL};
std::uniform_int_distribution<std::uint64_t> dist(0, (1ull << 40) - 2);
for (int i = 0; i < 256; ++i)
{
const std::uint64_t a = dist(rng);
const std::uint64_t b = a + 1;
EXPECT_EQ(dpf::local_ccmp(a, b), 1u) << a << " < " << b;
EXPECT_EQ(dpf::local_ccmp(b, a), 0u) << b << " > " << a;
EXPECT_EQ(dpf::local_ccmp_int(a, b), 1u);
EXPECT_EQ(dpf::local_ccmp_int(b, a), 0u);
}
}
TEST(ConstrainedCmp, PartyTermsMatchTheAndInputs)
{
uint8_t z0 = 9, z1 = 9, l = 9;
dpf::detail::ccmp_party_terms(0b10, 0, z0, z1, l);
EXPECT_EQ(l, 0u);
EXPECT_EQ(z0, 1u);
EXPECT_EQ(z1, 1u);
dpf::detail::ccmp_party_terms(0b11, 1, z0, z1, l);
EXPECT_EQ(l, 1u);
EXPECT_EQ(z0, 1u);
EXPECT_EQ(z1, static_cast<uint8_t>(1u ^ 1u ^ 1u));
}
TEST(ConstrainedCmp, AdjacentAtTheTopOfUint64)
{
const std::uint64_t top = std::numeric_limits<std::uint64_t>::max();
EXPECT_EQ(dpf::local_ccmp(top - 1, top), 1u);
EXPECT_EQ(dpf::local_ccmp(top, top - 1), 0u);
}
TEST(ConstrainedCmp, ProtocolHook)
{
dpf::detail::urandom_pad_rng pads{};
dpf::local_cw_protocol<dpf::detail::urandom_pad_rng> proto{pads};
EXPECT_EQ(proto.open_ccmp(0, 1), 1u);
EXPECT_EQ(proto.open_ccmp(1, 0), 0u);
}

View file

@ -94,7 +94,7 @@ TEST(ContextBlast, ModintEdgesAndFullDomain)
every_point(m7{64}, dpf::xor_wrapper<uint32_t>{0x00ff}, 128);
for (unsigned a : {0u, 1u, 511u, 512u, 1023u})
every_point(m10{a}, uint32_t{0xabcdu}, 1024);
every_point(m10{static_cast<m10::integral_type>(a)}, uint32_t{0xabcdu}, 1024);
every_point(m9{0}, uint16_t{2}, 512);
every_point(m9{511}, uint16_t{2}, 512);
@ -140,11 +140,11 @@ TEST(ContextBlast, BitstringPointIntervalSequence)
const uint32_t y = 0x11111111u;
for (unsigned a : {0u, 1u, 31u, 32u, 63u})
{
bs alpha{a};
bs alpha{static_cast<bs::word_type>(a)};
auto [k0, k1] = dpf::make_dpf(alpha, y);
for (unsigned i = 0; i < 64; ++i)
{
bs q{i};
bs q{static_cast<bs::word_type>(i)};
EXPECT_EQ(opened(ev(k0, q), ev(k1, q)), q == alpha ? y : 0u) << i;
}
bs from{0};
@ -280,8 +280,11 @@ TEST(ContextBlast, XorWrapperAndGenevalIntegers)
const int32_t secret = -2;
const int32_t x1 = secret ^ x0;
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
auto g = dpf::geneval_point(x0, x1, secret,
dpf::ds_randomness<simde__m128i (*)(), Pad>{take_root, Pad{}}, X{7});
HEDLEY_PRAGMA(GCC diagnostic pop)
EXPECT_TRUE(g.leaf_live);
EXPECT_EQ(g.live_levels, std::decay_t<decltype(keys.first)>::depth);
EXPECT_EQ(opened(g.party0[0], g.party1[0]), X{7});
@ -289,8 +292,11 @@ TEST(ContextBlast, XorWrapperAndGenevalIntegers)
const int32_t far = 100;
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
auto off = dpf::geneval_point(x0, x1, far,
dpf::ds_randomness<simde__m128i (*)(), Pad>{take_root, Pad{}}, X{7});
HEDLEY_PRAGMA(GCC diagnostic pop)
EXPECT_FALSE(off.leaf_live);
EXPECT_GT(off.live_levels, 0u);
EXPECT_LT(off.live_levels, off.correction_words.size());
@ -307,8 +313,11 @@ TEST(ContextBlast, XorWrapperAndGenevalIntegers)
uint64_t{99});
reset_roots();
const uint32_t qs[] = {0u, 1u, u, u ^ 1u, 0xffffffffu};
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
auto sq = dpf::geneval_sequence(u0, u1, std::begin(qs), std::end(qs),
dpf::ds_randomness<simde__m128i (*)(), Pad>{take_root, Pad{}}, uint64_t{99});
HEDLEY_PRAGMA(GCC diagnostic pop)
EXPECT_TRUE(sq.leaf_live);
for (std::size_t i = 0; i < sq.live_levels; ++i)
EXPECT_EQ(std::memcmp(&sq.correction_words[i], &uk.first.correction_word(i),

View file

@ -384,7 +384,10 @@ TEST(CornerGaps, PrgRejectsUint32Seam)
EXPECT_EQ(std::memcmp(&out[i], &one, sizeof(one)), 0) << i;
}
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
EXPECT_THROW((dpf::randomness::detail::lane_codec<dpf::prg::aes128, simde__m128i>::fill(
seed, static_cast<std::uint64_t>(UINT32_MAX) - 1u, out, 4)),
std::invalid_argument);
HEDLEY_PRAGMA(GCC diagnostic pop)
}

View file

@ -213,3 +213,17 @@ TEST(DyadicLut, Int64Edges)
EXPECT_EQ(bit(std::int64_t{-5}), 1);
EXPECT_EQ(bit(std::int64_t{5}), 0);
}
TEST(DyadicLut, RejectsAFractionalWidthThatDoesNotFit)
{
EXPECT_THROW(grotto::make_signum_lut<std::int16_t>(63), std::invalid_argument);
EXPECT_THROW(grotto::make_positive_lut<std::int64_t>(63), std::invalid_argument);
EXPECT_THROW(grotto::make_ilogb_lut<std::int16_t>(64), std::invalid_argument);
}
TEST(DyadicLut, RejectsAnEncodedValueThatDoesNotFit)
{
// clz(0) is 64. 64 << 57 does not fit in int64; 64 << 56 does.
EXPECT_THROW(grotto::make_clz_lut<std::int64_t>(57), std::overflow_error);
EXPECT_NO_THROW(grotto::make_clz_lut<std::int64_t>(56));
}

57
test/tests/fp61_test.cpp Normal file
View file

@ -0,0 +1,57 @@
#include <gtest/gtest.h>
#include <cstdint>
#include <limits>
#include <sstream>
#include "dpf/fp61.hpp"
namespace
{
using dpf::fp61;
using dpf::fp61_mod;
} // namespace
TEST(Fp61, ReduceCanonicalizesTheModulus)
{
EXPECT_EQ(fp61::reduce(0), 0u);
EXPECT_EQ(fp61::reduce(1), 1u);
EXPECT_EQ(fp61::reduce(fp61_mod), 0u);
EXPECT_EQ(fp61::reduce(fp61_mod + 1), 1u);
EXPECT_EQ(fp61::reduce(std::uint64_t{1} << 61), 1u);
EXPECT_EQ(fp61::reduce(std::numeric_limits<std::uint64_t>::max()), 7u);
EXPECT_EQ(fp61::reduce(fp61::reduce(fp61_mod + 9)), fp61::reduce(9));
}
TEST(Fp61, ArithmeticWrapsInTheField)
{
const fp61 a{fp61_mod - 1};
const fp61 b{3};
EXPECT_EQ((a + b).raw(), 2u);
EXPECT_EQ((b - a).raw(), 4u);
EXPECT_EQ((-a).raw(), 1u);
EXPECT_EQ((-fp61{0}).raw(), 0u);
EXPECT_EQ((a - a).raw(), 0u);
EXPECT_EQ((-(-a)).raw(), a.raw());
EXPECT_EQ((a * fp61{1}).raw(), a.raw());
EXPECT_EQ((a * fp61{0}).raw(), 0u);
EXPECT_EQ((fp61{2} * fp61{3}).raw(), 6u);
}
TEST(Fp61, MultiplicationDistributes)
{
const fp61 a{1000};
const fp61 b{fp61_mod - 5};
const fp61 c{17};
EXPECT_EQ(((a + b) * c).raw(), (a * c + b * c).raw());
EXPECT_EQ((a * b).raw(), (b * a).raw());
}
TEST(Fp61, StreamPrintsTheReducedValue)
{
std::ostringstream os;
os << fp61{fp61_mod + 4};
EXPECT_EQ(os.str(), "4");
}

View file

@ -86,6 +86,8 @@ std::size_t live_through_lcp(std::size_t lcp, std::size_t depth)
return std::min(depth, lcp + 1);
}
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
template <typename Key>
void expect_prefix_words(const Key & key, const std::vector<simde__m128i,
dpf::aligned_allocator<simde__m128i>> & cws,
@ -112,6 +114,7 @@ dpf::ds_randomness<simde__m128i (*)(), Pad> rng()
{
return {take_root, Pad{}};
}
HEDLEY_PRAGMA(GCC diagnostic pop)
} // namespace
@ -508,7 +511,10 @@ TEST(Geneval, DoernerShelatKeyAgreesOnLivePrefix)
out_t y = 1;
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), Pad> ds_rng{take_root, Pad{}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, ds_rng, y);
reset_roots();
auto g = dpf::geneval_point(x0, x1, query, rng<in_t>(), y);
@ -631,7 +637,10 @@ TEST(Geneval, XorSplitAndPadStreamDoNotChangeLiveWords)
auto check = [&](in_t x0, auto pad, const char * name) {
in_t x1 = static_cast<in_t>(alpha ^ x0);
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), decltype(pad)> r{take_root, pad};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto g = dpf::geneval_point(x0, x1, query, r, y);
EXPECT_EQ(g.live_levels, live) << name;
expect_prefix_words(keys.first, g.correction_words, g.correction_advice,
@ -1190,7 +1199,10 @@ TEST(Geneval, DoernerShelatOnTargetSharesMatch)
const out_t y = 0x9f3c;
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), Pad> ds_rng{take_root, Pad{}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, ds_rng, y);
reset_roots();
auto g = dpf::geneval_point(x0, x1, alpha, rng<in_t>(), y);
@ -1251,7 +1263,10 @@ TEST(Geneval, CmpLeqGeqNonzeroElseAndDomainMin)
auto spec_ds = spec;
auto spec_g = spec;
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), Pad> ds_rng{take_root, Pad{}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, ds_rng, spec_ds);
reset_roots();
auto g = dpf::geneval_cmp(x0, x1, ends.begin(), ends.end(), rng<in_t>(), spec_g);

View file

@ -0,0 +1,322 @@
#include <gtest/gtest.h>
#include "dpf.hpp"
#include <cstdint>
#include <cstring>
#include <vector>
namespace
{
using ht_prg = dpf::prg::aes128_ccr;
using leaf_prg = dpf::prg::aes128;
simde__m128i g_roots[8];
int g_ri = 0;
simde__m128i take_root() { return g_roots[g_ri++]; }
struct Pad
{
uint64_t n = 1;
simde__m128i block()
{
auto v = simde_mm_set_epi64x(static_cast<long long>(n),
static_cast<long long>(n * 9 + 3));
n += 2;
return v;
}
uint8_t bit() { return static_cast<uint8_t>(n++ & 1u); }
};
void reset_roots()
{
g_ri = 0;
for (int i = 0; i < 8; ++i)
g_roots[i] = simde_mm_set_epi64x(0x1111 * (i + 1), 0xA5A50000u + i * 17);
}
template <typename T>
T bare(const T & v)
{
return v;
}
template <typename T, std::size_t Party, dpf::sharing Scheme>
T bare(const dpf::secret_share<T, Party, Scheme> & s)
{
return s.raw();
}
template <typename A, typename B>
auto recon(const A & a, const B & b)
{
using T = decltype(bare(a));
return static_cast<T>(bare(a) - bare(b));
}
template <typename Key, typename In>
auto ev(const Key & key, In x)
{
return bare(*dpf::eval_point(key, x));
}
} // namespace
TEST(HalfTree, TraitsSelectedByCcrPrg)
{
static_assert(dpf::tree_traits<ht_prg>::is_half_tree);
static_assert(!dpf::tree_traits<leaf_prg>::is_half_tree);
static_assert(dpf::tree_traits<ht_prg>::last_level_differs);
static_assert(!dpf::tree_traits<ht_prg>::stores_mid_advice);
}
TEST(HalfTree, DealerPointAndFullDomain)
{
using in_t = std::uint8_t;
using out_t = std::uint32_t;
const in_t alpha = 0x2a;
const out_t beta = 0x01020304;
auto [k0, k1] = dpf::make_dpf<ht_prg, leaf_prg>(alpha, beta);
using key_t = std::decay_t<decltype(k0)>;
static_assert(key_t::tree::is_half_tree);
// Mid-level advice unused; last level may pack BGI-style advice.
for (std::size_t i = 0; i + 1 < key_t::depth; ++i)
EXPECT_EQ(k0.correction_advice(i), 0) << "mid advice " << i;
for (int i = 0; i < 256; ++i)
{
const in_t q = static_cast<in_t>(i);
const out_t got = recon(ev(k0, q), ev(k1, q));
EXPECT_EQ(got, q == alpha ? beta : out_t{}) << i;
}
auto [buf0, it0] = dpf::eval_full(k0);
auto [buf1, it1] = dpf::eval_full(k1);
(void)it0;
(void)it1;
for (int i = 0; i < 256; ++i)
{
const out_t got = recon(buf0[i], buf1[i]);
EXPECT_EQ(got, static_cast<in_t>(i) == alpha ? beta : out_t{}) << i;
}
}
TEST(HalfTree, OutputParityVsBgi)
{
using in_t = std::uint8_t;
using out_t = std::uint16_t;
const in_t alpha = 0x7e;
const out_t beta = 0xabcd;
auto [h0, h1] = dpf::make_dpf<ht_prg, leaf_prg>(alpha, beta);
auto [b0, b1] = dpf::make_dpf<leaf_prg, leaf_prg>(alpha, beta);
// Keys differ (different tree), but reconstructed outputs match.
EXPECT_NE(std::memcmp(&h0.root(), &b0.root(), sizeof(simde__m128i)), 0);
for (int i = 0; i < 256; ++i)
{
const in_t q = static_cast<in_t>(i);
const out_t ht = recon(ev(h0, q), ev(h1, q));
const out_t bgi = recon(ev(b0, q), ev(b1, q));
EXPECT_EQ(ht, bgi) << i;
EXPECT_EQ(ht, q == alpha ? beta : out_t{}) << i;
}
}
TEST(HalfTree, DoernerShelatXorMatchesDealer)
{
using in_t = std::uint8_t;
using out_t = std::uint16_t;
const in_t alpha = 0x11;
const in_t x0 = 0x55;
const in_t x1 = static_cast<in_t>(alpha ^ x0);
const out_t y = 0x42;
reset_roots();
auto dealer = dpf::make_dpf<ht_prg, leaf_prg>(alpha,
dpf::root_sampler_t<ht_prg>{take_root}, y);
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), Pad> ds_rng{take_root, Pad{}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat<ht_prg, leaf_prg>(x0, x1, ds_rng, y);
using key_t = std::decay_t<decltype(dealer.first)>;
EXPECT_EQ(std::memcmp(&dealer.first.root(), &ds.first.root(),
sizeof(simde__m128i)), 0);
EXPECT_EQ(std::memcmp(&dealer.second.root(), &ds.second.root(),
sizeof(simde__m128i)), 0);
for (std::size_t i = 0; i < key_t::depth; ++i)
{
EXPECT_EQ(std::memcmp(&dealer.first.correction_word(i),
&ds.first.correction_word(i), sizeof(simde__m128i)), 0)
<< "cw " << i;
EXPECT_EQ(dealer.first.correction_advice(i),
ds.first.correction_advice(i))
<< "advice " << i;
}
for (int i = 0; i < 256; ++i)
{
const in_t q = static_cast<in_t>(i);
EXPECT_EQ(recon(ev(ds.first, q), ev(ds.second, q)),
q == alpha ? y : out_t{})
<< i;
}
}
TEST(HalfTree, DoernerShelatArithMatchesDealer)
{
using in_t = std::uint8_t;
using out_t = std::uint16_t;
const in_t alpha = 0xc0;
const in_t x0 = 0x40;
const in_t x1 = static_cast<in_t>(alpha - x0);
const out_t y = 9;
reset_roots();
auto dealer = dpf::make_dpf<ht_prg, leaf_prg>(alpha,
dpf::root_sampler_t<ht_prg>{take_root}, y);
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), Pad> ds_rng{take_root, Pad{}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat<ht_prg, leaf_prg>(dpf::arith_input,
x0, x1, ds_rng, y);
for (int i = 0; i < 256; ++i)
{
const in_t q = static_cast<in_t>(i);
EXPECT_EQ(recon(ev(ds.first, q), ev(ds.second, q)),
q == alpha ? y : out_t{})
<< i;
EXPECT_EQ(ev(ds.first, q), ev(dealer.first, q)) << "p0 " << i;
EXPECT_EQ(ev(ds.second, q), ev(dealer.second, q)) << "p1 " << i;
}
}
TEST(HalfTree, GenevalPointMatchesDealer)
{
using in_t = std::uint8_t;
using out_t = std::uint16_t;
const in_t alpha = 0x33;
const in_t x0 = 0x0f;
const in_t x1 = static_cast<in_t>(alpha ^ x0);
const out_t y = 0x77;
reset_roots();
auto keys = dpf::make_dpf<ht_prg, leaf_prg>(alpha,
dpf::root_sampler_t<ht_prg>{take_root}, y);
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), Pad> g_rng{take_root, Pad{}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto g = dpf::geneval_point<ht_prg, leaf_prg>(x0, x1, alpha, g_rng, y);
using key_t = std::decay_t<decltype(keys.first)>;
EXPECT_TRUE(g.leaf_live);
EXPECT_EQ(g.live_levels, key_t::depth);
for (std::size_t i = 0; i < g.live_levels; ++i)
{
EXPECT_EQ(std::memcmp(&g.correction_words[i],
&keys.first.correction_word(i), sizeof(simde__m128i)), 0)
<< "cw " << i;
EXPECT_EQ(g.correction_advice[i], keys.first.correction_advice(i))
<< "advice " << i;
}
ASSERT_EQ(g.party0.size(), 1u);
EXPECT_EQ(g.party0[0], ev(keys.first, alpha));
EXPECT_EQ(g.party1[0], ev(keys.second, alpha));
EXPECT_EQ(recon(g.party0[0], g.party1[0]), y);
}
TEST(HalfTree, GenevalArithPointMatchesDealer)
{
using in_t = std::uint8_t;
using out_t = std::uint16_t;
const in_t alpha = 0x90;
const in_t x0 = 0x20;
const in_t x1 = static_cast<in_t>(alpha - x0);
const out_t y = 3;
reset_roots();
auto keys = dpf::make_dpf<ht_prg, leaf_prg>(alpha,
dpf::root_sampler_t<ht_prg>{take_root}, y);
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), Pad> g_rng{take_root, Pad{}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto g = dpf::geneval_point<ht_prg, leaf_prg>(dpf::arith_input, x0, x1,
alpha, g_rng, y);
EXPECT_TRUE(g.leaf_live);
ASSERT_EQ(g.party0.size(), 1u);
EXPECT_EQ(recon(g.party0[0], g.party1[0]), y);
EXPECT_EQ(g.party0[0], ev(keys.first, alpha));
EXPECT_EQ(g.party1[0], ev(keys.second, alpha));
}
TEST(HalfTree, IncrementalCmpSmoke)
{
using in_t = std::uint8_t;
using ht_prg = dpf::prg::aes128_ccr;
using leaf_prg = dpf::prg::aes128;
const in_t alpha = 0x40;
const uint64_t if_true = 4;
const uint64_t if_false = 1;
auto ht = dpf::make_dpf<ht_prg, leaf_prg>(alpha,
dpf::lt(if_true, if_false));
auto bgi = dpf::make_dpf<leaf_prg, leaf_prg>(alpha,
dpf::lt(if_true, if_false));
using key_t = std::decay_t<decltype(ht.first)>;
static_assert(key_t::tree::is_half_tree);
for (std::size_t i = 0; i + 1 < key_t::depth; ++i)
EXPECT_EQ(ht.first.correction_advice(i), 0) << "mid advice " << i;
const uint64_t mask = ht.first.cmp().mask;
auto recon_cmp = [&](const auto & k0, const auto & k1, in_t q) {
return dpf::reconstruct(dpf::eval_point(dpf::cmp, k0, q),
dpf::eval_point(dpf::cmp, k1, q))
& mask;
};
for (int i = 0; i < 256; ++i)
{
const in_t q = static_cast<in_t>(i);
const auto ht_got = recon_cmp(ht.first, ht.second, q);
const auto bgi_got = recon_cmp(bgi.first, bgi.second, q);
const uint64_t expect = (q < alpha) ? if_true : if_false;
EXPECT_EQ(ht_got, expect) << i;
EXPECT_EQ(ht_got, bgi_got) << "parity " << i;
}
}
TEST(HalfTree, IncrementalPlacementSmoke)
{
using in_t = std::uint8_t;
const in_t alpha = 0x40;
auto keys = dpf::make_dpf<ht_prg, leaf_prg>(alpha, dpf::at<8>(uint8_t{7}));
using key_t = std::decay_t<decltype(keys.first)>;
static_assert(key_t::tree::is_half_tree);
for (std::size_t i = 0; i + 1 < key_t::depth; ++i)
EXPECT_EQ(keys.first.correction_advice(i), 0) << "mid advice " << i;
for (int i = 0; i < 256; ++i)
{
const in_t q = static_cast<in_t>(i);
const auto got = recon(*dpf::eval_point(keys.first, q),
*dpf::eval_point(keys.second, q));
EXPECT_EQ(got, q == alpha ? uint8_t{7} : uint8_t{0}) << i;
}
}

View file

@ -3,6 +3,7 @@
#include "dpf.hpp"
#include <cstdint>
#include <initializer_list>
#include <random>
#include <vector>
@ -40,6 +41,39 @@ void expect_domain(Input r, Input p, Input q, Beta if_true, Beta if_false,
}
}
struct IcPad
{
simde__m128i block() { return dpf::uniform_sample<simde__m128i>(); }
uint8_t bit() { return static_cast<uint8_t>(dpf::uniform_sample<uint8_t>() & 1u); }
};
using IcRng = dpf::ds_randomness<decltype(&dpf::uniform_sample<simde__m128i>), IcPad>;
IcRng ic_rng()
{
return {&dpf::uniform_sample<simde__m128i>, {}};
}
template <typename Input>
void expect_samples(Input r, Input p, Input q, uint32_t if_true, uint32_t if_false,
std::initializer_list<Input> xs)
{
auto keys = dpf::make_dpf(r, dpf::ic(p, q, if_true, if_false));
const uint64_t nmask = keys.first.input_mask;
const uint64_t rb = static_cast<uint64_t>(r);
const uint64_t pb = static_cast<uint64_t>(p);
const uint64_t qb = static_cast<uint64_t>(q);
for (Input x : xs)
{
const uint64_t got = static_cast<uint64_t>(dpf::reconstruct(
dpf::eval_point(dpf::ic, keys.first, x),
dpf::eval_point(dpf::ic, keys.second, x))) & 0xffffffffu;
const uint64_t want = oracle(static_cast<uint64_t>(x), rb, pb, qb, nmask,
if_true, if_false, 0xffffffffu);
EXPECT_EQ(got, want) << "r=" << rb << " x=" << static_cast<uint64_t>(x);
}
}
} // namespace
TEST(Ic, Uint8FullDomainCorners)
@ -222,3 +256,92 @@ TEST(Ic, BitPayload)
EXPECT_EQ(static_cast<bool>(y), w >= 1 && w <= 3) << x;
}
}
TEST(Ic, Uint16FullDomain)
{
expect_domain<uint16_t>(uint16_t{0x0100}, uint16_t{20}, uint16_t{400},
uint32_t{9}, uint32_t{3}, 0xffffffffu);
expect_domain<uint16_t>(uint16_t{0xFF00}, uint16_t{0}, uint16_t{1},
uint32_t{1}, uint32_t{0}, 0xffffffffu);
}
TEST(Ic, WideMasksSampleTheWrap)
{
const uint32_t p32 = 10, q32 = 1000;
const uint32_t r32 = 0xFFFFFFF0u;
expect_samples<uint32_t>(r32, p32, q32, 7u, 2u, {
0u, 1u, p32, q32, q32 + 1u, r32, r32 - 1u, r32 + 1u,
0x80000000u, 0xffffffffu
});
const uint64_t p64 = 1, q64 = 3;
const uint64_t r64 = ~uint64_t{0};
expect_samples<uint64_t>(r64, p64, q64, 5u, 4u, {
0ull, 1ull, 2ull, 3ull, 4ull, r64, r64 - 1ull,
uint64_t{1} << 63
});
auto keys = dpf::make_dpf(r64, dpf::ic(p64, q64, uint32_t{5}, uint32_t{4}));
EXPECT_EQ(keys.first.input_mask, ~uint64_t{0});
}
TEST(Ic, AdditiveDoernerShelatMatchesDealer)
{
auto check = [](uint8_t r0, uint8_t r1, uint8_t p, uint8_t q,
uint32_t beta, uint32_t fals) {
const uint8_t r = static_cast<uint8_t>(r0 + r1);
auto dealer = dpf::make_dpf(r, dpf::ic(p, q, beta, fals));
auto ds = dpf::make_dpf_doerner_shelat(dpf::arith_input, r0, r1, ic_rng(),
dpf::ic(p, q, beta, fals));
for (int x = 0; x < 256; ++x)
{
const auto dealer_y = dpf::reconstruct(
dpf::eval_point(dpf::ic, dealer.first, static_cast<uint8_t>(x)),
dpf::eval_point(dpf::ic, dealer.second, static_cast<uint8_t>(x)));
const auto ds_y = dpf::reconstruct(
dpf::eval_point(dpf::ic, ds.first, static_cast<uint8_t>(x)),
dpf::eval_point(dpf::ic, ds.second, static_cast<uint8_t>(x)));
EXPECT_EQ(dealer_y, ds_y) << "r0=" << int(r0) << " r1=" << int(r1)
<< " x=" << x;
}
};
check(9, 100, 3, 50, 4, 0);
check(200, 100, 0, 255, 8, 1);
check(200, 200, 10, 20, 6, 2);
check(0, 0, 1, 1, 1, 0);
check(1, 0, 0, 0, 3, 9);
}
TEST(Ic, AdditiveGeneval)
{
const uint8_t r0 = 250, r1 = 20, p = 4, q = 8;
const uint32_t beta = 11, fals = 2;
const uint8_t r = static_cast<uint8_t>(r0 + r1);
const uint8_t queries[] = {0, 1, 4, 8, 9, 255};
auto opened = dpf::geneval_ic(dpf::arith_input, r0, r1,
std::begin(queries), std::end(queries), ic_rng(),
dpf::ic(p, q, beta, fals));
ASSERT_EQ(opened.party0.size(), 6u);
for (std::size_t i = 0; i < 6; ++i)
{
const uint64_t got = (opened.party0[i] + opened.party1[i]) & 0xffffffffu;
const uint64_t w = static_cast<uint64_t>(static_cast<uint8_t>(queries[i] - r));
const uint64_t want = (w >= p && w <= q) ? beta : fals;
EXPECT_EQ(got, want) << i;
}
const uint8_t none[] = {0};
auto empty = dpf::geneval_ic(dpf::arith_input, r0, r1,
std::begin(none), std::begin(none), ic_rng(), dpf::ic(p, q, beta));
EXPECT_TRUE(empty.party0.empty());
EXPECT_EQ(empty.live_levels, 0u);
}
TEST(Ic, IntervalRejectsDescendingEndpoints)
{
auto keys = dpf::make_dpf(uint8_t{4}, dpf::ic(uint8_t{1}, uint8_t{6}, uint32_t{1}));
auto buf = dpf::make_output_buffer(dpf::ic, keys.first, 1);
EXPECT_THROW(dpf::eval_interval(dpf::ic, keys.first, uint8_t{9}, uint8_t{2}, buf),
std::invalid_argument);
EXPECT_THROW(dpf::make_output_buffer(dpf::ic, keys.first, uint8_t{9}, uint8_t{2}),
std::invalid_argument);
}

View file

@ -7,6 +7,11 @@
#include "dpf.hpp"
#include "dpf/json.hpp"
static_assert(NLOHMANN_JSON_VERSION_MAJOR == 3
&& NLOHMANN_JSON_VERSION_MINOR == 12
&& NLOHMANN_JSON_VERSION_PATCH == 0,
"JSON tests build against nlohmann 3.12.0");
namespace
{
@ -17,15 +22,16 @@ TEST(IncrementalJsonTest, CmpKeyRoundTrips)
const uint32_t alpha = 0x00abcdefu;
const uint64_t yt = 42u;
auto [k0, k1] = dpf::make_dpf(alpha, dpf::lt(yt));
using KT = std::decay_t<decltype(k0)>;
static_assert(KT::is_multilevel, "cmp key must be multi-level");
ASSERT_EQ(KT::num_outputs, 0u);
using KT0 = std::decay_t<decltype(k0)>;
using KT1 = std::decay_t<decltype(k1)>;
static_assert(KT0::is_multilevel, "cmp key must be multi-level");
ASSERT_EQ(KT0::num_outputs, 0u);
const std::string s0 = dpf::json::to_json(k0);
const std::string s1 = dpf::json::to_json(k1);
auto r0 = dpf::json::from_json<KT>(s0);
auto r1 = dpf::json::from_json<KT>(s1);
auto r0 = dpf::json::from_json<KT0>(s0);
auto r1 = dpf::json::from_json<KT1>(s1);
const uint64_t mask = k0.cmp().mask;
EXPECT_EQ(r0.cmp().nbits, k0.cmp().nbits);
@ -48,10 +54,11 @@ TEST(IncrementalJsonTest, CmpGeqRoundTrips)
const uint32_t alpha = 100u;
const uint64_t yt = 5u, yf = 9u;
auto [k0, k1] = dpf::make_dpf(alpha, dpf::geq(yt, yf));
using KT = std::decay_t<decltype(k0)>;
using KT0 = std::decay_t<decltype(k0)>;
using KT1 = std::decay_t<decltype(k1)>;
auto r0 = dpf::json::from_json<KT>(dpf::json::to_json(k0));
auto r1 = dpf::json::from_json<KT>(dpf::json::to_json(k1));
auto r0 = dpf::json::from_json<KT0>(dpf::json::to_json(k0));
auto r1 = dpf::json::from_json<KT1>(dpf::json::to_json(k1));
const uint64_t mask = k0.cmp().mask;
auto r = [&](uint32_t q) {
@ -62,4 +69,184 @@ TEST(IncrementalJsonTest, CmpGeqRoundTrips)
EXPECT_EQ(r(101u), yt);
}
// A single-output classic key, including its leaf share.
TEST(IncrementalJsonTest, ClassicPointRoundTrips)
{
const uint32_t alpha = 0x00abcdefu;
const uint32_t y = 99u;
auto [k0, k1] = dpf::make_dpf(alpha, y);
using P0 = std::decay_t<decltype(k0)>;
using P1 = std::decay_t<decltype(k1)>;
auto r0 = dpf::json::from_json<P0>(dpf::json::to_json(k0));
auto r1 = dpf::json::from_json<P1>(dpf::json::to_json(k1));
EXPECT_EQ(static_cast<uint32_t>(dpf::reconstruct(
*dpf::eval_point(r0, alpha), *dpf::eval_point(r1, alpha))), y);
EXPECT_EQ(static_cast<uint32_t>(dpf::reconstruct(
*dpf::eval_point(r0, alpha ^ 1u), *dpf::eval_point(r1, alpha ^ 1u))), 0u);
}
// Multi-lane outputs are stored as leaf blocks, not truncated integers.
TEST(IncrementalJsonTest, VecOutputRoundTrips)
{
using out_t = dpf::vec<std::uint32_t, 4>;
const std::uint16_t alpha = 0x1234;
out_t y;
y[0] = 1;
y[1] = 0xffffffffu;
y[2] = 7;
y[3] = 100;
auto [k0, k1] = dpf::make_dpf(alpha, y);
using P0 = std::decay_t<decltype(k0)>;
using P1 = std::decay_t<decltype(k1)>;
auto r0 = dpf::json::from_json<P0>(dpf::json::to_json(k0));
auto r1 = dpf::json::from_json<P1>(dpf::json::to_json(k1));
auto at = [&](std::uint16_t x) {
return dpf::reconstruct(*dpf::eval_point(r0, x), *dpf::eval_point(r1, x));
};
EXPECT_EQ(at(alpha), y);
EXPECT_EQ(at(0), out_t{});
EXPECT_EQ(at(static_cast<std::uint16_t>(alpha + 1)), out_t{});
}
// `at<>` keys carry leaf outputs the comparison-only path used to drop.
TEST(IncrementalJsonTest, AtOutputRoundTrips)
{
const uint32_t x = 0x00abcdefu;
auto [k0, k1] = dpf::make_dpf(x, dpf::at<10>(dpf::bit::one));
using P0 = std::decay_t<decltype(k0)>;
using P1 = std::decay_t<decltype(k1)>;
static_assert(P0::is_multilevel, "at<> key must be multi-level");
ASSERT_GT(P0::num_outputs, 0u);
auto r0 = dpf::json::from_json<P0>(dpf::json::to_json(k0));
auto r1 = dpf::json::from_json<P1>(dpf::json::to_json(k1));
EXPECT_TRUE(static_cast<bool>(dpf::reconstruct(
*dpf::eval_point(dpf::out<0, 10>, r0, x),
*dpf::eval_point(dpf::out<0, 10>, r1, x))));
const uint32_t neighbor = x ^ (1u << (32 - 10));
EXPECT_FALSE(static_cast<bool>(dpf::reconstruct(
*dpf::eval_point(dpf::out<0, 10>, r0, neighbor),
*dpf::eval_point(dpf::out<0, 10>, r1, neighbor))));
}
// Point output and comparison channel on one key.
TEST(IncrementalJsonTest, OutputAndCmpRoundTrips)
{
const uint32_t alpha = 0x00abcdefu;
const uint64_t yt = 42u;
auto [k0, k1] = dpf::make_dpf(alpha, uint32_t{7}, dpf::lt(yt));
using P0 = std::decay_t<decltype(k0)>;
using P1 = std::decay_t<decltype(k1)>;
auto r0 = dpf::json::from_json<P0>(dpf::json::to_json(k0));
auto r1 = dpf::json::from_json<P1>(dpf::json::to_json(k1));
EXPECT_EQ(static_cast<uint32_t>(dpf::reconstruct(
*dpf::eval_point(r0, alpha), *dpf::eval_point(r1, alpha))), 7u);
EXPECT_EQ(static_cast<uint32_t>(dpf::reconstruct(
*dpf::eval_point(r0, alpha ^ 1u), *dpf::eval_point(r1, alpha ^ 1u))), 0u);
const uint64_t mask = k0.cmp().mask;
auto recon_cmp = [&](uint32_t q) {
return dpf::reconstruct(dpf::eval_point(dpf::cmp, r0, q),
dpf::eval_point(dpf::cmp, r1, q)) & mask;
};
EXPECT_EQ(recon_cmp(alpha - 1u), yt);
EXPECT_EQ(recon_cmp(alpha), 0u);
EXPECT_EQ(recon_cmp(alpha + 1u), 0u);
}
// Comparison payloads wider than 64 bits stay in the correction words.
TEST(IncrementalJsonTest, Uint128CmpRoundTrips)
{
using beta = simde_uint128;
const std::uint8_t alpha = 0x20;
const beta hi = (beta{1} << 80) + 9;
const beta lo = beta{3};
auto [k0, k1] = dpf::make_dpf(alpha, dpf::gt(hi, lo));
using P0 = std::decay_t<decltype(k0)>;
using P1 = std::decay_t<decltype(k1)>;
const std::string s0 = dpf::json::to_json(k0);
EXPECT_NE(s0.find("\"cw_last\":["), std::string::npos);
auto r0 = dpf::json::from_json<P0>(s0);
auto r1 = dpf::json::from_json<P1>(dpf::json::to_json(k1));
EXPECT_EQ(r0.cw_last_word(), k0.cw_last_word());
EXPECT_EQ(r0.cmp_addend_word(), k0.cmp_addend_word());
EXPECT_EQ(r0.value_cw(), k0.value_cw());
for (int x = 0; x < 256; ++x)
{
const auto q = static_cast<std::uint8_t>(x);
const beta got = dpf::reconstruct(
dpf::eval_point<beta>(dpf::cmp, r0, q),
dpf::eval_point<beta>(dpf::cmp, r1, q));
const beta want = q > alpha ? hi : lo;
EXPECT_EQ(got, want) << int(q);
}
}
// iDCF prefix corrections are part of the same wide word.
TEST(IncrementalJsonTest, Uint128IdcfRoundTrips)
{
using beta = simde_uint128;
const std::uint8_t alpha = 0x6e;
const beta y = (beta{1} << 100) + 13;
auto [k0, k1] = dpf::make_dpf(alpha, dpf::idcf(dpf::gt(y)));
using P0 = std::decay_t<decltype(k0)>;
using P1 = std::decay_t<decltype(k1)>;
auto r0 = dpf::json::from_json<P0>(dpf::json::to_json(k0));
auto r1 = dpf::json::from_json<P1>(dpf::json::to_json(k1));
EXPECT_EQ(r0.prefix_cws(), k0.prefix_cws());
for (std::uint8_t q : {std::uint8_t{0}, std::uint8_t{0x60}, alpha, std::uint8_t{0x70}})
{
const beta got = dpf::reconstruct(
dpf::eval_point<beta>(dpf::cmp, r0, q),
dpf::eval_point<beta>(dpf::cmp, r1, q));
const beta want = dpf::reconstruct(
dpf::eval_point<beta>(dpf::cmp, k0, q),
dpf::eval_point<beta>(dpf::cmp, k1, q));
EXPECT_EQ(got, want) << int(q);
}
}
// Wildcard coefficients survive, so assign_cmp still works after a round-trip.
TEST(IncrementalJsonTest, WildcardCmpAssignAfterRoundTrip)
{
const uint32_t alpha = 0x00abcdefu;
const uint64_t yt = 42u;
auto [k0, k1] = dpf::make_dpf(alpha, dpf::lt(dpf::wildcard_value<uint64_t>{}));
using P0 = std::decay_t<decltype(k0)>;
using P1 = std::decay_t<decltype(k1)>;
static_assert(P0::cmp_is_wildcard, "expected a wildcard comparison key");
auto r0 = dpf::json::from_json<P0>(dpf::json::to_json(k0));
auto r1 = dpf::json::from_json<P1>(dpf::json::to_json(k1));
EXPECT_FALSE(r0.cmp_assigned());
EXPECT_EQ(r0.value_cw_coeff(), k0.value_cw_coeff());
EXPECT_EQ(r0.cw_last_coeff_word(), k0.cw_last_coeff_word());
dpf::assign_cmp(r0, r1, yt);
EXPECT_TRUE(r0.cmp_assigned());
const uint64_t mask = k0.cmp().mask;
auto r = [&](uint32_t q) {
return dpf::reconstruct(dpf::eval_point(dpf::cmp, r0, q),
dpf::eval_point(dpf::cmp, r1, q)) & mask;
};
EXPECT_EQ(r(alpha - 1u), yt);
EXPECT_EQ(r(0u), yt);
EXPECT_EQ(r(alpha), 0u);
EXPECT_EQ(r(alpha + 1u), 0u);
auto a0 = dpf::json::from_json<P0>(dpf::json::to_json(r0));
auto a1 = dpf::json::from_json<P1>(dpf::json::to_json(r1));
EXPECT_TRUE(a0.cmp_assigned());
auto again = [&](uint32_t q) {
return dpf::reconstruct(dpf::eval_point(dpf::cmp, a0, q),
dpf::eval_point(dpf::cmp, a1, q)) & mask;
};
EXPECT_EQ(again(alpha - 1u), yt);
EXPECT_EQ(again(alpha), 0u);
}
} // namespace

View file

@ -447,7 +447,10 @@ TEST_F(IncrementalDpfTest, DealerMatchesDoernerShelat)
dpf::xor_wrapper<uint16_t>{0xcafe});
reset_tape_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng,
dpf::at<10>(dpf::bit::one),
dpf::at<14>(uint8_t{3}, uint8_t{5}),
@ -458,7 +461,10 @@ TEST_F(IncrementalDpfTest, DealerMatchesDoernerShelat)
EXPECT_TRUE(same_incr_key(dealer.second, ds.second));
reset_tape_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadB> rngb{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds2 = dpf::make_dpf_doerner_shelat(x0, x1, rngb,
dpf::at<10>(dpf::bit::one),
dpf::at<14>(uint8_t{3}, uint8_t{5}),
@ -483,7 +489,10 @@ TEST_F(IncrementalDpfTest, IntermediateWildcardSameKey)
uint64_t{42});
reset_tape_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng,
dpf::at<10>(dpf::bit::one), dpf::at<16>(wc), uint64_t{42});
@ -615,7 +624,10 @@ TEST_F(IncrementalDpfTest, DsEvalAgreesWithDealer)
dpf::at<10>(dpf::bit::one), uint32_t{99});
reset_tape_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng,
dpf::at<10>(dpf::bit::one), uint32_t{99});
@ -796,7 +808,10 @@ TEST_F(IncrementalDpfTest, FixedpointDealerMatchesDoernerShelat)
fp16::from_raw(0x00030000));
reset_tape_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng,
dpf::at<12>(y, z), dpf::at<16>(wc), fp16::from_raw(0x00030000));
@ -1010,7 +1025,10 @@ TEST_F(IncrementalDpfTest, LocalCwProtocolMatchesDsRandomness)
uint32_t x1 = x ^ x0;
reset_tape_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto via_rng = dpf::make_dpf_doerner_shelat(x0, x1, rng,
dpf::at<10>(dpf::bit::one), uint32_t{5});
@ -1156,7 +1174,10 @@ TEST_F(IncrementalDpfTest, CmpDealerMatchesDoernerShelat)
dpf::lt(uint64_t{42}));
reset_tape_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng,
dpf::at<10>(dpf::bit::one),
dpf::lt(uint64_t{42}));
@ -1335,7 +1356,10 @@ TEST_F(IncrementalDpfTest, CmpValueCwGroupWidthDsParity)
dpf::lt(uint16_t{42}));
reset_tape_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng, dpf::lt(uint16_t{42}));
using KT = std::decay_t<decltype(dealer.first)>;
@ -1627,7 +1651,10 @@ TEST_F(IncrementalDpfTest, DsWildcardCmpMatchesDealerThenAssign)
dpf::root_sampler_t<dpf::prg::aes128>{take_root},
dpf::lt(dpf::wildcard_value<uint64_t>{}));
reset_tape_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(a0, a1, rng,
dpf::lt(dpf::wildcard_value<uint64_t>{}));
@ -1686,7 +1713,10 @@ TEST_F(IncrementalDpfTest, DsWildcardCmpWithAtAndNarrowPayload)
dpf::at<16>(uint16_t{9}),
dpf::lt(dpf::wildcard_value<uint16_t>{}));
reset_tape_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(a0, a1, rng,
dpf::at<16>(uint16_t{9}),
dpf::lt(dpf::wildcard_value<uint16_t>{}));

View file

@ -392,7 +392,10 @@ TEST(LaneBlast, GenevalMatchesKeyOnPackedLanes)
dpf::root_sampler_t<dpf::prg::aes128>{take_root}, y);
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
auto on = dpf::geneval_point(x0, x1, alpha, dpf::ds_randomness<simde__m128i (*)(), Pad>{take_root, Pad{}}, y);
HEDLEY_PRAGMA(GCC diagnostic pop)
expect_live_words(keys.first, on);
EXPECT_TRUE(on.leaf_live);
EXPECT_EQ(opened(on.party0[0], on.party1[0]), y);
@ -401,8 +404,11 @@ TEST(LaneBlast, GenevalMatchesKeyOnPackedLanes)
const in_t neighbor = static_cast<in_t>(alpha ^ 1u);
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
auto lane = dpf::geneval_point(x0, x1, neighbor,
dpf::ds_randomness<simde__m128i (*)(), Pad>{take_root, Pad{}}, y);
HEDLEY_PRAGMA(GCC diagnostic pop)
EXPECT_TRUE(lane.leaf_live);
expect_live_words(keys.first, lane);
EXPECT_EQ(opened(lane.party0[0], lane.party1[0]), out_t{});
@ -410,7 +416,10 @@ TEST(LaneBlast, GenevalMatchesKeyOnPackedLanes)
const in_t far = 200;
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
auto off = dpf::geneval_point(x0, x1, far, dpf::ds_randomness<simde__m128i (*)(), Pad>{take_root, Pad{}}, y);
HEDLEY_PRAGMA(GCC diagnostic pop)
EXPECT_FALSE(off.leaf_live);
EXPECT_LT(off.live_levels, off.correction_words.size());
expect_live_words(keys.first, off);
@ -418,8 +427,11 @@ TEST(LaneBlast, GenevalMatchesKeyOnPackedLanes)
EXPECT_NE(off.party0[0], ev(keys.first, far));
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
auto iv = dpf::geneval_interval(x0, x1, in_t{41}, in_t{50},
dpf::ds_randomness<simde__m128i (*)(), Pad>{take_root, Pad{}}, y);
HEDLEY_PRAGMA(GCC diagnostic pop)
EXPECT_TRUE(iv.leaf_live);
expect_live_words(keys.first, iv);
ASSERT_EQ(iv.party0.size(), 10u);
@ -427,7 +439,10 @@ TEST(LaneBlast, GenevalMatchesKeyOnPackedLanes)
EXPECT_EQ(opened(iv.party0[i], iv.party1[i]), out_t{});
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
auto full = dpf::geneval_full(x0, x1, dpf::ds_randomness<simde__m128i (*)(), Pad>{take_root, Pad{}}, y);
HEDLEY_PRAGMA(GCC diagnostic pop)
EXPECT_EQ(full.party0.size(), 256u);
EXPECT_TRUE(full.leaf_live);
expect_live_words(keys.first, full);
@ -440,8 +455,11 @@ TEST(LaneBlast, GenevalMatchesKeyOnPackedLanes)
const in_t seq[] = {0, 255, alpha, neighbor, alpha};
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
auto sq = dpf::geneval_sequence(x0, x1, std::begin(seq), std::end(seq),
dpf::ds_randomness<simde__m128i (*)(), Pad>{take_root, Pad{}}, y);
HEDLEY_PRAGMA(GCC diagnostic pop)
EXPECT_TRUE(sq.leaf_live);
expect_live_words(keys.first, sq);
for (std::size_t i = 0; i < 5; ++i)
@ -463,15 +481,21 @@ TEST(LaneBlast, GenevalNybleArithAndSigned)
auto keys = dpf::make_dpf(secret,
dpf::root_sampler_t<dpf::prg::aes128>{take_root}, y);
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
auto g = dpf::geneval_point(dpf::arith_input, a0, a1, secret,
dpf::ds_randomness<simde__m128i (*)(), Pad>{take_root, Pad{}}, y);
HEDLEY_PRAGMA(GCC diagnostic pop)
EXPECT_TRUE(g.leaf_live);
expect_live_words(keys.first, g);
EXPECT_EQ(opened(g.party0[0], g.party1[0]), y);
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
auto miss = dpf::geneval_point(dpf::arith_input, a0, a1, in_t{250},
dpf::ds_randomness<simde__m128i (*)(), Pad>{take_root, Pad{}}, y);
HEDLEY_PRAGMA(GCC diagnostic pop)
EXPECT_EQ(opened(miss.party0[0], miss.party1[0]), out_t{});
expect_live_words(keys.first, miss);
}
@ -484,7 +508,10 @@ TEST(LaneBlast, GenevalNybleArithAndSigned)
auto keys = dpf::make_dpf(alpha,
dpf::root_sampler_t<dpf::prg::aes128>{take_root}, y);
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
auto full = dpf::geneval_full(x0, x1, dpf::ds_randomness<simde__m128i (*)(), Pad>{take_root, Pad{}}, y);
HEDLEY_PRAGMA(GCC diagnostic pop)
EXPECT_EQ(full.party0.size(), 256u);
EXPECT_TRUE(full.leaf_live);
expect_live_words(keys.first, full);
@ -497,8 +524,11 @@ TEST(LaneBlast, GenevalNybleArithAndSigned)
EXPECT_EQ(full.party0[i], ev(keys.first, v));
}
reset_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
auto iv = dpf::geneval_interval(x0, x1, in_t{-2}, in_t{2},
dpf::ds_randomness<simde__m128i (*)(), Pad>{take_root, Pad{}}, y);
HEDLEY_PRAGMA(GCC diagnostic pop)
ASSERT_EQ(iv.party0.size(), 5u);
for (int q = -2; q <= 2; ++q)
{

View file

@ -0,0 +1,180 @@
#include <gtest/gtest.h>
#include <array>
#include <cstdint>
#include <stdexcept>
#include <vector>
#include "dpf.hpp"
using Interior = dpf::prg::aes128;
using Exterior = dpf::prg::aes128;
namespace
{
template <typename Input, typename Output, typename Key0, typename Key1>
void expect_points(const Key0 & k0, const Key1 & k1,
const std::vector<Input> & alphas, const std::vector<Output> & betas)
{
for (std::size_t i = 0; i < alphas.size(); ++i)
{
EXPECT_EQ(dpf::reconstruct(dpf::eval_multipoint(k0, alphas[i]),
dpf::eval_multipoint(k1, alphas[i])),
betas[i]);
}
}
} // namespace
TEST(Multipoint, PlainPointsAndZeros)
{
using Input = std::uint8_t;
const std::vector<Input> alphas{1, 2, 9, 40};
const std::vector<std::uint64_t> betas{7, 11, 1000, 3};
auto [k0, k1] = dpf::make_multipoint(alphas, betas);
EXPECT_FALSE(decltype(k0)::is_verifiable);
EXPECT_TRUE(decltype(k0)::is_multipoint);
EXPECT_EQ(k0.bucket_count, k1.bucket_count);
EXPECT_EQ(k0.bucket_domain, k1.bucket_domain);
EXPECT_GT(k0.bucket_count, alphas.size());
expect_points(k0, k1, alphas, betas);
for (Input x = 0; x < 255; ++x)
{
const bool hot = x == 1 || x == 2 || x == 9 || x == 40;
const auto y = dpf::reconstruct(dpf::eval_multipoint(k0, x),
dpf::eval_multipoint(k1, x));
if (!hot)
EXPECT_EQ(y, 0u);
}
EXPECT_EQ(dpf::reconstruct(dpf::eval_multipoint(k0, Input{255}),
dpf::eval_multipoint(k1, Input{255})),
0u);
}
TEST(Multipoint, VerifiableFullDomain)
{
using Input = std::uint8_t;
const std::vector<Input> alphas{4, 8, 15, 16};
const std::vector<std::uint64_t> betas{1, 2, 3, 4};
auto [k0, k1] = dpf::make_multipoint(alphas, betas, dpf::verifiable{});
EXPECT_TRUE(decltype(k0)::is_verifiable);
dpf::proof_token batch0{}, batch1{};
std::array<Input, 256> xs{};
for (int i = 0; i < 256; ++i)
xs[static_cast<std::size_t>(i)] = static_cast<Input>(i);
std::vector<decltype(k0)::share_type> y0(xs.size());
std::vector<decltype(k1)::share_type> y1(xs.size());
dpf::eval_multipoint(k0, xs, y0.begin(), dpf::prove(batch0));
dpf::eval_multipoint(k1, xs, y1.begin(), dpf::prove(batch1));
EXPECT_TRUE(dpf::verify(batch0, batch1));
for (std::size_t i = 0; i < xs.size(); ++i)
{
std::uint64_t expect = 0;
for (std::size_t p = 0; p < alphas.size(); ++p)
if (alphas[p] == xs[i])
expect = betas[p];
EXPECT_EQ(dpf::reconstruct(y0[i], y1[i]), expect);
}
dpf::proof_token a0{}, a1{};
dpf::audit_multipoint(k0, dpf::prove(a0));
dpf::audit_multipoint(k1, dpf::prove(a1));
EXPECT_TRUE(dpf::verify(a0, a1));
}
TEST(Multipoint, TamperedBucketRejects)
{
using Input = std::uint8_t;
const std::vector<Input> alphas{3, 5, 7, 9};
const std::vector<std::uint64_t> betas{1, 1, 1, 1};
auto [k0, k1] = dpf::make_multipoint(alphas, betas, dpf::verifiable{});
auto & cs = const_cast<dpf::cs_block &>(k0.buckets[0].correction_seeds()[0]);
cs[0] = simde_mm_xor_si128(cs[0], simde_mm_set1_epi8(1));
dpf::proof_token a0{}, a1{};
dpf::audit_multipoint(k0, dpf::prove(a0));
dpf::audit_multipoint(k1, dpf::prove(a1));
EXPECT_FALSE(dpf::verify(a0, a1));
}
TEST(Multipoint, XorAndHalfTree)
{
using Input = std::uint8_t;
using Ht = dpf::prg::aes128_ccr;
const std::array<Input, 4> alphas{10, 20, 30, 40};
const std::array<dpf::xor_wrapper<std::uint32_t>, 4> betas{
dpf::xor_wrapper<std::uint32_t>{0x11u},
dpf::xor_wrapper<std::uint32_t>{0x22u},
dpf::xor_wrapper<std::uint32_t>{0x33u},
dpf::xor_wrapper<std::uint32_t>{0x44u}};
auto [k0, k1] = dpf::make_multipoint<Ht, Ht>(alphas, betas, dpf::verifiable{});
EXPECT_TRUE(std::decay_t<decltype(k0.buckets[0])>::tree::is_half_tree);
for (std::size_t i = 0; i < alphas.size(); ++i)
{
dpf::proof_token p0{}, p1{};
const auto y0 = dpf::eval_multipoint(k0, alphas[i], dpf::prove(p0));
const auto y1 = dpf::eval_multipoint(k1, alphas[i], dpf::prove(p1));
EXPECT_EQ(dpf::reconstruct(y0, y1), betas[i]);
EXPECT_TRUE(dpf::verify(p0, p1));
}
EXPECT_EQ(dpf::reconstruct(dpf::eval_multipoint(k0, Input{0}),
dpf::eval_multipoint(k1, Input{0})),
dpf::xor_wrapper<std::uint32_t>{0});
}
TEST(Multipoint, Remark1Packing)
{
using Input = std::uint16_t;
std::vector<Input> alphas;
std::vector<std::uint64_t> betas;
alphas.reserve(32);
betas.reserve(32);
for (int i = 0; i < 32; ++i)
{
alphas.push_back(static_cast<Input>(100 + i * 17));
betas.push_back(static_cast<std::uint64_t>(i + 1));
}
auto [k0, k1] = dpf::make_multipoint(alphas, betas);
EXPECT_LE(k0.bucket_count, alphas.size() * 2);
expect_points(k0, k1, alphas, betas);
EXPECT_EQ(dpf::reconstruct(dpf::eval_multipoint(k0, Input{1}),
dpf::eval_multipoint(k1, Input{1})),
0u);
}
TEST(Multipoint, RejectsAnEmptyListAndALengthMismatch)
{
const std::vector<std::uint8_t> alphas{1, 2};
const std::vector<std::uint64_t> betas{1};
EXPECT_THROW(
{
auto keys = dpf::make_multipoint(alphas, betas);
(void)keys;
},
std::invalid_argument);
EXPECT_THROW(
{
auto keys = dpf::make_multipoint(
std::vector<std::uint8_t>{}, std::vector<std::uint64_t>{});
(void)keys;
},
std::invalid_argument);
}
TEST(Multipoint, RejectsDuplicates)
{
const std::vector<std::uint8_t> alphas{1, 2, 1, 4};
const std::vector<std::uint64_t> betas{1, 2, 3, 4};
EXPECT_THROW(
{
auto keys = dpf::make_multipoint(alphas, betas);
(void)keys;
},
std::invalid_argument);
}

View file

@ -168,6 +168,19 @@ TEST(Nmod, RejectsAZeroReciprocalAndAHugeQuotient)
std::overflow_error);
}
TEST(Nmod, ScalePastTheProductWindow)
{
const auto pos = grotto::nmod(1, 200, 1, 60, 4);
EXPECT_EQ(pos.quotient, 0);
EXPECT_EQ(pos.residue, 0);
const auto neg = grotto::nmod(-1, 200, 1, 60, 4);
EXPECT_EQ(neg.quotient, -1);
EXPECT_EQ(neg.residue, 15);
EXPECT_THROW(grotto::nmod(1, 100001u, 1, 0, 4), std::invalid_argument);
}
TEST(Nmod, MostNegativeInputModuloOne)
{
const auto split = grotto::nmod(INT64_MIN, 0, 1, 0, 4);

View file

@ -172,6 +172,8 @@ TEST(PackedLane, ScalarRing)
EXPECT_EQ(10_nyble, dpf::nyble{10});
EXPECT_EQ(dpf::utils::bitlength_of_v<dpf::twobit>, 2u);
EXPECT_EQ(dpf::utils::bitlength_of_v<dpf::nyble>, 4u);
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
EXPECT_EQ((dpf::utils::bitlength_of_output_v<dpf::twobit, simde__m256i>), 2u);
EXPECT_EQ((dpf::utils::bitlength_of_output_v<dpf::nyble, simde__m128i>), 4u);
EXPECT_EQ((dpf::outputs_per_leaf_v<dpf::twobit, simde__m128i>), 64u);
@ -179,6 +181,7 @@ TEST(PackedLane, ScalarRing)
EXPECT_EQ((dpf::outputs_per_leaf_v<dpf::nyble, simde__m128i>), 32u);
EXPECT_EQ((dpf::outputs_per_leaf_v<dpf::nyble, simde__m256i>), 64u);
EXPECT_EQ((dpf::lg_outputs_per_leaf_v<dpf::twobit, simde__m256i>), 7u);
HEDLEY_PRAGMA(GCC diagnostic pop)
}
TEST(PackedLane, SimdMatchesScalar)
@ -229,8 +232,11 @@ TEST(PackedLane, LeafFunctorsMatchSimd)
auto via4 = dpf::lane_arith::mul_epi4(a256, dpf::nyble{7});
EXPECT_EQ(std::memcmp(&scaled, &via4, sizeof(via4)), 0);
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
std::array<simde__m128i, 2> aa{a128, b128};
std::array<simde__m128i, 2> bb{b128, a128};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto both = dpf::add_leaf<dpf::nyble>(aa, bb);
auto e0 = dpf::lane_arith::add_epi4(a128, b128);
auto e1 = dpf::lane_arith::add_epi4(b128, a128);

View file

@ -209,8 +209,11 @@ TEST(PathRecipe, DoernerShelatAndGenevalMatchLength)
lcp_len(static_cast<uint8_t>(x), alpha));
}
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), dpf::detail::urandom_pad_rng> rng{
dpf::uniform_sample<simde__m128i>, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
const uint8_t ends[] = {0x00, 0x91, 0xFF};
auto g = dpf::geneval_cmp(x0, x1, std::begin(ends), std::end(ends), rng,
dpf::lcp(uint64_t{1}));

View file

@ -0,0 +1,135 @@
#include <gtest/gtest.h>
#include <cstdint>
#include <cstring>
#include <stdexcept>
#include <type_traits>
#include "dpf.hpp"
#include "simde/simde/x86/avx2.h"
namespace
{
bool blocks_equal(simde__m128i a, simde__m128i b)
{
return simde_mm_movemask_epi8(simde_mm_cmpeq_epi8(a, b)) == 0xFFFF;
}
simde__m128i block_from_lanes(std::uint64_t lo, std::uint64_t hi)
{
return simde_mm_set_epi64x(static_cast<long long>(hi),
static_cast<long long>(lo));
}
} // namespace
TEST(AesCcrPrg, SigmaRoundTrip)
{
using prg = dpf::prg::aes128_ccr;
const simde__m128i samples[] = {
block_from_lanes(0, 0),
block_from_lanes(1, 0),
block_from_lanes(0, 1),
block_from_lanes(0x0123456789abcdefull, 0xfedcba9876543210ull),
block_from_lanes(~0ull, ~0ull),
};
for (simde__m128i x : samples)
{
EXPECT_TRUE(blocks_equal(prg::sigma_inv(prg::sigma(x)), x));
EXPECT_TRUE(blocks_equal(prg::sigma(prg::sigma_inv(x)), x));
EXPECT_TRUE(blocks_equal(prg::sigma_prime(x),
simde_mm_xor_si128(prg::sigma(x), x)));
}
}
TEST(AesCcrPrg, HashIsAesMmoOfSigma)
{
using prg = dpf::prg::aes128_ccr;
simde__m128i x = block_from_lanes(0x1111222233334444ull, 0x5555666677778888ull);
EXPECT_TRUE(blocks_equal(prg::hash(x),
dpf::prg::aes128::eval(prg::sigma(x), 0)));
EXPECT_TRUE(blocks_equal(prg::H(x), prg::hash(x)));
EXPECT_TRUE(blocks_equal(prg::eval(x, 0), prg::hash(x)));
EXPECT_TRUE(blocks_equal(prg::eval(x, 3),
dpf::prg::aes128::eval(prg::sigma(x), 3)));
}
TEST(AesCcrPrg, Eval01IsHalfTreeChildren)
{
using prg = dpf::prg::aes128_ccr;
simde__m128i seed = block_from_lanes(0x0123456789abcdefull, 0xfedcba9876543210ull);
auto kids = prg::eval01(seed);
const simde__m128i h = prg::hash(seed);
EXPECT_TRUE(blocks_equal(kids[0], h));
EXPECT_TRUE(blocks_equal(kids[1], simde_mm_xor_si128(h, seed)));
EXPECT_TRUE(blocks_equal(simde_mm_xor_si128(kids[0], kids[1]), seed));
}
TEST(AesCcrPrg, TwoTweakDiffersFromHalfExpand)
{
using prg = dpf::prg::aes128_ccr;
simde__m128i seed = block_from_lanes(7, 9);
auto half = prg::eval01(seed);
auto tw = prg::eval01_twotweak(seed);
const simde__m128i base = dpf::unset_lo_bit(seed);
EXPECT_TRUE(blocks_equal(tw[0], prg::hash(base)));
EXPECT_TRUE(blocks_equal(tw[1], prg::hash(dpf::set_lo_bit(base))));
EXPECT_FALSE(blocks_equal(half[0], tw[0])); // half hashes full seed
EXPECT_FALSE(blocks_equal(half[1], tw[1]));
}
TEST(AesCcrPrg, HashX4MatchesScalar)
{
using prg = dpf::prg::aes128_ccr;
alignas(16) simde__m128i in[4] = {
block_from_lanes(1, 2),
block_from_lanes(3, 4),
block_from_lanes(5, 6),
block_from_lanes(7, 8),
};
alignas(16) simde__m128i out[4];
prg::hash_x4(in, out);
for (int i = 0; i < 4; ++i)
EXPECT_TRUE(blocks_equal(out[i], prg::hash(in[i]))) << i;
}
TEST(AesCcrPrg, Eval01X4MatchesScalar)
{
using prg = dpf::prg::aes128_ccr;
alignas(16) simde__m128i seeds[4] = {
block_from_lanes(10, 20),
block_from_lanes(30, 40),
block_from_lanes(50, 60),
block_from_lanes(70, 80),
};
alignas(16) simde__m128i left[4], right[4];
prg::eval01_x4(seeds, left, right);
for (int i = 0; i < 4; ++i)
{
auto kids = prg::eval01(seeds[i]);
EXPECT_TRUE(blocks_equal(left[i], kids[0])) << i;
EXPECT_TRUE(blocks_equal(right[i], kids[1])) << i;
}
}
TEST(AesCcrPrg, BulkEvalMatchesScalarAndRejectsAWrappingSpan)
{
using prg = dpf::prg::aes128_ccr;
simde__m128i seed = block_from_lanes(0xabcdu, 0x1234u);
alignas(16) simde__m128i out[3];
out[0] = block_from_lanes(1, 1);
prg::eval(seed, out, 0);
EXPECT_TRUE(blocks_equal(out[0], block_from_lanes(1, 1)));
prg::eval(seed, out, 3, 5);
for (int i = 0; i < 3; ++i)
EXPECT_TRUE(blocks_equal(out[i], prg::eval(seed, static_cast<std::uint32_t>(5 + i))))
<< i;
EXPECT_THROW(prg::eval(seed, out, 2, 0xffffffffu), std::invalid_argument);
}
TEST(AesCcrPrg, HalfTreeTagIsPresent)
{
static_assert(std::is_void_v<dpf::prg::aes128_ccr::half_tree_tag>);
SUCCEED();
}

View file

@ -243,3 +243,41 @@ TEST(ChachaPrg, ExpandAndBufferedReplay)
EXPECT_EQ(replay.get<0>(), s0);
EXPECT_NE(s0, streamed.get<0>());
}
template <typename PRG>
void expect_chacha_comparison_and_interval()
{
const std::uint8_t alpha = 0x3C;
auto [c0, c1] = dpf::make_dpf<PRG>(alpha, dpf::lt(std::uint32_t{7}, std::uint32_t{1}));
for (int i = 0; i < 256; ++i)
{
const auto x = static_cast<std::uint8_t>(i);
const auto got = dpf::reconstruct(
dpf::eval_point(dpf::cmp, c0, x),
dpf::eval_point(dpf::cmp, c1, x));
EXPECT_EQ(static_cast<std::uint64_t>(got) & 0xffffffffu,
x < alpha ? 7u : 1u)
<< "x=" << i;
}
const std::uint8_t r = 9, p = 2, q = 5;
auto keys = dpf::make_dpf<PRG>(r, dpf::ic(p, q, std::uint32_t{4}, std::uint32_t{1}));
for (int i = 0; i < 256; ++i)
{
const auto x = static_cast<std::uint8_t>(i);
const auto got = dpf::reconstruct(
dpf::eval_point(dpf::ic, keys.first, x),
dpf::eval_point(dpf::ic, keys.second, x));
const auto w = static_cast<std::uint8_t>(x - r);
EXPECT_EQ(static_cast<std::uint64_t>(got) & 0xffffffffu,
(w >= p && w <= q) ? 4u : 1u)
<< "x=" << i;
}
}
TEST(ChachaPrg, ComparisonAndIntervalKeys)
{
expect_chacha_comparison_and_interval<dpf::prg::chacha20>();
expect_chacha_comparison_and_interval<dpf::prg::chacha12>();
expect_chacha_comparison_and_interval<dpf::prg::chacha8>();
}

View file

@ -34,6 +34,8 @@ long double truth_of(grotto::reduced which, long double x)
case grotto::reduced::inv: return 1.0L / x;
case grotto::reduced::rsqrt: return 1.0L / std::sqrt(x);
case grotto::reduced::invsq: return 1.0L / (x * x);
case grotto::reduced::expm1: return expm1l(x);
case grotto::reduced::log1p: return log1pl(x);
}
return 0;
}
@ -64,6 +66,8 @@ const char * name_of(grotto::reduced which)
case grotto::reduced::inv: return "inv";
case grotto::reduced::rsqrt: return "rsqrt";
case grotto::reduced::invsq: return "invsq";
case grotto::reduced::expm1: return "expm1";
case grotto::reduced::log1p: return "log1p";
}
return "?";
}
@ -244,3 +248,99 @@ TEST(RangeLut, RejectsPolesAndNonPositiveLogarithms)
EXPECT_THROW(grotto::eval_reduced(grotto::reduced::csch, 16, 0), std::domain_error);
EXPECT_THROW(grotto::eval_reduced(grotto::reduced::ln, 7, 32), std::invalid_argument);
}
TEST(RangeLut, TanAndSecPolesThrow)
{
const unsigned k = 16;
auto pole_of = [&](grotto::reduced which) {
const std::int64_t lo = raw_of(-8.0L, k);
const std::int64_t hi = raw_of(8.0L, k);
for (std::int64_t raw = lo; raw <= hi; ++raw)
{
try
{
(void)grotto::eval_reduced(which, k, raw);
}
catch (const std::domain_error &)
{
return raw;
}
}
return std::int64_t{0};
};
for (auto which : {grotto::reduced::tan, grotto::reduced::sec})
{
const std::int64_t raw = pole_of(which);
ASSERT_NE(raw, 0) << name_of(which);
EXPECT_THROW(grotto::eval_reduced(which, k, raw), std::domain_error)
<< name_of(which) << " raw=" << raw;
if (raw > raw_of(-8.0L, k))
{
EXPECT_NO_THROW(grotto::eval_reduced(which, k, raw - 1))
<< name_of(which);
}
}
}
TEST(RangeLut, Exp10RejectsAnIntegerPowerPast18)
{
const unsigned k = 16;
int first_overflow = -1;
for (int n = 0; n <= 20; ++n)
{
try
{
(void)grotto::eval_reduced(grotto::reduced::exp10, k, raw_of(static_cast<long double>(n), k));
}
catch (const std::overflow_error &)
{
first_overflow = n;
break;
}
}
ASSERT_GT(first_overflow, 0);
ASSERT_LE(first_overflow, 19);
EXPECT_NO_THROW(grotto::eval_reduced(grotto::reduced::exp10, k,
raw_of(static_cast<long double>(first_overflow - 1), k)));
EXPECT_THROW(grotto::eval_reduced(grotto::reduced::exp10, k, raw_of(19.0L, k)),
std::overflow_error);
EXPECT_THROW(grotto::eval_reduced(grotto::reduced::exp10, k, raw_of(-19.0L, k)),
std::overflow_error);
}
TEST(RangeLut, Expm1AndLog1pTrackLibm)
{
const long double expm1_samples[] = {
-4.0L, -2.0L, -1.0L, -0.7L, -0.5L, -0.25L, -0.125L, -0.015625L,
0.0L, 0.015625L, 0.125L, 0.25L, 0.5L, 0.7L, 1.0L, 1.5L, 2.0L,
};
const long double log1p_samples[] = {
-0.75L, -0.5L, -0.25L, -0.125L, -0.015625L, 0.0L, 0.015625L,
0.125L, 0.25L, 0.5L, 1.0L, 1.5L, 3.0L, 7.5L, 16.0L, 100.0L,
};
for (unsigned k : grotto::principal_precisions)
{
expect_ulps(grotto::reduced::expm1, k, std::ldexp(1.0L, -static_cast<int>(k)), 2.0L);
expect_ulps(grotto::reduced::expm1, k, -std::ldexp(1.0L, -static_cast<int>(k)), 2.0L);
expect_ulps(grotto::reduced::log1p, k, std::ldexp(1.0L, -static_cast<int>(k)), 2.0L);
expect_ulps(grotto::reduced::log1p, k, -std::ldexp(1.0L, -static_cast<int>(k)), 2.0L);
for (long double x : expm1_samples)
expect_ulps(grotto::reduced::expm1, k, x, 20.0L);
for (long double x : log1p_samples)
expect_ulps(grotto::reduced::log1p, k, x, 8.0L);
}
}
TEST(RangeLut, Expm1AndLog1pFixpoints)
{
for (unsigned k : grotto::principal_precisions)
{
EXPECT_EQ(grotto::eval_reduced(grotto::reduced::expm1, k, 0), 0);
EXPECT_EQ(grotto::eval_reduced(grotto::reduced::log1p, k, 0), 0);
const std::int64_t neg_one = -raw_of(1.0L, k);
EXPECT_THROW(grotto::eval_reduced(grotto::reduced::log1p, k, neg_one), std::domain_error);
EXPECT_THROW(grotto::eval_reduced(grotto::reduced::log1p, k, neg_one - 1), std::domain_error);
const std::int64_t far = raw_of(-40.0L, k);
EXPECT_EQ(grotto::eval_reduced(grotto::reduced::expm1, k, far), neg_one);
}
}

View file

@ -639,7 +639,10 @@ TEST_F(StressScenariosTest, ClassicMixedDealerMatchesDoernerShelat)
dpf::root_sampler_t<dpf::prg::aes128>{take_root},
uint32_t{7}, dpf::xor_wrapper<uint32_t>{0xdeadbeef}, uint32_t{42});
reset_tape_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng,
uint32_t{7}, dpf::xor_wrapper<uint32_t>{0xdeadbeef}, uint32_t{42});
@ -974,7 +977,10 @@ TEST_F(StressScenariosTest, CmpPackedAtDeepestUnderDoernerShelat)
uint32_t{9},
dpf::lt(uint64_t{42}));
reset_tape_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng,
dpf::at<14>(uint8_t{3}, uint8_t{5}),
uint32_t{9},
@ -1455,7 +1461,7 @@ TEST_F(StressScenariosTest, ModintMultilevelWithCmp)
// Full-domain cmp on the 12-bit input: true-value 4 below threshold, 1 at/above.
for (unsigned q = 0; q < 0x1000u; q += 0x11u)
{
in_t qi{q};
in_t qi{static_cast<in_t::integral_type>(q)};
const auto got = dpf::reconstruct(dpf::eval_point(dpf::cmp, k0, qi), dpf::eval_point(dpf::cmp, k1, qi)) & mask;
const auto want = (q < 0x2abu) ? uint64_t{4} : uint64_t{1};
EXPECT_EQ(got, want) << "q=" << q;
@ -1509,7 +1515,10 @@ TEST_F(StressScenariosTest, DsIdentityManyLevelsMixedWidthsXorAdditiveCmpLt)
uint32_t{9},
dpf::lt(uint64_t{42}));
reset_tape_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng,
dpf::at<8>(dpf::bit::one),
dpf::at<16>(uint8_t{3}, dpf::xor_wrapper<uint8_t>{0xa5}),
@ -1551,7 +1560,10 @@ TEST_F(StressScenariosTest, DsIdentityPacked16xU8DeepestGeq)
uint32_t{5},
dpf::geq(uint16_t{100}, uint16_t{1}));
reset_tape_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng,
dpf::at<12>(
uint8_t{1}, uint8_t{2}, uint8_t{3}, uint8_t{4},
@ -1580,7 +1592,10 @@ TEST_F(StressScenariosTest, DsIdentityClassicMultiLeafXorAdditiveBytes)
dpf::root_sampler_t<dpf::prg::aes128>{take_root},
uint32_t{7}, dpf::xor_wrapper<uint32_t>{0xdeadbeef});
reset_tape_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng,
uint32_t{7}, dpf::xor_wrapper<uint32_t>{0xdeadbeef});
@ -1605,7 +1620,10 @@ TEST_F(StressScenariosTest, DsIdentityIntermediateWildcardConcreteSiblings)
dpf::at<16>(uint16_t{5}, wc), // concrete sibling next to the wildcard
uint32_t{9});
reset_tape_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng,
dpf::at<10>(dpf::bit::one),
dpf::at<16>(uint16_t{5}, wc),
@ -1637,7 +1655,10 @@ TEST_F(StressScenariosTest, DsIdentityFixedpointAtWithCmp)
fp16::from_raw(0x00030000),
dpf::lt(uint64_t{1000}));
reset_tape_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng,
dpf::at<12>(y),
fp16::from_raw(0x00030000),
@ -1684,7 +1705,10 @@ TEST_F(StressScenariosTest, DsIdentityAesInteriorLowmcExteriorMultilevel)
dpf::root_sampler_t<dpf::prg::aes128>{take_root},
dpf::at<12>(uint8_t{9}), uint16_t{4});
reset_tape_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat<dpf::prg::aes128, dpf::prg::lowmc128>(
x0, x1, rng, dpf::at<12>(uint8_t{9}), uint16_t{4});
@ -1711,7 +1735,10 @@ TEST_F(StressScenariosTest, DsIdentityWildcardCmpThenAssign)
uint32_t{7},
dpf::geq(dpf::wildcard_value<uint64_t>{}));
reset_tape_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng,
dpf::at<12>(uint8_t{5}),
uint32_t{7},
@ -1787,7 +1814,10 @@ TEST_F(StressScenariosTest, DsIdentityModintMultilevelXorAdditiveCmp)
uint16_t{9},
dpf::lt(uint16_t{4}, uint16_t{1}));
reset_tape_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng,
dpf::at<6>(uint8_t{5}, dpf::xor_wrapper<uint8_t>{0x3c}),
uint16_t{9},
@ -1817,7 +1847,10 @@ TEST_F(StressScenariosTest, DsIdentityKeywordMultilevel)
dpf::root_sampler_t<dpf::prg::aes128>{take_root},
dpf::at<6>(uint8_t{1}), uint16_t{2});
reset_tape_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng,
dpf::at<6>(uint8_t{1}), uint16_t{2});
@ -2091,7 +2124,10 @@ TEST_F(StressScenariosTest, DsIdentityPackedSmallWildcardAtNonTerminal)
dpf::at<14>(w, w, w, w, uint8_t{9}, w, w, w),
uint32_t{5});
reset_tape_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng,
dpf::at<14>(w, w, w, w, uint8_t{9}, w, w, w),
uint32_t{5});
@ -2174,7 +2210,10 @@ TEST_F(StressScenariosTest, DsIdentityPackedWildcardLowmcExteriorThenAssign)
dpf::at<12>(w, w, w, w),
uint16_t{77});
reset_tape_roots();
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), PadA> rng{take_root, {}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto ds = dpf::make_dpf_doerner_shelat<dpf::prg::aes128, dpf::prg::lowmc128>(
x0, x1, rng, dpf::at<12>(w, w, w, w), uint16_t{77});

View file

@ -189,8 +189,11 @@ TEST(TypeTraits, IntegralSelectionAndBuiltinWidths)
EXPECT_EQ(dpf::utils::bitlength_of_v<simde_uint128>, 128u);
EXPECT_EQ(dpf::utils::bitlength_of_v<uint128_t>, 128u);
EXPECT_EQ(dpf::utils::bitlength_of_v<uint256_t>, 256u);
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
EXPECT_EQ(dpf::utils::bitlength_of_v<simde__m128i>, 128u);
EXPECT_EQ(dpf::utils::bitlength_of_v<simde__m256i>, 256u);
HEDLEY_PRAGMA(GCC diagnostic pop)
EXPECT_EQ((dpf::utils::bitlength_of_v<std::array<uint32_t, 4>>), 128u);
EXPECT_TRUE(dpf::utils::uses_signed_msb_v<int32_t>);
@ -272,6 +275,8 @@ TEST(TypeTraits, BoundaryWidthsAndOutputPacking)
static_assert(boundary_width_traits<255>());
static_assert(boundary_width_traits<256>());
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
EXPECT_EQ((dpf::utils::bitlength_of_output_v<uint8_t, simde__m128i>), 8u);
EXPECT_EQ((dpf::utils::bitlength_of_output_v<uint16_t, simde__m128i>), 16u);
EXPECT_EQ((dpf::utils::bitlength_of_output_v<uint32_t, simde__m128i>), 32u);
@ -281,7 +286,10 @@ TEST(TypeTraits, BoundaryWidthsAndOutputPacking)
EXPECT_EQ((dpf::utils::bitlength_of_output_v<dpf::bit, simde__m128i>), 1u);
EXPECT_EQ((dpf::utils::bitlength_of_output_v<dpf::modint<10>, simde__m128i>), 16u);
EXPECT_EQ((dpf::utils::bitlength_of_output_v<dpf::wildcard_value<uint32_t>, simde__m128i>), 32u);
HEDLEY_PRAGMA(GCC diagnostic pop)
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
EXPECT_EQ((dpf::outputs_per_leaf_v<uint8_t, simde__m128i>), 16u);
EXPECT_EQ((dpf::outputs_per_leaf_v<uint32_t, simde__m128i>), 4u);
EXPECT_EQ((dpf::outputs_per_leaf_v<dpf::bit, simde__m128i>), 128u);
@ -289,6 +297,7 @@ TEST(TypeTraits, BoundaryWidthsAndOutputPacking)
EXPECT_EQ((dpf::block_length_of_leaf_v<uint32_t, simde__m128i>), 1u);
EXPECT_EQ((dpf::block_length_of_leaf_v<uint256_t, simde__m128i>), 2u);
EXPECT_EQ((dpf::block_length_of_leaf_v<dpf::modint<200>, simde__m128i>), 2u);
HEDLEY_PRAGMA(GCC diagnostic pop)
EXPECT_TRUE(std::is_trivially_copyable_v<uint32_t>);
EXPECT_TRUE(std::is_trivially_copyable_v<dpf::bit>);
@ -463,7 +472,10 @@ TEST(Bit, ConversionArithmeticAndStreams)
EXPECT_EQ(unchanged, dpf::bit::one);
EXPECT_EQ(dpf::utils::bitlength_of_v<dpf::bit>, 1u);
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
EXPECT_EQ((dpf::utils::bitlength_of_output_v<dpf::bit, simde__m128i>), 1u);
HEDLEY_PRAGMA(GCC diagnostic pop)
EXPECT_TRUE(std::numeric_limits<dpf::bit>::is_specialized);
EXPECT_EQ(std::numeric_limits<dpf::bit>::digits, 1);
EXPECT_EQ(std::numeric_limits<dpf::bit const>::digits, 1);
@ -751,7 +763,10 @@ TEST(Wildcard, TraitsAndDeferredValues)
EXPECT_TRUE(dpf::is_wildcard_v<dpf::wildcard_value<uint32_t>>);
EXPECT_TRUE((std::is_same_v<dpf::concrete_type_t<dpf::wildcard_value<dpf::wildcard_value<int>>>, int>));
EXPECT_EQ(dpf::utils::bitlength_of_v<dpf::wildcard_value<uint32_t>>, 32u);
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
EXPECT_EQ((dpf::utils::bitlength_of_output_v<dpf::wildcard_value<dpf::bit>, simde__m256i>), 1u);
HEDLEY_PRAGMA(GCC diagnostic pop)
EXPECT_FALSE(dpf::concrete_value<dpf::wildcard_value<uint32_t>>{}(dpf::wildcard_value<uint32_t>{}).has_value());
EXPECT_EQ(dpf::concrete_value<uint32_t>{}(7u), std::optional<uint32_t>{7u});
@ -865,8 +880,11 @@ TEST(LeafArithmetic, GroupsWiderThanOneLane)
auto pack_sum = [](auto value)
{
using value_type = decltype(value);
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
constexpr auto blocks = dpf::block_length_of_leaf_v<value_type, simde__m128i>;
std::array<simde__m128i, blocks> node{};
HEDLEY_PRAGMA(GCC diagnostic pop)
static_assert(sizeof(node) == sizeof(value_type));
std::memcpy(node.data(), &value, sizeof(value));
return node;
@ -953,14 +971,20 @@ TEST(LeafArithmetic, XorGroupsAndModintLanes)
psnip_uint32_t one_bits = 0;
std::memcpy(&one_bits, &one, sizeof(one_bits));
expect_lanes128<uint32_t>(dpf::multiply_leaf(ones, one), one_bits);
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
EXPECT_TRUE(same128(dpf::leaf_arithmetic::add_t<float, simde__m128i>{}(ones, ones),
simde_mm_setzero_si128()));
HEDLEY_PRAGMA(GCC diagnostic pop)
float two = 2.0f;
simde__m128i one_node{};
simde__m128i two_node{};
std::memcpy(&one_node, &one, sizeof(one));
std::memcpy(&two_node, &two, sizeof(two));
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
auto xored = dpf::leaf_arithmetic::add_t<float, simde__m128i>{}(one_node, two_node);
HEDLEY_PRAGMA(GCC diagnostic pop)
float xored_float = 0;
std::memcpy(&xored_float, &xored, sizeof(xored_float));
EXPECT_NE(xored_float, one + two);
@ -970,9 +994,12 @@ TEST(LeafArithmetic, XorGroupsAndModintLanes)
std::memcpy(&dbl_bits, &dbl, sizeof(dbl_bits));
auto dbl_scaled = dpf::multiply_leaf(simde_mm_set1_epi64x(-1), dbl);
EXPECT_EQ(lane128<uint64_t>(dbl_scaled, 0), dbl_bits);
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
EXPECT_TRUE(same256(dpf::leaf_arithmetic::add_t<double, simde__m256i>{}(
splat256<uint64_t>(dbl_bits), splat256<uint64_t>(dbl_bits)),
simde_mm256_setzero_si256()));
HEDLEY_PRAGMA(GCC diagnostic pop)
auto wildcard_sum = dpf::add_leaf<dpf::wildcard_value<uint32_t>>(
splat128<uint32_t>(40), splat128<uint32_t>(2));
@ -996,8 +1023,11 @@ TEST(CustomTypes, InputWalkAndOutputLeaf)
auto sum = dpf::add_leaf<custom_output_type_small>(splat128<uint64_t>(9), splat128<uint64_t>(4));
auto diff = dpf::subtract_leaf<custom_output_type_small>(splat128<uint64_t>(9), splat128<uint64_t>(4));
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
auto prod = dpf::leaf_arithmetic::multiply_t<custom_output_type_small, simde__m128i>{}(
splat128<uint64_t>(6), static_cast<psnip_uint64_t>(7));
HEDLEY_PRAGMA(GCC diagnostic pop)
expect_lanes128<uint64_t>(sum, 13);
expect_lanes128<uint64_t>(diff, 5);
expect_lanes128<uint64_t>(prod, 42);

View file

@ -0,0 +1,211 @@
#include <gtest/gtest.h>
#include <array>
#include <cstdint>
#include <cstring>
#include <vector>
#include "dpf.hpp"
using Interior = dpf::prg::aes128;
using Exterior = dpf::prg::aes128;
TEST(Verifiable, HonestPointAccepts)
{
using Input = std::uint8_t;
const Input alpha = 0x2a;
const std::uint64_t beta = 7;
auto [k0, k1] = dpf::make_dpf<Interior, Exterior>(alpha, beta, dpf::verifiable{});
EXPECT_TRUE(decltype(k0)::is_verifiable);
EXPECT_FALSE(decltype(k0)::is_multilevel);
dpf::proof_token pi0{}, pi1{};
const auto y0 = *dpf::eval_point(k0, alpha, dpf::prove(pi0));
const auto y1 = *dpf::eval_point(k1, alpha, dpf::prove(pi1));
EXPECT_EQ(dpf::reconstruct(y0, y1), beta);
EXPECT_TRUE(dpf::verify(pi0, pi1));
dpf::proof_token q0{}, q1{};
const Input other = static_cast<Input>(alpha ^ 1);
EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(k0, other, dpf::prove(q0)),
*dpf::eval_point(k1, other, dpf::prove(q1))),
0);
EXPECT_TRUE(dpf::verify(q0, q1));
}
TEST(Verifiable, TamperedCwRejects)
{
using Input = std::uint8_t;
auto [k0, k1] = dpf::make_dpf<Interior, Exterior>(Input{3},
std::uint64_t{1}, dpf::verifiable{});
// Flip one bit of a public correction word on party 0's view of the
// shared CW array by rebuilding an otherwise-identical key is hard;
// instead flip cs after the fact via const_cast of the seed storage.
auto & cs = const_cast<dpf::cs_block &>(k0.correction_seeds()[0]);
cs[0] = simde_mm_xor_si128(cs[0], simde_mm_set1_epi8(1));
dpf::proof_token pi0{}, pi1{};
(void)*dpf::eval_point(k0, Input{3}, dpf::prove(pi0));
(void)*dpf::eval_point(k1, Input{3}, dpf::prove(pi1));
EXPECT_FALSE(dpf::verify(pi0, pi1));
}
TEST(Verifiable, BatchVerify)
{
using Input = std::uint8_t;
auto [k0, k1] = dpf::make_dpf<Interior, Exterior>(Input{1},
std::uint64_t{9}, dpf::verifiable{});
std::vector<dpf::proof_token> left, right;
for (Input x = 0; x < 8; ++x)
{
dpf::proof_token a{}, b{};
(void)*dpf::eval_point(k0, x, dpf::prove(a));
(void)*dpf::eval_point(k1, x, dpf::prove(b));
left.push_back(a);
right.push_back(b);
}
EXPECT_TRUE(dpf::verify_batch(left, right));
left[2][0] = simde_mm_xor_si128(left[2][0], simde_mm_set1_epi8(0xff));
EXPECT_FALSE(dpf::verify_batch(left, right));
right.pop_back();
EXPECT_FALSE(dpf::verify_batch(left, right));
}
TEST(Verifiable, HalfTreeXorPayload)
{
using Input = std::uint16_t;
using Ht = dpf::prg::aes128_ccr;
const Input alpha = 0x0101;
auto [k0, k1] = dpf::make_dpf<Ht, Ht>(alpha,
dpf::xor_wrapper<std::uint64_t>{0xdeadbeefull}, dpf::verifiable{});
EXPECT_TRUE(decltype(k0)::tree::is_half_tree);
dpf::proof_token pi0{}, pi1{};
const auto y0 = *dpf::eval_point(k0, alpha, dpf::prove(pi0));
const auto y1 = *dpf::eval_point(k1, alpha, dpf::prove(pi1));
EXPECT_EQ(dpf::reconstruct(y0, y1), dpf::xor_wrapper<std::uint64_t>{0xdeadbeefull});
EXPECT_TRUE(dpf::verify(pi0, pi1));
}
TEST(Verifiable, SamePublicPart)
{
using Input = std::uint8_t;
auto [k0, k1] = dpf::make_dpf<Interior, Exterior>(Input{5},
std::uint64_t{2}, dpf::verifiable{});
EXPECT_TRUE(dpf::same_public_part(k0, k1));
auto & cw = const_cast<typename std::decay_t<decltype(k0)>::interior_node &>(
k0.correction_words()[0]);
cw = simde_mm_xor_si128(cw, simde_mm_set1_epi8(1));
EXPECT_FALSE(dpf::same_public_part(k0, k1));
}
TEST(Verifiable, DefaultKeyUnchangedLayout)
{
using Input = std::uint8_t;
auto [a0, a1] = dpf::make_dpf<Interior, Exterior>(Input{1}, std::uint64_t{3});
auto [b0, b1] = dpf::make_dpf<Interior, Exterior>(Input{1}, std::uint64_t{3},
dpf::verifiable{});
EXPECT_FALSE(decltype(a0)::is_verifiable);
EXPECT_TRUE(decltype(b0)::is_verifiable);
EXPECT_EQ(sizeof(a0.correction_words()), sizeof(b0.correction_words()));
EXPECT_EQ(std::tuple_size_v<typename decltype(a0)::correction_seeds_array>, 0u);
EXPECT_GT(std::tuple_size_v<typename decltype(b0)::correction_seeds_array>, 0u);
}
TEST(Extractable, Fp61ReconstructAndSketch)
{
using Input = std::uint8_t;
const Input alpha = 0x11;
const dpf::fp61 beta{42};
auto [k0, k1] = dpf::make_dpf<Interior, Exterior>(alpha, beta,
dpf::extractable{}, dpf::verifiable{});
EXPECT_TRUE(decltype(k0)::is_extractable);
EXPECT_TRUE(decltype(k0)::is_verifiable);
EXPECT_TRUE(dpf::same_public_part(k0, k1));
const auto y0 = *dpf::eval_point(k0, alpha);
const auto y1 = *dpf::eval_point(k1, alpha);
EXPECT_EQ(dpf::reconstruct(y0, y1), beta);
std::array<Input, 4> pts{0x10, 0x11, 0x12, 0x13};
std::array<dpf::fp61, 4> r{
dpf::fp61{3}, dpf::fp61{5}, dpf::fp61{7}, dpf::fp61{11}};
std::array<dpf::fp61, 4> s0{}, s1{};
for (std::size_t i = 0; i < pts.size(); ++i)
{
s0[i] = (*dpf::eval_point(k0, pts[i])).raw();
s1[i] = (*dpf::eval_point(k1, pts[i])).raw();
}
auto sk0 = dpf::sketch_fold(s0, r);
auto sk1 = dpf::sketch_fold(s1, r);
EXPECT_TRUE(dpf::sketch_verify(sk0, sk1));
// Two hot points: forge by XORing a second beta into another share.
s0[0] = s0[0] + beta;
sk0 = dpf::sketch_fold(s0, r);
sk1 = dpf::sketch_fold(s1, r);
EXPECT_FALSE(dpf::sketch_verify(sk0, sk1));
}
TEST(Extractable, IncrementalPrefix)
{
using Input = std::uint16_t;
const Input alpha = 0x00ab;
auto [k0, k1] = dpf::make_dpf<Interior, Exterior>(alpha,
dpf::at<8>(dpf::fp61{1}), dpf::extractable{});
EXPECT_TRUE(decltype(k0)::is_extractable);
EXPECT_TRUE(decltype(k0)::is_multilevel);
const auto p0 = *dpf::eval_point(dpf::out<0>, k0, alpha);
const auto p1 = *dpf::eval_point(dpf::out<0>, k1, alpha);
EXPECT_EQ(dpf::reconstruct(p0, p1), dpf::fp61{1});
}
TEST(Verifiable, IntervalProve)
{
using Input = std::uint8_t;
auto [k0, k1] = dpf::make_dpf<Interior, Exterior>(Input{0x20},
std::uint64_t{1}, dpf::verifiable{});
dpf::proof_token a{}, b{};
dpf::prove_interval(k0, Input{0x1c}, Input{0x24}, dpf::prove(a));
dpf::prove_interval(k1, Input{0x1c}, Input{0x24}, dpf::prove(b));
EXPECT_TRUE(dpf::verify(a, b));
}
TEST(Verifiable, DoernerShelatProve)
{
using Input = std::uint8_t;
const Input alpha = 0x44;
const std::uint64_t beta = 5;
Input x0 = 0x12;
Input x1 = static_cast<Input>(alpha ^ x0);
struct Pad
{
std::uint64_t n = 1;
simde__m128i block()
{
auto v = simde_mm_set_epi64x(static_cast<long long>(n),
static_cast<long long>(n * 9 + 3));
n += 2;
return v;
}
std::uint8_t bit() { return static_cast<std::uint8_t>(n++ & 1u); }
};
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
dpf::ds_randomness<simde__m128i (*)(), Pad> rng{
dpf::uniform_sample<simde__m128i>, Pad{}};
HEDLEY_PRAGMA(GCC diagnostic pop)
auto [s0, s1] = dpf::make_dpf_doerner_shelat<Interior, Exterior>(
x0, x1, rng, beta, dpf::verifiable{});
EXPECT_TRUE(decltype(s0)::is_verifiable);
dpf::proof_token a{}, b{};
EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(s0, alpha, dpf::prove(a)),
*dpf::eval_point(s1, alpha, dpf::prove(b))),
beta);
EXPECT_TRUE(dpf::verify(a, b));
}

View file

@ -0,0 +1,345 @@
#include <gtest/gtest.h>
#include "dpf.hpp"
#include "grotto/fixedpoint.hpp"
#include <cstdint>
#include <utility>
namespace
{
template <typename Beta, typename A, typename B, typename X>
Beta recon_cmp(const A & a, const B & b, X x)
{
return dpf::reconstruct(
dpf::eval_point<Beta>(dpf::cmp, a, x),
dpf::eval_point<Beta>(dpf::cmp, b, x));
}
template <typename Beta, typename A, typename B, typename X>
Beta recon_prefix4(const A & a, const B & b, X x)
{
return dpf::reconstruct(
dpf::eval_point<4, Beta>(dpf::cmp_prefix<4>, a, x),
dpf::eval_point<4, Beta>(dpf::cmp_prefix<4>, b, x));
}
} // namespace
TEST(WidePayload, VectorPointAddsComponentwise)
{
using out_t = dpf::vec<std::uint32_t, 4>;
const std::uint16_t alpha = 0x1234;
out_t y;
y[0] = 1;
y[1] = 0xffffffffu;
y[2] = 7;
y[3] = 100;
auto [k0, k1] = dpf::make_dpf(alpha, y);
auto at = [&](std::uint16_t x) {
return dpf::reconstruct(*dpf::eval_point(k0, x), *dpf::eval_point(k1, x));
};
EXPECT_EQ(at(alpha), y);
EXPECT_EQ(at(0), out_t{});
EXPECT_EQ(at(static_cast<std::uint16_t>(alpha + 1)), out_t{});
}
TEST(WidePayload, Uint128Comparison)
{
using beta = simde_uint128;
const std::uint8_t alpha = 0x20;
const beta hi = (beta{1} << 80) + 9;
const beta lo = beta{3};
auto [k0, k1] = dpf::make_dpf(alpha, dpf::gt(hi, lo));
for (int x = 0; x < 256; ++x)
{
const auto q = static_cast<std::uint8_t>(x);
const beta got = recon_cmp<beta>(k0, k1, q);
const beta want = q > alpha ? hi : lo;
EXPECT_EQ(got, want) << int(q);
}
}
TEST(WidePayload, FixedpointComparison)
{
using beta = grotto::fixedpoint<8, std::uint32_t>;
const std::uint8_t alpha = 10;
const beta hi = beta::from_raw(0x01020304u);
const beta lo = beta::from_raw(0x00000007u);
auto [k0, k1] = dpf::make_dpf(alpha, dpf::lt(hi, lo));
EXPECT_EQ(recon_cmp<beta>(k0, k1, std::uint8_t{9}), hi);
EXPECT_EQ(recon_cmp<beta>(k0, k1, alpha), lo);
EXPECT_EQ(recon_cmp<beta>(k0, k1, std::uint8_t{11}), lo);
}
TEST(WidePayload, WideFixedpointComparison)
{
using beta = grotto::fixedpoint<4, simde_uint128>;
const std::uint8_t alpha = 4;
const beta hi = beta::from_raw((simde_uint128{1} << 70) + 11);
const beta lo = beta::from_raw(simde_uint128{2});
auto [k0, k1] = dpf::make_dpf(alpha, dpf::geq(hi, lo));
EXPECT_EQ(recon_cmp<beta>(k0, k1, std::uint8_t{3}), lo);
EXPECT_EQ(recon_cmp<beta>(k0, k1, alpha), hi);
EXPECT_EQ(recon_cmp<beta>(k0, k1, std::uint8_t{5}), hi);
}
TEST(WidePayload, VectorComparison)
{
using beta = dpf::vec<std::uint16_t, 3>;
const std::uint8_t alpha = 7;
beta hi;
hi[0] = 9;
hi[1] = 1000;
hi[2] = 4;
beta lo;
lo[1] = 1;
auto [k0, k1] = dpf::make_dpf(alpha, dpf::gt(hi, lo));
EXPECT_EQ(recon_cmp<beta>(k0, k1, std::uint8_t{6}), lo);
EXPECT_EQ(recon_cmp<beta>(k0, k1, std::uint8_t{8}), hi);
}
TEST(WidePayload, IntervalContainmentUint128)
{
using beta = simde_uint128;
const std::uint8_t r = 20;
const beta hi = (beta{1} << 96) + 5;
auto [k0, k1] = dpf::make_dpf(r, dpf::ic(std::uint8_t{3}, std::uint8_t{5}, hi));
auto at = [&](std::uint8_t opened) {
return dpf::reconstruct(
dpf::eval_point(dpf::ic, k0, opened),
dpf::eval_point(dpf::ic, k1, opened));
};
EXPECT_EQ(at(23), hi);
EXPECT_EQ(at(25), hi);
EXPECT_EQ(at(22), beta{});
EXPECT_EQ(at(26), beta{});
}
TEST(WidePayload, IdcfUint128MatchesFullComparison)
{
using beta = simde_uint128;
const std::uint8_t alpha = 0x6e;
const beta y = (beta{1} << 100) + 13;
auto [i0, i1] = dpf::make_dpf(alpha, dpf::idcf(dpf::gt(y)));
auto [f0, f1] = dpf::make_dpf(alpha, dpf::gt(y));
for (int x = 0; x < 256; ++x)
{
const auto q = static_cast<std::uint8_t>(x);
EXPECT_EQ(recon_cmp<beta>(i0, i1, q), recon_cmp<beta>(f0, f1, q));
const bool above = (q >> 4) > (alpha >> 4);
const beta prefix = recon_prefix4<beta>(i0, i1, q);
const beta want = above ? y : beta{};
EXPECT_EQ(prefix, want) << int(q);
}
}
TEST(WidePayload, DealerMatchesDoernerShelat)
{
using beta = simde_uint128;
const std::uint16_t alpha = 0x0102;
const std::uint16_t x0 = 0x00f0;
const std::uint16_t x1 = static_cast<std::uint16_t>(alpha ^ x0);
const beta y = (beta{1} << 77) + 4;
auto dealer = dpf::make_dpf(alpha, dpf::gt(y));
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
auto ds = dpf::make_dpf_doerner_shelat(x0, x1,
dpf::ds_randomness<simde__m128i (*)(), dpf::detail::urandom_pad_rng>{
dpf::uniform_sample<simde__m128i>, {}},
dpf::gt(y));
HEDLEY_PRAGMA(GCC diagnostic pop)
for (std::uint16_t q : {std::uint16_t{0}, std::uint16_t{0x0101}, alpha,
std::uint16_t{0x0103}, std::uint16_t{0xffff}})
{
const beta from_dealer = recon_cmp<beta>(dealer.first, dealer.second, q);
const beta from_ds = recon_cmp<beta>(ds.first, ds.second, q);
EXPECT_EQ(from_dealer, from_ds) << q;
}
}
TEST(WidePayload, XorWrapperComparison)
{
using beta = dpf::xor_wrapper<std::uint32_t>;
const std::uint8_t alpha = 9;
const beta hi{0x01020304u};
const beta lo{0x0000000fu};
auto [k0, k1] = dpf::make_dpf(alpha, dpf::gt(hi, lo));
EXPECT_EQ(recon_cmp<beta>(k0, k1, std::uint8_t{8}), lo);
EXPECT_EQ(recon_cmp<beta>(k0, k1, std::uint8_t{10}), hi);
}
TEST(WidePayload, WildcardAssignUint128)
{
using beta = simde_uint128;
const std::uint8_t alpha = 15;
const beta hi = (beta{1} << 90) + 8;
const beta lo = beta{2};
const auto wild = dpf::wildcard<beta>;
auto [k0, k1] = dpf::make_dpf(alpha, dpf::gt(wild));
dpf::assign_cmp(k0, k1, hi, lo);
EXPECT_EQ(recon_cmp<beta>(k0, k1, std::uint8_t{14}), lo);
EXPECT_EQ(recon_cmp<beta>(k0, k1, std::uint8_t{16}), hi);
}
TEST(WidePayload, IntervalContainmentUint128FalseAndTopBound)
{
using beta = simde_uint128;
const std::uint8_t r = 10;
const beta hi = (beta{1} << 90) + 8;
const beta lo = beta{3};
auto [k0, k1] = dpf::make_dpf(r,
dpf::ic(std::uint8_t{5}, std::uint8_t{255}, hi, lo));
for (int x = 0; x < 256; ++x)
{
const auto q = static_cast<std::uint8_t>(x);
const auto w = static_cast<std::uint8_t>(q - r);
const beta want = (w >= 5) ? hi : lo;
EXPECT_EQ(dpf::reconstruct(
dpf::eval_point(dpf::ic, k0, q),
dpf::eval_point(dpf::ic, k1, q)),
want) << int(q);
}
}
TEST(WidePayload, IntervalContainmentXorGroup)
{
using beta = dpf::xor_wrapper<std::uint32_t>;
const std::uint8_t r = 40;
const beta hi{0x11111111u};
const beta lo{0x01010101u};
auto [k0, k1] = dpf::make_dpf(r,
dpf::ic(std::uint8_t{1}, std::uint8_t{255}, hi, lo));
for (int x = 0; x < 256; ++x)
{
const auto q = static_cast<std::uint8_t>(x);
const auto w = static_cast<std::uint8_t>(q - r);
const beta want = (w >= 1) ? hi : lo;
EXPECT_EQ(dpf::reconstruct(
dpf::eval_point(dpf::ic, k0, q),
dpf::eval_point(dpf::ic, k1, q)),
want) << int(q);
}
}
TEST(WidePayload, IntervalContainmentVector)
{
using beta = dpf::vec<std::uint32_t, 2>;
beta hi;
hi[0] = 9;
hi[1] = 0xffffffffu;
beta lo;
lo[0] = 1;
lo[1] = 2;
const std::uint8_t r = 8;
auto [k0, k1] = dpf::make_dpf(r,
dpf::ic(std::uint8_t{2}, std::uint8_t{4}, hi, lo));
auto at = [&](std::uint8_t opened) {
return dpf::reconstruct(
dpf::eval_point(dpf::ic, k0, opened),
dpf::eval_point(dpf::ic, k1, opened));
};
EXPECT_EQ(at(10), hi);
EXPECT_EQ(at(12), hi);
EXPECT_EQ(at(9), lo);
EXPECT_EQ(at(13), lo);
}
TEST(WidePayload, IntervalWildcardAssignUint128)
{
using beta = simde_uint128;
const std::uint8_t r = 15;
const beta hi = (beta{1} << 90) + 8;
const beta lo = beta{2};
auto [k0, k1] = dpf::make_dpf(r,
dpf::ic(std::uint8_t{2}, std::uint8_t{8}, dpf::wildcard<beta>));
EXPECT_THROW(dpf::eval_point(dpf::ic, k0, std::uint8_t{0}), std::invalid_argument);
dpf::assign_cmp(k0, k1, hi, lo);
auto at = [&](std::uint8_t opened) {
return dpf::reconstruct(
dpf::eval_point(dpf::ic, k0, opened),
dpf::eval_point(dpf::ic, k1, opened));
};
EXPECT_EQ(at(17), hi);
EXPECT_EQ(at(23), hi);
EXPECT_EQ(at(16), lo);
EXPECT_EQ(at(24), lo);
}
TEST(WidePayload, IntervalDoernerShelatUint128)
{
using beta = simde_uint128;
const std::uint8_t r0 = 9;
const std::uint8_t r1 = 100;
const std::uint8_t r = static_cast<std::uint8_t>(r0 ^ r1);
const beta hi = (beta{1} << 70) + 4;
const beta lo = beta{1};
auto dealer = dpf::make_dpf(r, dpf::ic(std::uint8_t{3}, std::uint8_t{50}, hi, lo));
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
auto ds = dpf::make_dpf_doerner_shelat(r0, r1,
dpf::ds_randomness<simde__m128i (*)(), dpf::detail::urandom_pad_rng>{
dpf::uniform_sample<simde__m128i>, {}},
dpf::ic(std::uint8_t{3}, std::uint8_t{50}, hi, lo));
HEDLEY_PRAGMA(GCC diagnostic pop)
for (int x = 0; x < 256; x += 17)
{
const auto q = static_cast<std::uint8_t>(x);
EXPECT_EQ(dpf::reconstruct(
dpf::eval_point(dpf::ic, dealer.first, q),
dpf::eval_point(dpf::ic, dealer.second, q)),
dpf::reconstruct(
dpf::eval_point(dpf::ic, ds.first, q),
dpf::eval_point(dpf::ic, ds.second, q)))
<< int(q);
}
}
TEST(WidePayload, Modint96ComparisonAndInterval)
{
using beta = dpf::modint<96>;
const beta hi{simde_uint128{1} << 80};
const beta lo{simde_uint128{6}};
const std::uint8_t alpha = 12;
auto cmp = dpf::make_dpf(alpha, dpf::lt(hi, lo));
for (int x = 0; x < 256; ++x)
{
const auto q = static_cast<std::uint8_t>(x);
const beta want = q < alpha ? hi : lo;
EXPECT_EQ(recon_cmp<beta>(cmp.first, cmp.second, q), want) << int(q);
}
const std::uint8_t r = 4;
auto ic = dpf::make_dpf(r, dpf::ic(std::uint8_t{1}, std::uint8_t{3}, hi, lo));
auto at = [&](std::uint8_t opened) {
return dpf::reconstruct(
dpf::eval_point(dpf::ic, ic.first, opened),
dpf::eval_point(dpf::ic, ic.second, opened));
};
EXPECT_EQ(at(5), hi);
EXPECT_EQ(at(7), hi);
EXPECT_EQ(at(4), lo);
EXPECT_EQ(at(8), lo);
}
TEST(WidePayload, VectorLaneArithmeticDoesNotCarry)
{
dpf::vec<std::uint32_t, 2> a;
dpf::vec<std::uint32_t, 2> b;
a[0] = 0xffffffffu;
a[1] = 1u;
b[0] = 2u;
b[1] = 3u;
const auto sum = a + b;
EXPECT_EQ(sum[0], 1u);
EXPECT_EQ(sum[1], 4u);
const auto prod = a * b;
EXPECT_EQ(prod[0], 0xfffffffeu);
EXPECT_EQ(prod[1], 3u);
const auto neg = -a;
EXPECT_EQ(neg[0], 1u);
EXPECT_EQ(neg[1], 0xffffffffu);
EXPECT_EQ(a, a);
EXPECT_NE(a, b);
}