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>
This commit is contained in:
parent
695f8e84f7
commit
0d22946a0e
1835 changed files with 170291 additions and 2849 deletions
506
include/dpf/run_config.hpp
Normal file
506
include/dpf/run_config.hpp
Normal file
|
|
@ -0,0 +1,506 @@
|
|||
/// @file dpf/run_config.hpp
|
||||
/// @brief One explicit configuration for harness runs, sweeps, and nodes.
|
||||
/// @details Everything the harness decides is a field here: transport, host,
|
||||
/// lanes, framing, instances, wire policy (window, chunking,
|
||||
/// coalescing, socket options), pipelining, wait budgets, compute
|
||||
/// threads, warmup and trials, CPU pins, and setup deadlines.
|
||||
/// `from_env()` and `apply_args()` both go through `set(key, value)`,
|
||||
/// so `DPF_LANES=4` and `--lanes=4` mean the same thing, and an
|
||||
/// unknown key or value throws. The run log is configured the same
|
||||
/// way (`log_level`, `log`, `log_seeds`) and applied by
|
||||
/// `app::start_logging`. So is link security: `encryption`,
|
||||
/// `identity` (this party's key file), `peer.N` / `dealer_key` (keys
|
||||
/// this party checks), and the `client_*` / `server_*` keys for client
|
||||
/// links. `config=FILE` (or `DPF_CONFIG`) applies a file of
|
||||
/// `key = value` lines through the same parser.
|
||||
#ifndef LIBDPF_INCLUDE_DPF_RUN_CONFIG_HPP__
|
||||
#define LIBDPF_INCLUDE_DPF_RUN_CONFIG_HPP__
|
||||
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include "dpf/compose.hpp"
|
||||
#include "dpf/log.hpp"
|
||||
#include "dpf/net/identity.hpp"
|
||||
#include "dpf/net/policy.hpp"
|
||||
#include "dpf/net/round_lane.hpp"
|
||||
#include "dpf/net/security.hpp"
|
||||
|
||||
namespace dpf
|
||||
{
|
||||
namespace app
|
||||
{
|
||||
|
||||
struct run_config
|
||||
{
|
||||
net::transport kind = net::transport::async_memory;
|
||||
/// Address the socket transports connect to (in-process runs bind all
|
||||
/// interfaces and connect here, so a NIC address exercises the NIC path).
|
||||
std::string host = "127.0.0.1";
|
||||
std::size_t n_lanes = 0;
|
||||
net::framing_mode framing = net::framing_mode::automatic;
|
||||
/// RoundSink batch width: protocol instances carried per round.
|
||||
std::size_t instances = 1;
|
||||
net::wire_policy policy{};
|
||||
/// Rounds a session may submit ahead of its current one, while that edge's
|
||||
/// window has room. Only rounds whose bytes do not depend on the peer
|
||||
/// qualify; compose plans are fully dependent, so overlap them with
|
||||
/// `instances` instead.
|
||||
std::size_t pipeline_credit = 0;
|
||||
std::chrono::milliseconds wait_timeout{30000};
|
||||
/// Kernels run on a compute pool of this size (0 = inline on the party
|
||||
/// thread).
|
||||
std::size_t compute_threads = 0;
|
||||
std::size_t warmup = 0;
|
||||
std::size_t trials = 1;
|
||||
/// Pin party threads to CPUs (-1 = no pin).
|
||||
int cpu[3] = {-1, -1, -1};
|
||||
net::deadlines limits{};
|
||||
/// Run-log threshold, sinks (`stderr`, `syslog`, `file:PATH`, `none`,
|
||||
/// comma-separated), and how seeds are printed (`full`, `hash`, `off`).
|
||||
log::level log_level = log::level::info;
|
||||
std::string log_sinks = "stderr";
|
||||
log::seed_policy log_seeds = log::seed_policy::full;
|
||||
/// Party links: TLS 1.3 unless `encryption=off`; this party's key
|
||||
/// (`identity`) and the keys it checks (`peer.N`, `dealer_key`).
|
||||
net::peer_security security{};
|
||||
/// Client links: what a client accepts (`client_*`) and what a server
|
||||
/// presents (`server_*`).
|
||||
net::client_security client{};
|
||||
net::server_security server{};
|
||||
|
||||
protocol::drive_options drive() const
|
||||
{
|
||||
protocol::drive_options o;
|
||||
o.n_lanes = n_lanes;
|
||||
o.framing = framing;
|
||||
o.pipeline_credit = pipeline_credit;
|
||||
o.wait_timeout = wait_timeout;
|
||||
return o;
|
||||
}
|
||||
|
||||
void set(const std::string & key, const std::string & v)
|
||||
{
|
||||
if (key == "transport")
|
||||
kind = net::parse_transport(v);
|
||||
else if (key == "host")
|
||||
host = v;
|
||||
else if (key == "lanes")
|
||||
n_lanes = v == "per_round" ? net::lanes_one_per_round : num(key, v);
|
||||
else if (key == "framing")
|
||||
framing = net::parse_framing(v);
|
||||
else if (key == "instances")
|
||||
instances = positive(key, v);
|
||||
else if (key == "window")
|
||||
policy.window_bytes = num(key, v);
|
||||
else if (key == "max_frame")
|
||||
policy.max_frame = positive(key, v);
|
||||
else if (key == "chunk")
|
||||
policy.chunk_bytes = num(key, v);
|
||||
else if (key == "coalesce_frames")
|
||||
policy.coalesce_frames = positive(key, v);
|
||||
else if (key == "coalesce_bytes")
|
||||
policy.coalesce_bytes = num(key, v);
|
||||
else if (key == "compact")
|
||||
policy.compact_bytes = num(key, v);
|
||||
else if (key == "nodelay")
|
||||
policy.socket.no_delay = flag(key, v);
|
||||
else if (key == "quickack")
|
||||
policy.socket.quickack = flag(key, v);
|
||||
else if (key == "keepalive")
|
||||
policy.socket.keepalive = flag(key, v);
|
||||
else if (key == "keepalive_idle")
|
||||
policy.socket.keepalive_idle_s = static_cast<int>(num(key, v));
|
||||
else if (key == "keepalive_interval")
|
||||
policy.socket.keepalive_interval_s = static_cast<int>(num(key, v));
|
||||
else if (key == "keepalive_count")
|
||||
policy.socket.keepalive_count = static_cast<int>(num(key, v));
|
||||
else if (key == "sndbuf")
|
||||
policy.socket.send_buffer = static_cast<int>(num(key, v));
|
||||
else if (key == "rcvbuf")
|
||||
policy.socket.recv_buffer = static_cast<int>(num(key, v));
|
||||
else if (key == "pipeline")
|
||||
pipeline_credit = num(key, v);
|
||||
else if (key == "wait_ms")
|
||||
wait_timeout = std::chrono::milliseconds(num(key, v));
|
||||
else if (key == "compute_threads")
|
||||
compute_threads = num(key, v);
|
||||
else if (key == "warmup")
|
||||
warmup = num(key, v);
|
||||
else if (key == "trials")
|
||||
trials = positive(key, v);
|
||||
else if (key == "cpu0" || key == "cpu1" || key == "cpu2")
|
||||
cpu[key[3] - '0'] = v == "-1" ? -1 : static_cast<int>(num(key, v));
|
||||
else if (key == "connect_ms")
|
||||
limits.connect = std::chrono::milliseconds(num(key, v));
|
||||
else if (key == "accept_ms")
|
||||
limits.accept = std::chrono::milliseconds(num(key, v));
|
||||
else if (key == "join_ms")
|
||||
limits.join = std::chrono::milliseconds(num(key, v));
|
||||
else if (key == "handshake_ms")
|
||||
limits.handshake = std::chrono::milliseconds(num(key, v));
|
||||
else if (key == "log_level")
|
||||
log_level = log::parse_level(v);
|
||||
else if (key == "log")
|
||||
log_sinks = v;
|
||||
else if (key == "log_seeds")
|
||||
log_seeds = log::parse_seed_policy(v);
|
||||
else if (key == "config")
|
||||
load_file(v);
|
||||
else if (key == "encryption")
|
||||
security.encrypt = flag(key, v);
|
||||
else if (key == "identity")
|
||||
security.self = load_identity(key, v);
|
||||
else if (key.rfind("peer.", 0) == 0)
|
||||
security.trusted[party_index(key)] = parse_key(key, v);
|
||||
else if (key == "dealer_key")
|
||||
security.trusted[net::dealer_id] = parse_key(key, v);
|
||||
else if (key == "client_verify")
|
||||
client.verify = flag(key, v);
|
||||
else if (key == "client_pin")
|
||||
client.pins = parse_keys(key, v);
|
||||
else if (key == "client_ca")
|
||||
client.ca_file = v;
|
||||
else if (key == "client_server_name")
|
||||
client.server_name = v;
|
||||
else if (key == "client_identity")
|
||||
client.self = load_identity(key, v);
|
||||
else if (key == "server_cert")
|
||||
server.cert_file = v;
|
||||
else if (key == "server_key")
|
||||
server.key_file = v;
|
||||
else if (key == "server_identity")
|
||||
server.self = load_identity(key, v);
|
||||
else if (key == "server_client_pin")
|
||||
server.client_pins = parse_keys(key, v);
|
||||
else
|
||||
throw std::invalid_argument("run_config: unknown key '" + key + "'");
|
||||
policy.validate();
|
||||
}
|
||||
|
||||
/// @brief `DPF_<KEY>` for every key (`DPF_WIRE_WINDOW` for `window`;
|
||||
/// `DPF_LANES=0` keeps its old meaning, one stream per round).
|
||||
static run_config from_env()
|
||||
{
|
||||
run_config c;
|
||||
c.merge_env();
|
||||
return c;
|
||||
}
|
||||
|
||||
void merge_env()
|
||||
{
|
||||
static const char * const keys[] = {"config", "encryption", "identity",
|
||||
"transport", "host", "lanes",
|
||||
"framing", "instances", "window", "max_frame", "chunk",
|
||||
"coalesce_frames", "coalesce_bytes", "compact", "nodelay", "quickack",
|
||||
"keepalive", "keepalive_idle", "keepalive_interval", "keepalive_count",
|
||||
"sndbuf", "rcvbuf", "pipeline", "wait_ms", "compute_threads",
|
||||
"warmup", "trials", "cpu0", "cpu1", "cpu2", "connect_ms", "accept_ms",
|
||||
"join_ms", "handshake_ms", "log_level", "log", "log_seeds"};
|
||||
for (const char * k : keys)
|
||||
{
|
||||
std::string env = "DPF_";
|
||||
for (const char * p = k; *p != '\0'; ++p)
|
||||
env += static_cast<char>(*p >= 'a' && *p <= 'z' ? *p - 32 : *p);
|
||||
if (std::string(k) == "window")
|
||||
env = "DPF_WIRE_WINDOW";
|
||||
const char * v = std::getenv(env.c_str());
|
||||
if (v == nullptr || v[0] == '\0')
|
||||
continue;
|
||||
if (std::string(k) == "lanes" && std::string(v) == "0")
|
||||
{
|
||||
n_lanes = net::lanes_one_per_round;
|
||||
continue;
|
||||
}
|
||||
set(k, v);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Apply `--key=value` arguments; returns the arguments it did not
|
||||
/// recognise as `--key=value` (e.g. positional ones).
|
||||
std::vector<std::string> apply_args(int argc, char ** argv)
|
||||
{
|
||||
std::vector<std::string> rest;
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
const std::string a = argv[i];
|
||||
const auto eq = a.find('=');
|
||||
if (a.rfind("--", 0) != 0 || eq == std::string::npos)
|
||||
{
|
||||
rest.push_back(a);
|
||||
continue;
|
||||
}
|
||||
const std::string key = a.substr(2, eq - 2);
|
||||
if (key == "party" || key == "peers" || key == "dealer")
|
||||
{
|
||||
rest.push_back(a);
|
||||
continue;
|
||||
}
|
||||
set(key, a.substr(eq + 1));
|
||||
}
|
||||
return rest;
|
||||
}
|
||||
|
||||
/// @brief Every setting as text, for `config.csv` and bench output.
|
||||
std::vector<std::pair<std::string, std::string>> describe() const
|
||||
{
|
||||
std::vector<std::pair<std::string, std::string>> kv;
|
||||
kv.emplace_back("transport", net::transport_name(kind));
|
||||
kv.emplace_back("host", host);
|
||||
kv.emplace_back("lanes", n_lanes == net::lanes_one_per_round
|
||||
? std::string("per_round")
|
||||
: std::to_string(n_lanes == 0 ? net::default_lanes : n_lanes));
|
||||
kv.emplace_back("framing", net::framing_name(framing));
|
||||
kv.emplace_back("instances", std::to_string(instances));
|
||||
kv.emplace_back("window", std::to_string(policy.window_bytes));
|
||||
kv.emplace_back("max_frame", std::to_string(policy.max_frame));
|
||||
kv.emplace_back("chunk", std::to_string(policy.chunk_bytes));
|
||||
kv.emplace_back("coalesce_frames", std::to_string(policy.coalesce_frames));
|
||||
kv.emplace_back("coalesce_bytes", std::to_string(policy.coalesce_bytes));
|
||||
kv.emplace_back("compact", std::to_string(policy.compact_bytes));
|
||||
kv.emplace_back("nodelay", policy.socket.no_delay ? "1" : "0");
|
||||
kv.emplace_back("quickack", policy.socket.quickack ? "1" : "0");
|
||||
kv.emplace_back("keepalive", policy.socket.keepalive ? "1" : "0");
|
||||
kv.emplace_back("keepalive_idle", std::to_string(policy.socket.keepalive_idle_s));
|
||||
kv.emplace_back("keepalive_interval",
|
||||
std::to_string(policy.socket.keepalive_interval_s));
|
||||
kv.emplace_back("keepalive_count", std::to_string(policy.socket.keepalive_count));
|
||||
kv.emplace_back("sndbuf", std::to_string(policy.socket.send_buffer));
|
||||
kv.emplace_back("rcvbuf", std::to_string(policy.socket.recv_buffer));
|
||||
kv.emplace_back("pipeline", std::to_string(pipeline_credit));
|
||||
kv.emplace_back("wait_ms", std::to_string(wait_timeout.count()));
|
||||
kv.emplace_back("compute_threads", std::to_string(compute_threads));
|
||||
kv.emplace_back("warmup", std::to_string(warmup));
|
||||
kv.emplace_back("trials", std::to_string(trials));
|
||||
kv.emplace_back("cpu0", std::to_string(cpu[0]));
|
||||
kv.emplace_back("cpu1", std::to_string(cpu[1]));
|
||||
kv.emplace_back("cpu2", std::to_string(cpu[2]));
|
||||
kv.emplace_back("connect_ms", std::to_string(limits.connect.count()));
|
||||
kv.emplace_back("accept_ms", std::to_string(limits.accept.count()));
|
||||
kv.emplace_back("join_ms", std::to_string(limits.join.count()));
|
||||
kv.emplace_back("handshake_ms", std::to_string(limits.handshake.count()));
|
||||
kv.emplace_back("log_level", log::level_name(log_level));
|
||||
kv.emplace_back("log", log_sinks);
|
||||
kv.emplace_back("log_seeds", log::seed_policy_name(log_seeds));
|
||||
kv.emplace_back("encryption", security.encrypt ? "on" : "off");
|
||||
kv.emplace_back("identity",
|
||||
security.self ? security.self->key().base64() : std::string("ephemeral"));
|
||||
std::string trusted;
|
||||
for (const auto & t : security.trusted)
|
||||
trusted += (trusted.empty() ? "" : ",")
|
||||
+ (t.first == net::dealer_id ? std::string("dealer")
|
||||
: std::to_string(t.first));
|
||||
kv.emplace_back("trusted", trusted.empty() ? std::string("none") : trusted);
|
||||
kv.emplace_back("client_verify", client.verify ? "on" : "off");
|
||||
kv.emplace_back("client_pins", std::to_string(client.pins.size()));
|
||||
kv.emplace_back("client_ca", client.ca_file.empty() ? std::string("none")
|
||||
: client.ca_file);
|
||||
kv.emplace_back("server_cert", !server.cert_file.empty() ? server.cert_file
|
||||
: server.self ? std::string("identity")
|
||||
: std::string("development"));
|
||||
return kv;
|
||||
}
|
||||
|
||||
/// @brief One line: `key=value key=value ...`.
|
||||
std::string summary() const
|
||||
{
|
||||
std::string out;
|
||||
for (const auto & kv : describe())
|
||||
{
|
||||
if (!out.empty())
|
||||
out += ' ';
|
||||
out += kv.first + "=" + kv.second;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/// @brief Apply `key = value` lines from `path` through `set`. `#` starts
|
||||
/// a comment; relative paths in the file resolve against its
|
||||
/// directory. Errors name the file and line.
|
||||
void load_file(const std::string & path)
|
||||
{
|
||||
std::ifstream in(path);
|
||||
if (!in)
|
||||
throw std::invalid_argument("run_config: cannot read config '" + path + "'");
|
||||
const auto slash = path.find_last_of('/');
|
||||
const std::string dir = slash == std::string::npos ? std::string()
|
||||
: path.substr(0, slash);
|
||||
std::string line;
|
||||
std::size_t lineno = 0;
|
||||
while (std::getline(in, line))
|
||||
{
|
||||
++lineno;
|
||||
const auto hash = line.find('#');
|
||||
if (hash != std::string::npos)
|
||||
line.erase(hash);
|
||||
line = trim(line);
|
||||
if (line.empty())
|
||||
continue;
|
||||
const auto eq = line.find('=');
|
||||
const std::string where = path + ":" + std::to_string(lineno) + ": ";
|
||||
if (eq == std::string::npos)
|
||||
throw std::invalid_argument("run_config: " + where
|
||||
+ "expected key = value, got '" + line + "'");
|
||||
const std::string key = trim(line.substr(0, eq));
|
||||
const std::string value = trim(line.substr(eq + 1));
|
||||
if (key == "config")
|
||||
throw std::invalid_argument("run_config: " + where
|
||||
+ "a config file cannot include another");
|
||||
try
|
||||
{
|
||||
set(key, resolve(dir, key, value));
|
||||
}
|
||||
catch (const std::exception & e)
|
||||
{
|
||||
throw std::invalid_argument(where + e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static std::string trim(const std::string & s)
|
||||
{
|
||||
std::size_t b = 0;
|
||||
std::size_t e = s.size();
|
||||
while (b < e && (s[b] == ' ' || s[b] == '\t' || s[b] == '\r'))
|
||||
++b;
|
||||
while (e > b && (s[e - 1] == ' ' || s[e - 1] == '\t' || s[e - 1] == '\r'))
|
||||
--e;
|
||||
return s.substr(b, e - b);
|
||||
}
|
||||
|
||||
/// @brief Make paths in a config file relative to the file.
|
||||
static std::string resolve(const std::string & dir, const std::string & key,
|
||||
const std::string & value)
|
||||
{
|
||||
if (dir.empty() || value.empty())
|
||||
return value;
|
||||
auto rel = [&](const std::string & p) {
|
||||
return p.empty() || p[0] == '/' ? p : dir + "/" + p;
|
||||
};
|
||||
const bool path_key = key == "identity" || key == "client_identity"
|
||||
|| key == "server_identity" || key == "server_cert" || key == "server_key"
|
||||
|| (key == "client_ca" && value != "system");
|
||||
if (path_key)
|
||||
return rel(value);
|
||||
const bool key_list = key.rfind("peer.", 0) == 0 || key == "dealer_key"
|
||||
|| key == "client_pin" || key == "server_client_pin";
|
||||
if (!key_list)
|
||||
return value;
|
||||
std::string out;
|
||||
std::size_t start = 0;
|
||||
while (start <= value.size())
|
||||
{
|
||||
const auto comma = value.find(',', start);
|
||||
std::string item = trim(value.substr(start,
|
||||
comma == std::string::npos ? std::string::npos : comma - start));
|
||||
if (item.rfind("file:", 0) == 0)
|
||||
item = "file:" + rel(item.substr(5));
|
||||
out += (out.empty() ? "" : ",") + item;
|
||||
if (comma == std::string::npos)
|
||||
break;
|
||||
start = comma + 1;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::uint32_t party_index(const std::string & key)
|
||||
{
|
||||
return static_cast<std::uint32_t>(num(key, key.substr(5)));
|
||||
}
|
||||
|
||||
static net::public_key parse_key(const std::string & key, const std::string & v)
|
||||
{
|
||||
try
|
||||
{
|
||||
return net::public_key::parse(v);
|
||||
}
|
||||
catch (const std::exception & e)
|
||||
{
|
||||
throw std::invalid_argument("run_config: " + key + ": " + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
static std::vector<net::public_key> parse_keys(const std::string & key,
|
||||
const std::string & v)
|
||||
{
|
||||
std::vector<net::public_key> out;
|
||||
std::size_t start = 0;
|
||||
while (start <= v.size())
|
||||
{
|
||||
const auto comma = v.find(',', start);
|
||||
const std::string item = trim(v.substr(start,
|
||||
comma == std::string::npos ? std::string::npos : comma - start));
|
||||
if (!item.empty())
|
||||
out.push_back(parse_key(key, item));
|
||||
if (comma == std::string::npos)
|
||||
break;
|
||||
start = comma + 1;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::shared_ptr<const net::identity> load_identity(const std::string & key,
|
||||
const std::string & path)
|
||||
{
|
||||
try
|
||||
{
|
||||
return std::make_shared<net::identity>(net::identity::load(path));
|
||||
}
|
||||
catch (const std::exception & e)
|
||||
{
|
||||
throw std::invalid_argument("run_config: " + key + ": " + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
static std::size_t num(const std::string & key, const std::string & v)
|
||||
{
|
||||
std::size_t pos = 0;
|
||||
unsigned long long x = 0;
|
||||
try
|
||||
{
|
||||
x = std::stoull(v, &pos, 10);
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
pos = 0;
|
||||
}
|
||||
if (pos != v.size() || v.empty())
|
||||
throw std::invalid_argument("run_config: " + key
|
||||
+ " needs a number, got '" + v + "'");
|
||||
return static_cast<std::size_t>(x);
|
||||
}
|
||||
|
||||
static std::size_t positive(const std::string & key, const std::string & v)
|
||||
{
|
||||
const auto x = num(key, v);
|
||||
if (x == 0)
|
||||
throw std::invalid_argument("run_config: " + key + " must be > 0");
|
||||
return x;
|
||||
}
|
||||
|
||||
static bool flag(const std::string & key, const std::string & v)
|
||||
{
|
||||
if (v == "1" || v == "on" || v == "true")
|
||||
return true;
|
||||
if (v == "0" || v == "off" || v == "false")
|
||||
return false;
|
||||
throw std::invalid_argument("run_config: " + key + " needs 0/1, got '"
|
||||
+ v + "'");
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
} // namespace dpf
|
||||
|
||||
#endif // LIBDPF_INCLUDE_DPF_RUN_CONFIG_HPP__
|
||||
Loading…
Add table
Add a link
Reference in a new issue