libdpf/party/run.cpp
Ryan Henry 0d22946a0e Checkpoint the party/runtime stack before share-program and malicious-mode work.
Ship the TLS mesh, composer, Beaver/Yao/leaf MPC, prep/online paths, apps, and docs so the tree is pushable before elevating share_expr, security_mode, and prep resume.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-28 05:59:19 -06:00

1518 lines
50 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/// @file party/run.cpp
/// @brief Shared (2+1) party case implementations.
#include "cases.hpp"
#include "dist_ds.hpp"
#include "iknp_deal.hpp"
#include "flow_util.hpp"
#include "key_io.hpp"
#include "registry.hpp"
#include <algorithm>
#include <array>
#include <chrono>
#include <cstdint>
#include <cstring>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
#include "asio.hpp"
#include "dpf.hpp"
#include "dpf/beaver.hpp"
#include "dpf/net/party_tape_io.hpp"
#include "dpf/net/trio.hpp"
#include "grotto/carry.hpp"
bool do_quickack = false;
namespace dpf
{
namespace party
{
namespace
{
using net::role;
using net::trio;
using u64 = std::uint64_t;
using util::evaluate_online;
using util::evaluate_online_auth;
struct Counter
{
int draws = 0;
u64 operator()()
{
++draws;
return 0x9e3779b97f4a7c15ull * static_cast<u64>(draws);
}
};
std::pair<u64, u64> split_u64(u64 secret, unsigned tag = 1)
{
u64 p0 = 0x9e3779b97f4a7c15ull * (tag + 1u);
return {p0, secret - p0};
}
template <typename Ring>
std::pair<Ring, Ring> split_ring(Ring secret, unsigned tag = 1)
{
using traits = beavers::ring_traits<Ring>;
Ring p0 = Ring{static_cast<u64>(0x9e3779b97f4a7c15ull * (tag + 1u))};
return {p0, traits::sub(secret, p0)};
}
void require(bool cond, const char * msg)
{
if (!cond)
throw std::runtime_error(msg);
}
template <typename Ring>
Ring open_additive(trio & net, role self, Ring mine)
{
role peer = self == role::p0 ? role::p1 : role::p0;
return net.open_with(peer, mine);
}
template <typename T>
T open_subtractive(trio & net, role self, T mine)
{
role peer = self == role::p0 ? role::p1 : role::p0;
T theirs = net.exchange_with(peer, mine);
return self == role::p0 ? static_cast<T>(mine - theirs)
: static_cast<T>(theirs - mine);
}
template <typename T>
constexpr T share_bits(T s) noexcept
{
return s;
}
template <typename T, std::size_t Party, sharing Scheme>
constexpr T share_bits(const secret_share<T, Party, Scheme> & s) noexcept
{
return s.raw();
}
template <typename Ring>
void install_and_bind(beavers::session<Ring> & s, trio & net, role self,
const std::vector<typename beavers::session<Ring>::wire> & inputs,
const std::vector<Ring> & secrets)
{
auto tape = net::accept_session<Ring>(net);
s.install_party(self == role::p0 ? 0u : 1u, tape);
for (std::size_t i = 0; i < inputs.size(); ++i)
{
auto [p0, p1] = split_ring<Ring>(secrets[i], static_cast<unsigned>(i + 1));
s.bind_party(inputs[i], self == role::p0 ? p0 : p1);
}
}
void install_and_bind_u64(beavers::session<u64> & s, trio & net, role self,
const std::vector<beavers::session<u64>::wire> & inputs,
const std::vector<u64> & secrets)
{
auto tape = net::accept_session<u64>(net);
s.install_party(self == role::p0 ? 0u : 1u, tape);
for (std::size_t i = 0; i < inputs.size(); ++i)
{
auto [p0, p1] = split_u64(secrets[i], static_cast<unsigned>(i + 1));
s.bind_party(inputs[i], self == role::p0 ? p0 : p1);
}
}
// ---------------------------------------------------------------------------
// Beaver
// ---------------------------------------------------------------------------
int beaver_product_two(role self, trio & net)
{
beavers::session<u64> s;
auto x = s.input();
auto y = s.input();
auto z = s(x * y);
if (self == role::p2)
{
Counter rng;
s.sample(rng);
require(rng.draws == 5, "draws");
require(s.preprocessing_count() == 3u, "prep");
require(s.monomial({{x, 1u}, {y, 1u}}).open()
== s.lambda(x).open() * s.lambda(y).open(),
"mono");
net::deal_session(net, s);
return 0;
}
install_and_bind_u64(s, net, self, {x, y}, {7u, 9u});
evaluate_online(s, net, self);
auto o = open_additive(net, self, s.value_party(z));
if (self == role::p0)
require(o == 63u, "product");
return 0;
}
int beaver_product_three_square(role self, trio & net)
{
beavers::session<u64> s;
auto a = s.input();
auto b = s.input();
auto c = s.input();
auto p = s.product(a, b, c);
auto sq = s(b * b);
if (self == role::p2)
{
require(s.monomial_count() == 5u, "monos");
Counter rng;
s.sample(rng);
net::deal_session(net, s);
return 0;
}
install_and_bind_u64(s, net, self, {a, b, c}, {2u, 3u, 5u});
evaluate_online(s, net, self);
auto op = open_additive(net, self, s.value_party(p));
auto os = open_additive(net, self, s.value_party(sq));
if (self == role::p0)
{
require(op == 30u, "p");
require(os == 9u, "sq");
}
return 0;
}
int beaver_mul_square(role self, trio & net)
{
beavers::session<u64> s;
auto a = s.input();
auto x = s.input();
auto z = s(a * x * x);
if (self == role::p2)
{
Counter rng;
s.sample(rng);
require(s.round_of(z) == 1, "round");
require(s.preprocessing_count() == 5u, "prep");
require(rng.draws == 7, "draws");
net::deal_session(net, s);
return 0;
}
install_and_bind_u64(s, net, self, {a, x}, {4u, 5u});
evaluate_online(s, net, self);
auto o = open_additive(net, self, s.value_party(z));
if (self == role::p0)
require(o == 100u, "mul_square");
return 0;
}
int beaver_chained_square(role self, trio & net)
{
beavers::session<u64> s;
auto a = s.input();
auto x = s.input();
auto x2 = s(x * x);
auto z = s(a * x2);
if (self == role::p2)
{
require(s.round_of(x2) == 1 && s.round_of(z) == 2, "rounds");
Counter rng;
s.sample(rng);
net::deal_session(net, s);
return 0;
}
install_and_bind_u64(s, net, self, {a, x}, {4u, 5u});
evaluate_online(s, net, self);
auto o2 = open_additive(net, self, s.value_party(x2));
auto oz = open_additive(net, self, s.value_party(z));
if (self == role::p0)
{
require(o2 == 25u, "x2");
require(oz == 100u, "z");
}
return 0;
}
int beaver_dot(role self, trio & net)
{
beavers::session<u64> s;
auto x0 = s.input();
auto x1 = s.input();
auto x2 = s.input();
auto y0 = s.input();
auto y1 = s.input();
auto y2 = s.input();
auto z = s.dot({x0, x1, x2}, {y0, y1, y2});
if (self == role::p2)
{
Counter rng;
s.sample(rng);
require(s.preprocessing_count() == 7u, "prep");
require(rng.draws == 13, "draws");
net::deal_session(net, s);
return 0;
}
install_and_bind_u64(s, net, self, {x0, x1, x2, y0, y1, y2},
{1u, 2u, 3u, 4u, 5u, 6u});
evaluate_online(s, net, self);
auto o = open_additive(net, self, s.value_party(z));
if (self == role::p0)
require(o == 32u, "dot");
return 0;
}
int beaver_dot_reuse(role self, trio & net)
{
beavers::session<u64> s;
auto a = s.input();
auto b = s.input();
auto z = s.dot({a, a}, {b, b});
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}, {3u, 4u});
evaluate_online(s, net, self);
auto o = open_additive(net, self, s.value_party(z));
if (self == role::p0)
require(o == 24u, "dot_reuse");
return 0;
}
int beaver_inner_product(role self, trio & net)
{
using wire = beavers::session<u64>::wire;
beavers::session<u64> s;
std::vector<wire> fx, fy;
for (int i = 0; i < 3; ++i)
{
fx.push_back(s.input());
fy.push_back(s.input());
}
auto z = s.dot(fx, fy);
if (self == role::p2)
{
Counter rng;
s.sample(rng);
net::deal_session(net, s);
return 0;
}
std::vector<wire> wires = fx;
wires.insert(wires.end(), fy.begin(), fy.end());
install_and_bind_u64(s, net, self, wires, {0u, 2u, 5u, 4u, 0u, 7u});
evaluate_online(s, net, self);
auto o = open_additive(net, self, s.value_party(z));
if (self == role::p0)
require(o == 35u, "inner");
return 0;
}
int beaver_scale(role self, trio & net)
{
beavers::session<u64> s;
auto sc = s.input();
auto l0 = s.input();
auto l1 = s.input();
auto l2 = s.input();
auto out = s.scale(sc, {l0, l1, l2});
if (self == role::p2)
{
Counter rng;
s.sample(rng);
net::deal_session(net, s);
return 0;
}
install_and_bind_u64(s, net, self, {sc, l0, l1, l2}, {3u, 1u, 2u, 4u});
evaluate_online(s, net, self);
const u64 expect[] = {3u, 6u, 12u};
for (std::size_t i = 0; i < out.size(); ++i)
{
auto o = open_additive(net, self, s.value_party(out[i]));
if (self == role::p0)
require(o == expect[i], "scale");
}
return 0;
}
int beaver_bit_mul_mux(role self, trio & net)
{
beavers::session<u64> s;
auto b = s.bit();
auto x = s.input();
auto y = s.input();
auto bm = s.bit_mul(b, x);
auto mx = s.mux(b, x, y);
if (self == role::p2)
{
Counter rng;
s.sample(rng);
net::deal_session(net, s);
return 0;
}
install_and_bind_u64(s, net, self, {b, x, y}, {1u, 10u, 3u});
evaluate_online(s, net, self);
auto ob = open_additive(net, self, s.value_party(bm));
auto om = open_additive(net, self, s.value_party(mx));
if (self == role::p0)
{
require(ob == 10u, "bit_mul");
require(om == 10u, "mux");
}
return 0;
}
int beaver_xor_mux(role self, trio & net)
{
using ring = xor_wrapper<std::uint64_t>;
beavers::session<ring> s;
auto b = s.bit();
auto x = s.input();
auto y = s.input();
auto mx = s.mux(b, x, y);
if (self == role::p2)
{
struct XorCounter
{
Counter c;
ring operator()() { return ring{c()}; }
} rng;
s.sample(rng);
net::deal_session(net, s);
return 0;
}
install_and_bind(s, net, self, {b, x, y},
{ring{0}, ring{0xaaaau}, ring{0x1111u}});
evaluate_online(s, net, self);
auto o = open_additive(net, self, s.value_party(mx));
if (self == role::p0)
require(o == ring{0x1111u}, "xor_mux");
return 0;
}
int beaver_modint_product(role self, trio & net)
{
using ring = modint<61>;
beavers::session<ring> s;
auto x = s.input();
auto y = s.input();
auto z = s(x * y);
if (self == role::p2)
{
s.sample();
net::deal_session(net, s);
return 0;
}
install_and_bind(s, net, self, {x, y}, {ring{7}, ring{9}});
evaluate_online(s, net, self);
auto o = open_additive(net, self, s.value_party(z));
if (self == role::p0)
require(o == ring{63}, "modint");
return 0;
}
int beaver_oracle_stream(role self, trio & net)
{
using block = prg::aes128::block_type;
block seed = simde_mm_set_epi64x(0x9, 0x9);
constexpr std::size_t n = 32;
if (self == role::p2)
{
beavers::oracle<u64> src(seed, 4);
beavers::beaver2<u64> bulk[n];
beavers::fill_beaver2(src, 0, bulk, n);
auto at31 = beavers::beaver2_at(src, 31);
require(bulk[31].a == at31.a && bulk[31].ab == at31.ab, "stream");
require(bulk[0].ab.open() == bulk[0].a.open() * bulk[0].b.open(), "o0");
std::vector<u64> p0, p1;
for (std::size_t i = 0; i < n; ++i)
{
p0.insert(p0.end(),
{bulk[i].a.p0, bulk[i].b.p0, bulk[i].ab.p0, bulk[i].out.p0});
p1.insert(p1.end(),
{bulk[i].a.p1, bulk[i].b.p1, bulk[i].ab.p1, bulk[i].out.p1});
}
net.to(role::p0).send_vec(p0);
net.to(role::p1).send_vec(p1);
return 0;
}
auto shares = net.to(role::p2).recv_vec<u64>();
require(shares.size() == n * 4, "size");
const std::size_t i = 7;
u64 a = shares[i * 4 + 0];
u64 b = shares[i * 4 + 1];
u64 ab = shares[i * 4 + 2];
auto [x0, x1] = split_u64(6u, 1);
auto [y0, y1] = split_u64(7u, 2);
u64 xs = self == role::p0 ? x0 : x1;
u64 ys = self == role::p0 ? y0 : y1;
role peer = self == role::p0 ? role::p1 : role::p0;
u64 dx_mine = xs + a;
u64 dy_mine = ys + b;
u64 dx = dx_mine + net.exchange_with(peer, dx_mine);
u64 dy = dy_mine + net.exchange_with(peer, dy_mine);
u64 z = ab;
z -= dx * b;
z -= dy * a;
if (self == role::p0)
z += dx * dy;
auto open = open_additive(net, self, z);
if (self == role::p0)
require(open == 42u, "oracle online");
return 0;
}
int beaver_scheduled_replay(role self, trio & net)
{
using block = prg::aes128::block_type;
block seed = simde_mm_set_epi64x(0x55, 0xaa);
beavers::session<u64> left;
auto x = left.input();
auto y = left.input();
auto z = left(3 * x * x + 2 * y + 5);
if (self == role::p2)
{
beavers::oracle<u64> src(seed, 4);
left.sample_from(src, 3);
beavers::session<u64> right;
auto rx = right.input();
auto ry = right.input();
(void)right(3 * rx * rx + 2 * ry + 5);
right.sample_from(src, 3);
require(left.lambda(x) == right.lambda(rx), "replay");
net::deal_session(net, left);
return 0;
}
install_and_bind_u64(left, net, self, {x, y}, {4u, 6u});
evaluate_online(left, net, self);
auto o = open_additive(net, self, left.value_party(z));
if (self == role::p0)
require(o == 3u * 16u + 2u * 6u + 5u, "scheduled");
return 0;
}
int beaver_grotto_appendix_e(role self, trio & net)
{
beavers::session<u64> linear;
auto x = linear.input();
auto sgn = linear.input();
auto a0 = linear.input();
auto a1 = linear.input();
auto lin = linear(sgn * (a1 * x + a0));
if (self == role::p2)
{
require(linear.round_of(lin) == 2, "round");
require(linear.preprocessing_count() == 6u, "prep");
Counter rng;
linear.sample(rng);
net::deal_session(net, linear);
return 0;
}
install_and_bind_u64(linear, net, self, {x, sgn, a0, a1}, {2u, 3u, 4u, 5u});
evaluate_online(linear, net, self);
auto o = open_additive(net, self, linear.value_party(lin));
if (self == role::p0)
require(o == 3u * (5u * 2u + 4u), "grotto_e");
return 0;
}
int beaver_polynomial_share_powers(role self, trio & net)
{
beavers::session<u64> s;
auto x = s.input();
auto y = s.input();
auto z = s(x * x * y + x * y);
if (self == role::p2)
{
Counter rng;
s.sample(rng);
net::deal_session(net, s);
return 0;
}
install_and_bind_u64(s, net, self, {x, y}, {3u, 4u});
evaluate_online(s, net, self);
auto o = open_additive(net, self, s.value_party(z));
if (self == role::p0)
require(o == 3u * 3u * 4u + 3u * 4u, "poly");
return 0;
}
int beaver_rejects_bad_use(role self, trio & net)
{
if (self == role::p2)
{
bool threw = false;
try
{
beavers::session<u64> s;
auto x = s.input();
auto y = s.input();
(void)s.dot({x}, {y, y});
}
catch (const std::invalid_argument &)
{
threw = true;
}
require(threw, "reject");
net.to(role::p0).send(net::msg::case_ok, std::uint8_t{1});
net.to(role::p1).send(net::msg::case_ok, std::uint8_t{1});
return 0;
}
(void)net.to(role::p2).recv<std::uint8_t>(net::msg::case_ok);
return 0;
}
int beaver_one_shot_triples(role self, trio & net)
{
if (self == role::p2)
{
Counter rng;
auto t2 = beavers::sample_beaver2<u64>(rng);
require(t2.ab.open() == t2.a.open() * t2.b.open(), "b2");
auto t3 = beavers::sample_beaver3<u64>(rng);
require(t3.abc.open() == t3.a.open() * t3.b.open() * t3.c.open(), "b3");
net.to(role::p0).send(net::msg::case_ok, std::uint8_t{1});
net.to(role::p1).send(net::msg::case_ok, std::uint8_t{1});
beavers::session<u64> s;
auto x = s.input();
auto y = s.input();
(void)s(x * y);
s.sample(rng);
net::deal_session(net, s);
return 0;
}
(void)net.to(role::p2).recv<std::uint8_t>(net::msg::case_ok);
beavers::session<u64> s;
auto x = s.input();
auto y = s.input();
auto z = s(x * y);
install_and_bind_u64(s, net, self, {x, y}, {8u, 9u});
evaluate_online(s, net, self);
auto o = open_additive(net, self, s.value_party(z));
if (self == role::p0)
require(o == 72u, "one_shot");
return 0;
}
int beaver_batch_one_round(role self, trio & net)
{
beavers::session<u64> s;
auto a = s.input();
auto b = s.input();
auto c = s.input();
auto p = s(a * b);
auto q = s(a * c);
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}, {2u, 3u, 5u});
evaluate_online(s, net, self);
auto op = open_additive(net, self, s.value_party(p));
auto oq = open_additive(net, self, s.value_party(q));
if (self == role::p0)
{
require(op == 6u && oq == 10u, "batch");
}
return 0;
}
int beaver_blind_survives(role self, trio & net)
{
beavers::session<u64> s;
auto a = s.input();
auto b = s.input();
auto p = s(a * b);
auto q = s(p * a);
if (self == role::p2)
{
Counter rng;
s.sample(rng);
auto lam = s.lambda(a);
net::deal_session(net, s);
require(s.lambda(a) == lam, "survived");
return 0;
}
install_and_bind_u64(s, net, self, {a, b}, {3u, 4u});
evaluate_online(s, net, self);
auto op = open_additive(net, self, s.value_party(p));
auto oq = open_additive(net, self, s.value_party(q));
if (self == role::p0)
require(op == 12u && oq == 36u, "blind");
return 0;
}
int beaver_sums_cancellations(role self, trio & net)
{
beavers::session<u64> s;
auto x = s.input();
auto y = s.input();
auto z = s(x + y - x);
if (self == role::p2)
{
require(s.monomial_count() == 0u, "no products");
Counter rng;
s.sample(rng);
net::deal_session(net, s);
return 0;
}
install_and_bind_u64(s, net, self, {x, y}, {11u, 22u});
evaluate_online(s, net, self);
auto o = open_additive(net, self, s.value_party(z));
if (self == role::p0)
require(o == 22u, "sum");
return 0;
}
// ---------------------------------------------------------------------------
// Wildcard: parties assign over the p0–p1 link, then evaluate.
// ---------------------------------------------------------------------------
template <typename Key, typename Share>
void assign_wildcard_over_link(Key & key, Share my_share, trio & net, role self)
{
role peer = self == role::p0 ? role::p1 : role::p0;
auto & wrap = std::get<0>(key.leaf_nodes);
if (wrap.is_ready())
wrap.begin_update();
auto blinded = wrap.compute_and_get_blinded_output_share(my_share);
auto peer_blinded = net.exchange_with(peer, blinded);
auto leaf = wrap.compute_and_get_leaf_share(peer_blinded);
auto peer_leaf = net.exchange_with(peer, leaf);
wrap.reconstruct_correction_word(peer_leaf);
}
int wildcard_single_leaf(role self, trio & net)
{
using input_type = std::uint8_t;
using concrete_type = std::uint32_t;
using output_type = wildcard_value<concrete_type>;
input_type x = 0xAA;
input_type x0 = 0x31;
input_type x1 = static_cast<input_type>(x ^ x0);
concrete_type y_exp = 0xAAAAAAAA;
concrete_type y_shr0 = 0x12345678;
concrete_type y_shr1 = y_exp - y_shr0;
auto on = [&](auto key) {
const concrete_type my_share =
self == role::p0 ? y_shr0 : y_shr1;
assign_wildcard_over_link(key, my_share, net, self);
auto y = *eval_point(key, x);
auto open = open_subtractive(net, self, share_bits(y));
if (self == role::p0)
require(open == y_exp, "wildcard on");
auto yz = *eval_point(key, static_cast<input_type>(x ^ 1));
auto oz = open_subtractive(net, self, share_bits(yz));
if (self == role::p0)
require(oz == 0u, "wildcard off");
};
const auto encoded = encoded_xor_point(x0, x1);
require_tree_prefix(
dist_with_point_key<prg::aes128, prg::aes128, true>(
net, self, x0, x1, output_type{}, on, on),
self,
verifiable_tree_prefix<prg::aes128, prg::aes128, output_type>(x0, x1),
verifiable_lane<prg::aes128, prg::aes128, output_type>(encoded));
dist_with_point_key(net, self, x0, x1, output_type{}, on, on);
return 0;
}
int wildcard_fail_before_assign(role self, trio & net)
{
using input_type = std::uint8_t;
using output_type = wildcard_value<std::uint32_t>;
const input_type alpha = 0xAA;
const input_type x0 = 0x31;
const input_type x1 = static_cast<input_type>(alpha ^ x0);
auto on = [&](const auto & key) {
bool threw = false;
try
{
(void)eval_point(key, alpha);
}
catch (const std::runtime_error &)
{
threw = true;
}
require(threw, "fail before");
};
const auto encoded = encoded_xor_point(x0, x1);
require_tree_prefix(
dist_with_point_key<prg::aes128, prg::aes128, true>(
net, self, x0, x1, output_type{}, on, on),
self,
verifiable_tree_prefix<prg::aes128, prg::aes128, output_type>(x0, x1),
verifiable_lane<prg::aes128, prg::aes128, output_type>(encoded));
return 0;
}
// ---------------------------------------------------------------------------
// Geneval / DS — p0 and p1 run Doerner–Shelat. p2 deals pads only.
// ---------------------------------------------------------------------------
int geneval_point(role self, trio & net)
{
using Input = std::uint8_t;
const Input alpha = 0x2a;
const Input x0 = 0x10;
const Input x1 = static_cast<Input>(alpha ^ x0);
const std::uint64_t beta = 7;
require_tree_prefix(dist_with_point_key<prg::aes128, prg::aes128, true>(net, self, x0, x1, beta, [&](const auto & key) {
const auto y = *eval_point(key, alpha);
const auto open = open_subtractive(net, self, share_bits(y));
if (self == role::p0)
require(open == beta, "geneval");
}, [&](const auto & key) {
const auto y = *eval_point(key, alpha);
(void)open_subtractive(net, self, share_bits(y));
}), self, verifiable_tree_prefix<prg::aes128, prg::aes128, std::uint64_t>(x0, x1));
return 0;
}
int geneval_arith_point(role self, trio & net)
{
using Input = std::uint8_t;
const Input alpha = 0x2a;
const Input x0 = 0x10;
const Input x1 = static_cast<Input>(alpha - x0);
const std::uint64_t beta = 11;
require_tree_prefix(dist_with_point_key<prg::aes128, prg::aes128, true>(
net, self, x0, x1, beta, [&](const auto & key) {
const auto y = *eval_point(key, alpha);
const auto open = open_subtractive(net, self, share_bits(y));
if (self == role::p0)
require(open == beta, "arith");
}, [&](const auto & key) {
const auto y = *eval_point(key, alpha);
(void)open_subtractive(net, self, share_bits(y));
}, false, true), self,
verifiable_tree_prefix<prg::aes128, prg::aes128, std::uint64_t>(
alpha, Input{}, true));
return 0;
}
int ds_key_agrees(role self, trio & net)
{
using Input = std::uint8_t;
const Input alpha = 0x44;
const Input x0 = 0x12;
const Input x1 = static_cast<Input>(alpha ^ x0);
const std::uint64_t beta = 5;
require_tree_prefix(dist_with_point_key<prg::aes128, prg::aes128, true>(net, self, x0, x1, beta, [&](const auto & key) {
const auto y = *eval_point(key, alpha);
const auto open = open_subtractive(net, self, share_bits(y));
if (self == role::p0)
require(open == beta, "ds");
}, [&](const auto & key) {
const auto y = *eval_point(key, alpha);
(void)open_subtractive(net, self, share_bits(y));
}), self, verifiable_tree_prefix<prg::aes128, prg::aes128, std::uint64_t>(x0, x1));
return 0;
}
// ---------------------------------------------------------------------------
// Verifiable
// ---------------------------------------------------------------------------
int verifiable_honest_point(role self, trio & net)
{
using Input = std::uint8_t;
const Input alpha = 0x2a;
const Input x0 = 0x10;
const Input x1 = static_cast<Input>(alpha ^ x0);
const std::uint64_t beta = 7;
auto on = [&](const auto & key) {
proof_token pi{};
auto y = *eval_point(key, alpha, prove(pi));
role peer = self == role::p0 ? role::p1 : role::p0;
auto send_pi = [&](const proof_token & t) {
net.to(peer).send_bytes(net::msg::proof_token,
reinterpret_cast<const std::uint8_t *>(&t), sizeof(t));
};
auto recv_pi = [&]() {
proof_token t{};
auto b = net.to(peer).recv_bytes(net::msg::proof_token);
require(b.size() == sizeof(t), "pi size");
std::memcpy(&t, b.data(), sizeof(t));
return t;
};
proof_token peer_pi{};
if (self == role::p0)
{
send_pi(pi);
peer_pi = recv_pi();
}
else
{
peer_pi = recv_pi();
send_pi(pi);
}
bool ok = verify(pi, peer_pi);
auto open = open_subtractive(net, self, share_bits(y));
if (self == role::p0)
{
require(ok, "verify");
require(open == beta, "vpoint");
}
};
require_tree_prefix(dist_with_point_key<prg::aes128, prg::aes128, true>(
net, self, x0, x1, beta, on, on), self,
verifiable_tree_prefix<prg::aes128, prg::aes128, std::uint64_t>(x0, x1));
return 0;
}
int verifiable_tampered_rejects(role self, trio & net)
{
using Input = std::uint8_t;
const Input alpha = 0x2a;
const Input x0 = 0x10;
const Input x1 = static_cast<Input>(alpha ^ x0);
const std::uint64_t beta = 7;
auto on = [&](const auto & key) {
proof_token pi{};
(void)*eval_point(key, alpha, prove(pi));
if (self == role::p0)
pi[0] = simde_mm_xor_si128(pi[0], simde_mm_set1_epi8(0xff));
role peer = self == role::p0 ? role::p1 : role::p0;
auto send_pi = [&](const proof_token & t) {
net.to(peer).send_bytes(net::msg::proof_token,
reinterpret_cast<const std::uint8_t *>(&t), sizeof(t));
};
auto recv_pi = [&]() {
proof_token t{};
auto b = net.to(peer).recv_bytes(net::msg::proof_token);
std::memcpy(&t, b.data(), sizeof(t));
return t;
};
proof_token peer_pi{};
if (self == role::p0)
{
send_pi(pi);
peer_pi = recv_pi();
}
else
{
peer_pi = recv_pi();
send_pi(pi);
}
bool ok = verify(pi, peer_pi);
if (self == role::p0)
require(!ok, "tampered token");
};
require_tree_prefix(dist_with_point_key<prg::aes128, prg::aes128, true>(
net, self, x0, x1, beta, on, on), self,
verifiable_tree_prefix<prg::aes128, prg::aes128, std::uint64_t>(x0, x1));
return 0;
}
// ---------------------------------------------------------------------------
// Grotto / DCF
// ---------------------------------------------------------------------------
int blocked_dcf_point(role self, trio & net)
{
using Input = std::uint8_t;
const Input alpha = 0x40;
const Input x0 = 0x14;
const Input x1 = static_cast<Input>(alpha ^ x0);
const std::uint64_t beta = 9;
auto on = [&](const auto & key) {
auto y = eval_point(cmp, key, static_cast<Input>(alpha - 1));
auto open =
open_additive(net, self, share_bits(y)) & key.cmp().mask;
if (self == role::p0)
require(open == beta, "dcf lt");
auto y2 = eval_point(cmp, key, alpha);
auto o2 =
open_additive(net, self, share_bits(y2)) & key.cmp().mask;
if (self == role::p0)
require(o2 == 0u, "dcf on");
};
require_opened(dist_with_cmp_key<prg::aes128, prg::aes128, true>(net, self, x0, x1, lt(beta), on, on),
self, encoded_xor_point(x0, x1));
return 0;
}
int grotto_prefix_horner(role self, trio & net)
{
beavers::session<u64> s;
auto x = s.input();
auto z = s.horner(x, {u64{1}, u64{2}, u64{3}});
if (self == role::p2)
{
Counter rng;
s.sample(rng);
net::deal_session(net, s);
return 0;
}
install_and_bind_u64(s, net, self, {x}, {5u});
evaluate_online(s, net, self);
auto o = open_additive(net, self, s.value_party(z));
if (self == role::p0)
require(o == 1u + 2u * 5u + 3u * 25u, "horner");
return 0;
}
/// @brief Opt-in MAC path: dealer sets Δ, parties open with auth openings.
int beaver_auth_horner(role self, trio & net)
{
beavers::session<u64> s;
auto x = s.input();
auto z = s.horner(x, {u64{1}, u64{2}, u64{3}});
const u64 secret = 5u;
if (self == role::p2)
{
auto key = sample_mac_key<u64>();
s.set_mac_key(key);
Counter rng;
s.sample(rng);
s.bind(x, secret, rng);
auto xv = s.value_auth(x);
net::deal_session(net, s);
net.to(role::p0).send(net::msg::mac_key, key.delta);
net.to(role::p1).send(net::msg::mac_key, key.delta);
net.to(role::p0).send(net::msg::mac_share, xv.party(0));
net.to(role::p1).send(net::msg::mac_share, xv.party(1));
return 0;
}
auto tape = net::accept_session<u64>(net);
s.install_party(self == role::p0 ? 0u : 1u, tape);
auto delta = net.to(role::p2).recv<u64>(net::msg::mac_key);
s.set_mac_key(mac_key<u64>{delta});
auto xin = net.to(role::p2).recv<mac_share<u64>>(net::msg::mac_share);
s.bind_party(x, xin);
evaluate_online_auth(s, net, self);
auto o = open_additive(net, self, s.value_party(z));
require(s.verify_delta(x, mac_key<u64>{delta}), "auth delta x");
if (self == role::p0)
require(o == 1u + 2u * 5u + 3u * 25u, "auth horner");
return 0;
}
/// @brief Carry with verifiable DCF tokens and an output MAC.
int carry_verifiable_mac(role self, trio & net)
{
using Key0 = grotto::carry_detail::lt_v_pair::first_type;
using Key1 = grotto::carry_detail::lt_v_pair::second_type;
constexpr unsigned n = 8;
constexpr unsigned s = 3;
const std::uint64_t opened = 0x3cu;
if (self == role::p2)
{
grotto::carry_auth auth{};
auth.verifiable = true;
auth.output_mac = true;
auto keys = grotto::make_carry_in_keys(n, s, auth);
require(keys.low_lt_v.has_value(), "carry lt v");
require(keys.has_mac, "carry mac");
auto send_one = [&](role r) {
send_key(net.to(r), keys.low_lt_v->first);
send_key(net.to(r), keys.low_lt_v->second);
net.to(r).send(net::msg::mac_key, keys.mac.delta);
net.to(r).send(net::msg::bytes, keys.rin);
net.to(r).send(net::msg::bytes, keys.rout0);
net.to(r).send(net::msg::bytes, keys.rout1);
};
send_one(role::p0);
send_one(role::p1);
return 0;
}
auto k0 = recv_key<Key0>(net.to(role::p2));
auto k1 = recv_key<Key1>(net.to(role::p2));
auto delta = net.to(role::p2).recv<std::uint64_t>(net::msg::mac_key);
auto rin = net.to(role::p2).recv<std::uint64_t>(net::msg::bytes);
auto rout0 = net.to(role::p2).recv<std::uint64_t>(net::msg::bytes);
auto rout1 = net.to(role::p2).recv<std::uint64_t>(net::msg::bytes);
grotto::carry_detail::carry_key_pair keys{};
keys.recipe = grotto::plan_carry_in(n, s);
keys.auth.verifiable = true;
keys.auth.output_mac = true;
keys.rin = rin;
keys.rout0 = rout0;
keys.rout1 = rout1;
keys.low_lt_v.emplace(std::move(k0), std::move(k1));
keys.mac = mac_key<std::uint64_t>{delta};
keys.has_mac = true;
const unsigned party = self == role::p0 ? 0u : 1u;
dpf::proof_token toks[4]{};
const auto nt = grotto::prove_carry_keys(keys, party, opened, toks, 4);
require(nt >= 1u, "carry tokens");
role peer = self == role::p0 ? role::p1 : role::p0;
auto send_pi = [&](const dpf::proof_token & t) {
net.to(peer).send_bytes(net::msg::proof_token,
reinterpret_cast<const std::uint8_t *>(&t), sizeof(t));
};
auto recv_pi = [&]() {
dpf::proof_token t{};
auto b = net.to(peer).recv_bytes(net::msg::proof_token);
require(b.size() == sizeof(t), "pi size");
std::memcpy(&t, b.data(), sizeof(t));
return t;
};
std::vector<dpf::proof_token> mine(toks, toks + nt);
std::vector<dpf::proof_token> theirs(nt);
if (self == role::p0)
{
for (std::size_t i = 0; i < nt; ++i)
{
send_pi(mine[i]);
theirs[i] = recv_pi();
}
}
else
{
for (std::size_t i = 0; i < nt; ++i)
{
theirs[i] = recv_pi();
send_pi(mine[i]);
}
}
require(dpf::verify_batch(mine, theirs), "carry verify_batch");
auto y = grotto::eval_carry_in(keys, party, opened);
auto y_peer = open_additive(net, self, y.value);
// Reconstruct shares for MAC: party 0 holds y.value, peer opened sum is
// y0+y1; split as (y.value, sum - y.value) on p0 for the check.
std::uint64_t y0 = 0;
std::uint64_t y1 = 0;
if (self == role::p0)
{
y0 = y.value;
y1 = y_peer - y.value;
auto [m0, m1] = grotto::mac_carry_result(keys, y0, y1);
require(dpf::mac_verify(m0, m1, keys.mac, mine[0], theirs[0]),
"carry mac");
require(!dpf::mac_verify(m0, m1, keys.mac, detail::vdpf::zero_proof(),
theirs[0]), "carry mac zero token");
(void)m0;
(void)m1;
}
return 0;
}
/// @brief Extractable sketch: fold opened payloads, exchange, verify.
int extractable_sketch_point(role self, trio & net)
{
using Input = std::uint8_t;
const Input alpha = 0x2a;
const Input x0 = 0x10;
const Input x1 = static_cast<Input>(alpha ^ x0);
const dpf::fp61 beta{7};
auto on = [&](const auto & key) {
const std::array<dpf::fp61, 1> rs{dpf::fp61{3}};
sketch_share local{};
auto sk = sketch(local, rs);
(void)*eval_point(key, alpha, sk);
role peer = self == role::p0 ? role::p1 : role::p0;
sketch_share theirs =
net.exchange_with(peer, local, net::msg::sketch_share);
bool ok = self == role::p0 ? sketch_verify(local, theirs)
: sketch_verify(theirs, local);
if (self == role::p0)
require(ok, "extractable sketch");
};
dist_with_extractable_point_key(
net, self, x0, x1, beta, on, on);
return 0;
}
/// @brief Two-party geneval point. Same check as `geneval_point`, pads from IKNP.
int iknp_geneval_point(role self, trio & net)
{
using Input = std::uint8_t;
const Input alpha = 0x2a;
const Input x0 = 0x10;
const Input x1 = static_cast<Input>(alpha ^ x0);
const u64 beta = 7;
require_tree_prefix(dist_with_point_key_iknp<prg::aes128, prg::aes128, true>(
net, self, x0, x1, beta, [&](const auto & key) {
const auto open = open_subtractive(net, self, share_bits(*eval_point(key, alpha)));
if (self == role::p0)
require(open == beta, "iknp geneval");
}, [&](const auto & key) {
(void)open_subtractive(net, self, share_bits(*eval_point(key, alpha)));
}), self, verifiable_tree_prefix<prg::aes128, prg::aes128, u64>(x0, x1));
return 0;
}
/// @brief Two-party additive-share input. Same check as `geneval_arith_point`.
int iknp_arith_point(role self, trio & net)
{
using Input = std::uint8_t;
const Input alpha = 0x2a;
const Input x0 = 0x10;
const Input x1 = static_cast<Input>(alpha - x0);
const u64 beta = 11;
require_tree_prefix(dist_with_point_key_iknp<prg::aes128, prg::aes128, true>(
net, self, x0, x1, beta, [&](const auto & key) {
const auto open = open_subtractive(net, self, share_bits(*eval_point(key, alpha)));
if (self == role::p0)
require(open == beta, "iknp arith");
}, [&](const auto & key) {
(void)open_subtractive(net, self, share_bits(*eval_point(key, alpha)));
}, false, true), self,
verifiable_tree_prefix<prg::aes128, prg::aes128, u64>(alpha, Input{}, true));
return 0;
}
/// @brief Two-party wildcard: keygen hides the lane, then β is assigned on the link.
int iknp_wildcard_leaf(role self, trio & net)
{
using input_type = std::uint8_t;
using concrete_type = std::uint32_t;
using output_type = wildcard_value<concrete_type>;
const input_type x = 0xAA;
const input_type x0 = 0x31;
const input_type x1 = static_cast<input_type>(x ^ x0);
const concrete_type y_exp = 0xAAAAAAAA;
const concrete_type y_shr0 = 0x12345678;
const concrete_type y_shr1 = y_exp - y_shr0;
auto on = [&](auto key) {
const concrete_type my_share = self == role::p0 ? y_shr0 : y_shr1;
assign_wildcard_over_link(key, my_share, net, self);
auto open = open_subtractive(net, self, share_bits(*eval_point(key, x)));
if (self == role::p0)
require(open == y_exp, "iknp wildcard on");
auto oz = open_subtractive(net, self,
share_bits(*eval_point(key, static_cast<input_type>(x ^ 1))));
if (self == role::p0)
require(oz == 0u, "iknp wildcard off");
};
dist_with_point_key_iknp(net, self, x0, x1, output_type{}, on, on);
return 0;
}
/// @brief Two-party extractable sketch. Same check as `extractable_sketch_point`.
int iknp_extractable_sketch(role self, trio & net)
{
using Input = std::uint8_t;
const Input alpha = 0x2a;
const Input x0 = 0x10;
const Input x1 = static_cast<Input>(alpha ^ x0);
const dpf::fp61 beta{7};
auto on = [&](const auto & key) {
const std::array<dpf::fp61, 1> rs{dpf::fp61{3}};
sketch_share local{};
auto sk = sketch(local, rs);
(void)*eval_point(key, alpha, sk);
role peer = self == role::p0 ? role::p1 : role::p0;
sketch_share theirs = net.exchange_with(peer, local, net::msg::sketch_share);
bool ok = self == role::p0 ? sketch_verify(local, theirs)
: sketch_verify(theirs, local);
if (self == role::p0)
require(ok, "iknp extractable sketch");
};
dist_with_extractable_point_key_iknp(net, self, x0, x1, beta, on, on);
return 0;
}
/// @brief Two-party comparison, point stays shared.
int iknp_cmp_oblivious(role self, trio & net)
{
using Input = std::uint8_t;
const Input alpha = 0x40;
const Input x0 = 0x14;
const Input x1 = static_cast<Input>(alpha ^ x0);
const u64 beta = 9;
auto on = [&](const auto & key) {
const u64 mask = key.cmp().mask;
auto below = open_additive(net, self, share_bits(eval_point(cmp, key,
static_cast<Input>(alpha - 1)))) & mask;
auto at = open_additive(net, self, share_bits(eval_point(cmp, key, alpha))) & mask;
if (self == role::p0)
{
require(below == beta, "iknp cmp below");
require(at == 0u, "iknp cmp at");
}
};
dist_with_cmp_key_iknp(net, self, x0, x1, lt(beta), on, on);
return 0;
}
void register_core_flows_impl()
{
register_flow({"beaver_product_two", "beaver product bench", beaver_product_two, true});
register_flow({"beaver_product_three_square", "beaver product bench", beaver_product_three_square, true});
register_flow({"beaver_mul_square", "beaver product bench", beaver_mul_square, true});
register_flow({"beaver_chained_square", "beaver product bench", beaver_chained_square, true});
register_flow({"beaver_dot", "beaver dot bench", beaver_dot, true});
register_flow({"beaver_dot_reuse", "beaver dot bench", beaver_dot_reuse, true});
register_flow({"beaver_inner_product", "beaver dot bench", beaver_inner_product, true});
register_flow({"beaver_scale", "beaver scale bench", beaver_scale, true});
register_flow({"beaver_bit_mul_mux", "beaver mux bench", beaver_bit_mul_mux, true});
register_flow({"beaver_xor_mux", "beaver mux bench", beaver_xor_mux, true});
register_flow({"beaver_modint_product", "beaver product bench", beaver_modint_product, true});
register_flow({"beaver_oracle_stream", "beaver stream bench", beaver_oracle_stream, true});
register_flow({"beaver_scheduled_replay", "beaver stream bench", beaver_scheduled_replay, true});
register_flow({"beaver_grotto_appendix_e", "beaver grotto poly bench", beaver_grotto_appendix_e, true});
register_flow({"beaver_polynomial_share_powers", "beaver poly bench", beaver_polynomial_share_powers, true});
register_flow({"beaver_rejects_bad_use", "beaver smoke", beaver_rejects_bad_use, false});
register_flow({"beaver_one_shot_triples", "beaver product bench", beaver_one_shot_triples, true});
register_flow({"beaver_batch_one_round", "beaver product bench", beaver_batch_one_round, true});
register_flow({"beaver_blind_survives", "beaver product bench", beaver_blind_survives, true});
register_flow({"beaver_sums_cancellations", "beaver poly bench", beaver_sums_cancellations, true});
register_flow({"wildcard_single_leaf", "wildcard dpf bench", wildcard_single_leaf, true});
register_flow({"wildcard_fail_before_assign", "wildcard smoke", wildcard_fail_before_assign, false});
register_flow({"geneval_point", "geneval dpf bench", geneval_point, true});
register_flow({"geneval_arith_point", "geneval dpf bench", geneval_arith_point, true});
register_flow({"ds_key_agrees", "ds geneval dpf bench", ds_key_agrees, true});
register_flow({"verifiable_honest_point", "verifiable dpf bench", verifiable_honest_point, true});
register_flow({"verifiable_tampered_rejects", "verifiable smoke", verifiable_tampered_rejects, false});
register_flow({"blocked_dcf_point", "dcf grotto bench", blocked_dcf_point, true});
register_flow({"grotto_prefix_horner", "beaver horner grotto bench", grotto_prefix_horner, true});
register_flow({"beaver_auth_horner", "beaver auth mac", beaver_auth_horner, false});
register_flow({"carry_verifiable_mac", "carry vdpf mac", carry_verifiable_mac, false});
register_flow({"extractable_sketch_point", "extractable sketch", extractable_sketch_point, false});
// Tag is `iknp`, not `bench`: party_bench spawns these with p0/p1 only.
register_flow({"iknp_geneval_point", "iknp geneval dpf", iknp_geneval_point, true});
register_flow({"iknp_arith_point", "iknp geneval dpf", iknp_arith_point, true});
register_flow({"iknp_wildcard_leaf", "iknp wildcard dpf", iknp_wildcard_leaf, true});
register_flow({"iknp_extractable_sketch", "iknp extractable", iknp_extractable_sketch, true});
register_flow({"iknp_cmp_oblivious", "iknp dcf", iknp_cmp_oblivious, true});
}
} // namespace
void register_core_flows()
{
register_core_flows_impl();
}
namespace
{
struct run_opts
{
std::string dir;
std::string case_name;
std::string tag;
std::uint64_t repeat = 1;
std::uint64_t warmup = 0;
bool metrics = false;
bool list_only = false;
bool pair = false;
};
run_opts parse_opts(int argc, char ** argv)
{
run_opts o;
for (int i = 1; i < argc; ++i)
{
std::string a = argv[i];
auto need = [&](const char * flag) -> std::string {
if (i + 1 >= argc)
throw std::invalid_argument(std::string("missing value for ") + flag);
return argv[++i];
};
if (a == "--dir")
o.dir = need("--dir");
else if (a == "--case")
o.case_name = need("--case");
else if (a == "--tag")
o.tag = need("--tag");
else if (a == "--repeat")
o.repeat = std::stoull(need("--repeat"));
else if (a == "--warmup")
o.warmup = std::stoull(need("--warmup"));
else if (a == "--metrics")
o.metrics = true;
else if (a == "--list")
o.list_only = true;
else if (a == "--pair")
o.pair = true;
}
return o;
}
int run_one_flow(role self, const flow & f, trio & net, const run_opts & o)
{
using clock = std::chrono::steady_clock;
std::uint64_t total_ns = 0;
std::uint64_t min_ns = ~std::uint64_t{0};
std::uint64_t max_ns = 0;
std::uint64_t bytes_sent = 0;
std::uint64_t bytes_recv = 0;
std::uint64_t frames_sent = 0;
std::uint64_t frames_recv = 0;
std::uint64_t bytes_recv_from_p2 = 0;
std::uint64_t bytes_recv_from_peer = 0;
std::uint64_t bytes_sent_to_p2 = 0;
std::uint64_t bytes_sent_to_peer = 0;
std::uint64_t payload_sent = 0;
std::uint64_t payload_recv = 0;
std::uint64_t rounds = 0;
std::uint64_t prg_evals = 0;
const std::uint64_t iters = o.warmup + o.repeat;
for (std::uint64_t i = 0; i < iters; ++i)
{
// Align parties before each iteration (including first).
if (o.pair)
{
if (self == role::p2)
throw std::invalid_argument("pair flows have no p2");
const role peer = self == role::p0 ? role::p1 : role::p0;
(void)net.exchange_with(peer, i, net::msg::case_ok);
}
else if (self == role::p2)
{
net.to(role::p0).send(net::msg::case_ok, i);
net.to(role::p1).send(net::msg::case_ok, i);
}
else
{
auto got = net.to(role::p2).recv<std::uint64_t>(net::msg::case_ok);
if (got != i)
throw std::runtime_error("repeat barrier mismatch");
}
// The barrier is harness traffic. The tally below is the flow itself.
net.reset_tally();
dpf::prg::reset_eval_count();
const auto t0 = clock::now();
int rc = f.run(self, net);
const auto t1 = clock::now();
if (rc != 0)
return rc;
if (i < o.warmup)
continue;
const auto ns = static_cast<std::uint64_t>(
std::chrono::duration_cast<std::chrono::nanoseconds>(t1 - t0).count());
total_ns += ns;
min_ns = std::min(min_ns, ns);
max_ns = std::max(max_ns, ns);
const auto tally = net.tally();
const auto from_p2 = net.tally_from_p2();
const auto from_peer = net.tally_from_peer();
bytes_sent += tally.bytes_sent;
bytes_recv += tally.bytes_recv;
frames_sent += tally.frames_sent;
frames_recv += tally.frames_recv;
bytes_recv_from_p2 += from_p2.bytes_recv;
bytes_recv_from_peer += from_peer.bytes_recv;
bytes_sent_to_p2 += from_p2.bytes_sent;
bytes_sent_to_peer += from_peer.bytes_sent;
payload_sent += tally.payload_sent;
payload_recv += tally.payload_recv;
rounds += tally.exchanges;
prg_evals += dpf::prg::eval_count();
}
if (o.metrics)
{
const std::uint64_t reps = o.repeat == 0 ? 1 : o.repeat;
const std::uint64_t avg = total_ns / reps;
const std::uint64_t wire_overhead =
(bytes_sent + bytes_recv) - (payload_sent + payload_recv);
std::string path = o.dir + "/metrics." + role_name(self);
std::ofstream out(path, std::ios::app);
out << "flow=" << f.name
<< " role=" << role_name(self)
<< " repeat=" << o.repeat
<< " warmup=" << o.warmup
<< " avg_ns=" << avg
<< " min_ns=" << (o.repeat ? min_ns : 0)
<< " max_ns=" << max_ns
<< " total_ns=" << total_ns
<< " bytes_sent=" << bytes_sent
<< " bytes_recv=" << bytes_recv
<< " frames_sent=" << frames_sent
<< " frames_recv=" << frames_recv
<< " bytes_recv_from_p2=" << bytes_recv_from_p2
<< " bytes_recv_from_peer=" << bytes_recv_from_peer
<< " bytes_sent_to_p2=" << bytes_sent_to_p2
<< " bytes_sent_to_peer=" << bytes_sent_to_peer
<< " payload_sent=" << payload_sent
<< " payload_recv=" << payload_recv
<< " wire_overhead=" << wire_overhead
<< " rounds=" << rounds
<< " prg_evals=" << prg_evals
<< " avg_bytes_sent=" << (bytes_sent / reps)
<< " avg_bytes_recv=" << (bytes_recv / reps)
<< " avg_frames_sent=" << (frames_sent / reps)
<< " avg_frames_recv=" << (frames_recv / reps)
<< '\n';
}
return 0;
}
} // namespace
int run(role self, int argc, char ** argv)
{
register_all_flows();
try
{
auto o = parse_opts(argc, argv);
if (o.list_only)
{
if (self == role::p0)
list_flows(std::cout, o.tag);
return 0;
}
if (o.dir.empty())
{
std::cerr << role_name(self) << ": need --dir\n";
return 2;
}
if (o.case_name.empty() && o.tag.empty())
{
std::cerr << role_name(self) << ": need --case or --tag\n";
return 2;
}
if (o.repeat == 0)
o.repeat = 1;
std::vector<const flow *> flows;
if (!o.case_name.empty())
{
auto * f = find_flow(o.case_name);
if (!f)
throw std::invalid_argument("unknown flow: " + o.case_name);
flows.push_back(f);
}
else
{
flows = select_flows(o.tag);
if (flows.empty())
throw std::invalid_argument("no flows match --tag " + o.tag);
}
auto net = o.pair ? trio::connect_pair(self, o.dir)
: trio::connect_local(self, o.dir);
for (const flow * f : flows)
{
int rc = run_one_flow(self, *f, net, o);
if (rc != 0)
return rc;
}
return 0;
}
catch (const std::exception & ex)
{
std::cerr << role_name(self) << " failed: " << ex.what() << "\n";
return 1;
}
}
} // namespace party
} // namespace dpf
// Amalgamate bulk + extreme flows into this TU: libdpf headers define
// non-inline symbols (utils/AES/SHA256), so a second party .cpp would ODR-collide.
#include "flows_bulk.cpp"
#include "flows_extreme.cpp"
#include "flows_recent.cpp"
#include "flows_coverage.cpp"
#include "flows_gadget.cpp"