libdpf/examples/protocol/compose_schedule.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

285 lines
9.2 KiB
C++
Raw Permalink 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.

#include <cstddef>
#include <cstdint>
#include <cstring>
#include <iostream>
#include <thread>
#include <vector>
#include "dpf.hpp"
#include "dpf/net/stream_array.hpp"
#include "dpf/protocol_factory.hpp"
// Protocol composition schedules (compose.hpp). Records FSS walks, ABY
// products, and RSS refreshes on one RoundSink plan — the shapes Express,
// Sabre, Pika early-stop, Poplar prefixes, and Duoram scale use by hand.
//
// The final block drives a composed open on the stream framework
// (drive_both_on_streams). The identical schedule also runs over the truly
// asynchronous backends (dpf::net::async_stream_array + async_round_sink, or
// dpf::async::overlapped_byte_protocol) and, on Linux, over real SCTP
// (async_sctp_stream_array). The experiment_bench harness picks the transport
// via DPF_TRANSPORT=memory|stream|async|mux|parallel|sctp.
//
// c++ -std=c++17 -march=native -I include -I thirdparty
// examples/protocol/compose_schedule.cpp
namespace
{
using dpf::protocol::composer;
using dpf::protocol::domain;
using dpf::protocol::effect;
namespace opcodes = dpf::protocol::opcodes;
} // namespace
int main()
{
// Express / Sabre: audit rides in the last CW flush (depth exchanges, not
// depth+1).
{
composer naive(0);
auto seed = naive.input(domain::fss, 16);
auto sketch = naive.input(domain::a, 8);
auto leaf = naive.fss_point(seed, 4, 16);
auto dep = naive.compute(opcodes::user_base + 1, {leaf, sketch},
domain::a, 8);
(void)naive.exchange(dep);
const auto naive_ex = naive.schedule().exchange_waves();
composer fused(0);
auto seed_f = fused.input(domain::fss, 16);
auto sketch_f = fused.input(domain::a, 8);
auto wr = fused.fss_point_fused(seed_f, 4, 16, sketch_f);
const auto fused_ex = fused.schedule().exchange_waves();
if (fused_ex >= naive_ex || fused.domain_of(wr.trailer_open) != domain::a)
{
std::cerr << "compose fuse\n";
return 1;
}
std::cout << "express_fuse " << naive_ex << "->" << fused_ex << "\n";
}
// Pika / small-output PIR: BGI Remark 3.4 early-stop drops ν CW rounds.
{
composer full(0);
auto seed = full.input(domain::fss, 16);
(void)full.fss_point(seed, 8, 16);
composer early(0);
auto seed_e = early.input(domain::fss, 16);
(void)early.fss_point_early_stop(seed_e, 8, /*early_stop=*/3, 16);
const auto a = full.schedule().exchange_waves();
const auto b = early.schedule().exchange_waves();
if (b != a - 3)
{
std::cerr << "compose early_stop\n";
return 1;
}
std::cout << "early_stop " << a << "->" << b << "\n";
}
// Poplar: prefix share after each CW, same exchange-wave depth.
{
composer c(0);
auto seed = c.input(domain::fss, 16);
auto wr = c.level_walk_prefixes(seed, 5, 16, /*prefix_bytes=*/8);
auto p = c.schedule();
if (wr.at_level.size() != 5 || p.exchange_waves() != 5)
{
std::cerr << "compose prefixes\n";
return 1;
}
std::cout << "prefixes " << p.exchange_waves() << "\n";
}
// DCF block_width: variable CW slot sizes.
{
composer c(0);
auto seed = c.input(domain::fss, 16);
const std::vector<std::size_t> slots = {16, 4, 4, 16};
(void)c.level_walk_sized(seed, slots);
auto p = c.schedule();
if (p.value_bytes_of(p.wave(1).exchanges[0].id) != 4)
{
std::cerr << "compose sized\n";
return 1;
}
std::cout << "sized_slots ok\n";
}
// RSS product → neighbor y-exchange → RSS (one round).
{
composer c(0);
auto x = c.input(domain::rss, 16);
auto y = c.input(domain::rss, 16);
auto z = c.rss_product_replicated(x, y);
if (c.domain_of(z) != domain::rss || c.schedule().exchange_waves() != 1)
{
std::cerr << "compose rss\n";
return 1;
}
std::cout << "rss_refresh 1\n";
}
// Duoram write-scale: FSS leaf feeds ABY; beaver waits for the leaf wave.
{
composer c(0);
auto seed = c.input(domain::fss, 16);
auto leaf = c.fss_point(seed, 4, 16);
auto scale = c.input(domain::a, 8);
auto scaled = c.aby_product<std::uint64_t>(leaf, scale);
auto p = c.schedule();
if (p.wave_of(scaled) < p.wave_of(leaf))
{
std::cerr << "compose duoram_scale\n";
return 1;
}
std::cout << "duoram_scale wave " << p.wave_of(scaled) << "\n";
}
// Round-aware ABY: sign×linear stays one online round.
{
composer c(0);
auto & s = c.aby<std::uint64_t>();
auto sgn = s.input();
auto x = s.input();
auto a0 = s.input();
auto a1 = s.input();
auto lin = s(sgn * (a1 * x + a0));
if (s.round_of(lin) != 1)
{
std::cerr << "compose aby_rounds\n";
return 1;
}
std::cout << "aby_rounds 1\n";
}
// Doerner–Shelat: 5 opens per level; OH adds 80 AND-layers / level.
{
composer c(0);
auto seed = c.input(domain::fss, 16);
auto tip = c.level_walk_ds(seed, 2, 16);
if (c.schedule().exchange_waves() != 10 || tip.id == seed.id)
{
std::cerr << "compose ds_walk\n";
return 1;
}
std::cout << "ds_walk 10\n";
composer c_oh(0);
auto tip_oh = c_oh.level_walk_ds(c_oh.input(domain::fss, 16), 1, 16,
/*oh=*/true);
(void)tip_oh;
const auto want = dpf::net::compose_ds_slot_bytes(1, 16, true).size();
if (c_oh.schedule().exchange_waves() != want)
{
std::cerr << "compose ds_oh\n";
return 1;
}
std::cout << "ds_oh " << want << "\n";
}
// Adaptive idpf: one packed L‖R open per step.
{
composer c(0);
auto seed = c.input(domain::fss, 16);
auto f = c.begin_adaptive_prefix(seed);
f = c.step_adaptive_prefix(f, 16, 8);
f = c.retain_adaptive_prefix(f, 0);
if (c.schedule().exchange_waves() != 1)
{
std::cerr << "compose adaptive\n";
return 1;
}
std::cout << "adaptive 1\n";
}
// default_plan: rounds == exchange_waves (sink-aligned).
{
composer c(0);
auto leaf = c.fss_point(c.input(domain::fss, 16), 4, 16);
(void)leaf;
auto p = c.default_plan();
if (p.rounds() != p.exchange_waves()
|| p.rounds() != p.slot_bytes_all().size() || p.rounds() != 4)
{
std::cerr << "compose default_plan\n";
return 1;
}
std::cout << "default_plan 4\n";
}
// Multipoint buckets: CW waves pack; answers one open.
{
composer c(0);
auto mr = c.multipoint_fan(2,
[&](std::size_t) { return c.input(domain::fss, 16); }, 3, 16, 8);
(void)mr;
if (c.schedule().exchange_waves() != 4)
{
std::cerr << "compose multipoint\n";
return 1;
}
std::cout << "multipoint 4\n";
}
// Multi-lane ABY: independent barriers, one wave.
{
composer c(0);
auto z0 = c.aby_product<std::uint64_t>(c.input(domain::a, 8),
c.input(domain::a, 8), 0);
auto z1 = c.aby_product<std::uint64_t>(c.input(domain::a, 8),
c.input(domain::a, 8), 1);
if (c.schedule().exchange_waves() != 1
|| c.schedule().effect_count(effect::exchange) != 2
|| z0.id == z1.id)
{
std::cerr << "compose multilane\n";
return 1;
}
std::cout << "multilane 1\n";
}
// Prepaid defer + rotate: zero online FSS rounds.
{
composer c(0);
auto buf = c.defer_expand(c.input(domain::fss, 16), 4, 16);
auto rot = c.rotate_share(buf, 7);
if (c.schedule().exchange_waves() != 0 || rot.id == buf.id)
{
std::cerr << "compose defer\n";
return 1;
}
std::cout << "defer 0\n";
}
// stream_array: drive a compose open through drive_both_on_streams.
{
composer c0(0);
composer c1(1);
auto x0 = c0.input(domain::a, 8);
auto x1 = c1.input(domain::a, 8);
auto e0 = c0.exchange(x0);
auto e1 = c1.exchange(x1);
auto p0 = c0.schedule();
auto p1 = c1.schedule();
std::vector<std::vector<std::uint8_t>> v0(p0.nodes().size()),
v1(p1.nodes().size());
const std::uint64_t a = 3, b = 5;
v0[x0.id].assign(8, 0);
v1[x1.id].assign(8, 0);
std::memcpy(v0[x0.id].data(), &a, 8);
std::memcpy(v1[x1.id].data(), &b, 8);
dpf::protocol::drive_both_on_streams(p0, p1, v0, v1);
std::uint64_t open0 = 0, open1 = 0;
std::memcpy(&open0, v0[e0.id].data(), 8);
std::memcpy(&open1, v1[e1.id].data(), 8);
if (open0 != 8 || open1 != 8)
{
std::cerr << "drive_plan_on_streams open\n";
return 1;
}
std::cout << "drive_plan_on_streams_ok " << open0 << "\n";
}
return 0;
}