2026-09-24 14:08:32 -06:00
/// @file dpf/dpf_key.hpp
2026-09-24 23:18:10 -06:00
/// @brief The DPF key, its correction words, and interior traversal.
2026-09-24 14:08:32 -06:00
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
/// @copyright Copyright (c) 2019-2024 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_DPF_KEY_HPP__
# define LIBDPF_INCLUDE_DPF_DPF_KEY_HPP__
# include "hedley/hedley.h"
# include <cstddef>
# include <utility>
# include <tuple>
# include <array>
# include <bitset>
# include <atomic>
2026-09-28 05:59:19 -06:00
# include "dpf/experiment_note.hpp"
2026-09-24 14:08:32 -06:00
# include "dpf/prg_aes.hpp"
2026-09-24 23:18:10 -06:00
# include "dpf/tree_traits.hpp"
2026-09-24 14:08:32 -06:00
# include "dpf/wildcard.hpp"
# include "dpf/twiddle.hpp"
# include "dpf/leaf_node.hpp"
# include "dpf/offset_wrapper.hpp"
# include "dpf/leaf_wrapper.hpp"
# include "dpf/emplace.hpp"
# include "dpf/placement.hpp"
2026-09-24 23:18:10 -06:00
# include "dpf/verifiable.hpp"
2026-09-24 14:08:32 -06:00
# include "dpf/dcf.hpp"
namespace dpf
{
# ifdef LIBDPF_HAS_ASIO
namespace asio
{
2026-09-28 05:59:19 -06:00
/// @brief Exchange a wildcard output share and install the opened leaf.
/// @see dpf::leaf_wrapper
/// \complexity O(leaf bytes) for the Beaver leaf arithmetic, plus the transfers.
/// \rounds 2. Write/read the blinded output share, then write/read the leaf share (`async_assign_wildcard_output`).
/// \communication `sizeof(output_type)` plus `sizeof(leaf_type)` each way.
/// \preprocessing The leaf Beaver triple (`vector_blind`, `output_blind`, `blinded_vector`) was stored at keygen.
2026-09-24 14:08:32 -06:00
template < std : : size_t I ,
typename PeerT ,
typename DpfKey ,
typename OutputType ,
typename CompletionToken >
auto async_assign_wildcard_output ( PeerT & peer , DpfKey & dpf ,
OutputType & & output_share , CompletionToken & & token ) ;
}
# endif
template < typename InputT ,
typename OutputT = dpf : : bit ,
typename . . . OutputTs >
HEDLEY_WARN_UNUSED_RESULT
auto make_dpfargs ( InputT & & x , OutputT & & y = dpf : : bit : : one , OutputTs & & . . . ys ) ;
template < typename InputT ,
typename OutputT ,
typename . . . OutputTs >
struct dpfargs final
{
using input_type = InputT ;
using output_type = std : : tuple < OutputT , OutputTs . . . > ;
dpfargs ( ) = delete ;
dpfargs ( dpfargs & & ) = default ;
dpfargs ( const dpfargs & ) = default ;
input_type x ;
output_type y ;
private :
dpfargs ( input_type x_ , output_type y_ ) { x = x_ ; y = y_ ; }
template < typename I , typename O , typename . . . Os >
friend auto make_dpfargs ( I & & , O & & , Os & & . . . ) ;
// friend auto make_dpfargs(InputT && x, OutputT && y, OutputTs && ...ys)
} ;
template < typename InputT ,
typename OutputT ,
typename . . . OutputTs >
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_WARN_UNUSED_RESULT
auto make_dpfargs ( InputT & & x , OutputT & & y , OutputTs & & . . . ys )
{
return dpfargs < std : : decay_t < InputT > ,
std : : decay_t < OutputT > ,
std : : decay_t < OutputTs > . . . >
{ std : : forward < InputT > ( x ) ,
std : : make_tuple ( std : : forward < OutputT > ( y ) ,
std : : forward < OutputTs > ( ys ) . . . ) } ;
}
2026-09-24 23:18:10 -06:00
HEDLEY_PRAGMA ( GCC diagnostic push )
HEDLEY_PRAGMA ( GCC diagnostic ignored " -Wignored-attributes " )
2026-09-24 14:08:32 -06:00
template < typename InteriorPRG >
using root_sampler_t = std : : add_pointer_t < typename InteriorPRG : : block_type ( ) > ;
2026-09-24 23:18:10 -06:00
HEDLEY_PRAGMA ( GCC diagnostic pop )
2026-09-24 14:08:32 -06:00
namespace detail
{
2026-09-24 23:18:10 -06:00
/// @brief Classic single-level DPF key body (all outputs bare, at full input width,
2026-09-24 14:08:32 -06:00
/// equal widths, no comparison channel). `Derived` is the public `dpf_key`
/// specialization that inherits this body — threaded through only so that
/// `emplace`/`emplace_back` construct the public key type.
2026-09-24 23:18:10 -06:00
/// @tparam Derived derived
/// @tparam InteriorPRG PRG that expands interior nodes. Defaults to `dpf::prg::aes128`
/// @tparam ExteriorPRG PRG that expands the root. Defaults to `InteriorPRG`
/// @tparam InputT input domain type
/// @tparam OutputT output type
/// @tparam OutputTs output ts
2026-09-24 14:08:32 -06:00
template < typename Derived ,
typename InteriorPRG ,
typename ExteriorPRG ,
typename InputT ,
typename OutputT ,
typename . . . OutputTs >
struct classic_dpf_key_impl
{
public :
using interior_prg = InteriorPRG ;
2026-09-24 23:18:10 -06:00
using tree = dpf : : tree_traits < InteriorPRG > ;
2026-09-24 14:08:32 -06:00
using interior_node = typename InteriorPRG : : block_type ;
using exterior_prg = ExteriorPRG ;
using exterior_node = typename ExteriorPRG : : block_type ;
using input_type = dpf : : concrete_type_t < InputT > ;
using raw_input_type = InputT ;
using integral_type = utils : : integral_type_from_bitlength_t < utils : : bitlength_of_v < input_type > , utils : : bitlength_of_v < std : : size_t > > ;
using outputs_tuple = std : : tuple < OutputT , OutputTs . . . > ;
template < std : : size_t I >
using output_type_t = std : : tuple_element_t < I , outputs_tuple > ;
using concrete_outputs_tuple
= std : : tuple < concrete_type_t < OutputT > , concrete_type_t < OutputTs > . . . > ;
template < std : : size_t I >
using concrete_output_type = std : : tuple_element_t < I , concrete_outputs_tuple > ;
using offset_type = offset_wrapper < InputT > ; // N.B.: `InputT`, not `input_type`
HEDLEY_PRAGMA ( GCC diagnostic push )
HEDLEY_PRAGMA ( GCC diagnostic ignored " -Wignored-attributes " )
using leaf_tuple = dpf : : leaf_tuple_t < exterior_node , OutputT , OutputTs . . . > ;
using beaver_tuple = dpf : : beaver_tuple_t < exterior_node , OutputT , OutputTs . . . > ;
using leaf_wrapper_tuple = std : : tuple < dpf : : leaf_wrapper < OutputT , exterior_node > , dpf : : leaf_wrapper < OutputTs , exterior_node > . . . > ;
HEDLEY_PRAGMA ( GCC diagnostic pop )
static constexpr std : : size_t outputs_per_leaf = dpf : : outputs_per_leaf_v < OutputT , exterior_node > ;
static constexpr std : : size_t lg_outputs_per_leaf = dpf : : lg_outputs_per_leaf_v < OutputT , exterior_node > ;
static constexpr std : : size_t depth
= utils : : bitlength_of_v < input_type > - lg_outputs_per_leaf ;
static constexpr auto msb_mask = utils : : msb_of_v < input_type > ;
// -----------------------------------------------------------------------
// Slot-meta foundation (unified with the multi-level `incr_key_base`).
// Every key carries a `slot_meta` table; for a classic (single-level,
// equal-width, no-cmp) pack all slots sit at `prefix == bitlen` and the
// packing positions match the classic `leaf_prg` layout. These are all
// `static constexpr`, so the object layout is unchanged.
// -----------------------------------------------------------------------
static constexpr std : : size_t num_outputs = 1 + sizeof . . . ( OutputTs ) ;
static constexpr std : : size_t cmp_depth = 0 ;
static constexpr std : : size_t cmp_out_bits = 0 ;
2026-09-24 20:44:07 -06:00
static constexpr std : : size_t cmp_block = 0 ;
static constexpr bool cmp_idcf = false ;
static constexpr std : : size_t cmp_q = 0 ;
static constexpr std : : size_t cmp_h = 0 ;
static constexpr std : : size_t cmp_checkpoints = 0 ;
static constexpr std : : size_t cmp_tail = 0 ;
2026-09-24 23:18:10 -06:00
/// @brief Classic keys are single-level; the unified eval surface keeps routing
2026-09-24 14:08:32 -06:00
/// them through the classic `eval_*` fast paths (see `is_multilevel_key`).
2026-09-24 23:18:10 -06:00
/// @see `is_multilevel_key`
2026-09-24 14:08:32 -06:00
static constexpr bool is_multilevel = false ;
2026-09-24 23:18:10 -06:00
static constexpr bool is_verifiable = false ;
static constexpr bool is_extractable = false ;
using correction_seeds_array = std : : array < cs_block , 0 > ;
const correction_seeds_array & correction_seeds ( ) const
{
static const correction_seeds_array empty { } ;
return empty ;
}
2026-09-24 14:08:32 -06:00
private :
using meta_placed_tuple = std : : tuple <
dpf : : detail : : incr : : placed < utils : : bitlength_of_v < input_type > , OutputT > ,
dpf : : detail : : incr : : placed < utils : : bitlength_of_v < input_type > ,
OutputTs > . . . > ;
public :
using meta_array = std : : array < dpf : : detail : : incr : : slot_meta , num_outputs > ;
static constexpr meta_array meta =
dpf : : detail : : incr : : build_meta < exterior_node , meta_placed_tuple > ( ) ;
static constexpr std : : size_t deepest_output = 0 ;
template < std : : size_t I >
static constexpr std : : size_t lg_outputs_per_leaf_of = meta [ I ] . lg_opl ;
template < std : : size_t I >
static constexpr std : : size_t outputs_per_leaf_of =
std : : size_t { 1 } < < lg_outputs_per_leaf_of < I > ;
2026-09-24 23:18:10 -06:00
HEDLEY_PRAGMA ( GCC diagnostic push )
HEDLEY_PRAGMA ( GCC diagnostic ignored " -Wignored-attributes " )
2026-09-24 14:08:32 -06:00
using correction_words_array = std : : array < interior_node , depth > ;
2026-09-24 23:18:10 -06:00
HEDLEY_PRAGMA ( GCC diagnostic pop )
2026-09-24 14:08:32 -06:00
using correction_advice_array = std : : array < psnip_uint8_t , depth > ;
template < typename Emplaceable >
HEDLEY_ALWAYS_INLINE
static void emplace ( Emplaceable & output ,
const interior_node & root ,
const correction_words_array & correction_words ,
const correction_advice_array & correction_advice ,
const leaf_tuple & leaves ,
const beaver_tuple & beavers ,
const input_type & offset_share )
{
utils : : dpf_emplacer < Derived , Emplaceable > : : emplace ( output , root , correction_words , correction_advice , leaves , beavers , offset_share ) ;
}
template < typename EmplaceableContainer >
HEDLEY_ALWAYS_INLINE
static void emplace_back ( EmplaceableContainer & output ,
const interior_node & root ,
const correction_words_array & correction_words ,
const correction_advice_array & correction_advice ,
const leaf_tuple & leaves ,
const beaver_tuple & beavers ,
const input_type & offset_share )
{
utils : : dpf_back_emplacer < Derived , EmplaceableContainer > : : emplace_back ( output , root , correction_words , correction_advice , leaves , beavers , offset_share ) ;
}
HEDLEY_PRAGMA ( GCC diagnostic push )
HEDLEY_PRAGMA ( GCC diagnostic ignored " -Wignored-attributes " )
static_assert ( ( ( dpf : : utils : : bitlength_of_output_v < OutputT , exterior_node >
= = dpf : : utils : : bitlength_of_output_v < OutputTs , exterior_node > ) & & . . . ) ,
" all output types must be the same length " ) ;
HEDLEY_PRAGMA ( GCC diagnostic pop )
static_assert ( std : : conjunction_v < std : : is_trivially_copyable < OutputT > ,
std : : is_trivially_copyable < OutputTs > . . . > ,
" all output types must be trivially copyable " ) ;
static_assert ( std : : conjunction_v < std : : is_standard_layout < OutputT > ,
std : : is_standard_layout < OutputTs > . . . > ,
" all output types must be standard layout " ) ;
// static_assert(std::has_unique_object_representations_v<input_type>);
HEDLEY_ALWAYS_INLINE
constexpr classic_dpf_key_impl ( interior_node root ,
const correction_words_array & correction_words ,
const correction_advice_array & correction_advice ,
const leaf_tuple & leaves ,
const beaver_tuple & beavers ,
input_type offset_share )
2026-09-28 05:59:19 -06:00
: leaf_nodes ( get_wrappers ( leaves , beavers ) ) ,
offset_x { offset_share } ,
root_ { root } ,
2026-09-24 14:08:32 -06:00
correction_words_ { correction_words } ,
correction_advice_ { correction_advice } ,
mutable_wildcard_mask_ { dpf : : utils : : make_bitset ( dpf : : is_wildcard_v < OutputT > ,
dpf : : is_wildcard_v < OutputTs > . . . ) } ,
2026-09-28 05:59:19 -06:00
common_part_hash_ { utils : : get_common_part_hash ( correction_words_ , correction_advice_ , leaf_nodes , wildcard_mask ) }
2026-09-24 14:08:32 -06:00
{ }
classic_dpf_key_impl ( const classic_dpf_key_impl & ) = default ;
classic_dpf_key_impl ( classic_dpf_key_impl & & ) = default ;
classic_dpf_key_impl & operator = ( const classic_dpf_key_impl & ) = default ;
classic_dpf_key_impl & operator = ( classic_dpf_key_impl & & ) = default ;
const interior_node & root ( ) const { return root_ ; }
const correction_words_array & correction_words ( ) const { return correction_words_ ; }
const correction_advice_array & correction_advice ( ) const { return correction_advice_ ; }
const digest_type & common_part_hash ( ) const { return common_part_hash_ ; }
std : : string wildcard_bitmask ( ) const
{
return mutable_wildcard_mask_ . to_string ( ) ;
}
HEDLEY_ALWAYS_INLINE
const interior_node & correction_word ( std : : size_t level ) const
{
return correction_words_ [ level ] ;
}
HEDLEY_ALWAYS_INLINE
psnip_uint8_t correction_advice ( std : : size_t level ) const
{
return correction_advice_ [ level ] ;
}
HEDLEY_ALWAYS_INLINE
auto correction_word ( std : : size_t level , bool direction ) const
{
2026-09-24 23:18:10 -06:00
return tree : : pack_cw ( correction_word ( level ) ,
correction_advice_ [ level ] , direction ,
tree : : is_last_level ( level , depth ) ) ;
2026-09-24 14:08:32 -06:00
}
template < std : : size_t I = 0 >
HEDLEY_ALWAYS_INLINE
const auto & leaf ( ) const
{
if constexpr ( dpf : : is_wildcard_v < output_type_t < I > > )
{
return std : : get < I > ( leaf_nodes ) . raw_leaf ( ) ;
}
else
{
return std : : get < I > ( leaf_nodes ) . get ( ) ;
}
}
template < std : : size_t I = 0 >
HEDLEY_ALWAYS_INLINE
const auto & beaver ( ) const
{
return std : : get < I > ( leaf_nodes ) . beaver ( ) ;
}
2026-09-24 20:44:07 -06:00
HEDLEY_NO_THROW
2026-09-24 14:08:32 -06:00
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
constexpr bool is_wildcard ( std : : size_t i ) const noexcept
{
return wildcard_mask [ i ] ;
}
# ifdef LIBDPF_HAS_ASIO
template < std : : size_t I = 0 ,
typename PeerT ,
typename OutputType ,
typename CompletionToken >
auto async_assign_leaf ( PeerT & peer , OutputType & & output_share ,
CompletionToken & & token )
{
return dpf : : asio : : async_assign_wildcard_output < I > (
peer , * this , std : : forward < OutputType > ( output_share ) ,
std : : forward < CompletionToken > ( token ) ) ;
}
# endif
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
static auto traverse_interior ( const interior_node & node ,
2026-09-24 23:18:10 -06:00
const interior_node & cw , bool dir , bool is_last = false ) noexcept
2026-09-24 14:08:32 -06:00
{
2026-09-24 23:18:10 -06:00
return tree : : traverse ( node , cw , dir , is_last ) ;
2026-09-24 14:08:32 -06:00
}
2026-09-24 23:18:10 -06:00
/// @brief Expand both children of `node` with one pipelined expand.
/// @details Equivalent to `traverse_interior(node, cw0, 0)` and
/// `traverse_interior(node, cw1, 1)`. Full-domain interval eval uses this
/// at almost every interior parent.
/// @param node the GGM node
/// @param cw0 correction word for the left child
/// @param cw1 correction word for the right child
/// @param is_last whether this is the last interior level
/// @return both children of `node`
2026-09-24 14:08:32 -06:00
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
static auto traverse_interior01 ( const interior_node & node ,
2026-09-24 23:18:10 -06:00
const interior_node & cw0 , const interior_node & cw1 ,
bool is_last = false ) noexcept
2026-09-24 14:08:32 -06:00
{
2026-09-24 23:18:10 -06:00
return tree : : traverse01 ( node , cw0 , cw1 , is_last ) ;
2026-09-24 14:08:32 -06:00
}
2026-09-24 23:18:10 -06:00
/// @brief Four independent `traverse_interior01` via traits batched expand.
/// @details `left[i]` / `right[i]` are the children of `parents[i]`.
/// @param parents the `parents`
/// @param cw0 the `cw0`
/// @param cw1 the `cw1`
/// @param left the `left`
/// @param right the `right`
/// @param is_last the `is_last`
2026-09-24 14:08:32 -06:00
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
static void traverse_interior01_x4 ( const interior_node * HEDLEY_RESTRICT parents ,
const interior_node & cw0 , const interior_node & cw1 ,
interior_node * HEDLEY_RESTRICT left ,
2026-09-24 23:18:10 -06:00
interior_node * HEDLEY_RESTRICT right , bool is_last = false ) noexcept
2026-09-24 14:08:32 -06:00
{
2026-09-24 23:18:10 -06:00
tree : : traverse01_x4 ( parents , cw0 , cw1 , left , right , is_last ) ;
2026-09-24 14:08:32 -06:00
}
template < std : : size_t I = 0 ,
typename LeafT >
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
static auto traverse_exterior ( const interior_node & node ,
const LeafT & correction_word ) noexcept
{
HEDLEY_PRAGMA ( GCC diagnostic push )
HEDLEY_PRAGMA ( GCC diagnostic ignored " -Wignored-attributes " )
using output_type = std : : tuple_element_t < I , concrete_outputs_tuple > ;
2026-09-24 23:18:10 -06:00
HEDLEY_PRAGMA ( GCC diagnostic pop )
2026-09-24 14:08:32 -06:00
// Subtractive share: CW_if_t − mask so reconstruct(y0, y1) = y0 − y1 = β.
return dpf : : subtract_leaf < output_type > (
dpf : : get_if_lo_bit ( correction_word , node ) ,
make_leaf_mask_inner < exterior_prg , I , concrete_outputs_tuple > ( unset_lo_2bits ( node ) ) ) ;
}
template < std : : size_t I = 0 >
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
auto traverse_exterior ( const interior_node & node ) const noexcept
{
return traverse_exterior < I > ( node , std : : get < I > ( leaf_nodes ) . get ( ) ) ;
}
2026-09-28 05:59:19 -06:00
/// @brief Eight one-block leaves. One `eval_x8` instead of eight `eval` calls.
template < std : : size_t I = 0 , typename Out >
void traverse_exterior_x8 ( const interior_node * HEDLEY_RESTRICT nodes ,
Out * HEDLEY_RESTRICT out ) const noexcept
{
using output_type = std : : tuple_element_t < I , concrete_outputs_tuple > ;
using block = typename exterior_prg : : block_type ;
alignas ( 64 ) block seeds [ 8 ] ;
alignas ( 64 ) block masks [ 8 ] ;
for ( std : : size_t t = 0 ; t < 8 ; + + t )
seeds [ t ] = utils : : to_exterior_node < block > ( unset_lo_2bits ( nodes [ t ] ) ) ;
constexpr auto pos = dpf : : block_offset_of_leaf_v < I , block ,
concrete_outputs_tuple > ;
exterior_prg : : eval_x8 ( seeds , masks , static_cast < psnip_uint32_t > ( pos ) ) ;
const auto & cw = std : : get < I > ( leaf_nodes ) . get ( ) ;
for ( std : : size_t t = 0 ; t < 8 ; + + t )
{
encode_curve_leaf_mask < concrete_type_t < output_type > > ( masks [ t ] ) ;
out [ t ] = dpf : : subtract_leaf < output_type > (
dpf : : get_if_lo_bit ( cw , nodes [ t ] ) , masks [ t ] ) ;
}
}
2026-09-24 14:08:32 -06:00
leaf_wrapper_tuple leaf_nodes ;
offset_type offset_x ;
static constexpr std : : array < bool , sizeof . . . ( OutputTs ) + 1 > wildcard_mask { dpf : : is_wildcard_v < OutputT > ,
dpf : : is_wildcard_v < OutputTs > . . . } ;
private :
static auto get_wrappers ( const leaf_tuple & leaves ,
const beaver_tuple & beavers )
{
outputs_tuple tmp { } ;
return std : : apply ( [ & beavers , & tmp ] ( auto & . . . leaf )
{
return std : : apply ( [ & leaf . . . , & tmp ] ( auto & . . . beaver )
{
return std : : apply ( [ & leaf . . . , & beaver . . . ] ( auto & . . . foo )
{
2026-09-24 23:18:10 -06:00
HEDLEY_PRAGMA ( GCC diagnostic push )
HEDLEY_PRAGMA ( GCC diagnostic ignored " -Wignored-attributes " )
2026-09-24 14:08:32 -06:00
return std : : make_tuple (
dpf : : leaf_wrapper < std : : decay_t < decltype ( foo ) > , exterior_node > ( leaf , beaver ) . . .
) ;
2026-09-24 23:18:10 -06:00
HEDLEY_PRAGMA ( GCC diagnostic pop )
2026-09-24 14:08:32 -06:00
} , tmp ) ;
} , beavers ) ;
} , leaves ) ;
}
interior_node root_ ;
HEDLEY_PRAGMA ( GCC diagnostic push )
HEDLEY_PRAGMA ( GCC diagnostic ignored " -Wignored-attributes " )
correction_words_array correction_words_ ;
HEDLEY_PRAGMA ( GCC diagnostic pop )
correction_advice_array correction_advice_ ;
std : : bitset < sizeof . . . ( OutputTs ) + 1 > mutable_wildcard_mask_ ;
digest_type common_part_hash_ ;
} ; // struct classic_dpf_key_impl
} // namespace detail
namespace detail
{
namespace incr
{
2026-09-24 23:18:10 -06:00
/// @brief Comparison-channel storage. Value CWs, `cw_last`, and the `cmp_addend`
2026-09-24 14:08:32 -06:00
/// share are held at the comparison group width (`ValueCwWord`), not a full
/// padded `uint64_t` per level: a bit comparison carries 1 byte/level, a
/// `uint16_t` payload 2 bytes/level, etc. Arithmetic still runs in `uint64_t`
/// (masked); the narrow word is only the on-key / on-wire representation.
2026-09-24 23:18:10 -06:00
/// @details Extra per-level δ-coefficients kept only for wildcard comparison payloads
2026-09-24 14:08:32 -06:00
/// (empty for concrete cmp keys, so their layout is unchanged). The value CWs
/// / `cw_last` are affine in the payload δ, so after keygen with δ = 0 the
/// concrete values are `base[i] + coeff[i]·δ`; `assign_cmp` patches them in
/// place with no tree re-walk / re-PRG.
2026-09-24 23:18:10 -06:00
/// @tparam Depth depth
/// @tparam ValueCwWord value cw word
/// @tparam Wild whether the payload is a wildcard
/// @tparam TailLen tail len
/// @tparam Idcf idcf
2026-09-24 20:44:07 -06:00
template < std : : size_t Depth , typename ValueCwWord , bool Wild ,
std : : size_t TailLen = 0 , bool Idcf = false >
2026-09-24 14:08:32 -06:00
struct cmp_wild_state { } ;
2026-09-24 20:44:07 -06:00
template < std : : size_t Depth , typename ValueCwWord , std : : size_t TailLen , bool Idcf >
struct cmp_wild_state < Depth , ValueCwWord , true , TailLen , Idcf >
2026-09-24 14:08:32 -06:00
{
std : : array < ValueCwWord , Depth > value_cw_coeff { } ;
ValueCwWord cw_last_coeff { 0 } ;
2026-09-24 20:44:07 -06:00
std : : array < ValueCwWord , TailLen > tail_coeff { } ;
std : : array < ValueCwWord , Idcf ? Depth + 1 : 0 > prefix_cw_coeff { } ;
2026-09-24 14:08:32 -06:00
bool assigned { false } ;
} ;
2026-09-24 20:44:07 -06:00
template < std : : size_t Depth , typename ValueCwWord , bool Wild = false ,
std : : size_t TailLen = 0 , bool Blocked = false , bool Idcf = false >
2026-09-24 14:08:32 -06:00
struct cmp_storage
{
using value_cw_word = ValueCwWord ;
using value_cw_array = std : : array < value_cw_word , Depth > ;
2026-09-24 20:44:07 -06:00
using tail_array = std : : array < value_cw_word , TailLen > ;
static constexpr std : : size_t prefix_cw_len = Idcf ? Depth + 1 : 0 ;
using prefix_cw_array = std : : array < value_cw_word , prefix_cw_len > ;
2026-09-24 14:08:32 -06:00
cmp_storage ( ) = default ;
cmp_storage ( detail : : cmp_meta cmp , value_cw_array value_cws ,
2026-09-24 20:44:07 -06:00
value_cw_word cw_last_in , value_cw_word cmp_addend_in ,
tail_array tail = { } , tail_array tail_coeff = { } ,
prefix_cw_array prefix = { } , prefix_cw_array prefix_coeff = { } )
2026-09-24 14:08:32 -06:00
: cmp_ { cmp } , value_cw_ { value_cws } , cw_last_ { cw_last_in } ,
2026-09-24 20:44:07 -06:00
cmp_addend_ { cmp_addend_in } , tail_ { tail } , prefix_cw_ { prefix }
{
if constexpr ( Wild & & Blocked )
wild_ . tail_coeff = tail_coeff ;
else
( void ) tail_coeff ;
if constexpr ( Wild & & Idcf )
wild_ . prefix_cw_coeff = prefix_coeff ;
else
( void ) prefix_coeff ;
}
2026-09-24 14:08:32 -06:00
cmp_storage ( detail : : cmp_meta cmp , value_cw_array value_cws ,
value_cw_word cw_last_in , value_cw_word cmp_addend_in ,
2026-09-24 20:44:07 -06:00
value_cw_array coeff , value_cw_word cw_last_coeff ,
tail_array tail = { } , tail_array tail_coeff = { } ,
prefix_cw_array prefix = { } , prefix_cw_array prefix_coeff = { } )
2026-09-24 14:08:32 -06:00
: cmp_ { cmp } , value_cw_ { value_cws } , cw_last_ { cw_last_in } ,
2026-09-24 20:44:07 -06:00
cmp_addend_ { cmp_addend_in } , tail_ { tail } , prefix_cw_ { prefix }
2026-09-24 14:08:32 -06:00
{
if constexpr ( Wild )
{
wild_ . value_cw_coeff = coeff ;
wild_ . cw_last_coeff = cw_last_coeff ;
2026-09-24 20:44:07 -06:00
if constexpr ( Blocked )
wild_ . tail_coeff = tail_coeff ;
if constexpr ( Idcf )
wild_ . prefix_cw_coeff = prefix_coeff ;
2026-09-24 14:08:32 -06:00
}
else
{
( void ) coeff ;
( void ) cw_last_coeff ;
2026-09-24 20:44:07 -06:00
( void ) tail_coeff ;
( void ) prefix_coeff ;
2026-09-24 14:08:32 -06:00
}
}
const value_cw_array & value_cw ( ) const { return value_cw_ ; }
uint64_t value_cw ( std : : size_t level ) const
{
return static_cast < uint64_t > ( value_cw_ [ level ] ) ;
}
2026-09-24 20:44:07 -06:00
HEDLEY_NO_THROW
2026-09-24 23:18:10 -06:00
value_cw_word cw_last_word ( ) const noexcept { return cw_last_ ; }
HEDLEY_NO_THROW
value_cw_word cmp_addend_word ( ) const noexcept { return cmp_addend_ ; }
HEDLEY_NO_THROW
2026-09-24 14:08:32 -06:00
uint64_t cw_last ( ) const noexcept { return static_cast < uint64_t > ( cw_last_ ) ; }
2026-09-24 20:44:07 -06:00
HEDLEY_NO_THROW
const tail_array & tail_cw ( ) const noexcept { return tail_ ; }
uint64_t tail_cw ( std : : size_t i ) const
{
return static_cast < uint64_t > ( tail_ [ i ] ) ;
}
HEDLEY_NO_THROW
2026-09-24 14:08:32 -06:00
uint64_t cmp_addend ( ) const noexcept
{
return static_cast < uint64_t > ( cmp_addend_ ) ;
}
2026-09-24 20:44:07 -06:00
HEDLEY_NO_THROW
2026-09-24 14:08:32 -06:00
const detail : : cmp_meta & cmp ( ) const noexcept { return cmp_ ; }
2026-09-24 20:44:07 -06:00
HEDLEY_NO_THROW
2026-09-24 14:08:32 -06:00
bool has_cmp ( ) const noexcept { return cmp_ . active ; }
2026-09-24 20:44:07 -06:00
HEDLEY_NO_THROW
const prefix_cw_array & prefix_cws ( ) const noexcept { return prefix_cw_ ; }
uint64_t prefix_cw ( std : : size_t i ) const
{
return static_cast < uint64_t > ( prefix_cw_ [ i ] ) ;
}
2026-09-24 14:08:32 -06:00
static constexpr bool is_wildcard = Wild ;
2026-09-24 20:44:07 -06:00
HEDLEY_NO_THROW
2026-09-24 14:08:32 -06:00
bool cmp_assigned ( ) const noexcept
{
if constexpr ( Wild )
return wild_ . assigned ;
else
return true ;
}
2026-09-24 23:18:10 -06:00
/// @brief Patch the (public) value CWs / `cw_last` in place for a resolved δ and
2026-09-24 14:08:32 -06:00
/// install this party's `cmp_addend` share. No-op on the CWs when there is
/// no wildcard coefficient table (trivial domain-edge cmp).
2026-09-24 23:18:10 -06:00
/// @param delta the payload difference `if_true - if_false`
/// @param addend_share the `addend_share`
2026-09-24 14:08:32 -06:00
void assign_cmp_delta ( uint64_t delta , uint64_t addend_share )
{
static_assert ( Wild ,
" assign_cmp on a key whose comparison payload is not a wildcard " ) ;
if constexpr ( Wild )
{
const uint64_t mask = cmp_ . mask ;
for ( std : : size_t i = 0 ; i < Depth ; + + i )
{
const uint64_t base = static_cast < uint64_t > ( value_cw_ [ i ] ) ;
const uint64_t c = static_cast < uint64_t > ( wild_ . value_cw_coeff [ i ] ) ;
value_cw_ [ i ] = static_cast < value_cw_word > ( ( base + c * delta ) & mask ) ;
}
2026-09-24 20:44:07 -06:00
if constexpr ( Blocked )
{
for ( std : : size_t i = 0 ; i < TailLen ; + + i )
{
const uint64_t base = static_cast < uint64_t > ( tail_ [ i ] ) ;
const uint64_t c = static_cast < uint64_t > ( wild_ . tail_coeff [ i ] ) ;
tail_ [ i ] = static_cast < value_cw_word > ( ( base + c * delta ) & mask ) ;
}
}
2026-09-24 14:08:32 -06:00
const uint64_t lbase = static_cast < uint64_t > ( cw_last_ ) ;
const uint64_t lc = static_cast < uint64_t > ( wild_ . cw_last_coeff ) ;
cw_last_ = static_cast < value_cw_word > ( ( lbase + lc * delta ) & mask ) ;
2026-09-24 20:44:07 -06:00
if constexpr ( Idcf )
{
for ( std : : size_t i = 0 ; i < prefix_cw_len ; + + i )
{
const uint64_t base = static_cast < uint64_t > ( prefix_cw_ [ i ] ) ;
const uint64_t c = static_cast < uint64_t > ( wild_ . prefix_cw_coeff [ i ] ) ;
prefix_cw_ [ i ] = static_cast < value_cw_word > ( ( base + c * delta ) & mask ) ;
}
}
2026-09-24 14:08:32 -06:00
cmp_addend_ = static_cast < value_cw_word > ( addend_share & mask ) ;
wild_ . assigned = true ;
}
}
2026-09-24 23:18:10 -06:00
/// @brief Overwrite the public final correction and this party's addend. Used when
/// the group element does not fit in the `uint64_t` the constructor takes.
/// @param last the past-the-end element of the range
/// @param addend the additive share of the off-point payload
/// @param last_coeff the `last_coeff`
void set_scalars ( value_cw_word last , value_cw_word addend ,
value_cw_word last_coeff )
{
cw_last_ = last ;
cmp_addend_ = addend ;
if constexpr ( Wild )
wild_ . cw_last_coeff = last_coeff ;
else
( void ) last_coeff ;
}
/// @brief `base + coeff · δ` in `delta`'s group, then install `addend`.
/// @param delta the payload difference `if_true - if_false`
/// @param addend the additive share of the off-point payload
void assign_group ( const detail : : group_elem & delta , value_cw_word addend )
{
static_assert ( Wild ,
" assign_cmp on a key whose comparison payload is not a wildcard " ) ;
if constexpr ( Wild )
{
auto mix = [ & ] ( value_cw_word base_w , value_cw_word coeff_w ) {
const auto base = detail : : group_from_word ( base_w , delta ) ;
const auto coeff = detail : : group_from_word ( coeff_w , delta ) ;
return detail : : group_to_word < value_cw_word > (
detail : : group_add ( base , detail : : group_mul ( coeff , delta ) ) ) ;
} ;
for ( std : : size_t i = 0 ; i < Depth ; + + i )
value_cw_ [ i ] = mix ( value_cw_ [ i ] , wild_ . value_cw_coeff [ i ] ) ;
if constexpr ( Blocked )
{
for ( std : : size_t i = 0 ; i < TailLen ; + + i )
tail_ [ i ] = mix ( tail_ [ i ] , wild_ . tail_coeff [ i ] ) ;
}
cw_last_ = mix ( cw_last_ , wild_ . cw_last_coeff ) ;
if constexpr ( Idcf )
{
for ( std : : size_t i = 0 ; i < prefix_cw_len ; + + i )
prefix_cw_ [ i ] = mix ( prefix_cw_ [ i ] , wild_ . prefix_cw_coeff [ i ] ) ;
}
cmp_addend_ = addend ;
wild_ . assigned = true ;
}
}
2026-09-28 05:59:19 -06:00
/// @brief `mix(base, coeff)` at every value CW, then install `addend`.
/// @tparam Mix word rewriter
/// @param mix combines one stored base word with its integer coefficient word
/// @param addend this party's share of the constant payload
template < typename Mix >
void assign_payload ( Mix mix , value_cw_word addend )
{
static_assert ( Wild ,
" assign_cmp on a key whose comparison payload is not a wildcard " ) ;
if constexpr ( Wild )
{
for ( std : : size_t i = 0 ; i < Depth ; + + i )
value_cw_ [ i ] = mix ( value_cw_ [ i ] , wild_ . value_cw_coeff [ i ] ) ;
if constexpr ( Blocked )
{
for ( std : : size_t i = 0 ; i < TailLen ; + + i )
tail_ [ i ] = mix ( tail_ [ i ] , wild_ . tail_coeff [ i ] ) ;
}
cw_last_ = mix ( cw_last_ , wild_ . cw_last_coeff ) ;
if constexpr ( Idcf )
{
for ( std : : size_t i = 0 ; i < prefix_cw_len ; + + i )
prefix_cw_ [ i ] = mix ( prefix_cw_ [ i ] , wild_ . prefix_cw_coeff [ i ] ) ;
}
cmp_addend_ = addend ;
wild_ . assigned = true ;
}
}
2026-09-24 23:18:10 -06:00
/// @brief Per-level δ coefficients for a wildcard comparison. Empty when the
/// payload is concrete.
/// @return the coefficient table
const value_cw_array & value_cw_coeff ( ) const noexcept
{
if constexpr ( Wild )
return wild_ . value_cw_coeff ;
else
{
static const value_cw_array empty { } ;
return empty ;
}
}
/// @brief Coefficient of δ in `cw_last`. Zero when the payload is concrete.
/// @return the final coefficient word
HEDLEY_NO_THROW
value_cw_word cw_last_coeff_word ( ) const noexcept
{
if constexpr ( Wild )
return wild_ . cw_last_coeff ;
else
return value_cw_word { } ;
}
/// @brief Tail δ coefficients for a blocked wildcard comparison.
/// @return the tail coefficient table
const tail_array & tail_coeff ( ) const noexcept
{
if constexpr ( Wild )
return wild_ . tail_coeff ;
else
{
static const tail_array empty { } ;
return empty ;
}
}
/// @brief Prefix δ coefficients for an iDCF wildcard comparison.
/// @return the prefix coefficient table
const prefix_cw_array & prefix_cw_coeff ( ) const noexcept
{
if constexpr ( Wild )
return wild_ . prefix_cw_coeff ;
else
{
static const prefix_cw_array empty { } ;
return empty ;
}
}
/// @brief Mark whether a wildcard comparison payload has been assigned.
/// @param assigned true once `assign_cmp` has run
HEDLEY_NO_THROW
void set_assigned ( bool assigned ) noexcept
{
if constexpr ( Wild )
wild_ . assigned = assigned ;
else
( void ) assigned ;
}
2026-09-24 14:08:32 -06:00
private :
detail : : cmp_meta cmp_ { } ;
value_cw_array value_cw_ { } ;
value_cw_word cw_last_ { 0 } ;
value_cw_word cmp_addend_ { 0 } ;
2026-09-24 20:44:07 -06:00
tail_array tail_ { } ;
prefix_cw_array prefix_cw_ { } ;
cmp_wild_state < Depth , value_cw_word , Wild , TailLen , Idcf > wild_ { } ;
2026-09-24 14:08:32 -06:00
} ;
2026-09-24 23:18:10 -06:00
/// @brief Multi-level / comparison DPF key body. `PlacedTuple` is a tuple of
2026-09-24 14:08:32 -06:00
/// `placed<N, T>` slots; `CmpDepth > 0` activates the comparison channel.
2026-09-24 23:18:10 -06:00
/// @tparam InteriorPRG PRG that expands interior nodes. Defaults to `dpf::prg::aes128`
/// @tparam ExteriorPRG PRG that expands the root. Defaults to `InteriorPRG`
/// @tparam InputT input domain type
/// @tparam PlacedTuple placed tuple
/// @tparam CmpDepth cmp depth
/// @tparam CmpOutBits cmp out bits
/// @tparam CmpWild cmp wild
/// @tparam CmpBlock cmp block
/// @tparam CmpIdcf cmp idcf
2026-09-24 14:08:32 -06:00
template < typename InteriorPRG , typename ExteriorPRG , typename InputT ,
typename PlacedTuple , std : : size_t CmpDepth = 0 ,
2026-09-24 20:44:07 -06:00
std : : size_t CmpOutBits = 0 , bool CmpWild = false ,
2026-09-24 23:18:10 -06:00
std : : size_t CmpBlock = 0 , bool CmpIdcf = false ,
bool IsVerifiable = false , bool IsExtractable = false >
2026-09-24 14:08:32 -06:00
struct incr_key_base
{
public :
using interior_prg = InteriorPRG ;
using exterior_prg = ExteriorPRG ;
2026-09-24 23:18:10 -06:00
using tree = dpf : : tree_traits < InteriorPRG > ;
2026-09-24 14:08:32 -06:00
using interior_node = typename InteriorPRG : : block_type ;
using exterior_node = typename ExteriorPRG : : block_type ;
using input_type = dpf : : concrete_type_t < InputT > ;
2026-09-28 05:59:19 -06:00
using raw_input_type = InputT ;
2026-09-24 14:08:32 -06:00
using placed_tuple = PlacedTuple ;
using node_type = exterior_node ;
static constexpr std : : size_t cmp_depth = CmpDepth ;
2026-09-24 23:18:10 -06:00
/// @brief Comparison output group width in bits (0 when there is no cmp channel).
2026-09-24 14:08:32 -06:00
static constexpr std : : size_t cmp_out_bits = CmpOutBits ;
2026-09-24 23:18:10 -06:00
/// @brief True when the comparison payload is an unassigned wildcard.
2026-09-24 14:08:32 -06:00
static constexpr bool cmp_is_wildcard = CmpWild ;
2026-09-24 23:18:10 -06:00
/// @brief 0 = per-level path-sum. `B >= 1` = blocked checkpoints of width `B`.
2026-09-24 20:44:07 -06:00
static constexpr std : : size_t cmp_block = CmpBlock ;
static constexpr bool cmp_idcf = CmpIdcf ;
2026-09-24 23:18:10 -06:00
static constexpr bool is_verifiable = IsVerifiable ;
static constexpr bool is_extractable = IsExtractable ;
2026-09-24 20:44:07 -06:00
static constexpr std : : size_t max_output_level =
detail : : incr : : max_tree_level_v < node_type , PlacedTuple > ;
2026-09-24 23:18:10 -06:00
/// @brief Residual tail width. 2 only when dropping those levels does not cut an
2026-09-24 20:44:07 -06:00
/// output and the comparison itself is what sets the tree height.
static constexpr std : : size_t cmp_q = [ ] {
if ( CmpBlock = = 0 | | CmpDepth < = 2 )
return std : : size_t { 0 } ;
if ( max_output_level > CmpDepth - 2 )
return std : : size_t { 0 } ;
return std : : size_t { 2 } ;
} ( ) ;
static constexpr std : : size_t cmp_h =
( CmpBlock = = 0 ) ? CmpDepth : ( CmpDepth - cmp_q ) ;
static constexpr std : : size_t cmp_checkpoints =
( CmpBlock = = 0 | | cmp_h = = 0 ) ? 0 : ( cmp_h + CmpBlock - 1 ) / CmpBlock ;
static constexpr std : : size_t cmp_tail =
( CmpBlock = = 0 | | cmp_q = = 0 ) ? 0 : ( std : : size_t { 1 } < < cmp_q ) ;
2026-09-24 23:18:10 -06:00
/// @brief Narrowest unsigned word that holds `cmp_out_bits` bits (1 byte for a
2026-09-28 05:59:19 -06:00
/// bit / ≤8-bit payload, 2 for ≤16, 4 for ≤32, 8 for ≤64). Payloads wider
/// than 256 bits are stored as their raw bytes. Value CWs and the addend
/// share use this word.
static constexpr std : : size_t cmp_word_bits =
CmpOutBits = = 0 ? std : : size_t { 1 } : CmpOutBits ;
using value_cw_word = std : : conditional_t <
( cmp_word_bits < = 256 ) ,
utils : : integral_type_from_bitlength_t < cmp_word_bits > ,
std : : array < std : : uint8_t , ( cmp_word_bits + 7 ) / 8 > > ;
2026-09-24 14:08:32 -06:00
static constexpr std : : size_t num_outputs = std : : tuple_size_v < PlacedTuple > ;
static constexpr std : : size_t input_bits = utils : : bitlength_of_v < input_type > ;
2026-09-24 20:44:07 -06:00
static constexpr std : : size_t depth = std : : max ( max_output_level ,
( CmpBlock = = 0 ) ? CmpDepth : cmp_h ) ;
static constexpr std : : size_t value_cw_len =
( CmpBlock = = 0 ) ? depth
: ( cmp_checkpoints = = 0 ? std : : size_t { 1 } : cmp_checkpoints ) ;
2026-09-24 14:08:32 -06:00
static constexpr auto msb_mask = utils : : msb_of_v < input_type > ;
using integral_type = utils : : integral_type_from_bitlength_t <
input_bits , utils : : bitlength_of_v < std : : size_t > > ;
static_assert ( num_outputs > 0 | | CmpDepth > 0 ,
" incremental DPF needs at least one output or a comparison channel " ) ;
2026-09-24 23:18:10 -06:00
HEDLEY_PRAGMA ( GCC diagnostic push )
HEDLEY_PRAGMA ( GCC diagnostic ignored " -Wignored-attributes " )
2026-09-24 14:08:32 -06:00
static_assert ( detail : : incr : : all_prefixes_ok_v < node_type , PlacedTuple > ,
" at<N> is shorter than the packing lanes required by an output " ) ;
using correction_words_array = std : : array < interior_node , depth > ;
2026-09-24 23:18:10 -06:00
HEDLEY_PRAGMA ( GCC diagnostic pop )
2026-09-24 14:08:32 -06:00
using correction_advice_array = std : : array < psnip_uint8_t , depth > ;
2026-09-24 23:18:10 -06:00
using correction_seeds_array = std : : array < cs_block , IsVerifiable ? depth : 0 > ;
2026-09-24 20:44:07 -06:00
using value_cw_array = std : : array < value_cw_word , value_cw_len > ;
using tail_array = std : : array < value_cw_word , cmp_tail > ;
static constexpr std : : size_t prefix_cw_len = CmpIdcf ? depth + 1 : 0 ;
using prefix_cw_array = std : : array < value_cw_word , prefix_cw_len > ;
2026-09-24 14:08:32 -06:00
using meta_array = std : : array < detail : : incr : : slot_meta , num_outputs > ;
static constexpr meta_array meta =
detail : : incr : : build_meta < node_type , PlacedTuple > ( ) ;
2026-09-24 23:18:10 -06:00
/// @brief Multi-level / comparison keys route through the slot-aware eval path.
/// Classic-shaped packs (every slot at full input width, no cmp) keep the
/// classic `eval_*` fast path even when verifiable/extractable phantoms are
2026-09-28 05:59:19 -06:00
/// present. `eq` / `eq_at` with a public `if_false` addend also take the
/// slot-aware path so party 0 can absorb that addend.
2026-09-24 23:18:10 -06:00
static constexpr bool is_multilevel = [ ] {
if constexpr ( CmpDepth > 0 )
return true ;
if constexpr ( num_outputs = = 0 )
return true ;
2026-09-28 05:59:19 -06:00
else if constexpr ( detail : : incr : : any_public_addend_v < PlacedTuple > )
return true ;
2026-09-24 23:18:10 -06:00
else
{
for ( std : : size_t i = 0 ; i < num_outputs ; + + i )
{
if ( meta [ i ] . prefix ! = input_bits )
return true ;
}
return false ;
}
} ( ) ;
2026-09-24 14:08:32 -06:00
template < std : : size_t I , typename = void >
struct output_type_at
{
using type = void ;
} ;
template < std : : size_t I >
struct output_type_at < I , std : : enable_if_t < ( I < num_outputs ) > >
{
using type = typename std : : tuple_element_t < I , PlacedTuple > : : output_type ;
} ;
template < std : : size_t I >
using output_type_t = typename output_type_at < I > : : type ;
template < std : : size_t I >
using concrete_output_type = concrete_type_t < output_type_t < I > > ;
2026-09-28 05:59:19 -06:00
private :
template < std : : size_t . . . Is >
static auto outputs_tuple_type ( std : : index_sequence < Is . . . > )
- > std : : tuple < output_type_t < Is > . . . > ;
template < std : : size_t . . . Is >
static auto concrete_outputs_tuple_type ( std : : index_sequence < Is . . . > )
- > std : : tuple < concrete_output_type < Is > . . . > ;
public :
using outputs_tuple = decltype ( outputs_tuple_type (
std : : make_index_sequence < num_outputs > { } ) ) ;
using concrete_outputs_tuple = decltype ( concrete_outputs_tuple_type (
std : : make_index_sequence < num_outputs > { } ) ) ;
2026-09-24 14:08:32 -06:00
template < std : : size_t I >
static constexpr std : : size_t lg_outputs_per_leaf_of =
( num_outputs > 0 ) ? meta [ I ] . lg_opl : 0 ;
template < std : : size_t I >
static constexpr std : : size_t outputs_per_leaf_of = std : : size_t { 1 }
< < lg_outputs_per_leaf_of < I > ;
private :
template < std : : size_t . . . Is >
static auto wrapper_tuple_t ( std : : index_sequence < Is . . . > )
- > std : : tuple <
dpf : : leaf_wrapper < output_type_t < Is > , exterior_node > . . . > ;
template < std : : size_t . . . Is >
static auto leaf_tuple_type ( std : : index_sequence < Is . . . > )
- > std : : tuple <
dpf : : leaf_node_t < exterior_node , concrete_output_type < Is > > . . . > ;
public :
using leaf_wrapper_tuple = decltype ( wrapper_tuple_t (
std : : make_index_sequence < num_outputs > { } ) ) ;
2026-09-24 23:18:10 -06:00
/// @brief Raw leaf shares (pre-wrapper), matching classic `leaf_tuple` for asio.
2026-09-24 14:08:32 -06:00
using leaf_tuple = decltype ( leaf_tuple_type (
std : : make_index_sequence < num_outputs > { } ) ) ;
using offset_type = offset_wrapper < InputT > ;
template < std : : size_t . . . Is >
static constexpr auto wildcard_mask_tuple ( std : : index_sequence < Is . . . > )
{
return std : : make_tuple ( dpf : : is_wildcard_v < output_type_t < Is > > . . . ) ;
}
static constexpr auto wildcard_mask =
wildcard_mask_tuple ( std : : make_index_sequence < num_outputs > { } ) ;
2026-09-28 05:59:19 -06:00
template < std : : size_t . . . Is >
static constexpr std : : array < bool , num_outputs >
wildcard_mask_array ( std : : index_sequence < Is . . . > )
{
return { { std : : get < Is > ( wildcard_mask ) . . . } } ;
}
static constexpr auto wildcard_bits =
wildcard_mask_array ( std : : make_index_sequence < num_outputs > { } ) ;
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
constexpr bool is_wildcard ( std : : size_t i ) const noexcept
{
return i < num_outputs & & wildcard_bits [ i ] ;
}
2026-09-24 14:08:32 -06:00
static constexpr std : : size_t deepest_prefix = [ ] {
if constexpr ( num_outputs = = 0 )
return CmpDepth ;
else
{
std : : size_t m = 0 ;
for ( std : : size_t i = 0 ; i < num_outputs ; + + i )
m = std : : max ( m , meta [ i ] . prefix ) ;
return m ;
}
} ( ) ;
2026-09-24 23:18:10 -06:00
/// @brief First output (source order) whose prefix equals `deepest_prefix`.
2026-09-24 14:08:32 -06:00
static constexpr std : : size_t deepest_output = [ ] {
if constexpr ( num_outputs = = 0 )
return std : : size_t { 0 } ;
else
{
for ( std : : size_t i = 0 ; i < num_outputs ; + + i )
{
if ( meta [ i ] . prefix = = deepest_prefix )
return i ;
}
return std : : size_t { 0 } ;
}
} ( ) ;
2026-09-24 23:18:10 -06:00
/// @brief Classic-shaped packing traits for deepest-group interval/sequence APIs.
2026-09-24 14:08:32 -06:00
static constexpr std : : size_t outputs_per_leaf =
( num_outputs > 0 ) ? outputs_per_leaf_of < deepest_output > : 1 ;
static constexpr std : : size_t lg_outputs_per_leaf =
( num_outputs > 0 ) ? lg_outputs_per_leaf_of < deepest_output > : 0 ;
template < std : : size_t . . . Is >
static auto addend_tuple_t ( std : : index_sequence < Is . . . > )
- > std : : tuple < output_type_t < Is > . . . > ;
using addend_tuple = decltype ( addend_tuple_t (
std : : make_index_sequence < num_outputs > { } ) ) ;
2026-09-28 05:59:19 -06:00
static value_cw_word cw_word_from_u64 ( std : : uint64_t v )
{
if constexpr ( std : : is_integral_v < value_cw_word > )
return static_cast < value_cw_word > ( v ) ;
else
return value_cw_word { } ;
}
2026-09-24 14:08:32 -06:00
incr_key_base ( interior_node root ,
const correction_words_array & correction_words ,
const correction_advice_array & correction_advice ,
leaf_wrapper_tuple leaves , input_type offset_share ,
detail : : cmp_meta cmp = { } , value_cw_array value_cws = { } ,
uint64_t cw_last_in = 0 , uint64_t cmp_addend_in = 0 ,
addend_tuple addends = { } , value_cw_array value_cw_coeff = { } ,
2026-09-24 20:44:07 -06:00
uint64_t cw_last_coeff_in = 0 , tail_array tail_in = { } ,
tail_array tail_coeff_in = { } , prefix_cw_array prefix_in = { } ,
2026-09-24 23:18:10 -06:00
prefix_cw_array prefix_coeff_in = { } ,
correction_seeds_array correction_seeds = { } )
2026-09-24 14:08:32 -06:00
: leaf_nodes { std : : move ( leaves ) } ,
offset_x { offset_share } ,
2026-09-28 05:59:19 -06:00
public_addends { std : : move ( addends ) } ,
2026-09-24 14:08:32 -06:00
cmp_store_ { cmp , value_cws ,
2026-09-28 05:59:19 -06:00
cw_word_from_u64 ( cw_last_in ) ,
cw_word_from_u64 ( cmp_addend_in ) ,
2026-09-24 14:08:32 -06:00
value_cw_coeff ,
2026-09-28 05:59:19 -06:00
cw_word_from_u64 ( cw_last_coeff_in ) ,
2026-09-24 20:44:07 -06:00
tail_in , tail_coeff_in , prefix_in , prefix_coeff_in } ,
2026-09-24 14:08:32 -06:00
root_ { root } ,
correction_words_ { correction_words } ,
correction_advice_ { correction_advice } ,
2026-09-24 23:18:10 -06:00
correction_seeds_ { correction_seeds } ,
2026-09-24 14:08:32 -06:00
common_part_hash_ { utils : : get_common_part_hash ( correction_words_ ,
2026-09-24 23:18:10 -06:00
correction_advice_ , leaf_nodes , wildcard_mask , correction_seeds_ ) }
2026-09-24 14:08:32 -06:00
{ }
incr_key_base ( const incr_key_base & ) = default ;
incr_key_base ( incr_key_base & & ) = default ;
incr_key_base & operator = ( const incr_key_base & ) = default ;
incr_key_base & operator = ( incr_key_base & & ) = default ;
const interior_node & root ( ) const { return root_ ; }
const correction_words_array & correction_words ( ) const
{
return correction_words_ ;
}
const correction_advice_array & correction_advice ( ) const
{
return correction_advice_ ;
}
2026-09-24 23:18:10 -06:00
const correction_seeds_array & correction_seeds ( ) const
{
return correction_seeds_ ;
}
2026-09-24 14:08:32 -06:00
const value_cw_array & value_cw ( ) const { return cmp_store_ . value_cw ( ) ; }
2026-09-24 20:44:07 -06:00
HEDLEY_NO_THROW
2026-09-24 14:08:32 -06:00
uint64_t cw_last ( ) const noexcept { return cmp_store_ . cw_last ( ) ; }
2026-09-24 20:44:07 -06:00
HEDLEY_NO_THROW
2026-09-24 23:18:10 -06:00
value_cw_word cw_last_word ( ) const noexcept { return cmp_store_ . cw_last_word ( ) ; }
HEDLEY_NO_THROW
value_cw_word cmp_addend_word ( ) const noexcept
{
return cmp_store_ . cmp_addend_word ( ) ;
}
void set_cmp_scalars ( value_cw_word last , value_cw_word addend ,
value_cw_word last_coeff )
{
cmp_store_ . set_scalars ( last , addend , last_coeff ) ;
}
/// @brief Mark whether a wildcard comparison payload has been assigned.
/// @param assigned true once `assign_cmp` has run
HEDLEY_NO_THROW
void set_cmp_assigned ( bool assigned ) noexcept
{
cmp_store_ . set_assigned ( assigned ) ;
}
const value_cw_array & value_cw_coeff ( ) const noexcept
{
return cmp_store_ . value_cw_coeff ( ) ;
}
HEDLEY_NO_THROW
value_cw_word cw_last_coeff_word ( ) const noexcept
{
return cmp_store_ . cw_last_coeff_word ( ) ;
}
const tail_array & tail_coeff ( ) const noexcept
{
return cmp_store_ . tail_coeff ( ) ;
}
const prefix_cw_array & prefix_cw_coeff ( ) const noexcept
{
return cmp_store_ . prefix_cw_coeff ( ) ;
}
void assign_cmp_group ( const detail : : group_elem & delta , value_cw_word addend )
{
cmp_store_ . assign_group ( delta , addend ) ;
}
2026-09-28 05:59:19 -06:00
template < typename Mix >
void assign_cmp_payload ( Mix mix , value_cw_word addend )
{
cmp_store_ . assign_payload ( std : : move ( mix ) , addend ) ;
}
2026-09-24 23:18:10 -06:00
HEDLEY_NO_THROW
2026-09-24 20:44:07 -06:00
const prefix_cw_array & prefix_cws ( ) const noexcept
{
return cmp_store_ . prefix_cws ( ) ;
}
uint64_t prefix_cw ( std : : size_t i ) const { return cmp_store_ . prefix_cw ( i ) ; }
2026-09-24 23:18:10 -06:00
/// @brief Party-local share of the constant absorb (`if_false`, or
2026-09-24 14:08:32 -06:00
/// `δ + if_false` when `eval_as_ge`). Reconstructs with the peer share.
2026-09-24 23:18:10 -06:00
/// @return Party-local share of the constant absorb (`if_false`, or `δ + if_false` when
/// `eval_as_ge`)
2026-09-24 20:44:07 -06:00
HEDLEY_NO_THROW
2026-09-24 14:08:32 -06:00
uint64_t cmp_addend ( ) const noexcept { return cmp_store_ . cmp_addend ( ) ; }
2026-09-24 20:44:07 -06:00
HEDLEY_NO_THROW
2026-09-24 14:08:32 -06:00
const detail : : cmp_meta & cmp ( ) const noexcept { return cmp_store_ . cmp ( ) ; }
const digest_type & common_part_hash ( ) const { return common_part_hash_ ; }
const leaf_wrapper_tuple & leaves ( ) const { return leaf_nodes ; }
const interior_node & correction_word ( std : : size_t level ) const
{
return correction_words_ [ level ] ;
}
psnip_uint8_t correction_advice ( std : : size_t level ) const
{
return correction_advice_ [ level ] ;
}
auto correction_word ( std : : size_t level , bool direction ) const
{
2026-09-24 23:18:10 -06:00
return tree : : pack_cw ( correction_word ( level ) ,
correction_advice_ [ level ] , direction ,
tree : : is_last_level ( level , depth ) ) ;
2026-09-24 14:08:32 -06:00
}
uint64_t value_cw ( std : : size_t level ) const { return cmp_store_ . value_cw ( level ) ; }
2026-09-24 20:44:07 -06:00
const tail_array & tail_cw ( ) const { return cmp_store_ . tail_cw ( ) ; }
uint64_t tail_cw ( std : : size_t i ) const { return cmp_store_ . tail_cw ( i ) ; }
2026-09-24 14:08:32 -06:00
template < std : : size_t I = 0 >
const auto & leaf ( ) const
{
static_assert ( num_outputs > 0 , " cmp-only key has no leaves " ) ;
if constexpr ( dpf : : is_wildcard_v < output_type_t < I > > )
return std : : get < I > ( leaf_nodes ) . raw_leaf ( ) ;
else
return std : : get < I > ( leaf_nodes ) . get ( ) ;
}
template < std : : size_t I = 0 >
const auto & beaver ( ) const
{
static_assert ( num_outputs > 0 , " cmp-only key has no beavers " ) ;
return std : : get < I > ( leaf_nodes ) . beaver ( ) ;
}
# ifdef LIBDPF_HAS_ASIO
template < std : : size_t I = 0 ,
typename PeerT ,
typename OutputType ,
typename CompletionToken >
auto async_assign_leaf ( PeerT & peer , OutputType & & output_share ,
CompletionToken & & token )
{
static_assert ( num_outputs > 0 , " cmp-only key has no leaves " ) ;
static_assert ( dpf : : is_wildcard_v < output_type_t < I > > ,
" async_assign_leaf requires a wildcard output slot " ) ;
return dpf : : asio : : async_assign_wildcard_output < I > (
peer , * this , std : : forward < OutputType > ( output_share ) ,
std : : forward < CompletionToken > ( token ) ) ;
}
# endif
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
static auto traverse_interior ( const interior_node & node ,
2026-09-24 23:18:10 -06:00
const interior_node & cw , bool dir , bool is_last = false ) noexcept
2026-09-24 14:08:32 -06:00
{
2026-09-24 23:18:10 -06:00
return tree : : traverse ( node , cw , dir , is_last ) ;
2026-09-24 14:08:32 -06:00
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
static auto traverse_interior01 ( const interior_node & node ,
2026-09-24 23:18:10 -06:00
const interior_node & cw0 , const interior_node & cw1 ,
bool is_last = false ) noexcept
2026-09-24 14:08:32 -06:00
{
2026-09-24 23:18:10 -06:00
return tree : : traverse01 ( node , cw0 , cw1 , is_last ) ;
2026-09-24 14:08:32 -06:00
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
static void traverse_interior01_x4 ( const interior_node * HEDLEY_RESTRICT parents ,
const interior_node & cw0 , const interior_node & cw1 ,
interior_node * HEDLEY_RESTRICT left ,
2026-09-24 23:18:10 -06:00
interior_node * HEDLEY_RESTRICT right , bool is_last = false ) noexcept
2026-09-24 14:08:32 -06:00
{
2026-09-24 23:18:10 -06:00
tree : : traverse01_x4 ( parents , cw0 , cw1 , left , right , is_last ) ;
2026-09-24 14:08:32 -06:00
}
2026-09-28 05:59:19 -06:00
template < std : : size_t I = 0 , typename LeafT >
2026-09-24 20:44:07 -06:00
HEDLEY_NO_THROW
2026-09-28 05:59:19 -06:00
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
static auto traverse_exterior ( const interior_node & node ,
const LeafT & correction_word ) noexcept
2026-09-24 14:08:32 -06:00
{
static_assert ( num_outputs > 0 , " cmp-only key has no exterior outputs " ) ;
using Out = concrete_output_type < I > ;
constexpr auto pos =
meta [ I ] . pos_base + meta [ I ] . index_in_group * meta [ I ] . block_len ;
constexpr auto count = meta [ I ] . block_len ;
2026-09-24 23:18:10 -06:00
HEDLEY_PRAGMA ( GCC diagnostic push )
HEDLEY_PRAGMA ( GCC diagnostic ignored " -Wignored-attributes " )
2026-09-24 14:08:32 -06:00
using leaf_type = dpf : : leaf_node_t < exterior_node , Out > ;
2026-09-24 23:18:10 -06:00
HEDLEY_PRAGMA ( GCC diagnostic pop )
2026-09-24 14:08:32 -06:00
leaf_type mask { } ;
auto seed_ =
utils : : to_exterior_node < exterior_node > ( unset_lo_2bits ( node ) ) ;
2026-09-24 23:18:10 -06:00
if constexpr ( IsExtractable )
{
detail : : vdpf : : extractable_leaf_prg < exterior_prg > : : eval ( seed_ ,
leaf_blocks < exterior_node > ( mask ) ,
static_cast < psnip_uint32_t > ( count ) ,
static_cast < psnip_uint32_t > ( pos ) ) ;
}
else
{
exterior_prg : : eval ( seed_ , leaf_blocks < exterior_node > ( mask ) ,
static_cast < psnip_uint32_t > ( count ) ,
static_cast < psnip_uint32_t > ( pos ) ) ;
}
2026-09-28 05:59:19 -06:00
encode_curve_leaf_mask < Out > ( mask ) ;
2026-09-24 14:08:32 -06:00
return dpf : : subtract_leaf < Out > (
2026-09-28 05:59:19 -06:00
dpf : : get_if_lo_bit ( correction_word , node ) , mask ) ;
}
template < std : : size_t I = 0 >
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
auto traverse_exterior ( const interior_node & node ) const noexcept
{
static_assert ( num_outputs > 0 , " cmp-only key has no exterior outputs " ) ;
return traverse_exterior < I > ( node , std : : get < I > ( leaf_nodes ) . get ( ) ) ;
}
/// @brief Eight one-block leaves, or eight scalar leaves when the PRG is special.
template < std : : size_t I = 0 , typename Out >
void traverse_exterior_x8 ( const interior_node * HEDLEY_RESTRICT nodes ,
Out * HEDLEY_RESTRICT out ) const noexcept
{
const auto & cw = std : : get < I > ( leaf_nodes ) . get ( ) ;
if constexpr ( IsExtractable | | meta [ I ] . block_len ! = 1 )
{
for ( std : : size_t t = 0 ; t < 8 ; + + t )
out [ t ] = traverse_exterior < I > ( nodes [ t ] , cw ) ;
return ;
}
else
{
using OutT = concrete_output_type < I > ;
using block = exterior_node ;
alignas ( 64 ) block seeds [ 8 ] ;
alignas ( 64 ) block masks [ 8 ] ;
constexpr auto pos = meta [ I ] . pos_base
+ meta [ I ] . index_in_group * meta [ I ] . block_len ;
for ( std : : size_t t = 0 ; t < 8 ; + + t )
seeds [ t ] = utils : : to_exterior_node < block > ( unset_lo_2bits ( nodes [ t ] ) ) ;
exterior_prg : : eval_x8 ( seeds , masks , static_cast < psnip_uint32_t > ( pos ) ) ;
for ( std : : size_t t = 0 ; t < 8 ; + + t )
{
encode_curve_leaf_mask < OutT > ( masks [ t ] ) ;
out [ t ] = dpf : : subtract_leaf < OutT > (
dpf : : get_if_lo_bit ( cw , nodes [ t ] ) , masks [ t ] ) ;
}
}
2026-09-24 14:08:32 -06:00
}
leaf_wrapper_tuple leaf_nodes ;
offset_type offset_x ;
2026-09-24 23:18:10 -06:00
/// @brief Public `if_false` addends for `eq` / `eq_at` slots.
2026-09-24 14:08:32 -06:00
addend_tuple public_addends { } ;
2026-09-24 20:44:07 -06:00
HEDLEY_NO_THROW
2026-09-24 14:08:32 -06:00
bool has_cmp ( ) const noexcept { return cmp_store_ . has_cmp ( ) ; }
2026-09-24 23:18:10 -06:00
/// @brief True once a wildcard comparison payload has been assigned (always true
2026-09-24 14:08:32 -06:00
/// for concrete cmp keys and for keys without a comparison channel).
2026-09-24 23:18:10 -06:00
/// @return True once a wildcard comparison payload has been assigned (always true for concrete
/// cmp keys and for keys without a comparison channel)
2026-09-24 20:44:07 -06:00
HEDLEY_NO_THROW
2026-09-24 14:08:32 -06:00
bool cmp_assigned ( ) const noexcept { return cmp_store_ . cmp_assigned ( ) ; }
2026-09-24 23:18:10 -06:00
/// @brief Patch the value CWs / `cw_last` for a resolved payload δ and install
2026-09-24 14:08:32 -06:00
/// this party's `cmp_addend` share. Only valid for wildcard cmp keys; see
/// the free `dpf::assign_cmp`. No tree re-walk / re-PRG.
2026-09-24 23:18:10 -06:00
/// @param delta the payload difference `if_true - if_false`
/// @param addend_share the `addend_share`
2026-09-24 14:08:32 -06:00
void assign_cmp_delta ( uint64_t delta , uint64_t addend_share )
{
cmp_store_ . assign_cmp_delta ( delta , addend_share ) ;
}
private :
2026-09-24 20:44:07 -06:00
cmp_storage < value_cw_len , value_cw_word , CmpWild , cmp_tail , ( CmpBlock > 0 ) ,
CmpIdcf >
cmp_store_ { } ;
2026-09-24 14:08:32 -06:00
interior_node root_ ;
correction_words_array correction_words_ ;
correction_advice_array correction_advice_ ;
2026-09-24 23:18:10 -06:00
correction_seeds_array correction_seeds_ { } ;
2026-09-24 14:08:32 -06:00
digest_type common_part_hash_ ;
} ; // struct incr_key_base
} // namespace incr
} // namespace detail
// ---------------------------------------------------------------------------
// Unified `dpf_key`: one key type for classic, multi-level (`at<N>`), and
// comparison (`cmp_channel_tag`) packs. Each of OutputT/OutputTs is one of:
// - a bare output type (planted at full input bitlength)
// - a `detail::incr::placed<N,T>` (from `at<N>`)
// - a `cmp_channel_tag<Depth>` (phantom: sets the comparison channel depth)
// Classic-shaped packs (all bare) keep the byte-identical single-level layout.
// ---------------------------------------------------------------------------
namespace detail
{
template < typename Derived , typename InteriorPRG , typename ExteriorPRG ,
typename InputT , typename OutputT , typename . . . OutputTs >
using dpf_key_base_t = std : : conditional_t <
dpf : : detail : : incr : : is_classic_pack_v < OutputT , OutputTs . . . > ,
classic_dpf_key_impl < Derived , InteriorPRG , ExteriorPRG , InputT ,
OutputT , OutputTs . . . > ,
dpf : : detail : : incr : : incr_key_base < InteriorPRG , ExteriorPRG , InputT ,
typename dpf : : detail : : incr : : normalize_pack <
utils : : bitlength_of_v < dpf : : concrete_type_t < InputT > > ,
OutputT , OutputTs . . . > : : placed_tuple ,
dpf : : detail : : incr : : normalize_pack <
utils : : bitlength_of_v < dpf : : concrete_type_t < InputT > > ,
OutputT , OutputTs . . . > : : cmp_depth ,
dpf : : detail : : incr : : normalize_pack <
utils : : bitlength_of_v < dpf : : concrete_type_t < InputT > > ,
OutputT , OutputTs . . . > : : cmp_out_bits ,
dpf : : detail : : incr : : normalize_pack <
utils : : bitlength_of_v < dpf : : concrete_type_t < InputT > > ,
2026-09-24 20:44:07 -06:00
OutputT , OutputTs . . . > : : cmp_wild ,
dpf : : detail : : incr : : normalize_pack <
utils : : bitlength_of_v < dpf : : concrete_type_t < InputT > > ,
OutputT , OutputTs . . . > : : cmp_block ,
dpf : : detail : : incr : : normalize_pack <
utils : : bitlength_of_v < dpf : : concrete_type_t < InputT > > ,
2026-09-24 23:18:10 -06:00
OutputT , OutputTs . . . > : : cmp_idcf ,
dpf : : detail : : incr : : normalize_pack <
utils : : bitlength_of_v < dpf : : concrete_type_t < InputT > > ,
OutputT , OutputTs . . . > : : is_verifiable ,
dpf : : detail : : incr : : normalize_pack <
utils : : bitlength_of_v < dpf : : concrete_type_t < InputT > > ,
OutputT , OutputTs . . . > : : is_extractable > > ;
2026-09-24 14:08:32 -06:00
} // namespace detail
template < typename InteriorPRG ,
typename ExteriorPRG ,
typename InputT ,
typename OutputT ,
typename . . . OutputTs >
struct dpf_key
: detail : : dpf_key_base_t <
dpf_key < InteriorPRG , ExteriorPRG , InputT , OutputT , OutputTs . . . > ,
InteriorPRG , ExteriorPRG , InputT , OutputT , OutputTs . . . >
{
using base_type = detail : : dpf_key_base_t <
dpf_key < InteriorPRG , ExteriorPRG , InputT , OutputT , OutputTs . . . > ,
InteriorPRG , ExteriorPRG , InputT , OutputT , OutputTs . . . > ;
using base_type : : base_type ;
} ;
namespace detail
{
namespace incr
{
2026-09-24 23:18:10 -06:00
// Assemble the public dpf_key type for a (PlacedTuple, CmpDepth, flags) pack by
// expanding the placed slots into the output pack and appending phantom tags.
//
// `dpf_key` always takes an output type. A comparison-only key (empty placed
// pack, CmpDepth > 0) is `dpf_key<..., cmp_channel_tag<...>>`. The no-comparison
// form is a separate specialization so an empty pack is not named as
// `dpf_key<Interior, Exterior, Input>` — `std::conditional_t` would require
// that type to be valid even when CmpDepth > 0.
template < bool WithCmp , typename InteriorPRG , typename ExteriorPRG ,
typename InputT , std : : size_t CmpDepth , std : : size_t CmpOutBits ,
bool CmpWild , std : : size_t CmpBlock , bool CmpIdcf , typename . . . Ps >
struct plain_assembled_key ;
template < typename InteriorPRG , typename ExteriorPRG , typename InputT ,
std : : size_t CmpDepth , std : : size_t CmpOutBits , bool CmpWild ,
std : : size_t CmpBlock , bool CmpIdcf , typename . . . Ps >
struct plain_assembled_key < true , InteriorPRG , ExteriorPRG , InputT ,
CmpDepth , CmpOutBits , CmpWild , CmpBlock , CmpIdcf , Ps . . . >
{
using type = dpf : : dpf_key < InteriorPRG , ExteriorPRG , InputT , Ps . . . ,
dpf : : cmp_channel_tag < CmpDepth , CmpOutBits , CmpWild , CmpBlock , CmpIdcf > > ;
} ;
template < typename InteriorPRG , typename ExteriorPRG , typename InputT ,
std : : size_t CmpDepth , std : : size_t CmpOutBits , bool CmpWild ,
std : : size_t CmpBlock , bool CmpIdcf , typename P0 , typename . . . Ps >
struct plain_assembled_key < false , InteriorPRG , ExteriorPRG , InputT ,
CmpDepth , CmpOutBits , CmpWild , CmpBlock , CmpIdcf , P0 , Ps . . . >
{
using type = dpf : : dpf_key < InteriorPRG , ExteriorPRG , InputT , P0 , Ps . . . > ;
} ;
template < std : : size_t CmpDepth , std : : size_t CmpOutBits , bool CmpWild ,
std : : size_t CmpBlock , bool CmpIdcf , bool IsVerifiable ,
bool IsExtractable , typename InteriorPRG , typename ExteriorPRG ,
typename InputT , typename . . . Ps >
struct assemble_key ;
2026-09-24 14:08:32 -06:00
template < std : : size_t CmpDepth , std : : size_t CmpOutBits , bool CmpWild ,
2026-09-24 20:44:07 -06:00
std : : size_t CmpBlock , bool CmpIdcf , typename InteriorPRG ,
2026-09-24 14:08:32 -06:00
typename ExteriorPRG , typename InputT , typename . . . Ps >
2026-09-24 23:18:10 -06:00
struct assemble_key < CmpDepth , CmpOutBits , CmpWild , CmpBlock , CmpIdcf , false ,
false , InteriorPRG , ExteriorPRG , InputT , Ps . . . >
2026-09-24 14:08:32 -06:00
{
2026-09-24 23:18:10 -06:00
using type = typename plain_assembled_key < ( CmpDepth > 0 ) ,
InteriorPRG , ExteriorPRG , InputT , CmpDepth , CmpOutBits , CmpWild ,
CmpBlock , CmpIdcf , Ps . . . > : : type ;
} ;
template < std : : size_t CmpDepth , std : : size_t CmpOutBits , bool CmpWild ,
std : : size_t CmpBlock , bool CmpIdcf , typename InteriorPRG ,
typename ExteriorPRG , typename InputT , typename . . . Ps >
struct assemble_key < CmpDepth , CmpOutBits , CmpWild , CmpBlock , CmpIdcf , true ,
false , InteriorPRG , ExteriorPRG , InputT , Ps . . . >
{
using with_cmp = std : : conditional_t <
( CmpDepth > 0 ) ,
dpf : : dpf_key < InteriorPRG , ExteriorPRG , InputT , Ps . . . ,
dpf : : cmp_channel_tag < CmpDepth , CmpOutBits , CmpWild , CmpBlock , CmpIdcf > ,
dpf : : verifiable > ,
dpf : : dpf_key < InteriorPRG , ExteriorPRG , InputT , Ps . . . , dpf : : verifiable > > ;
using type = with_cmp ;
} ;
template < std : : size_t CmpDepth , std : : size_t CmpOutBits , bool CmpWild ,
std : : size_t CmpBlock , bool CmpIdcf , typename InteriorPRG ,
typename ExteriorPRG , typename InputT , typename . . . Ps >
struct assemble_key < CmpDepth , CmpOutBits , CmpWild , CmpBlock , CmpIdcf , false ,
true , InteriorPRG , ExteriorPRG , InputT , Ps . . . >
{
using type = std : : conditional_t <
( CmpDepth > 0 ) ,
dpf : : dpf_key < InteriorPRG , ExteriorPRG , InputT , Ps . . . ,
dpf : : cmp_channel_tag < CmpDepth , CmpOutBits , CmpWild , CmpBlock , CmpIdcf > ,
dpf : : extractable > ,
dpf : : dpf_key < InteriorPRG , ExteriorPRG , InputT , Ps . . . , dpf : : extractable > > ;
2026-09-24 14:08:32 -06:00
} ;
2026-09-24 23:18:10 -06:00
template < std : : size_t CmpDepth , std : : size_t CmpOutBits , bool CmpWild ,
std : : size_t CmpBlock , bool CmpIdcf , typename InteriorPRG ,
2026-09-24 14:08:32 -06:00
typename ExteriorPRG , typename InputT , typename . . . Ps >
2026-09-24 23:18:10 -06:00
struct assemble_key < CmpDepth , CmpOutBits , CmpWild , CmpBlock , CmpIdcf , true ,
true , InteriorPRG , ExteriorPRG , InputT , Ps . . . >
2026-09-24 14:08:32 -06:00
{
2026-09-24 23:18:10 -06:00
using type = std : : conditional_t <
( CmpDepth > 0 ) ,
dpf : : dpf_key < InteriorPRG , ExteriorPRG , InputT , Ps . . . ,
dpf : : cmp_channel_tag < CmpDepth , CmpOutBits , CmpWild , CmpBlock , CmpIdcf > ,
dpf : : verifiable , dpf : : extractable > ,
dpf : : dpf_key < InteriorPRG , ExteriorPRG , InputT , Ps . . . ,
dpf : : verifiable , dpf : : extractable > > ;
2026-09-24 14:08:32 -06:00
} ;
template < typename InteriorPRG , typename ExteriorPRG , typename InputT ,
typename PlacedTuple , std : : size_t CmpDepth , std : : size_t CmpOutBits = 0 ,
2026-09-24 23:18:10 -06:00
bool CmpWild = false , std : : size_t CmpBlock = 0 , bool CmpIdcf = false ,
bool IsVerifiable = false , bool IsExtractable = false >
2026-09-24 14:08:32 -06:00
struct incr_dpf_key_of ;
template < typename InteriorPRG , typename ExteriorPRG , typename InputT ,
typename . . . Ps , std : : size_t CmpDepth , std : : size_t CmpOutBits ,
2026-09-24 23:18:10 -06:00
bool CmpWild , std : : size_t CmpBlock , bool CmpIdcf ,
bool IsVerifiable , bool IsExtractable >
2026-09-24 14:08:32 -06:00
struct incr_dpf_key_of < InteriorPRG , ExteriorPRG , InputT , std : : tuple < Ps . . . > ,
2026-09-24 23:18:10 -06:00
CmpDepth , CmpOutBits , CmpWild , CmpBlock , CmpIdcf , IsVerifiable , IsExtractable >
2026-09-24 14:08:32 -06:00
{
2026-09-24 20:44:07 -06:00
using type = typename assemble_key < CmpDepth , CmpOutBits , CmpWild , CmpBlock ,
2026-09-24 23:18:10 -06:00
CmpIdcf , IsVerifiable , IsExtractable , InteriorPRG , ExteriorPRG , InputT ,
Ps . . . > : : type ;
2026-09-24 14:08:32 -06:00
} ;
template < typename InteriorPRG , typename ExteriorPRG , typename InputT ,
typename PlacedTuple , std : : size_t CmpDepth , std : : size_t CmpOutBits = 0 ,
2026-09-24 23:18:10 -06:00
bool CmpWild = false , std : : size_t CmpBlock = 0 , bool CmpIdcf = false ,
bool IsVerifiable = false , bool IsExtractable = false >
2026-09-24 14:08:32 -06:00
using incr_dpf_key_of_t = typename incr_dpf_key_of < InteriorPRG , ExteriorPRG ,
2026-09-24 23:18:10 -06:00
InputT , PlacedTuple , CmpDepth , CmpOutBits , CmpWild , CmpBlock , CmpIdcf ,
IsVerifiable , IsExtractable > : : type ;
2026-09-24 14:08:32 -06:00
} // namespace incr
} // namespace detail
2026-09-28 05:59:19 -06:00
/// @brief One uniform interior node. The default root draw for distributed dealers.
template < typename Node >
struct uniform_node_sampler
{
Node operator ( ) ( ) const
{
return dpf : : uniform_sample < Node > ( ) ;
}
} ;
2026-09-24 14:08:32 -06:00
template < typename PRG >
struct pseudorandom_root_sampler
{
using root_type = typename PRG : : block_type ;
pseudorandom_root_sampler (
root_type & & seed = dpf : : uniform_sample < root_type > ( ) )
2026-09-28 05:59:19 -06:00
: seed_ { seed } , counter_ { 0 }
{
note_experiment_seed ( " pseudorandom_root_sampler " , seed_ ) ;
}
2026-09-24 14:08:32 -06:00
root_type operator ( ) ( psnip_uint32_t i ) const
{
return PRG : : eval ( seed_ , i ) ;
}
root_type operator ( ) ( )
{
return this - > operator ( ) ( counter_ . fetch_add ( 1 ) ) ;
}
const root_type & seed ( ) const { return seed_ ; }
psnip_uint32_t count ( ) const { return counter_ ; }
private :
root_type seed_ ;
std : : atomic_uint32_t counter_ ;
} ;
namespace utils
{
template < typename InteriorPRG ,
typename ExteriorPRG ,
typename InputT ,
typename OutputT ,
typename . . . OutputTs >
struct dpf_type
{
using type = dpf_key < InteriorPRG , ExteriorPRG ,
std : : decay_t < InputT > ,
std : : decay_t < OutputT > ,
std : : decay_t < OutputTs > . . . > ;
} ;
template < typename InteriorPRG ,
typename ExteriorPRG ,
typename InputT ,
typename OutputT ,
typename . . . OutputTs >
using dpf_type_t = typename dpf_type < InteriorPRG , ExteriorPRG , InputT , OutputT , OutputTs . . . > : : type ;
} // namespace utils
namespace detail
{
template < typename InteriorPRG ,
typename ExteriorPRG ,
typename InputT ,
typename OutputT ,
typename . . . OutputTs >
auto make_dpf_impl ( dpfargs < InputT , OutputT , OutputTs . . . > args , root_sampler_t < InteriorPRG > & & root_sampler = dpf : : uniform_sample < typename InteriorPRG : : block_type > )
{
using dpf_type = utils : : dpf_type_t < InteriorPRG , ExteriorPRG , InputT ,
OutputT , OutputTs . . . > ;
using interior_node = typename dpf_type : : interior_node ;
using input_type = typename dpf_type : : input_type ;
using correction_words_array = typename dpf_type : : correction_words_array ;
using correction_advice_array = typename dpf_type : : correction_advice_array ;
constexpr auto depth = dpf_type : : depth ;
auto mask = dpf_type : : msb_mask ;
input_type x , x0 { } , x1 { } ;
if constexpr ( dpf : : is_wildcard_v < InputT > )
{
auto sampled = args . x ( ) ;
x = std : : get < 0 > ( sampled ) ;
x0 = std : : get < 1 > ( sampled ) . raw ( ) ;
x1 = std : : get < 2 > ( sampled ) . raw ( ) ;
}
else
{
x = args . x ;
}
utils : : flip_msb_if_signed_integral ( x ) ;
2026-09-24 23:18:10 -06:00
using tree = dpf : : tree_traits < InteriorPRG > ;
HEDLEY_PRAGMA ( GCC diagnostic push )
HEDLEY_PRAGMA ( GCC diagnostic ignored " -Wignored-attributes " )
interior_node root [ 2 ] ;
HEDLEY_PRAGMA ( GCC diagnostic pop )
tree : : root_init ( root , root_sampler ) ;
2026-09-24 14:08:32 -06:00
HEDLEY_PRAGMA ( GCC diagnostic push )
HEDLEY_PRAGMA ( GCC diagnostic ignored " -Wignored-attributes " )
correction_words_array correction_words ;
HEDLEY_PRAGMA ( GCC diagnostic pop )
correction_advice_array correction_advice ;
interior_node parent [ 2 ] = { root [ 0 ] , root [ 1 ] } ;
for ( std : : size_t level = 0 ; level < depth ; + + level , mask > > = 1 )
{
2026-09-24 23:18:10 -06:00
const bool bit = ! ! ( mask & x ) ;
const bool is_last = tree : : is_last_level ( level , depth ) ;
const bool ctrl0 = static_cast < bool > ( dpf : : get_lo_bit ( parent [ 0 ] ) ) ;
const bool ctrl1 = static_cast < bool > ( dpf : : get_lo_bit ( parent [ 1 ] ) ) ;
const auto child0 = tree : : expand ( parent [ 0 ] , is_last ) ;
const auto child1 = tree : : expand ( parent [ 1 ] , is_last ) ;
interior_node cw { } ;
psnip_uint8_t advice = 0 ;
tree : : make_cw ( cw , advice , child0 , child1 , parent [ 0 ] , parent [ 1 ] , bit ,
is_last ) ;
parent [ 0 ] = tree : : advance ( parent [ 0 ] , child0 , cw , advice , bit , ctrl0 ,
is_last ) ;
parent [ 1 ] = tree : : advance ( parent [ 1 ] , child1 , cw , advice , bit , ctrl1 ,
is_last ) ;
correction_words [ level ] = cw ;
correction_advice [ level ] = advice ;
2026-09-24 14:08:32 -06:00
}
bool sign0 = dpf : : get_lo_bit ( parent [ 0 ] ) ;
// bool sign1 = dpf::get_lo_bit(parent[1]);
auto [ pair0 , pair1 ] = std : : apply ( [ & x , & parent , & sign0 ] ( auto & & . . . ys )
{
return dpf : : make_leaves < ExteriorPRG > ( x ,
dpf : : unset_lo_2bits ( parent [ 0 ] ) ,
dpf : : unset_lo_2bits ( parent [ 1 ] ) ,
sign0 , std : : size_t { 0 } , ys . . . ) ; } , args . y ) ;
auto & & [ leaves0 , beavers0 ] = pair0 ;
auto & & [ leaves1 , beavers1 ] = pair1 ;
return std : : make_tuple ( correction_words , correction_advice ,
std : : make_tuple ( root [ 0 ] , leaves0 , beavers0 , x0 ) ,
std : : make_tuple ( root [ 1 ] , leaves1 , beavers1 , x1 ) ) ;
} // make_dpf_impl
} // namespace detail
2026-09-28 05:59:19 -06:00
/// @brief Dealer keygen. Returns the two party keys.
/// @param args plaintext point and payloads
/// @param root_sampler draws the interior roots
/// @return a `party_key` pair
/// @note Signed domains flip the MSB before the walk (`flip_msb_if_signed_integral`).
/// @see dpf::eval_point
/// \complexity O(n) time and O(n) key size. n is the domain bitlength (`depth`). The loop does two interior PRG expansions and writes one correction word per level, then builds one exterior leaf per output.
2026-09-24 14:08:32 -06:00
template < typename InteriorPRG = dpf : : prg : : aes128 ,
typename ExteriorPRG = InteriorPRG ,
typename InputT ,
typename OutputT = dpf : : bit ,
typename . . . OutputTs >
HEDLEY_WARN_UNUSED_RESULT
auto make_dpf ( dpfargs < InputT , OutputT , OutputTs . . . > args , root_sampler_t < InteriorPRG > & & root_sampler = dpf : : uniform_sample < typename InteriorPRG : : block_type > )
{
static_assert ( ! is_secret_share_v < InputT > ,
" make_dpf: domain point must be plaintext " ) ;
static_assert ( ! is_secret_share_v < OutputT >
& & ( ! is_secret_share_v < OutputTs > & & . . . ) ,
" make_dpf: payloads must be plaintext " ) ;
using dpf_type = utils : : dpf_type_t < InteriorPRG , ExteriorPRG , InputT ,
OutputT , OutputTs . . . > ;
auto [ correction_words , correction_advice ,
tuple0 , tuple1 ] = detail : : make_dpf_impl < InteriorPRG , ExteriorPRG > ( args ,
std : : forward < root_sampler_t < InteriorPRG > > ( root_sampler ) ) ;
auto & [ root0 , leaves0 , beavers0 , offset0 ] = tuple0 ;
auto & [ root1 , leaves1 , beavers1 , offset1 ] = tuple1 ;
return dpf : : make_party_key_pair (
dpf_type { root0 , correction_words , correction_advice ,
leaves0 , beavers0 , offset0 } ,
dpf_type { root1 , correction_words , correction_advice ,
leaves1 , beavers1 , offset1 } ) ;
} // make_dpf
// Convenience `make_dpf(x, y...)` lives in incremental.hpp so `at<>` and
// mixed-width packs share one entry point with the classic path.
namespace detail
{
template < typename DpfKey ,
std : : size_t . . . Is >
auto make_dpf_random_point_impl ( std : : index_sequence < Is . . . > )
{
using input_type = typename DpfKey : : input_type ;
using interior_prg = typename DpfKey : : interior_prg ;
using exterior_prg = typename DpfKey : : exterior_prg ;
input_type x = dpf : : uniform_sample < input_type > ( ) ;
input_type x0 = dpf : : uniform_sample < input_type > ( ) ;
input_type x1 = static_cast < input_type > ( x - x0 ) ;
auto keys = make_dpf < interior_prg , exterior_prg > (
x , typename DpfKey : : concrete_output_type < Is > ( 1 ) . . . ) ;
return std : : make_tuple ( std : : move ( keys . first ) , std : : move ( keys . second ) ,
x0 , x1 ) ;
}
} // namespace detail
template < typename DpfKey >
auto make_dpf_random_point ( )
{
return detail : : make_dpf_random_point_impl < DpfKey > (
std : : make_index_sequence <
std : : tuple_size_v < typename DpfKey : : outputs_tuple > > { } ) ;
}
template < typename InteriorPRG = dpf : : prg : : aes128 ,
typename ExteriorPRG = InteriorPRG ,
typename InputT ,
typename OutputT = dpf : : bit ,
typename . . . OutputTs >
auto deduce_dpf_type ( InputT x , OutputT y = dpf : : bit : : one , OutputTs . . . ys )
{
return utils : : dpf_type < InteriorPRG , ExteriorPRG , InputT , OutputT , OutputTs . . . > { } ;
}
template < typename InteriorPRG = dpf : : prg : : aes128 ,
typename ExteriorPRG = InteriorPRG ,
typename InputT ,
typename OutputT = dpf : : bit ,
typename . . . OutputTs >
auto deduce_dpf_type ( dpf : : dpfargs < InputT , OutputT , OutputTs . . . > args )
{
return utils : : dpf_type < InteriorPRG , ExteriorPRG , InputT , OutputT , OutputTs . . . > { } ;
}
# define DEDUCE_DPF_TYPE_T(...) typename decltype(dpf::deduce_dpf_type(__VA_ARGS__))::type;
} // namespace dpf
# endif // LIBDPF_INCLUDE_DPF_DPF_KEY_HPP__