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
241
include/dpf/path_sketch.hpp
Normal file
241
include/dpf/path_sketch.hpp
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
/// @file dpf/path_sketch.hpp
|
||||
/// @brief VIDPF-style path-consistency sketches for incremental / IDPF keys.
|
||||
/// @details Leaf one-hot is `sketch_fold` in verifiable.hpp. This header
|
||||
/// checks that prefix payloads form a single spine: each depth is
|
||||
/// weight-1 (or a single nonzero weight), and each parent equals the
|
||||
/// sum of its children. Used by Poplar / Mastic one-time verification.
|
||||
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
|
||||
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
|
||||
/// see [LICENSE.md](@ref license) for details.
|
||||
|
||||
#ifndef LIBDPF_INCLUDE_DPF_PATH_SKETCH_HPP__
|
||||
#define LIBDPF_INCLUDE_DPF_PATH_SKETCH_HPP__
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "hedley/hedley.h"
|
||||
|
||||
#include "dpf/dpf_key.hpp"
|
||||
#include "dpf/eval_target.hpp"
|
||||
#include "dpf/eval_walk.hpp"
|
||||
#include "dpf/fp61.hpp"
|
||||
#include "dpf/verifiable.hpp"
|
||||
|
||||
namespace dpf
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
namespace path_sketch_impl
|
||||
{
|
||||
|
||||
template <typename Y>
|
||||
HEDLEY_ALWAYS_INLINE
|
||||
fp61 value_to_fp61(const Y & y) noexcept
|
||||
{
|
||||
if constexpr (std::is_same_v<std::decay_t<Y>, fp61>)
|
||||
return y;
|
||||
else
|
||||
return fp61{static_cast<std::uint64_t>(y)};
|
||||
}
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct has_raw_method : std::false_type
|
||||
{ };
|
||||
|
||||
template <typename T>
|
||||
struct has_raw_method<T, std::void_t<decltype(std::declval<const T &>().raw())>>
|
||||
: std::true_type
|
||||
{ };
|
||||
|
||||
template <typename Share>
|
||||
HEDLEY_ALWAYS_INLINE
|
||||
fp61 share_to_fp61(const Share & s) noexcept
|
||||
{
|
||||
if constexpr (std::is_same_v<std::decay_t<Share>, fp61>)
|
||||
return s;
|
||||
else if constexpr (std::is_integral_v<std::decay_t<Share>>)
|
||||
return fp61{static_cast<std::uint64_t>(s)};
|
||||
else if constexpr (has_raw_method<std::decay_t<Share>>::value)
|
||||
return value_to_fp61(s.raw());
|
||||
else
|
||||
return value_to_fp61(s);
|
||||
}
|
||||
|
||||
} // namespace path_sketch_impl
|
||||
} // namespace detail
|
||||
|
||||
/// @brief Weight-1 sketch of the `2^N` prefix shares at output `I`.
|
||||
/// @details Opens to a valid `sketch_fold` iff exactly one prefix is nonzero
|
||||
/// (unit path) or a single weight w sits on one prefix (weighted
|
||||
/// IDPF). Consumes `2^N` challenges from `rs`.
|
||||
template <std::size_t I, std::size_t N, typename KeyT>
|
||||
sketch_share sketch_path_level(out_t<I, N>, const KeyT & key,
|
||||
const fp61 * rs, std::size_t n)
|
||||
{
|
||||
static_assert(is_multilevel_key_v<KeyT>,
|
||||
"sketch_path_level: key must be multilevel / idpf");
|
||||
constexpr std::size_t nprefix = std::size_t{1} << N;
|
||||
if (rs == nullptr || n < nprefix)
|
||||
throw std::invalid_argument("sketch_path_level: need 2^N challenges");
|
||||
|
||||
auto [buf, it] = eval_prefixes(out_t<I, N>{}, key);
|
||||
(void)buf;
|
||||
std::vector<fp61> ys;
|
||||
ys.reserve(nprefix);
|
||||
std::size_t i = 0;
|
||||
for (auto a = std::begin(it); a != std::end(it) && i < nprefix; ++a, ++i)
|
||||
ys.push_back(detail::path_sketch_impl::share_to_fp61(*a));
|
||||
if (ys.size() != nprefix)
|
||||
throw std::runtime_error("sketch_path_level: prefix count mismatch");
|
||||
return sketch_fold(ys, std::vector<fp61>(rs, rs + nprefix));
|
||||
}
|
||||
|
||||
/// @brief Random linear check that parent[p] = left[2p] + right[2p+1].
|
||||
/// @details Parent slot `I` has prefix length `N`; child is slot `I+1` with
|
||||
/// length `N+1`. Consumes `2^N` challenges. Opens to 0 when the
|
||||
/// two levels are consistent.
|
||||
template <std::size_t I, std::size_t N, typename KeyT>
|
||||
fp61 sketch_path_parent(out_t<I, N>, out_t<I + 1, N + 1>, const KeyT & key,
|
||||
const fp61 * gamma, std::size_t n)
|
||||
{
|
||||
static_assert(is_multilevel_key_v<KeyT>,
|
||||
"sketch_path_parent: key must be multilevel / idpf");
|
||||
constexpr std::size_t nparent = std::size_t{1} << N;
|
||||
if (gamma == nullptr || n < nparent)
|
||||
throw std::invalid_argument("sketch_path_parent: need 2^N challenges");
|
||||
|
||||
auto [bp, ip] = eval_prefixes(out_t<I, N>{}, key);
|
||||
auto [bc, ic] = eval_prefixes(out_t<I + 1, N + 1>{}, key);
|
||||
(void)bp;
|
||||
(void)bc;
|
||||
|
||||
std::vector<fp61> parents;
|
||||
std::vector<fp61> children;
|
||||
parents.reserve(nparent);
|
||||
children.reserve(nparent * 2);
|
||||
for (auto a = std::begin(ip); a != std::end(ip); ++a)
|
||||
parents.push_back(detail::path_sketch_impl::share_to_fp61(*a));
|
||||
for (auto a = std::begin(ic); a != std::end(ic); ++a)
|
||||
children.push_back(detail::path_sketch_impl::share_to_fp61(*a));
|
||||
if (parents.size() != nparent || children.size() != nparent * 2)
|
||||
throw std::runtime_error("sketch_path_parent: prefix count mismatch");
|
||||
|
||||
fp61 gap{};
|
||||
for (std::size_t p = 0; p < nparent; ++p)
|
||||
{
|
||||
const fp61 rel = parents[p] - children[2 * p] - children[2 * p + 1];
|
||||
gap = gap + gamma[p] * rel;
|
||||
}
|
||||
return gap;
|
||||
}
|
||||
|
||||
/// @brief Whether two parent-gap shares open to zero.
|
||||
HEDLEY_NO_THROW
|
||||
HEDLEY_ALWAYS_INLINE
|
||||
HEDLEY_CONST
|
||||
bool sketch_path_verify_parent(fp61 g0, fp61 g1) noexcept
|
||||
{
|
||||
return (g0 - g1).raw() == 0;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
namespace path_sketch_impl
|
||||
{
|
||||
|
||||
template <std::size_t Depth, std::size_t I>
|
||||
struct verify_levels
|
||||
{
|
||||
template <typename K0, typename K1>
|
||||
static bool run(const K0 & k0, const K1 & k1, const fp61 *& rs,
|
||||
std::size_t & left)
|
||||
{
|
||||
constexpr std::size_t N = I + 1;
|
||||
constexpr std::size_t nprefix = std::size_t{1} << N;
|
||||
if (left < nprefix)
|
||||
return false;
|
||||
const auto s0 = sketch_path_level(out_t<I, N>{}, k0, rs, left);
|
||||
const auto s1 = sketch_path_level(out_t<I, N>{}, k1, rs, left);
|
||||
if (!sketch_verify(s0, s1))
|
||||
return false;
|
||||
rs += nprefix;
|
||||
left -= nprefix;
|
||||
|
||||
if constexpr (I + 1 < Depth)
|
||||
{
|
||||
constexpr std::size_t nparent = std::size_t{1} << N;
|
||||
if (left < nparent)
|
||||
return false;
|
||||
const fp61 g0 = sketch_path_parent(out_t<I, N>{},
|
||||
out_t<I + 1, N + 1>{}, k0, rs, left);
|
||||
const fp61 g1 = sketch_path_parent(out_t<I, N>{},
|
||||
out_t<I + 1, N + 1>{}, k1, rs, left);
|
||||
if (!sketch_path_verify_parent(g0, g1))
|
||||
return false;
|
||||
rs += nparent;
|
||||
left -= nparent;
|
||||
return verify_levels<Depth, I + 1>::run(k0, k1, rs, left);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace path_sketch_impl
|
||||
} // namespace detail
|
||||
|
||||
/// @brief One-time VIDPF path check for an IDPF of consecutive depths 1..Depth.
|
||||
/// @details Challenge layout, consumed in order for I = 0 .. Depth-1:
|
||||
/// - `2^(I+1)` challenges for the weight-1 sketch at `out<I, I+1>`
|
||||
/// - if I+1 < Depth: `2^(I+1)` challenges for the parent relation
|
||||
/// between `out<I, I+1>` and `out<I+1, I+2>`
|
||||
/// @tparam Depth number of IDPF prefix slots (`idpf` with Depth betas)
|
||||
template <std::size_t Depth, typename Key0, typename Key1>
|
||||
bool verify_idpf_path(const Key0 & k0, const Key1 & k1,
|
||||
const fp61 * rs, std::size_t n)
|
||||
{
|
||||
static_assert(Depth >= 1, "verify_idpf_path: Depth >= 1");
|
||||
static_assert(is_multilevel_key_v<Key0> && is_multilevel_key_v<Key1>,
|
||||
"verify_idpf_path: multilevel keys");
|
||||
if (!same_public_part(k0, k1))
|
||||
return false;
|
||||
if (rs == nullptr)
|
||||
return false;
|
||||
const fp61 * cursor = rs;
|
||||
std::size_t left = n;
|
||||
return detail::path_sketch_impl::verify_levels<Depth, 0>::run(
|
||||
k0, k1, cursor, left);
|
||||
}
|
||||
|
||||
/// @brief Convenience overload taking a contiguous challenge vector.
|
||||
template <std::size_t Depth, typename Key0, typename Key1>
|
||||
bool verify_idpf_path(const Key0 & k0, const Key1 & k1,
|
||||
const std::vector<fp61> & rs)
|
||||
{
|
||||
return verify_idpf_path<Depth>(k0, k1, rs.data(), rs.size());
|
||||
}
|
||||
|
||||
/// @brief How many fp61 challenges `verify_idpf_path<Depth>` consumes.
|
||||
HEDLEY_CONST
|
||||
HEDLEY_ALWAYS_INLINE
|
||||
constexpr std::size_t path_sketch_challenge_count(std::size_t depth) noexcept
|
||||
{
|
||||
std::size_t n = 0;
|
||||
for (std::size_t i = 0; i < depth; ++i)
|
||||
{
|
||||
const std::size_t nprefix = std::size_t{1} << (i + 1);
|
||||
n += nprefix;
|
||||
if (i + 1 < depth)
|
||||
n += nprefix;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
} // namespace dpf
|
||||
|
||||
#endif // LIBDPF_INCLUDE_DPF_PATH_SKETCH_HPP__
|
||||
Loading…
Add table
Add a link
Reference in a new issue