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:
Ryan Henry 2026-09-28 05:59:19 -06:00
parent 695f8e84f7
commit 0d22946a0e
1835 changed files with 170291 additions and 2849 deletions

View file

@ -5,6 +5,7 @@
/// up to the next checkpoint; a full-domain memoizer already holds
/// those nodes. `q` tail bits, when the comparison sets the key
/// depth, are a residual table on the node at height `h`.
/// @note Boyle, Chandran, Gilboa, Gupta, Ishai, Kumar, and Rathee (EUROCRYPT 2021, ePrint 2020/1392) publish a value-correction word on every level. This implementation is ahead of that DCF on payload size: one ring word every B levels, about B times fewer value words, with the same one-seed-word spine. Point evaluation expands the siblings between checkpoints.
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
@ -25,6 +26,7 @@
#include "dpf/tree_traits.hpp"
#include "dpf/twiddle.hpp"
#include "dpf/utils.hpp"
#include "dpf/verifiable.hpp"
namespace dpf
{
@ -33,6 +35,20 @@ namespace detail
namespace blocked
{
/// @brief High bit set on `fold_node` level so blocked proofs diverge from native.
/// @details Must match the level passed to `make_cs` at blocked keygen. Parked
/// sibling folds reuse this tag with the sibling's tree depth so they
/// share `correction_seeds[depth-1]`.
inline constexpr std::size_t fold_spine_tag = std::size_t{1} << 15;
template <typename NodeT>
HEDLEY_ALWAYS_INLINE
void fold_spine_node(proof_token & pi, std::size_t level,
psnip_uint64_t x_bits, NodeT seed, const cs_block & cs) noexcept
{
detail::vdpf::fold_node(pi, fold_spine_tag | level, x_bits, seed, cs);
}
template <std::size_t H, std::size_t B>
struct schedule
{
@ -306,7 +322,8 @@ uint64_t finish_share(const KeyT & dpf, uint64_t suffix, uint64_t acc,
}
template <typename KeyT, typename InputT, typename PathMemoizer>
uint64_t eval_share(const KeyT & dpf, InputT tx, PathMemoizer & path)
uint64_t eval_share(const KeyT & dpf, InputT tx, PathMemoizer & path,
proof_token * pi = nullptr)
{
using node = typename KeyT::interior_node;
const auto & ch = dpf.cmp();
@ -326,18 +343,42 @@ uint64_t eval_share(const KeyT & dpf, InputT tx, PathMemoizer & path)
const std::size_t nbits = static_cast<std::size_t>(ch.nbits);
const int party = dpf::get_lo_bit(dpf.root()) ? 1 : 0;
// Expand the seed spine without native path folds; blocked proofs use
// domain-separated tags on newly filled levels only (path-memo safe).
const auto resume = dpf::detail::path_resume_for_level(path, dpf, tx, h);
dpf::detail::ensure_level(dpf, tx, path, h);
if constexpr (KeyT::is_verifiable)
{
if (pi != nullptr && resume <= h)
{
constexpr auto input_bits =
utils::bitlength_of_v<typename KeyT::input_type>;
for (std::size_t level = resume; level <= h; ++level)
{
const auto x_bits = static_cast<psnip_uint64_t>(
utils::to_integral_type<typename KeyT::input_type>{}(tx)
>> (input_bits - level));
fold_spine_node(*pi, level - 1, x_bits, path[level],
dpf.correction_seeds()[level - 1]);
}
}
}
struct parked
{
node seed;
std::size_t depth;
psnip_uint64_t prefix_bits;
};
parked pend[128];
std::size_t npend = 0;
uint64_t acc = 0;
auto bit_mask = KeyT::msb_mask;
// Value accumulation always walks from the root. Proof folds for parked
// siblings must not repeat depths already authenticated on a warm path:
// re-folding XORs the same contribution away (path-memo cancel bug).
for (std::size_t level = 0; level < h; ++level, bit_mask >>= 1)
{
const bool xi = !!(bit_mask & tx);
@ -349,6 +390,13 @@ uint64_t eval_share(const KeyT & dpf, InputT tx, PathMemoizer & path)
{
pend[npend].seed = right;
pend[npend].depth = level + 1;
// Sibling is the right child of `parent`: path bits with low bit 1.
const auto path_bits = static_cast<psnip_uint64_t>(
utils::to_integral_type<typename KeyT::input_type>{}(tx)
>> (utils::bitlength_of_v<typename KeyT::input_type>
- (level + 1)));
pend[npend].prefix_bits = (path_bits & ~static_cast<psnip_uint64_t>(1))
| static_cast<psnip_uint64_t>(1);
++npend;
}
const std::size_t c = level + 1;
@ -357,6 +405,15 @@ uint64_t eval_share(const KeyT & dpf, InputT tx, PathMemoizer & path)
const uint64_t word = dpf.value_cw(sched::index(c));
for (std::size_t p = 0; p < npend; ++p)
{
if constexpr (KeyT::is_verifiable)
{
if (pi != nullptr && pend[p].depth >= resume)
{
// Tree depth (not checkpoint index): must match CS level.
fold_spine_node(*pi, pend[p].depth - 1, pend[p].prefix_bits,
pend[p].seed, dpf.correction_seeds()[pend[p].depth - 1]);
}
}
acc = add_frontier<KeyT>(acc, pend[p].seed, pend[p].depth, c,
dpf, word, mask, party);
}
@ -392,9 +449,17 @@ const typename KeyT::interior_node & memo_node(const Memo & memo, Integral prefi
return memo[depth][idx];
}
template <typename KeyT, typename Integral, typename Memo>
/// @brief Evaluate one lane from an interval memo; optionally fold a VDPF proof.
/// @details When `pi == nullptr` or the key is not verifiable, matches the
/// historical body (no folds). When set, folds path / covered
/// checkpoint / frontier seeds with `fold_spine_node`, skipping depths
/// already covered by `path` (same resume rule as `eval_share`).
/// Interval BFS prove must keep passing a null `pi` here.
template <typename KeyT, typename Integral, typename Memo,
typename PathMemoizer = ::dpf::basic_path_memoizer<KeyT>>
uint64_t eval_share_memo(const KeyT & dpf, Integral lane,
Integral from_lane, Integral to_excl, const Memo & memo)
Integral from_lane, Integral to_excl, const Memo & memo,
proof_token * pi = nullptr, PathMemoizer * path = nullptr)
{
using node = typename KeyT::interior_node;
const auto & ch = dpf.cmp();
@ -412,6 +477,43 @@ uint64_t eval_share_memo(const KeyT & dpf, Integral lane,
constexpr std::size_t h = KeyT::cmp_h;
using sched = schedule<h, KeyT::cmp_block>;
const int party = dpf::get_lo_bit(dpf.root()) ? 1 : 0;
const std::size_t nbits = static_cast<std::size_t>(ch.nbits);
std::size_t resume = 0;
if constexpr (KeyT::is_verifiable)
{
if (pi != nullptr && path != nullptr)
{
const auto tx = static_cast<typename KeyT::input_type>(lane);
resume = dpf::detail::path_resume_for_level(*path, dpf, tx, h);
dpf::detail::path_note_filled_to(*path, h);
}
}
if constexpr (KeyT::is_verifiable)
{
if (pi != nullptr && resume <= h)
{
Integral pfx = 0;
for (std::size_t depth = 0; depth <= h; ++depth)
{
if (depth >= resume && depth >= 1)
{
fold_spine_node(*pi, depth - 1,
static_cast<psnip_uint64_t>(pfx),
memo_node<KeyT>(memo, pfx, depth, from_lane),
dpf.correction_seeds()[depth - 1]);
}
if (depth < h)
{
const bool xi =
((lane >> (nbits - 1 - depth)) & Integral{1}) != 0;
pfx = static_cast<Integral>(
(pfx << 1) | (xi ? Integral{1} : Integral{0}));
}
}
}
}
struct parked
{
@ -424,7 +526,6 @@ uint64_t eval_share_memo(const KeyT & dpf, Integral lane,
uint64_t acc = 0;
Integral path_pref = 0;
const std::size_t nbits = static_cast<std::size_t>(ch.nbits);
for (std::size_t level = 0; level < h; ++level)
{
const bool xi = ((lane >> (nbits - 1 - level)) & Integral{1}) != 0;
@ -440,7 +541,8 @@ uint64_t eval_share_memo(const KeyT & dpf, Integral lane,
pend[npend].depth = level + 1;
++npend;
}
path_pref = static_cast<Integral>((path_pref << 1) | Integral{xi ? 1 : 0});
path_pref = static_cast<Integral>(
(path_pref << 1) | (xi ? Integral{1} : Integral{0}));
const std::size_t c = level + 1;
if (!sched::contains(c))
continue;
@ -461,6 +563,16 @@ uint64_t eval_share_memo(const KeyT & dpf, Integral lane,
for (Integral k = 0; k < nleaf; ++k)
{
const auto pref = static_cast<Integral>(leftmost + k);
if constexpr (KeyT::is_verifiable)
{
if (pi != nullptr && c >= resume && c >= 1)
{
fold_spine_node(*pi, c - 1,
static_cast<psnip_uint64_t>(pref),
memo_node<KeyT>(memo, pref, c, from_lane),
dpf.correction_seeds()[c - 1]);
}
}
acc = add_membership<KeyT>(acc,
memo_node<KeyT>(memo, pref, c, from_lane), word, mask,
party);
@ -468,6 +580,17 @@ uint64_t eval_share_memo(const KeyT & dpf, Integral lane,
}
else
{
if constexpr (KeyT::is_verifiable)
{
if (pi != nullptr && pend[p].depth >= resume
&& pend[p].depth >= 1)
{
fold_spine_node(*pi, pend[p].depth - 1,
static_cast<psnip_uint64_t>(pend[p].prefix),
pend[p].seed,
dpf.correction_seeds()[pend[p].depth - 1]);
}
}
acc = add_frontier<KeyT>(acc, pend[p].seed, pend[p].depth, c,
dpf, word, mask, party);
}