/// @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 #include #include #include #include #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 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; static_assert(std::is_same_v>, "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(x0); input_type xx1 = static_cast(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(k0); const old_key & bk1 = static_cast(k1); const bool bit = detail::grow_impl::bit_at(x, old_key::depth); node s0{}; node s1{}; std::array 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 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(st, xx0, xx1, mask, level, new_depth, proto, cw, advice); node ns0 = st.seed0(); node ns1 = st.seed1(); return detail::grow_impl::grow_impl(bk0, bk1, &m0, &m1, bit, x, true, &cw, &advice, &ns0, &ns1, std::forward(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 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; using input_type = typename old_key::input_type; const old_key & bk0 = static_cast(k0); const old_key & bk1 = static_cast(k1); input_type xx0 = static_cast(x0); input_type xx1 = static_cast(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(bk0, bk1, &m0, &m1, /*bit=*/false, x, false, static_cast(nullptr), static_cast(nullptr), static_cast(nullptr), static_cast(nullptr), std::forward(specs)...); } } // namespace dpf #endif // LIBDPF_INCLUDE_DPF_GROW_DS_HPP__