/// @file party/flows_extreme.cpp /// @brief Extreme grotto / DCF / geneval / DS / complex-beaver party flows. /// @details Amalgamated into run.cpp (do not compile as a second TU). #include "cases.hpp" #include "dist_ds.hpp" #include "flow_util.hpp" #include "key_io.hpp" #include "registry.hpp" #include #include #include #include #include #include #include #include #include #include #include "simde/simde/x86/avx2.h" #include "dpf/beaver.hpp" #include "dpf/dcf.hpp" #include "dpf/dpf_key.hpp" #include "dpf/eval_full.hpp" #include "dpf/eval_interval.hpp" #include "dpf/eval_point.hpp" #include "dpf/eval_sequence.hpp" #include "dpf/eval_unified.hpp" #include "dpf/geneval.hpp" #include "dpf/incremental.hpp" #include "dpf/prg_aes.hpp" #include "grotto/constant_lut.hpp" #include "grotto/offset_horner.hpp" #include "grotto/prefix_parity.hpp" namespace dpf { namespace party { namespace extreme { using util::Counter; using util::evaluate_online; using util::install_and_bind_u64; using util::open_additive; using util::open_subtractive; using util::require; using util::role; using util::share_bits; using util::trio; using util::u64; // ---- helpers ---- bool open_xor_bit(trio & net, role self, bool mine) { role peer = self == role::p0 ? role::p1 : role::p0; bool theirs = net.exchange_with(peer, mine); return mine ^ theirs; } struct Pad { std::uint64_t n = 1; simde__m128i block() { auto v = simde_mm_set_epi64x(static_cast(n), static_cast(n * 9 + 3)); n += 2; return v; } std::uint8_t bit() { return static_cast(n++ & 1u); } }; HEDLEY_PRAGMA(GCC diagnostic push) HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes") ds_randomness make_ds_rng() { return {uniform_sample, Pad{}}; } HEDLEY_PRAGMA(GCC diagnostic pop) template int piece_containing(T alpha, const std::array & ends) { for (std::size_t i = 0; i + 1 < N; ++i) { if (alpha >= ends[i] && alpha < ends[i + 1]) return static_cast(i); } return static_cast(N - 1); } u64 expect_cmp(int kind, std::uint8_t x, std::uint8_t alpha, u64 beta) { // kind: 0=lt 1=leq 2=gt 3=geq bool hit = false; if (kind == 0) hit = x < alpha; else if (kind == 1) hit = x <= alpha; else if (kind == 2) hit = x > alpha; else hit = x >= alpha; return hit ? beta : 0u; } // ============================================================================ // Extreme beaver: large vectors, nested products, mixed batches // ============================================================================ template int beaver_dot_extreme_n(role self, trio & net) { beavers::session s; std::vector::wire> xs, ys; std::vector secrets; u64 expect = 0; for (std::size_t i = 0; i < N; ++i) { xs.push_back(s.input()); ys.push_back(s.input()); u64 a = static_cast(i * 17 + 3); u64 b = static_cast((N - i) * 11 + 5); secrets.push_back(a); secrets.push_back(b); expect += a * b; } auto z = s.dot(xs, ys); if (self == role::p2) { require(s.round_of(z) == 1, "dot extreme round"); Counter rng; s.sample(rng); net::deal_session(net, s); return 0; } std::vector::wire> inputs; inputs.reserve(2 * N); for (std::size_t i = 0; i < N; ++i) { inputs.push_back(xs[i]); inputs.push_back(ys[i]); } install_and_bind_u64(s, net, self, inputs, secrets); evaluate_online(s, net, self); auto o = open_additive(net, self, s.value_party(z)); if (self == role::p0) require(o == expect, "dot extreme"); return 0; } int beaver_nested_dots(role self, trio & net) { // (x·y) * (u·v) — two-round nested product of dots constexpr std::size_t N = 8; beavers::session s; std::vector::wire> xs, ys, us, vs; std::vector xsec, ysec, usec, vsec; u64 xy = 0, uv = 0; for (std::size_t i = 0; i < N; ++i) { xs.push_back(s.input()); ys.push_back(s.input()); us.push_back(s.input()); vs.push_back(s.input()); u64 a = i + 1, b = i + 2, c = i + 3, d = i + 4; xsec.push_back(a); ysec.push_back(b); usec.push_back(c); vsec.push_back(d); xy += a * b; uv += c * d; } auto d0 = s.dot(xs, ys); auto d1 = s.dot(us, vs); auto z = s(d0 * d1); if (self == role::p2) { require(s.round_of(d0) == 1 && s.round_of(d1) == 1, "inner dots"); require(s.round_of(z) == 2, "nested round"); Counter rng; s.sample(rng); net::deal_session(net, s); return 0; } std::vector::wire> inputs = xs; inputs.insert(inputs.end(), ys.begin(), ys.end()); inputs.insert(inputs.end(), us.begin(), us.end()); inputs.insert(inputs.end(), vs.begin(), vs.end()); std::vector secrets = xsec; secrets.insert(secrets.end(), ysec.begin(), ysec.end()); secrets.insert(secrets.end(), usec.begin(), usec.end()); secrets.insert(secrets.end(), vsec.begin(), vsec.end()); install_and_bind_u64(s, net, self, inputs, secrets); evaluate_online(s, net, self); auto o = open_additive(net, self, s.value_party(z)); if (self == role::p0) require(o == xy * uv, "nested dots"); return 0; } int beaver_tuple_cascade(role self, trio & net) { // Cascade of triples and squares on a 6-tuple beavers::session s; auto a = s.input(); auto b = s.input(); auto c = s.input(); auto d = s.input(); auto e = s.input(); auto f = s.input(); auto p = s.product(a, b, c); auto q = s(d * d); auto r = s(e * f); auto z = s(p * q + r * a + b * c * d); const u64 A = 2, B = 3, C = 5, D = 7, E = 11, F = 13; const u64 expect = (A * B * C) * (D * D) + (E * F) * A + B * C * D; if (self == role::p2) { Counter rng; s.sample(rng); net::deal_session(net, s); return 0; } install_and_bind_u64(s, net, self, {a, b, c, d, e, f}, {A, B, C, D, E, F}); evaluate_online(s, net, self); auto o = open_additive(net, self, s.value_party(z)); if (self == role::p0) require(o == expect, "cascade"); return 0; } int beaver_mixed_batch_poly(role self, trio & net) { // Many independent products + one dense Horner + one mux in one session beavers::session s; constexpr std::size_t K = 16; std::vector::wire> xs, ys, outs; std::vector secrets; u64 expect_sum = 0; for (std::size_t i = 0; i < K; ++i) { xs.push_back(s.input()); ys.push_back(s.input()); u64 a = static_cast(i + 2); u64 b = static_cast(3 * i + 1); secrets.push_back(a); secrets.push_back(b); outs.push_back(s(xs.back() * ys.back())); expect_sum += a * b; } auto t = s.input(); secrets.push_back(4u); auto h = s.horner(t, {u64{1}, u64{2}, u64{3}, u64{4}, u64{5}}); auto bit = s.input(); auto lo = s.input(); auto hi = s.input(); secrets.push_back(1u); secrets.push_back(9u); secrets.push_back(42u); auto mx = s.mux(bit, hi, lo); // bit=1 → hi auto total = outs[0]; for (std::size_t i = 1; i < K; ++i) total = s(total + outs[i]); auto z = s(total + h + mx); const u64 x = 4; const u64 horner = 1 + 2 * x + 3 * x * x + 4 * x * x * x + 5 * x * x * x * x; const u64 expect = expect_sum + horner + 42u; if (self == role::p2) { Counter rng; s.sample(rng); net::deal_session(net, s); return 0; } std::vector::wire> inputs; for (std::size_t i = 0; i < K; ++i) { inputs.push_back(xs[i]); inputs.push_back(ys[i]); } inputs.push_back(t); inputs.push_back(bit); inputs.push_back(lo); inputs.push_back(hi); install_and_bind_u64(s, net, self, inputs, secrets); evaluate_online(s, net, self); auto o = open_additive(net, self, s.value_party(z)); if (self == role::p0) require(o == expect, "mixed batch"); return 0; } int beaver_factored_multilinear(role self, trio & net) { // Multilinear over 4 bits expanded: product of (1 + a_i * x_i) beavers::session s; auto x0 = s.input(); auto x1 = s.input(); auto x2 = s.input(); auto x3 = s.input(); auto z = s((1 + 2 * x0) * (1 + 3 * x1) * (1 + 5 * x2) * (1 + 7 * x3)); const u64 X0 = 1, X1 = 1, X2 = 0, X3 = 1; const u64 expect = (1 + 2 * X0) * (1 + 3 * X1) * (1 + 5 * X2) * (1 + 7 * X3); if (self == role::p2) { Counter rng; s.sample(rng); net::deal_session(net, s); return 0; } install_and_bind_u64(s, net, self, {x0, x1, x2, x3}, {X0, X1, X2, X3}); evaluate_online(s, net, self); auto o = open_additive(net, self, s.value_party(z)); if (self == role::p0) require(o == expect, "multilinear"); return 0; } // ============================================================================ // DCF extreme: dense queries, blocked, interval / full / sequence / IP // ============================================================================ template int dcf_dense_grid(role self, trio & net) { using Input = std::uint8_t; const Input alpha = 0x73; const Input x0 = 0x19; const Input x1 = static_cast(alpha ^ x0); const u64 beta = 13; std::vector pts; for (int x = 0; x < 256; x += 4) pts.push_back(static_cast(x)); for (int d = -3; d <= 3; ++d) { int v = static_cast(alpha) + d; if (v >= 0 && v < 256) pts.push_back(static_cast(v)); } pts.push_back(0); pts.push_back(255); std::sort(pts.begin(), pts.end()); pts.erase(std::unique(pts.begin(), pts.end()), pts.end()); auto on = [&](const auto & key) { const u64 mask = key.cmp().mask; std::vector mine; mine.reserve(pts.size()); for (Input x : pts) mine.push_back(share_bits(eval_point(cmp, key, x)) & mask); auto opened = open_additive(net, self, mine); if (self == role::p0) { for (std::size_t i = 0; i < pts.size(); ++i) require((opened[i] & mask) == expect_cmp(Kind, pts[i], alpha, beta), "dense grid"); } }; const auto expect = encoded_xor_point(x0, x1); if constexpr (Kind == 0) require_opened(dist_with_cmp_key(net, self, x0, x1, lt(beta), on, on), self, expect); else if constexpr (Kind == 1) require_opened(dist_with_cmp_key(net, self, x0, x1, leq(beta), on, on), self, expect); else if constexpr (Kind == 2) require_opened(dist_with_cmp_key(net, self, x0, x1, gt(beta), on, on), self, expect); else require_opened(dist_with_cmp_key(net, self, x0, x1, geq(beta), on, on), self, expect); return 0; } template int dcf_full_domain(role self, trio & net) { using Input = std::uint8_t; const Input alpha = 0x40; const Input x0 = 0x23; const Input x1 = static_cast(alpha ^ x0); const u64 beta = 9; auto on = [&](const auto & key) { const u64 mask = key.cmp().mask; auto full = eval_full(cmp, key); require(full.size() == 256u, "full size"); std::vector mine(256); for (std::size_t i = 0; i < 256; ++i) mine[i] = share_bits(full[i]) & mask; auto opened = open_additive(net, self, mine); if (self == role::p0) { for (int x = 0; x < 256; ++x) require((opened[static_cast(x)] & mask) == expect_cmp( Kind, static_cast(x), alpha, beta), "full domain"); } }; const auto expect = encoded_xor_point(x0, x1); if constexpr (Kind == 0) require_opened(dist_with_cmp_key(net, self, x0, x1, lt(beta), on, on), self, expect); else if constexpr (Kind == 1) require_opened(dist_with_cmp_key(net, self, x0, x1, leq(beta), on, on), self, expect); else if constexpr (Kind == 2) require_opened(dist_with_cmp_key(net, self, x0, x1, gt(beta), on, on), self, expect); else require_opened(dist_with_cmp_key(net, self, x0, x1, geq(beta), on, on), self, expect); return 0; } int dcf_blocked_interval_ip(role self, trio & net) { using Input = std::uint8_t; const Input alpha = 100; const Input x0 = 0x35; const Input x1 = static_cast(alpha ^ x0); const u64 beta = 6; const u64 if_false = 1; auto on = [&](const auto & key) { const u64 mask = key.cmp().mask; const Input from = 90, to = 110; auto buf = make_output_buffer(cmp, key, from, to); eval_interval(cmp, key, from, to, buf); std::vector mine; for (Input x = from; x <= to; ++x) mine.push_back(share_bits(buf[x - from]) & mask); auto opened = open_additive(net, self, mine); if (self == role::p0) { for (std::size_t i = 0; i < mine.size(); ++i) { Input x = static_cast(from + i); u64 want = x < alpha ? beta : if_false; require((opened[i] & mask) == want, "blocked interval"); } } std::vector w(static_cast(to - from + 1), 1u); u64 ip = eval_inner_product(cmp, key, from, to, w) & mask; auto oip = open_additive(net, self, ip) & mask; if (self == role::p0) { u64 want_ip = 0; for (Input x = from; x <= to; ++x) want_ip = (want_ip + (x < alpha ? beta : if_false)) & mask; require(oip == want_ip, "blocked ip"); } std::array pts{{0, 99, 100, 101, 255}}; auto seq = make_output_buffer(cmp, key, pts.size()); auto path = make_basic_path_memoizer(key); eval_sequence(cmp, key, pts.begin(), pts.end(), seq, path); std::vector smine; for (std::size_t i = 0; i < pts.size(); ++i) smine.push_back(share_bits(seq[i]) & mask); auto sopen = open_additive(net, self, smine); if (self == role::p0) { for (std::size_t i = 0; i < pts.size(); ++i) require((sopen[i] & mask) == (pts[i] < alpha ? beta : if_false), "blocked seq"); } }; require_opened(dist_with_cmp_key(net, self, x0, x1, block_width<4>(lt(beta, if_false)), on, on), self, encoded_xor_point(x0, x1)); return 0; } // ============================================================================ // Grotto: signed prefix/segment, XOR prefix, LUT, offset Horner, geneval Horner // ============================================================================ int grotto_signed_prefix_dense(role self, trio & net) { using Input = std::uint8_t; const Input alpha = 0x3c; const Input x0 = 0x17; const Input x1 = static_cast(alpha ^ x0); const u64 beta = 1; std::array ends{{0, 1, 2, 10, 20, 40, alpha - 1, alpha, alpha + 1, 100, 150, 200, 220, 240, 254, 255}}; auto on = [&](const auto & key) { const u64 mask = key.cmp().mask; auto pref = grotto::signed_prefix_parities(key, ends); std::vector mine(ends.size()); for (std::size_t i = 0; i < ends.size(); ++i) mine[i] = pref[i] & mask; auto opened = open_additive(net, self, mine); std::vector pts(ends.size()); for (std::size_t i = 0; i < ends.size(); ++i) pts[i] = share_bits(eval_point(cmp, key, ends[i])) & mask; auto opt_all = open_additive(net, self, pts); if (self == role::p0) { for (std::size_t i = 0; i < ends.size(); ++i) { require((opened[i] & mask) == (ends[i] > alpha ? beta : 0u), "signed prefix"); require((opt_all[i] & mask) == (ends[i] > alpha ? beta : 0u), "prefix vs point"); } } }; require_opened(dist_with_cmp_key(net, self, x0, x1, gt(beta), on, on), self, encoded_xor_point(x0, x1)); return 0; } int grotto_signed_segment_onehot(role self, trio & net) { using Input = std::uint8_t; const Input alpha = 40; const Input x0 = 0x31; const Input x1 = static_cast(alpha ^ x0); const std::array ends{{0, 1, 10, 40, 80, 120, 200, 255}}; auto on = [&](const auto & key) { const u64 mask = key.cmp().mask; auto seg = grotto::signed_segment_parities(key, ends); std::vector mine(ends.size()); for (std::size_t i = 0; i < ends.size(); ++i) mine[i] = seg[i] & mask; auto opened = open_additive(net, self, mine); if (self == role::p0) { const int hot = piece_containing(alpha, ends); u64 sum = 0; for (std::size_t i = 0; i < ends.size(); ++i) { u64 bit = opened[i] & mask; sum = (sum + bit) & mask; require(bit == (i == static_cast(hot) ? 1u : 0u), "segment onehot"); } require(sum == 1u, "segment sum"); } }; require_opened(dist_with_cmp_key(net, self, x0, x1, gt(u64{1}), on, on), self, encoded_xor_point(x0, x1)); return 0; } int grotto_xor_prefix_parity(role self, trio & net) { using Input = std::uint8_t; const Input alpha = 0x55; const Input x0 = 0x1c; const Input x1 = static_cast(alpha ^ x0); const std::array ends{{0, 1, 16, 64, 128, 200, 254, 255}}; auto on = [&](const auto & key) { auto [pref, new_first] = grotto::prefix_parities(key, ends); (void)new_first; for (std::size_t i = 0; i < ends.size(); ++i) { bool open = open_xor_bit(net, self, pref[i]); if (self == role::p0) (void)open; } auto segs = grotto::segment_parities(key, ends); int hot = -1; for (std::size_t i = 0; i < ends.size(); ++i) { bool bit_value = open_xor_bit(net, self, segs[i]); if (self == role::p0 && bit_value) { require(hot < 0, "xor multi hot"); hot = static_cast(i); } } if (self == role::p0) require(hot == piece_containing(alpha, ends), "xor segment hot"); }; require_tree_prefix(dist_with_point_key(net, self, x0, x1, bit::one, on, on), self, verifiable_tree_prefix(x0, x1)); return 0; } int grotto_signum_lut(role self, trio & net) { // Evaluate signum LUT at several signed alphas via signed_segment. // Walks share one mux sink (count = alphas). Segment opens collapse into // one packed vector exchange after every keygen. const std::array alphas{{-100, -1, 0, 1, 40, 127, -128}}; auto lut = grotto::make_exact_constant_lut( grotto::exact_constant::signum, 0); require(lut.bounds.size() == 3u, "signum bounds"); std::array ends{}; for (std::size_t i = 0; i < 3; ++i) ends[i] = lut.bounds[i]; constexpr std::size_t n = alphas.size(); std::vector seg_shares; seg_shares.reserve(n * 3); u64 mask = 0; std::unique_ptr sink; if (self != role::p2) { using pair_type = dist::comparison_pair_t; constexpr std::size_t depth = pair_type::first_type::key_type::depth; sink = net.batch(n, dpf::net::ds_walk_slot_bytes(depth, false)); } for (std::size_t i = 0; i < n; ++i) { const std::int8_t alpha = alphas[i]; if (self == role::p2) { require_opened(dist_with_cmp_key( net, self, alpha, std::int8_t{0}, gt(u64{1}), [](const auto &) {}, [](const auto &) {}), self, encoded_xor_point(alpha, std::int8_t{0})); continue; } auto on = [&](const auto & key) { mask = key.cmp().mask; auto seg = grotto::signed_segment_parities(key, ends); for (std::size_t j = 0; j < 3; ++j) seg_shares.push_back(seg[j] & mask); }; if (self == role::p0) { auto result = dist::comparison_party( net, alpha, gt(u64{1}), sink.get(), i); on(result.dpf_key); require_opened(std::optional{result.opened_point}, self, encoded_xor_point(alpha, std::int8_t{0})); } else { auto result = dist::comparison_party( net, std::int8_t{0}, gt(u64{1}), sink.get(), i); on(result.dpf_key); require_opened(std::optional{result.opened_point}, self, encoded_xor_point(alpha, std::int8_t{0})); } } if (self == role::p2) return 0; auto opened = open_additive(net, self, seg_shares); if (self == role::p0) { for (std::size_t i = 0; i < n; ++i) { u64 acc = 0; for (std::size_t j = 0; j < 3; ++j) { acc += (opened[i * 3 + j] & mask) * static_cast(lut.values[j]); } const std::int8_t alpha = alphas[i]; const std::int64_t want = alpha < 0 ? -1 : (alpha > 0 ? 1 : 0); require(static_cast(acc) == want, "signum lut"); } } return 0; } template int dist_offset_horner(role self, trio & net, Input center0, Input center1, Input center, Input eta, const std::vector & knots, const std::vector> & coeff, const char * failure) { const auto pieces = grotto::offset_horner_detail::prepare_pieces( knots, coeff, eta); std::vector shifted(pieces.size()); std::vector> ordered(pieces.size()); std::vector kappa(pieces.size()); for (std::size_t i = 0; i < pieces.size(); ++i) { shifted[i] = pieces[i].knot; ordered[i] = pieces[i].coeff; kappa[i] = pieces[i].kappa; } std::array, Degree + 1> segments; u64 payload = 1; for (std::size_t m = 0; m <= Degree; ++m) { const u64 power = payload; auto on = [&](const auto & key) { const u64 wrap_share = self == role::p0 ? power : 0ULL; segments[m] = grotto::offset_horner_detail::segments_of( key, shifted, wrap_share); }; require_opened(dist_with_cmp_key( net, self, center0, center1, gt(power), on, on), self, encoded_xor_point(center0, center1)); payload *= center; } if (self == role::p2) return 0; const auto shares = grotto::offset_horner_detail::contributions( segments, ordered, kappa); u64 share = 0; for (u64 term : shares) share += term; const u64 open = open_additive(net, self, share); if (self == role::p0) { require(open == grotto::offset_horner_clear( center, knots, coeff, eta), failure); } return 0; } template int grotto_offset_horner_deg(role self, trio & net) { using Input = std::uint8_t; const Input center = 3; const Input eta = 4; const std::vector knots{0}; std::vector> coeff(1); const u64 raw[4] = {5, 0, 1, 2}; for (std::size_t k = 0; k <= Degree; ++k) coeff[0][k] = k < 4 ? raw[k] : 0u; const Input c0 = 0x11; const Input c1 = static_cast(center ^ c0); return dist_offset_horner( self, net, c0, c1, center, eta, knots, coeff, "offset horner"); } int grotto_offset_horner_multipiece(role self, trio & net) { constexpr std::size_t D = 3; using Input = std::uint8_t; const Input center = 12; const Input eta = 3; const std::vector knots{0, 10, 50}; std::vector> coeff{ {{1, 0, 0, 0}}, {{0, 2, 0, 0}}, {{7, 0, 0, 1}}, }; const Input c0 = 0x25; const Input c1 = static_cast(center ^ c0); return dist_offset_horner( self, net, c0, c1, center, eta, knots, coeff, "oh multipiece"); } int grotto_geneval_offset_horner(role self, trio & net) { constexpr std::size_t D = 3; using Input = std::uint8_t; const Input center = 3; const Input eta = 4; const Input c0 = 0x11; const Input c1 = static_cast(center ^ c0); const std::vector knots{0}; std::vector> coeff{{{5, 0, 1, 2}}}; return dist_offset_horner( self, net, c0, c1, center, eta, knots, coeff, "geneval oh"); } // ============================================================================ // Geneval / DS extreme // ============================================================================ int geneval_true_point_shares(role self, trio & net) { using Input = std::uint8_t; const Input alpha = 0x2a; const Input x0 = 0x10; const Input x1 = static_cast(alpha ^ x0); const u64 beta = 77; const Input off_x = static_cast(alpha ^ 0x80); auto on = [&](const auto & key) { std::vector mine{ share_bits(*eval_point(key, alpha)), share_bits(*eval_point(key, off_x))}; auto opened = open_subtractive(net, self, mine); if (self == role::p0) { require(opened[0] == beta, "geneval on"); require(opened[1] == 0u, "geneval off"); } }; require_tree_prefix(dist_with_point_key(net, self, x0, x1, beta, on, on), self, verifiable_tree_prefix(x0, x1)); return 0; } int geneval_interval_dense(role self, trio & net) { using Input = std::uint8_t; const Input alpha = 50; const Input x0 = 7; const Input x1 = static_cast(alpha ^ x0); const u64 beta = 3; const Input from = 40, to = 60; auto on = [&](const auto & key) { std::vector mine; mine.reserve(static_cast(to - from) + 1u); for (unsigned x = from;; ++x) { mine.push_back(share_bits(*eval_point(key, static_cast(x)))); if (x == to) break; } auto out = open_subtractive(net, self, mine); if (self == role::p0) { for (std::size_t i = 0; i < out.size(); ++i) { const Input x = static_cast(from + i); require(out[i] == (x == alpha ? beta : 0u), "geneval interval"); } } }; require_tree_prefix(dist_with_point_key(net, self, x0, x1, beta, on, on), self, verifiable_tree_prefix(x0, x1)); return 0; } int geneval_cmp_dense_gt(role self, trio & net) { using Input = std::uint8_t; const Input alpha = 40; const Input x0 = 0x11; const Input x1 = static_cast(alpha ^ x0); const u64 beta = 7; const std::vector ends{0, 1, 10, 39, 40, 41, 100, 200, 255}; auto on = [&](const auto & key) { const u64 mask = key.cmp().mask; std::vector mine; mine.reserve(ends.size()); for (Input x : ends) mine.push_back(share_bits(eval_point(cmp, key, x)) & mask); auto opened = open_additive(net, self, mine); if (self == role::p0) { for (std::size_t i = 0; i < ends.size(); ++i) require((opened[i] & mask) == (ends[i] > alpha ? beta : 0u), "geneval cmp"); } }; require_opened(dist_with_cmp_key(net, self, x0, x1, gt(beta), on, on), self, encoded_xor_point(x0, x1)); return 0; } template int geneval_cmp_kind(role self, trio & net) { using Input = std::uint8_t; const Input alpha = 100; const Input x0 = 3; const Input x1 = static_cast(alpha ^ x0); const u64 beta = 5; const std::vector ends{0, 50, 99, 100, 101, 150, 255}; auto run = [&](auto spec) { auto on = [&](const auto & key) { const u64 mask = key.cmp().mask; std::vector mine; mine.reserve(ends.size()); for (Input x : ends) mine.push_back(share_bits(eval_point(cmp, key, x)) & mask); auto opened = open_additive(net, self, mine); if (self == role::p0) { for (std::size_t i = 0; i < ends.size(); ++i) require((opened[i] & mask) == expect_cmp(Kind, ends[i], alpha, beta), "geneval cmp kind"); } }; require_opened(dist_with_cmp_key(net, self, x0, x1, spec, on, on), self, encoded_xor_point(x0, x1)); }; if constexpr (Kind == 0) run(lt(beta)); else if constexpr (Kind == 1) run(leq(beta)); else if constexpr (Kind == 2) run(gt(beta)); else run(geq(beta)); return 0; } int ds_cmp_many_points(role self, trio & net) { using Input = std::uint8_t; const Input alpha = 77; const Input x0 = 3; const Input x1 = static_cast(alpha ^ x0); const u64 beta = 11; std::vector pts; for (int x = 0; x < 256; x += 5) pts.push_back(static_cast(x)); pts.push_back(76); pts.push_back(77); pts.push_back(78); std::sort(pts.begin(), pts.end()); pts.erase(std::unique(pts.begin(), pts.end()), pts.end()); auto on = [&](const auto & key) { const u64 mask = key.cmp().mask; std::vector mine; for (Input x : pts) mine.push_back(share_bits(eval_point(cmp, key, x)) & mask); auto opened = open_additive(net, self, mine); if (self == role::p0) { for (std::size_t i = 0; i < pts.size(); ++i) require((opened[i] & mask) == (pts[i] > alpha ? beta : 0u), "ds cmp"); } }; require_opened(dist_with_cmp_key(net, self, x0, x1, gt(beta), on, on), self, encoded_xor_point(x0, x1)); return 0; } int ds_vs_geneval_cmp_agree(role self, trio & net) { using Input = std::uint8_t; const Input alpha = 40; const Input x0 = 0x11; const Input x1 = static_cast(alpha ^ x0); const u64 beta = 7; const std::vector ends{0, 1, 10, 40, 200, 255}; auto on = [&](const auto & key) { const u64 mask = key.cmp().mask; std::vector first; std::vector second; for (Input x : ends) { first.push_back(share_bits(eval_point(cmp, key, x)) & mask); second.push_back(share_bits(eval_point(cmp, key, x)) & mask); } const auto first_open = open_additive(net, self, first); const auto second_open = open_additive(net, self, second); if (self == role::p0) { for (std::size_t i = 0; i < ends.size(); ++i) { const u64 want = ends[i] > alpha ? beta : 0u; require((first_open[i] & mask) == want, "first open"); require((second_open[i] & mask) == want, "second open"); } } }; require_opened(dist_with_cmp_key(net, self, x0, x1, gt(beta), on, on), self, encoded_xor_point(x0, x1)); return 0; } int geneval_arith_point_true(role self, trio & net) { using Input = std::uint8_t; const Input alpha = 0x90; const Input x0 = 0x30; const Input x1 = static_cast(alpha - x0); // additive const u64 beta = 19; const Input off_x = static_cast(alpha + 1); auto on_key = [&](const auto & key) { std::vector mine{ share_bits(*eval_point(key, alpha)), share_bits(*eval_point(key, off_x))}; auto opened = open_subtractive(net, self, mine); if (self == role::p0) { require(opened[0] == beta, "geneval arith on"); require(opened[1] == 0u, "geneval arith off"); } }; require_tree_prefix( dist_with_point_key( net, self, x0, x1, beta, on_key, on_key, false, true), self, verifiable_tree_prefix( alpha, Input{}, true)); return 0; } // ============================================================================ // Registration // ============================================================================ #define REG(name, tags, fn, benchable) \ register_flow(flow{#name, tags, fn, benchable}) } // namespace extreme void register_extreme_flows() { using namespace extreme; // Complex beaver REG(beaver_dot_n64, "beaver dot extreme bench", beaver_dot_extreme_n<64>, true); REG(beaver_dot_n128, "beaver dot extreme bench", beaver_dot_extreme_n<128>, true); REG(beaver_nested_dots, "beaver poly extreme bench", beaver_nested_dots, true); REG(beaver_tuple_cascade, "beaver poly extreme bench", beaver_tuple_cascade, true); REG(beaver_mixed_batch_poly, "beaver poly horner mux extreme bench", beaver_mixed_batch_poly, true); REG(beaver_factored_multilinear, "beaver poly extreme bench", beaver_factored_multilinear, true); // DCF dense / full / blocked REG(dcf_dense_lt, "dcf extreme bench", dcf_dense_grid<0>, true); REG(dcf_dense_leq, "dcf extreme bench", dcf_dense_grid<1>, true); REG(dcf_dense_gt, "dcf extreme bench", dcf_dense_grid<2>, true); REG(dcf_dense_geq, "dcf extreme bench", dcf_dense_grid<3>, true); REG(dcf_full_lt, "dcf extreme bench", dcf_full_domain<0>, true); REG(dcf_full_leq, "dcf extreme bench", dcf_full_domain<1>, true); REG(dcf_full_gt, "dcf extreme bench", dcf_full_domain<2>, true); REG(dcf_full_geq, "dcf extreme bench", dcf_full_domain<3>, true); REG(dcf_blocked_interval_ip, "dcf blocked extreme bench", dcf_blocked_interval_ip, true); // Grotto REG(grotto_signed_prefix_dense, "grotto dcf extreme bench", grotto_signed_prefix_dense, true); REG(grotto_signed_segment_onehot, "grotto dcf extreme bench", grotto_signed_segment_onehot, true); REG(grotto_xor_prefix_parity, "grotto dpf extreme bench", grotto_xor_prefix_parity, true); REG(grotto_signum_lut, "grotto lut extreme bench", grotto_signum_lut, true); REG(grotto_offset_horner_d1, "grotto horner extreme bench", grotto_offset_horner_deg<1>, true); REG(grotto_offset_horner_d2, "grotto horner extreme bench", grotto_offset_horner_deg<2>, true); REG(grotto_offset_horner_d3, "grotto horner extreme bench", grotto_offset_horner_deg<3>, true); REG(grotto_offset_horner_multipiece, "grotto horner extreme bench", grotto_offset_horner_multipiece, true); REG(grotto_geneval_offset_horner, "grotto geneval horner extreme bench", grotto_geneval_offset_horner, true); // Geneval / DS REG(geneval_true_point_shares, "geneval extreme bench", geneval_true_point_shares, true); REG(geneval_interval_dense, "geneval extreme bench", geneval_interval_dense, true); REG(geneval_cmp_dense_gt, "geneval dcf extreme bench", geneval_cmp_dense_gt, true); REG(geneval_cmp_lt, "geneval dcf extreme bench", geneval_cmp_kind<0>, true); REG(geneval_cmp_leq, "geneval dcf extreme bench", geneval_cmp_kind<1>, true); REG(geneval_cmp_gt, "geneval dcf extreme bench", geneval_cmp_kind<2>, true); REG(geneval_cmp_geq, "geneval dcf extreme bench", geneval_cmp_kind<3>, true); REG(geneval_arith_point_true, "geneval extreme bench", geneval_arith_point_true, true); REG(ds_cmp_many_points, "ds dcf extreme bench", ds_cmp_many_points, true); REG(ds_vs_geneval_cmp_agree, "ds geneval dcf extreme bench", ds_vs_geneval_cmp_agree, true); } #undef REG } // namespace party } // namespace dpf