Checkpoint the party/runtime stack before share-program and malicious-mode work.
Ship the TLS mesh, composer, Beaver/Yao/leaf MPC, prep/online paths, apps, and docs so the tree is pushable before elevating share_expr, security_mode, and prep resume. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
695f8e84f7
commit
0d22946a0e
1835 changed files with 170291 additions and 2849 deletions
|
|
@ -1,4 +1,5 @@
|
|||
#include <gtest/gtest.h>
|
||||
#include <tuple>
|
||||
|
||||
#include "dpf.hpp"
|
||||
#include "grotto/fixedpoint.hpp"
|
||||
|
|
@ -1895,3 +1896,315 @@ TEST_F(IncrementalDpfTest, SequenceRecipeAtPrefixSlot)
|
|||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Regressions for bugs found under ASan/UBSan:
|
||||
// * eq(..., if_false) must absorb on every eval surface, not only point
|
||||
// * path memoizer resume past a full-width cmp depth must not shift by nbits
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
TEST_F(IncrementalDpfTest, EqIfFalseAbsorbsOnEveryEvalSurface)
|
||||
{
|
||||
const uint8_t alpha = 5;
|
||||
const uint8_t yt = 7;
|
||||
const uint8_t yf = 3;
|
||||
auto [k0, k1] = dpf::make_dpf(alpha, dpf::eq(yt, yf));
|
||||
using KT = std::decay_t<decltype(k0)>;
|
||||
EXPECT_TRUE(KT::is_multilevel);
|
||||
EXPECT_EQ(std::get<0>(k0.public_addends), yf);
|
||||
constexpr auto opl = KT::template outputs_per_leaf_of<0>;
|
||||
|
||||
auto expect_at = [&](uint8_t q) {
|
||||
return (q == alpha) ? yt : yf;
|
||||
};
|
||||
|
||||
for (unsigned q = 0; q < 8u; ++q)
|
||||
{
|
||||
EXPECT_EQ(recon(*dpf::eval_point(k0, uint8_t(q)),
|
||||
*dpf::eval_point(k1, uint8_t(q))),
|
||||
expect_at(uint8_t(q)))
|
||||
<< "point q=" << q;
|
||||
}
|
||||
|
||||
auto [f0, itf0] = dpf::eval_full(k0);
|
||||
auto [f1, itf1] = dpf::eval_full(k1);
|
||||
(void)itf0;
|
||||
(void)itf1;
|
||||
for (unsigned q = 0; q < 8u; ++q)
|
||||
{
|
||||
EXPECT_EQ(recon(f0[q], f1[q]), expect_at(uint8_t(q)))
|
||||
<< "full q=" << q;
|
||||
}
|
||||
|
||||
// Leaf-aligned interval so buffer index == lane.
|
||||
constexpr uint8_t from = 0;
|
||||
constexpr uint8_t to = 15; // one full packing leaf when opl==16
|
||||
auto [iv0, ii0] = dpf::eval_interval(dpf::out<0>, k0, from, to);
|
||||
auto [iv1, ii1] = dpf::eval_interval(dpf::out<0>, k1, from, to);
|
||||
(void)ii0;
|
||||
(void)ii1;
|
||||
ASSERT_EQ(iv0.size(), static_cast<std::size_t>(to - from + 1));
|
||||
for (unsigned q = from; q <= to; ++q)
|
||||
{
|
||||
const uint8_t want = (q < 8u) ? expect_at(uint8_t(q)) : yf;
|
||||
EXPECT_EQ(recon(iv0[q - from], iv1[q - from]), want)
|
||||
<< "interval q=" << q;
|
||||
}
|
||||
|
||||
std::array<uint8_t, 8> pts{{7, 0, 5, 2, 1, 6, 3, 4}};
|
||||
auto s0 = dpf::make_output_buffer_for(k0, pts.size());
|
||||
auto s1 = dpf::make_output_buffer_for(k1, pts.size());
|
||||
dpf::eval_sequence(dpf::out<0>, k0, pts.begin(), pts.end(), s0);
|
||||
dpf::eval_sequence(dpf::out<0>, k1, pts.begin(), pts.end(), s1);
|
||||
for (std::size_t i = 0; i < pts.size(); ++i)
|
||||
{
|
||||
auto e0 = dpf::eval_point(dpf::out<0>, k0, pts[i]);
|
||||
auto e1 = dpf::eval_point(dpf::out<0>, k1, pts[i]);
|
||||
EXPECT_EQ(recon(s0[i * opl + e0.offset], s1[i * opl + e1.offset]),
|
||||
expect_at(pts[i]))
|
||||
<< "sequence i=" << i;
|
||||
}
|
||||
|
||||
std::array<uint8_t, 8> sorted{{0, 1, 2, 3, 4, 5, 6, 7}};
|
||||
auto b0 = dpf::eval_sequence_breadth_first(dpf::out<0>, k0, sorted.begin(),
|
||||
sorted.end());
|
||||
auto b1 = dpf::eval_sequence_breadth_first(dpf::out<0>, k1, sorted.begin(),
|
||||
sorted.end());
|
||||
for (std::size_t i = 0; i < sorted.size(); ++i)
|
||||
{
|
||||
EXPECT_EQ(recon(b0[i], b1[i]), expect_at(sorted[i]))
|
||||
<< "breadth i=" << i;
|
||||
}
|
||||
|
||||
// Weights must cover every packing lane the interval exterior materialises.
|
||||
std::vector<uint64_t> w(iv0.size(), 1);
|
||||
uint64_t expect_ip = 0;
|
||||
for (std::size_t i = 0; i < iv0.size(); ++i)
|
||||
expect_ip += static_cast<uint64_t>(recon(iv0[i], iv1[i])) * w[i];
|
||||
const auto ip0 = dpf::eval_inner_product(dpf::out<0>, k0, from, to, w);
|
||||
const auto ip1 = dpf::eval_inner_product(dpf::out<0>, k1, from, to, w);
|
||||
EXPECT_EQ(static_cast<uint8_t>(recon(ip0, ip1)),
|
||||
static_cast<uint8_t>(expect_ip));
|
||||
}
|
||||
|
||||
TEST_F(IncrementalDpfTest, EqAtIfFalseWithPackedIntervalAndXor)
|
||||
{
|
||||
const uint32_t alpha = 0x00c0ffeeu;
|
||||
auto [k0, k1] = dpf::make_dpf(alpha,
|
||||
dpf::eq_at<16>(uint16_t{99}, uint16_t{7}),
|
||||
dpf::eq(dpf::xor_wrapper<uint32_t>{0x00ff00ffu},
|
||||
dpf::xor_wrapper<uint32_t>{0x00001111u}));
|
||||
using KT = std::decay_t<decltype(k0)>;
|
||||
constexpr auto opl = KT::template outputs_per_leaf_of<0>;
|
||||
|
||||
const uint16_t lane = static_cast<uint16_t>(alpha >> 16);
|
||||
// Align to a packing leaf so buffer index math is exact.
|
||||
const uint16_t from = static_cast<uint16_t>(lane & ~(opl - 1u));
|
||||
const uint16_t to = static_cast<uint16_t>(from + opl - 1u);
|
||||
auto [buf0, iit0] = dpf::eval_interval(dpf::out<0, 16>, k0, from, to);
|
||||
auto [buf1, iit1] = dpf::eval_interval(dpf::out<0, 16>, k1, from, to);
|
||||
(void)iit0;
|
||||
(void)iit1;
|
||||
for (uint16_t q = from; q <= to; ++q)
|
||||
{
|
||||
const std::size_t idx = static_cast<std::size_t>(q - from);
|
||||
const uint16_t want = (q == lane) ? uint16_t{99} : uint16_t{7};
|
||||
EXPECT_EQ(recon(buf0[idx], buf1[idx]), want) << "eq_at lane=" << q;
|
||||
const uint32_t full = static_cast<uint32_t>(q) << 16;
|
||||
EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 16>, k0, full),
|
||||
*dpf::eval_point(dpf::out<0, 16>, k1, full)),
|
||||
want);
|
||||
}
|
||||
|
||||
// Spot-check the full-width xor_wrapper eq (avoid 2^32 full-domain walk).
|
||||
const auto on = dpf::xor_wrapper<uint32_t>{0x00ff00ffu};
|
||||
const auto off = dpf::xor_wrapper<uint32_t>{0x00001111u};
|
||||
EXPECT_EQ(recon(*dpf::eval_point(dpf::out<1>, k0, alpha),
|
||||
*dpf::eval_point(dpf::out<1>, k1, alpha)),
|
||||
on);
|
||||
EXPECT_EQ(recon(*dpf::eval_point(dpf::out<1>, k0, alpha ^ 1u),
|
||||
*dpf::eval_point(dpf::out<1>, k1, alpha ^ 1u)),
|
||||
off);
|
||||
EXPECT_EQ(recon(*dpf::eval_point(dpf::out<1>, k0, 0u),
|
||||
*dpf::eval_point(dpf::out<1>, k1, 0u)),
|
||||
off);
|
||||
// Leaf-aligned interval around α for the xor slot.
|
||||
using KT1 = std::decay_t<decltype(k0)>;
|
||||
constexpr auto opl1 = KT1::template outputs_per_leaf_of<1>;
|
||||
const uint32_t xfrom = alpha & ~(opl1 - 1u);
|
||||
const uint32_t xto = xfrom + static_cast<uint32_t>(opl1 - 1u);
|
||||
auto [xf0, xi0] = dpf::eval_interval(dpf::out<1>, k0, xfrom, xto);
|
||||
auto [xf1, xi1] = dpf::eval_interval(dpf::out<1>, k1, xfrom, xto);
|
||||
(void)xi0;
|
||||
(void)xi1;
|
||||
for (uint32_t q = xfrom; q <= xto; ++q)
|
||||
{
|
||||
const auto want = (q == alpha) ? on : off;
|
||||
EXPECT_EQ(recon(xf0[q - xfrom], xf1[q - xfrom]), want) << "xor q=" << q;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(IncrementalDpfTest, EqIfFalseDealerMatchesDoernerShelatSurfaces)
|
||||
{
|
||||
const uint8_t alpha = 0x2au;
|
||||
const uint8_t x0s = 0x11u;
|
||||
const uint8_t x1s = static_cast<uint8_t>(alpha ^ x0s);
|
||||
auto dealer = dpf::make_dpf(alpha, dpf::eq(uint8_t{9}, uint8_t{4}));
|
||||
auto ds = dpf::make_dpf_doerner_shelat(x0s, x1s,
|
||||
dpf::eq(uint8_t{9}, uint8_t{4}));
|
||||
|
||||
for (unsigned q = 0; q < 256u; q += 17u)
|
||||
{
|
||||
const uint8_t qq = static_cast<uint8_t>(q);
|
||||
EXPECT_EQ(recon(*dpf::eval_point(dealer.first, qq),
|
||||
*dpf::eval_point(dealer.second, qq)),
|
||||
recon(*dpf::eval_point(ds.first, qq),
|
||||
*dpf::eval_point(ds.second, qq)));
|
||||
}
|
||||
auto [fa0, ia0] = dpf::eval_full(dealer.first);
|
||||
auto [fa1, ia1] = dpf::eval_full(dealer.second);
|
||||
auto [fb0, ib0] = dpf::eval_full(ds.first);
|
||||
auto [fb1, ib1] = dpf::eval_full(ds.second);
|
||||
(void)ia0;
|
||||
(void)ia1;
|
||||
(void)ib0;
|
||||
(void)ib1;
|
||||
for (unsigned q = 0; q < 256u; q += 17u)
|
||||
EXPECT_EQ(recon(fa0[q], fa1[q]), recon(fb0[q], fb1[q]));
|
||||
}
|
||||
|
||||
TEST_F(IncrementalDpfTest, PathMemoizerResumePastFullWidthCmpDepth)
|
||||
{
|
||||
const uint32_t alpha = 0x00abcdefu;
|
||||
auto [k0, k1] = dpf::make_dpf(alpha, uint32_t{7}, dpf::lt(uint64_t{3}));
|
||||
auto path0 = dpf::make_basic_path_memoizer(k0);
|
||||
auto path1 = dpf::make_basic_path_memoizer(k1);
|
||||
|
||||
const uint64_t mask = k0.cmp().mask;
|
||||
EXPECT_EQ(dpf::reconstruct(dpf::eval_point(dpf::cmp, k0, alpha, path0),
|
||||
dpf::eval_point(dpf::cmp, k1, alpha, path1))
|
||||
& mask,
|
||||
0u);
|
||||
EXPECT_EQ(dpf::reconstruct(
|
||||
dpf::eval_point(dpf::cmp, k0, alpha - 1u, path0),
|
||||
dpf::eval_point(dpf::cmp, k1, alpha - 1u, path1))
|
||||
& mask,
|
||||
3u);
|
||||
|
||||
EXPECT_EQ(recon(*dpf::eval_point(k0, alpha, path0),
|
||||
*dpf::eval_point(k1, alpha, path1)),
|
||||
7u);
|
||||
EXPECT_EQ(recon(*dpf::eval_point(k0, alpha ^ 1u, path0),
|
||||
*dpf::eval_point(k1, alpha ^ 1u, path1)),
|
||||
0u);
|
||||
|
||||
auto p0 = dpf::make_basic_path_memoizer(k0);
|
||||
auto p1 = dpf::make_basic_path_memoizer(k1);
|
||||
EXPECT_EQ(recon(*dpf::eval_point(k0, alpha, p0),
|
||||
*dpf::eval_point(k1, alpha, p1)),
|
||||
7u);
|
||||
EXPECT_EQ(dpf::reconstruct(dpf::eval_point(dpf::cmp, k0, alpha, p0),
|
||||
dpf::eval_point(dpf::cmp, k1, alpha, p1))
|
||||
& mask,
|
||||
0u);
|
||||
EXPECT_EQ(recon(*dpf::eval_point(k0, alpha ^ 2u, p0),
|
||||
*dpf::eval_point(k1, alpha ^ 2u, p1)),
|
||||
0u);
|
||||
}
|
||||
|
||||
TEST_F(IncrementalDpfTest, PathMemoizerResumeCmpOnlyFullWidth)
|
||||
{
|
||||
const uint32_t alpha = 0x80000001u;
|
||||
auto [k0, k1] = dpf::make_dpf(alpha, dpf::geq(uint64_t{5}, uint64_t{1}));
|
||||
auto path0 = dpf::make_basic_path_memoizer(k0);
|
||||
auto path1 = dpf::make_basic_path_memoizer(k1);
|
||||
const uint64_t mask = k0.cmp().mask;
|
||||
auto rq = [&](uint32_t q) {
|
||||
return dpf::reconstruct(dpf::eval_point(dpf::cmp, k0, q, path0),
|
||||
dpf::eval_point(dpf::cmp, k1, q, path1))
|
||||
& mask;
|
||||
};
|
||||
EXPECT_EQ(rq(alpha), 5u);
|
||||
EXPECT_EQ(rq(alpha), 5u);
|
||||
EXPECT_EQ(rq(alpha - 1u), 1u);
|
||||
EXPECT_EQ(rq(alpha + 1u), 5u);
|
||||
}
|
||||
|
||||
TEST_F(IncrementalDpfTest, EqMixedWithBlockedCmpSharesMemoizer)
|
||||
{
|
||||
const uint8_t alpha = 0x5au;
|
||||
auto [k0, k1] = dpf::make_dpf(alpha, dpf::eq(uint8_t{11}, uint8_t{2}),
|
||||
dpf::block_width<3>(dpf::lt(uint64_t{9}, uint64_t{1})));
|
||||
auto path0 = dpf::make_basic_path_memoizer(k0);
|
||||
auto path1 = dpf::make_basic_path_memoizer(k1);
|
||||
|
||||
EXPECT_EQ(recon(*dpf::eval_point(k0, alpha, path0),
|
||||
*dpf::eval_point(k1, alpha, path1)),
|
||||
11u);
|
||||
const uint64_t mask = k0.cmp().mask;
|
||||
EXPECT_EQ(dpf::reconstruct(
|
||||
dpf::eval_point(dpf::cmp, k0, uint8_t(alpha - 1), path0),
|
||||
dpf::eval_point(dpf::cmp, k1, uint8_t(alpha - 1), path1))
|
||||
& mask,
|
||||
9u);
|
||||
EXPECT_EQ(dpf::reconstruct(dpf::eval_point(dpf::cmp, k0, alpha, path0),
|
||||
dpf::eval_point(dpf::cmp, k1, alpha, path1))
|
||||
& mask,
|
||||
1u);
|
||||
|
||||
auto [f0, i0] = dpf::eval_full(k0);
|
||||
auto [f1, i1] = dpf::eval_full(k1);
|
||||
(void)i0;
|
||||
(void)i1;
|
||||
EXPECT_EQ(recon(f0[alpha], f1[alpha]), 11u);
|
||||
EXPECT_EQ(recon(f0[uint8_t(alpha ^ 1)], f1[uint8_t(alpha ^ 1)]), 2u);
|
||||
}
|
||||
|
||||
TEST_F(IncrementalDpfTest, IntervalMemoizerReuseAcrossEqAndShallowAt)
|
||||
{
|
||||
const uint32_t alpha = 0x00a1b2c3u;
|
||||
auto [k0, k1] = dpf::make_dpf(alpha, dpf::eq(uint32_t{8}, uint32_t{1}),
|
||||
dpf::at<8>(uint8_t{42}));
|
||||
using KT = std::decay_t<decltype(k0)>;
|
||||
constexpr auto opl0 = KT::template outputs_per_leaf_of<0>;
|
||||
|
||||
const uint32_t efrom = alpha & ~(opl0 - 1u);
|
||||
const uint32_t eto = efrom + static_cast<uint32_t>(opl0 - 1u);
|
||||
auto [f0, fi0] = dpf::eval_interval(dpf::out<0>, k0, efrom, eto);
|
||||
auto [f1, fi1] = dpf::eval_interval(dpf::out<0>, k1, efrom, eto);
|
||||
(void)fi0;
|
||||
(void)fi1;
|
||||
for (uint32_t q = efrom; q <= eto; ++q)
|
||||
{
|
||||
const uint32_t want = (q == alpha) ? 8u : 1u;
|
||||
EXPECT_EQ(recon(f0[q - efrom], f1[q - efrom]), want) << "eq q=" << q;
|
||||
}
|
||||
|
||||
const uint8_t top = static_cast<uint8_t>(alpha >> 24);
|
||||
auto [ait0, ai0] = dpf::eval_interval(dpf::out<1, 8>, k0, uint8_t{0},
|
||||
uint8_t{255});
|
||||
auto [ait1, ai1] = dpf::eval_interval(dpf::out<1, 8>, k1, uint8_t{0},
|
||||
uint8_t{255});
|
||||
(void)ai0;
|
||||
(void)ai1;
|
||||
EXPECT_EQ(recon(ait0[top], ait1[top]), 42u);
|
||||
EXPECT_EQ(recon(ait0[uint8_t(top ^ 1)], ait1[uint8_t(top ^ 1)]), 0u);
|
||||
|
||||
// Re-walk a different eq window after the shallow interval.
|
||||
const uint32_t from = alpha - 3u;
|
||||
const uint32_t to = alpha + 3u;
|
||||
const uint32_t afrom = from & ~(opl0 - 1u);
|
||||
const uint32_t ato = (to + opl0 - 1u) & ~(opl0 - 1u);
|
||||
const uint32_t ato_inclusive = ato + static_cast<uint32_t>(opl0 - 1u);
|
||||
auto [eit0, ee0] = dpf::eval_interval(dpf::out<0>, k0, afrom,
|
||||
ato_inclusive);
|
||||
auto [eit1, ee1] = dpf::eval_interval(dpf::out<0>, k1, afrom,
|
||||
ato_inclusive);
|
||||
(void)ee0;
|
||||
(void)ee1;
|
||||
for (uint32_t q = from; q <= to; ++q)
|
||||
{
|
||||
const uint32_t want = (q == alpha) ? 8u : 1u;
|
||||
EXPECT_EQ(recon(eit0[q - afrom], eit1[q - afrom]), want) << "q=" << q;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue