Document the new DPF surfaces in one command set, and test the field, half-tree, and multipoint edges.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
0d8a5a8131
commit
0dff6df8ed
250 changed files with 12199 additions and 1981 deletions
|
|
@ -14,7 +14,9 @@
|
|||
/// A polynomial is a sum of monomials in several wires.
|
||||
/// `2 + 3*x + 4*y + 5*x*y + 6*pow(x, 2) + pow(x, 2)*y + x*y*z`
|
||||
/// is one round. `λ_x²` is stored once whether it appears as `x²`,
|
||||
/// inside `x² y`, or in a second polynomial. Wires that occur with the
|
||||
/// inside `x² y`, or in a second polynomial. An inner product is that
|
||||
/// sum: `dot({x0,x1}, {y0,y1})` is `x0*y0 + x1*y1`, and the pair
|
||||
/// products share one preprocessing value. Wires that occur with the
|
||||
/// same exponents in every term, as in `a3*(x*z)^3 + a2*(x*z)^2 + a1*(x*z) + a0`,
|
||||
/// are multiplied first and the univariate polynomial is a later round.
|
||||
/// A factor shared by every term, such as a sign or a piecewise scale,
|
||||
|
|
@ -70,8 +72,9 @@ namespace dpf
|
|||
namespace beavers
|
||||
{
|
||||
|
||||
/// Ring operations used to build and consume triples.
|
||||
/// Specialize for a ring whose multiplicative identity is not `Ring{1}`.
|
||||
/// @brief Ring operations used to build and consume triples.
|
||||
/// @details Specialize for a ring whose multiplicative identity is not `Ring{1}`.
|
||||
/// @tparam Ring payload ring
|
||||
template <typename Ring>
|
||||
struct ring_traits
|
||||
{
|
||||
|
|
@ -90,7 +93,8 @@ struct ring_traits
|
|||
static Ring sample() { return dpf::uniform_sample<Ring>(); }
|
||||
};
|
||||
|
||||
/// Bitwise AND uses the all-ones word as its multiplicative identity.
|
||||
/// @brief Bitwise AND uses the all-ones word as its multiplicative identity.
|
||||
/// @tparam T value type
|
||||
template <typename T>
|
||||
struct ring_traits<dpf::xor_wrapper<T>>
|
||||
{
|
||||
|
|
@ -121,7 +125,8 @@ struct default_sampler
|
|||
Ring operator()() const { return ring_traits<Ring>::sample(); }
|
||||
};
|
||||
|
||||
/// Additive (2,2) split. `open()` is `p0 + p1`.
|
||||
/// @brief Additive (2,2) split. `open()` is `p0 + p1`.
|
||||
/// @tparam Ring payload ring
|
||||
template <typename Ring>
|
||||
struct split
|
||||
{
|
||||
|
|
@ -157,9 +162,10 @@ struct split
|
|||
template <typename Ring>
|
||||
class session;
|
||||
|
||||
/// A value in a session. Copying a wire copies its id; it does not copy the
|
||||
/// @brief A value in a session. Copying a wire copies its id; it does not copy the
|
||||
/// blind. The ring argument is on the type so `a * x * x` can build an
|
||||
/// expression without naming the session.
|
||||
/// @tparam Ring payload ring
|
||||
template <typename Ring>
|
||||
class wire
|
||||
{
|
||||
|
|
@ -192,15 +198,16 @@ private:
|
|||
std::uint32_t id_ = 0;
|
||||
};
|
||||
|
||||
/// Unevaluated sum of monomials. `*` distributes over `+`. A public
|
||||
/// @brief Unevaluated sum of monomials. `*` distributes over `+`. A public
|
||||
/// coefficient scales a term. Nothing is sampled until `session::operator()`.
|
||||
/// @tparam Ring payload ring
|
||||
template <typename Ring>
|
||||
struct expr
|
||||
{
|
||||
struct term
|
||||
{
|
||||
Ring coeff{};
|
||||
/// Positive exponents, sorted by wire id.
|
||||
/// @brief Positive exponents, sorted by wire id.
|
||||
std::vector<std::pair<std::uint32_t, std::uint8_t>> powers;
|
||||
};
|
||||
|
||||
|
|
@ -226,10 +233,20 @@ expr<Ring> wire_expr(wire<Ring> w);
|
|||
template <typename Ring>
|
||||
expr<Ring> horner_expr(wire<Ring> x, std::initializer_list<Ring> coeffs);
|
||||
|
||||
/// One PRG lane per blind role, plus the share-mask stream for that role.
|
||||
/// `blind(role, index)` and `share(role, index, value)` do not depend on
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename Ring, typename ContX, typename ContY>
|
||||
expr<Ring> dot_expr(const ContX & xs, const ContY & ys);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// @brief One PRG lane per blind role, plus the share-mask stream for that role.
|
||||
/// @details `blind(role, index)` and `share(role, index, value)` do not depend on
|
||||
/// call order. Walking `index` forward stays inside a refilled window.
|
||||
/// `PRG` defaults to `dpf::prg::aes128`.
|
||||
/// @tparam Ring payload ring
|
||||
/// @tparam PRG pseudorandom generator
|
||||
template <typename Ring, typename PRG = dpf::prg::aes128>
|
||||
class oracle
|
||||
{
|
||||
|
|
@ -241,7 +258,7 @@ public:
|
|||
using seed_type = typename PRG::block_type;
|
||||
using traits = ring_traits<Ring>;
|
||||
|
||||
/// Monomial roles sit above wire ids. Dot-cross roles sit above those.
|
||||
/// @brief Monomial roles sit above wire ids. Dot-cross roles sit above those.
|
||||
static constexpr std::uint32_t mono_role_base = 0x40000000u;
|
||||
static constexpr std::uint32_t dot_role_base = 0x80000000u;
|
||||
|
||||
|
|
@ -260,7 +277,7 @@ public:
|
|||
return dot_role_base + gate;
|
||||
}
|
||||
|
||||
/// Fused within-polynomial λ combinations (Appendix E groupings).
|
||||
/// @brief Fused within-polynomial λ combinations (Appendix E groupings).
|
||||
static constexpr std::uint32_t bundle_role_base = 0xC0000000u;
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
|
|
@ -290,7 +307,11 @@ public:
|
|||
return lanes_.mask_at(role, index);
|
||||
}
|
||||
|
||||
/// Additive split of `value`. The mask is the role's mask stream at `index`.
|
||||
/// @brief Additive split of `value`. The mask is the role's mask stream at `index`.
|
||||
/// @param role the `role`
|
||||
/// @param index the index
|
||||
/// @param value the value to convert or store
|
||||
/// @return Additive split of `value`
|
||||
split<Ring> share(std::uint32_t role, std::uint64_t index, const Ring & value) const
|
||||
{
|
||||
Ring p0 = mask(role, index);
|
||||
|
|
@ -311,20 +332,22 @@ private:
|
|||
dpf::randomness::lane_table<Ring, PRG> lanes_;
|
||||
};
|
||||
|
||||
/// Shares produced for one copy index of a recorded formula.
|
||||
/// @brief Shares produced for one copy index of a recorded formula.
|
||||
/// @tparam Ring payload ring
|
||||
template <typename Ring>
|
||||
struct prg_material
|
||||
{
|
||||
std::vector<split<Ring>> lambda;
|
||||
std::vector<split<Ring>> monomial;
|
||||
/// Fused λ-combinations for polynomial gates, in bundle index order.
|
||||
/// @brief Fused λ-combinations for polynomial gates, in bundle index order.
|
||||
std::vector<split<Ring>> bundles;
|
||||
/// Parallel to the session's gates. Empty split when the gate is not a dot.
|
||||
/// @brief Parallel to the session's gates. Empty split when the gate is not a dot.
|
||||
std::vector<split<Ring>> dot_cross;
|
||||
};
|
||||
|
||||
/// Dealer session: record formulae, `sample` blinds and monomials, `bind`
|
||||
/// @brief Dealer session: record formulae, `sample` blinds and monomials, `bind`
|
||||
/// input secrets, `evaluate` every round.
|
||||
/// @tparam Ring payload ring
|
||||
template <typename Ring>
|
||||
class session
|
||||
{
|
||||
|
|
@ -338,7 +361,7 @@ public:
|
|||
using exp_list = std::vector<std::pair<std::uint32_t, std::uint8_t>>;
|
||||
using wire = ::dpf::beavers::wire<Ring>;
|
||||
|
||||
/// One factor of a monomial query: `{{x, 2}, {a, 1}}`.
|
||||
/// @brief One factor of a monomial query: `{{x, 2}, {a, 1}}`.
|
||||
struct power
|
||||
{
|
||||
wire base{};
|
||||
|
|
@ -351,29 +374,34 @@ public:
|
|||
session(session &&) = delete;
|
||||
session & operator=(session &&) = delete;
|
||||
|
||||
/// Arithmetic input. Its blind is sampled once and then reused.
|
||||
/// @brief Arithmetic input. Its blind is sampled once and then reused.
|
||||
/// @return Arithmetic input
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
wire input()
|
||||
{
|
||||
return emplace_wire(0, true, false);
|
||||
}
|
||||
|
||||
/// Sample this wire's blind even if no recorded formula opens it.
|
||||
/// One-shot triples use this for the product wire, so it can be reused.
|
||||
/// @brief Sample this wire's blind even if no recorded formula opens it.
|
||||
/// @details One-shot triples use this for the product wire, so it can be reused.
|
||||
/// @param w the `w`
|
||||
void pin(wire w)
|
||||
{
|
||||
wires_[check(w)].pinned = true;
|
||||
}
|
||||
|
||||
/// 0/1 wire in this ring. `bind` accepts only `zero()` or `one()`
|
||||
/// @brief 0/1 wire in this ring. `bind` accepts only `zero()` or `one()`
|
||||
/// (`1` for integer rings, the all-ones word for `xor_wrapper`).
|
||||
/// @return 0/1 wire in this ring
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
wire bit()
|
||||
{
|
||||
return emplace_wire(0, true, true);
|
||||
}
|
||||
|
||||
/// One-round product. Repeated wires share a blind.
|
||||
/// @brief One-round product. Repeated wires share a blind.
|
||||
/// @param factors the `factors`
|
||||
/// @return One-round product
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
wire product(std::initializer_list<wire> factors)
|
||||
{
|
||||
|
|
@ -396,7 +424,10 @@ public:
|
|||
return commit_product({check(a), check(b), check(c)});
|
||||
}
|
||||
|
||||
/// Record a sum of monomials. Like terms share one blind product.
|
||||
/// @brief Record a sum of monomials. Like terms share one blind product.
|
||||
/// @param e the `e`
|
||||
/// @return Record a sum of monomials
|
||||
/// @throws std::invalid_argument if `beaver expression is from a different session`
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
wire operator()(const expr<Ring> & e)
|
||||
{
|
||||
|
|
@ -405,15 +436,23 @@ public:
|
|||
return commit_poly(e);
|
||||
}
|
||||
|
||||
/// `c[0] + c[1] x + c[2] x^2 + ...` in one round.
|
||||
/// @brief `c[0] + c[1] x + c[2] x^2 + ...` in one round.
|
||||
/// @param x the `x`
|
||||
/// @param coeffs the public coefficients
|
||||
/// @return `c[0] + c[1] x + c[2] x^2 + ...` in one round
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
wire horner(wire x, std::initializer_list<Ring> coeffs)
|
||||
{
|
||||
return (*this)(horner_expr(x, coeffs));
|
||||
}
|
||||
|
||||
/// Sign-corrected Horner: `sign * (c[0] + c[1] x + ...)`, still one round
|
||||
/// @brief Sign-corrected Horner: `sign * (c[0] + c[1] x + ...)`, still one round
|
||||
/// when `sign` and `x` are inputs.
|
||||
/// @param sign the sign bit or sign value
|
||||
/// @param x the `x`
|
||||
/// @param coeffs the public coefficients
|
||||
/// @return Sign-corrected Horner: `sign * (c[0] + c[1] x + ...)`, still one round when `sign`
|
||||
/// and `x` are inputs
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
wire horner(wire sign, wire x, std::initializer_list<Ring> coeffs)
|
||||
{
|
||||
|
|
@ -426,7 +465,10 @@ public:
|
|||
return product(x, x);
|
||||
}
|
||||
|
||||
/// One-round `a * x * x` (one blind for `x`).
|
||||
/// @brief One-round `a * x * x` (one blind for `x`).
|
||||
/// @param a the `a`
|
||||
/// @param x the `x`
|
||||
/// @return One-round `a * x * x` (one blind for `x`)
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
wire mul_square(wire a, wire x)
|
||||
{
|
||||
|
|
@ -437,31 +479,21 @@ public:
|
|||
HEDLEY_WARN_UNUSED_RESULT
|
||||
wire dot(const ContX & xs, const ContY & ys)
|
||||
{
|
||||
std::vector<std::uint32_t> x;
|
||||
std::vector<std::uint32_t> y;
|
||||
for (const auto & w : xs)
|
||||
x.push_back(check(w));
|
||||
for (const auto & w : ys)
|
||||
y.push_back(check(w));
|
||||
return commit_dot(std::move(x), std::move(y));
|
||||
return finish_dot(detail::dot_expr<Ring>(xs, ys));
|
||||
}
|
||||
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
wire dot(std::initializer_list<wire> xs, std::initializer_list<wire> ys)
|
||||
{
|
||||
std::vector<std::uint32_t> x;
|
||||
std::vector<std::uint32_t> y;
|
||||
x.reserve(xs.size());
|
||||
y.reserve(ys.size());
|
||||
for (auto w : xs)
|
||||
x.push_back(check(w));
|
||||
for (auto w : ys)
|
||||
y.push_back(check(w));
|
||||
return commit_dot(std::move(x), std::move(y));
|
||||
return finish_dot(detail::dot_expr<Ring>(xs, ys));
|
||||
}
|
||||
|
||||
/// `z_i = scalar * lanes[i]`, one output wire per lane. The scalar blind
|
||||
/// @brief `z_i = scalar * lanes[i]`, one output wire per lane. The scalar blind
|
||||
/// is shared. Each lane gets its own `λ_s λ_i` share.
|
||||
/// @tparam Cont cont
|
||||
/// @param scalar the `scalar`
|
||||
/// @param lanes the lane values
|
||||
/// @return `z_i = scalar * lanes[i]`, one output wire per lane
|
||||
template <typename Cont>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
std::vector<wire> scale(wire scalar, const Cont & lanes)
|
||||
|
|
@ -482,7 +514,11 @@ public:
|
|||
return out;
|
||||
}
|
||||
|
||||
/// `bit * scalar`. `bit` must come from `bit()`.
|
||||
/// @brief `bit * scalar`. `bit` must come from `bit()`.
|
||||
/// @param selector the `selector`
|
||||
/// @param scalar the `scalar`
|
||||
/// @return `bit * scalar`
|
||||
/// @throws std::invalid_argument if `bit_mul selector must come from bit()`
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
wire bit_mul(wire selector, wire scalar)
|
||||
{
|
||||
|
|
@ -492,7 +528,11 @@ public:
|
|||
return commit_product({id, check(scalar)});
|
||||
}
|
||||
|
||||
/// One-round `selector ? when1 : when0`, i.e. `when0 + selector * (when1 - when0)`.
|
||||
/// @brief One-round `selector ? when1 : when0`, i.e. `when0 + selector * (when1 - when0)`.
|
||||
/// @param selector the `selector`
|
||||
/// @param when1 the `when1`
|
||||
/// @param when0 the `when0`
|
||||
/// @return One-round `selector ? when1 : when0`, i.e
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
wire mux(wire selector, wire when1, wire when0)
|
||||
{
|
||||
|
|
@ -515,8 +555,11 @@ public:
|
|||
return out;
|
||||
}
|
||||
|
||||
/// Sample every missing wire blind and every missing monomial.
|
||||
/// Blinds already sampled are left alone.
|
||||
/// @brief Sample every missing wire blind and every missing monomial.
|
||||
/// @details Blinds already sampled are left alone.
|
||||
/// @tparam Sample sample
|
||||
/// @param sampler the randomness sampler
|
||||
/// @throws std::logic_error if `beaver blind is missing`
|
||||
template <typename Sample>
|
||||
void sample(Sample && sampler)
|
||||
{
|
||||
|
|
@ -572,9 +615,12 @@ public:
|
|||
sample(default_sampler<Ring>{});
|
||||
}
|
||||
|
||||
/// Install missing blinds and product shares from copy `index` of `src`.
|
||||
/// Already-sampled wires keep their λ. New product shares are built from
|
||||
/// @brief Install missing blinds and product shares from copy `index` of `src`.
|
||||
/// @details Already-sampled wires keep their λ. New product shares are built from
|
||||
/// those stored blinds, then split with the oracle's share lane.
|
||||
/// @tparam PRG pseudorandom generator
|
||||
/// @param src the source
|
||||
/// @param index the index
|
||||
template <typename PRG>
|
||||
void sample_from(const oracle<Ring, PRG> & src, std::uint64_t index = 0)
|
||||
{
|
||||
|
|
@ -614,9 +660,13 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
/// Every wire, monomial, and dot cross of this formula at copy `index`.
|
||||
/// Does not change the session. Copies are independent lanes samples, so
|
||||
/// @brief Every wire, monomial, and dot cross of this formula at copy `index`.
|
||||
/// @details Does not change the session. Copies are independent lanes samples, so
|
||||
/// `material_at(src, 5)` does not depend on having asked for 0..4.
|
||||
/// @tparam PRG pseudorandom generator
|
||||
/// @param src the source
|
||||
/// @param index the index
|
||||
/// @return Every wire, monomial, and dot cross of this formula at copy `index`
|
||||
template <typename PRG>
|
||||
prg_material<Ring> material_at(const oracle<Ring, PRG> & src, std::uint64_t index) const
|
||||
{
|
||||
|
|
@ -665,7 +715,13 @@ public:
|
|||
return out;
|
||||
}
|
||||
|
||||
/// Split `secret` into fresh additive shares and bind them to an input.
|
||||
/// @brief Split `secret` into fresh additive shares and bind them to an input.
|
||||
/// @tparam Sample sample
|
||||
/// @param w the `w`
|
||||
/// @param secret the secret value
|
||||
/// @param sampler the randomness sampler
|
||||
/// @throws std::invalid_argument if `only input wires can be bound`
|
||||
/// @throws std::logic_error if `input wire is already bound`
|
||||
template <typename Sample>
|
||||
void bind(wire w, Ring secret, Sample && sampler)
|
||||
{
|
||||
|
|
@ -686,7 +742,12 @@ public:
|
|||
bind(w, secret, default_sampler<Ring>{});
|
||||
}
|
||||
|
||||
/// Bind shares the caller already holds. Their sum is the secret.
|
||||
/// @brief Bind shares the caller already holds. Their sum is the secret.
|
||||
/// @param w the `w`
|
||||
/// @param p0 the `p0`
|
||||
/// @param p1 the `p1`
|
||||
/// @throws std::invalid_argument if `only input wires can be bound`
|
||||
/// @throws std::logic_error if `input wire is already bound`
|
||||
void bind_shares(wire w, Ring p0, Ring p1)
|
||||
{
|
||||
auto id = check(w);
|
||||
|
|
@ -700,9 +761,10 @@ public:
|
|||
wires_[id].value_ready = true;
|
||||
}
|
||||
|
||||
/// Open every ready round. Inputs used by round-1 gates are opened
|
||||
/// @brief Open every ready round. Inputs used by round-1 gates are opened
|
||||
/// together; a gate output is a later round's input and keeps the blind
|
||||
/// chosen in `sample`.
|
||||
/// @throws std::logic_error if `beaver wire is not ready to open`
|
||||
void evaluate()
|
||||
{
|
||||
int max_round = 0;
|
||||
|
|
@ -774,7 +836,10 @@ public:
|
|||
return wires_[id].lambda;
|
||||
}
|
||||
|
||||
/// Share of `Π λ_i^{e_i}`. A lone `λ_w` is the wire blind itself.
|
||||
/// @brief Share of `Π λ_i^{e_i}`. A lone `λ_w` is the wire blind itself.
|
||||
/// @param spec the specification
|
||||
/// @return Share of `Π λ_i^{e_i}`
|
||||
/// @throws std::invalid_argument if `beaver power is zero`
|
||||
split<Ring> monomial(const std::vector<power> & spec) const
|
||||
{
|
||||
std::vector<std::pair<std::uint32_t, unsigned>> raw;
|
||||
|
|
@ -807,7 +872,10 @@ public:
|
|||
return traits::add(v.p0, v.p1);
|
||||
}
|
||||
|
||||
/// Public ABY2.0 mask δ = x + λ, after `evaluate` has opened the wire.
|
||||
/// @brief Public ABY2.0 mask δ = x + λ, after `evaluate` has opened the wire.
|
||||
/// @param w the `w`
|
||||
/// @return Public ABY2.0 mask δ = x + λ, after `evaluate` has opened the wire
|
||||
/// @throws std::logic_error if `beaver wire has not been opened`
|
||||
Ring delta(wire w) const
|
||||
{
|
||||
auto id = check(w);
|
||||
|
|
@ -819,12 +887,35 @@ public:
|
|||
split<Ring> dot_cross(wire w) const
|
||||
{
|
||||
auto id = check(w);
|
||||
int g = wires_[id].gate;
|
||||
if (g < 0 || gates_[static_cast<std::size_t>(g)].kind != gate_kind::dot)
|
||||
if (!wires_[id].dot_output)
|
||||
throw std::invalid_argument("wire is not a dot output");
|
||||
if (!gates_[static_cast<std::size_t>(g)].cross_ready)
|
||||
int g = wires_[id].gate;
|
||||
if (g < 0)
|
||||
throw std::invalid_argument("wire is not a dot output");
|
||||
const auto & gate = gates_[static_cast<std::size_t>(g)];
|
||||
if (gate.kind == gate_kind::dot)
|
||||
{
|
||||
if (!gate.cross_ready)
|
||||
throw std::logic_error("call sample() before reading a dot cross term");
|
||||
return gate.cross;
|
||||
}
|
||||
Ring s0 = traits::zero();
|
||||
Ring s1 = traits::zero();
|
||||
bool found = false;
|
||||
for (const auto & step : gate.steps)
|
||||
{
|
||||
if (step.bundle < 0 || !step.delta.empty())
|
||||
continue;
|
||||
const auto & bundle = bundles_[static_cast<std::size_t>(step.bundle)];
|
||||
if (!bundle.ready)
|
||||
throw std::logic_error("call sample() before reading a dot cross term");
|
||||
s0 = traits::add(s0, traits::mul(step.scale, bundle.share.p0));
|
||||
s1 = traits::add(s1, traits::mul(step.scale, bundle.share.p1));
|
||||
found = true;
|
||||
}
|
||||
if (!found)
|
||||
throw std::logic_error("call sample() before reading a dot cross term");
|
||||
return gates_[static_cast<std::size_t>(g)].cross;
|
||||
return split<Ring>{s0, s1};
|
||||
}
|
||||
|
||||
int round_of(wire w) const
|
||||
|
|
@ -835,16 +926,19 @@ public:
|
|||
HEDLEY_NO_THROW
|
||||
std::size_t wire_count() const noexcept { return wires_.size(); }
|
||||
|
||||
/// Product shares beyond the per-wire blinds: subset monomials from
|
||||
/// @brief Product shares beyond the per-wire blinds: subset monomials from
|
||||
/// `product` gates, plus one fused bundle per public-δ class in a
|
||||
/// polynomial (Appendix E). A lone mask is not counted.
|
||||
/// @return Product shares beyond the per-wire blinds: subset monomials from `product` gates,
|
||||
/// plus one fused bundle per public-δ class in a polynomial (Appendix E)
|
||||
HEDLEY_NO_THROW
|
||||
std::size_t monomial_count() const noexcept
|
||||
{
|
||||
return monos_.size() + bundles_.size();
|
||||
}
|
||||
|
||||
/// Wire blinds that the recorded formulae actually open, plus product shares.
|
||||
/// @brief Wire blinds that the recorded formulae actually open, plus product shares.
|
||||
/// @return Wire blinds that the recorded formulae actually open, plus product shares
|
||||
std::size_t preprocessing_count() const
|
||||
{
|
||||
std::size_t n = monomial_count();
|
||||
|
|
@ -865,7 +959,7 @@ private:
|
|||
std::vector<std::uint32_t> factors;
|
||||
};
|
||||
|
||||
/// One λ-monomial in a fused preprocessing share.
|
||||
/// @brief One λ-monomial in a fused preprocessing share.
|
||||
struct bundle_part
|
||||
{
|
||||
Ring coeff{};
|
||||
|
|
@ -879,7 +973,7 @@ private:
|
|||
bool ready = false;
|
||||
};
|
||||
|
||||
/// Online: `public(δ) * scale * share`, where share is a wire mask,
|
||||
/// @brief Online: `public(δ) * scale * share`, where share is a wire mask,
|
||||
/// a raw monomial, or a fused sum of monomials.
|
||||
struct poly_step
|
||||
{
|
||||
|
|
@ -896,6 +990,7 @@ private:
|
|||
bool is_input = false;
|
||||
bool is_bit = false;
|
||||
bool pinned = false;
|
||||
bool dot_output = false;
|
||||
bool lambda_ready = false;
|
||||
bool value_ready = false;
|
||||
bool delta_ready = false;
|
||||
|
|
@ -1369,25 +1464,10 @@ private:
|
|||
return last;
|
||||
}
|
||||
|
||||
wire commit_dot(std::vector<std::uint32_t> xs, std::vector<std::uint32_t> ys)
|
||||
wire finish_dot(const expr<Ring> & e)
|
||||
{
|
||||
if (xs.empty() || xs.size() != ys.size())
|
||||
throw std::invalid_argument(
|
||||
"beaver dot operands must have the same non-zero length");
|
||||
int round = 1;
|
||||
for (std::size_t i = 0; i < xs.size(); ++i)
|
||||
{
|
||||
round = std::max(round, wires_[xs[i]].ready_round + 1);
|
||||
round = std::max(round, wires_[ys[i]].ready_round + 1);
|
||||
}
|
||||
auto out = emplace_wire(round, false, false);
|
||||
gate g;
|
||||
g.kind = gate_kind::dot;
|
||||
g.out = out.id_;
|
||||
g.lhs = std::move(xs);
|
||||
g.rhs = std::move(ys);
|
||||
gates_.push_back(std::move(g));
|
||||
wires_[out.id_].gate = static_cast<int>(gates_.size() - 1);
|
||||
auto out = (*this)(e);
|
||||
wires_[out.id_].dot_output = true;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
@ -1572,8 +1652,10 @@ private:
|
|||
return false;
|
||||
}
|
||||
|
||||
/// Group λ-monomials that share a public δ monomial into one share.
|
||||
/// A bucket that is only `c · λ_i` reuses the wire blind.
|
||||
/// @brief Group λ-monomials that share a public δ monomial into one share.
|
||||
/// @details A bucket that is only `c · λ_i` reuses the wire blind.
|
||||
/// @param terms the polynomial terms
|
||||
/// @return Group λ-monomials that share a public δ monomial into one share
|
||||
std::vector<poly_step> compile_poly(const std::vector<poly_term> & terms)
|
||||
{
|
||||
struct bucket
|
||||
|
|
@ -1665,9 +1747,20 @@ private:
|
|||
steps.push_back(std::move(step));
|
||||
continue;
|
||||
}
|
||||
Ring scale = parts[0].coeff;
|
||||
for (const auto & part : parts)
|
||||
{
|
||||
if (!(part.coeff == scale))
|
||||
scale = traits::one();
|
||||
}
|
||||
if (!(scale == traits::one()))
|
||||
{
|
||||
for (auto & part : parts)
|
||||
part.coeff = traits::one();
|
||||
}
|
||||
poly_step step;
|
||||
step.delta = delta;
|
||||
step.scale = traits::one();
|
||||
step.scale = scale;
|
||||
step.bundle = require_bundle(std::move(parts));
|
||||
steps.push_back(std::move(step));
|
||||
}
|
||||
|
|
@ -1852,8 +1945,40 @@ Ring coeff_of(Coeff value)
|
|||
return Ring{value};
|
||||
}
|
||||
|
||||
template <typename Ring, typename ContX, typename ContY>
|
||||
expr<Ring> dot_expr(const ContX & xs, const ContY & ys)
|
||||
{
|
||||
std::vector<wire<Ring>> x;
|
||||
std::vector<wire<Ring>> y;
|
||||
for (const auto & w : xs)
|
||||
x.push_back(w);
|
||||
for (const auto & w : ys)
|
||||
y.push_back(w);
|
||||
if (x.empty() || x.size() != y.size())
|
||||
throw std::invalid_argument(
|
||||
"beaver dot operands must have the same non-zero length");
|
||||
expr<Ring> acc = wire_expr(x[0]) * wire_expr(y[0]);
|
||||
for (std::size_t i = 1; i < x.size(); ++i)
|
||||
acc = add_exprs(std::move(acc), wire_expr(x[i]) * wire_expr(y[i]));
|
||||
return acc;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename Ring>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
expr<Ring> dot(std::initializer_list<wire<Ring>> xs, std::initializer_list<wire<Ring>> ys)
|
||||
{
|
||||
return detail::dot_expr<Ring>(xs, ys);
|
||||
}
|
||||
|
||||
template <typename Ring>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
expr<Ring> dot(const std::vector<wire<Ring>> & xs, const std::vector<wire<Ring>> & ys)
|
||||
{
|
||||
return detail::dot_expr<Ring>(xs, ys);
|
||||
}
|
||||
|
||||
template <typename Ring>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
expr<Ring> wire_expr(wire<Ring> w)
|
||||
|
|
@ -1895,7 +2020,13 @@ expr<Ring> horner_expr(wire<Ring> x, std::initializer_list<Ring> coeffs)
|
|||
return e;
|
||||
}
|
||||
|
||||
/// `coeff * v0 * v1 * ...`, with repeated wires counting as a power.
|
||||
/// @brief `coeff * v0 * v1 * ...`, with repeated wires counting as a power.
|
||||
/// @tparam Ring payload ring
|
||||
/// @tparam Wires wires
|
||||
/// @param coeff the public coefficient
|
||||
/// @param first the first element of the range
|
||||
/// @param rest the remaining arguments
|
||||
/// @return `coeff * v0 * v1 * ...`, with repeated wires counting as a power
|
||||
template <typename Ring, typename... Wires>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
expr<Ring> monomial(Ring coeff, wire<Ring> first, Wires... rest)
|
||||
|
|
@ -2128,8 +2259,10 @@ expr<Ring> operator+(expr<Ring> e, Coeff coeff)
|
|||
// `out` is the ABY2.0 blind of the product wire, for a later round.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// `subset[mask - 1]` is `Π λ_i` over the bits set in `mask` (bit i selects
|
||||
/// @brief `subset[mask - 1]` is `Π λ_i` over the bits set in `mask` (bit i selects
|
||||
/// `in[i]`). Singleton masks are the wire blinds themselves.
|
||||
/// @tparam Arity arity
|
||||
/// @tparam Ring payload ring
|
||||
template <std::size_t Arity, typename Ring>
|
||||
struct fresh_beaver
|
||||
{
|
||||
|
|
@ -2259,9 +2392,14 @@ struct mux_beaver
|
|||
split<Ring> out{};
|
||||
};
|
||||
|
||||
/// One fresh Beaver pair from copy `index` of an oracle.
|
||||
/// Roles match a session that records `input, input, product`: wires 0 and 1,
|
||||
/// @brief One fresh Beaver pair from copy `index` of an oracle.
|
||||
/// @details Roles match a session that records `input, input, product`: wires 0 and 1,
|
||||
/// the product wire, and monomial 0.
|
||||
/// @tparam Ring payload ring
|
||||
/// @tparam PRG pseudorandom generator
|
||||
/// @param src the source
|
||||
/// @param index the index
|
||||
/// @return One fresh Beaver pair from copy `index` of an oracle
|
||||
template <typename Ring, typename PRG = dpf::prg::aes128>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
beaver2<Ring> beaver2_at(const oracle<Ring, PRG> & src, std::uint64_t index)
|
||||
|
|
@ -2278,7 +2416,14 @@ beaver2<Ring> beaver2_at(const oracle<Ring, PRG> & src, std::uint64_t index)
|
|||
src.share(oracle<Ring>::wire_role(2), index, out)};
|
||||
}
|
||||
|
||||
/// `n` copies starting at `begin`. Each role is one contiguous lane read.
|
||||
/// @brief `n` copies starting at `begin`. Each role is one contiguous lane read.
|
||||
/// @tparam Ring payload ring
|
||||
/// @tparam PRG pseudorandom generator
|
||||
/// @param src the source
|
||||
/// @param begin the iterator to the first query
|
||||
/// @param out the output buffer
|
||||
/// @param n the `n`
|
||||
/// @throws std::invalid_argument if `beaver2 output is null`
|
||||
template <typename Ring, typename PRG = dpf::prg::aes128>
|
||||
void fill_beaver2(const oracle<Ring, PRG> & src, std::uint64_t begin,
|
||||
beaver2<Ring> * out, std::size_t n)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue