libdpf/include/dpf/grow_ds.hpp

133 lines
5.6 KiB
C++
Raw Normal View History

/// @file dpf/grow_ds.hpp
/// @brief Doerner–Shelat interactive / joint grow: `extend_ds` and
/// `add_output_ds` on path-memoizer frontiers.
/// @details Joint (2+1 / local) view holds both keys and both memoizers. One
/// correction-word round uses `ds_advance_level`; leaf planting reuses
/// dealer `grow_impl` with the opened CW. A socket backend swaps in a
/// `CwProtocol` that exchanges blinds without revealing the peer seed.
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
#ifndef LIBDPF_INCLUDE_DPF_GROW_DS_HPP__
#define LIBDPF_INCLUDE_DPF_GROW_DS_HPP__
#include <array>
#include <cstddef>
#include <cstdint>
#include <type_traits>
#include <utility>
#include "hedley/hedley.h"
#include "dpf/doerner_shelat.hpp"
#include "dpf/grow.hpp"
#include "dpf/path_memoizer.hpp"
#include "dpf/utils.hpp"
namespace dpf
{
/// @brief One interactive interior level from memoizer frontiers, then plant
/// `specs` via the same assembly as dealer `extend`.
/// @details `x0` / `x1` are XOR shares of the programmed point (use `(x, 0)` in
/// a local joint test). Memoizers must already be filled for the clear
/// point `x0 ⊕ x1` through the old depth.
/// \complexity One `ds_advance_level` (two PRG expands + `prepare_level` /
/// `open_cw` / AND opens) plus the same leaf plants as dealer
/// `extend`. No O(d) rewalk when memoizers are warm.
/// \rounds One interactive CW round for the new level (local_cw_protocol opens
/// in-process; a socket `CwProtocol` is one peer exchange round).
/// \communication Local: none on the wire. Networked: one level's blinds, CW
/// shares, and advice (same shape as one `point_party` level),
/// plus leaf pads when planting.
template <typename K0, typename K1, typename Memo0, typename Memo1,
typename InputT, typename CwProtocol, typename... Specs>
HEDLEY_WARN_UNUSED_RESULT
auto extend_ds(const K0 & k0, const K1 & k1, Memo0 & m0, Memo1 & m1, InputT x0,
InputT x1, CwProtocol & proto, Specs &&... specs)
{
using old_key = detail::grow_impl::bare_key_t<K0>;
static_assert(std::is_same_v<old_key, detail::grow_impl::bare_key_t<K1>>,
"extend_ds: both keys must have the same type");
using input_type = typename old_key::input_type;
using node = typename old_key::interior_node;
using interior = typename old_key::interior_prg;
input_type xx0 = static_cast<input_type>(x0);
input_type xx1 = static_cast<input_type>(x1);
utils::flip_msb_if_signed_integral(xx0);
// Party 1 share is not MSB-flipped in DS (same as make_dpf_doerner_shelat).
const input_type x = utils::xor_input_shares(xx0, xx1);
const old_key & bk0 = static_cast<const old_key &>(k0);
const old_key & bk1 = static_cast<const old_key &>(k1);
const bool bit = detail::grow_impl::bit_at(x, old_key::depth);
node s0{};
node s1{};
std::array<bool, old_key::depth == 0 ? 1 : old_key::depth> path{};
detail::grow_impl::frontier_from_memos(bk0, bk1, m0, m1, x, old_key::depth, s0,
s1, old_key::depth == 0 ? nullptr : path.data());
detail::ds_gen_state<node> st;
st.init(s0, s1);
const std::size_t level = old_key::depth;
const std::size_t new_depth = old_key::depth + 1;
auto mask = old_key::msb_mask;
for (std::size_t i = 0; i < level; ++i)
mask >>= 1;
node cw{};
psnip_uint8_t advice = 0;
detail::ds_advance_level<interior>(st, xx0, xx1, mask, level, new_depth,
proto, cw, advice);
node ns0 = st.seed0();
node ns1 = st.seed1();
return detail::grow_impl::grow_impl<true, true>(bk0, bk1, &m0, &m1, bit, x,
true, &cw, &advice, &ns0, &ns1, std::forward<Specs>(specs)...);
}
/// @brief Plant outputs on existing levels using memoizer seeds. Joint leaf
/// construction matches dealer `add_output`; `proto.open_leaf_group` is
/// invoked so a non-local protocol can hide the clear point.
/// \complexity O(1) frontier reads plus leaf plants. No new interior CW.
/// \rounds Leaf-open only (local: one `open_leaf_group` callback; networked:
/// the leaf pad / mux pattern of `point_party`).
/// \communication none for `local_cw_protocol`; otherwise leaf pads and the
/// leaf CW open.
template <typename K0, typename K1, typename Memo0, typename Memo1,
typename InputT, typename CwProtocol, typename... Specs>
HEDLEY_WARN_UNUSED_RESULT
auto add_output_ds(const K0 & k0, const K1 & k1, Memo0 & m0, Memo1 & m1,
InputT x0, InputT x1, CwProtocol & proto, Specs &&... specs)
{
using old_key = detail::grow_impl::bare_key_t<K0>;
using input_type = typename old_key::input_type;
const old_key & bk0 = static_cast<const old_key &>(k0);
const old_key & bk1 = static_cast<const old_key &>(k1);
input_type xx0 = static_cast<input_type>(x0);
input_type xx1 = static_cast<input_type>(x1);
utils::flip_msb_if_signed_integral(xx0);
input_type x{};
proto.open_leaf_group(xx0, xx1, [&](input_type sx0, input_type sx1) {
x = utils::xor_input_shares(sx0, sx1);
});
return detail::grow_impl::grow_impl<false, true>(bk0, bk1, &m0, &m1,
/*bit=*/false, x, false,
static_cast<typename old_key::interior_node *>(nullptr),
static_cast<psnip_uint8_t *>(nullptr),
static_cast<typename old_key::interior_node *>(nullptr),
static_cast<typename old_key::interior_node *>(nullptr),
std::forward<Specs>(specs)...);
}
} // namespace dpf
#endif // LIBDPF_INCLUDE_DPF_GROW_DS_HPP__