#include #include #include #include #include "asio.hpp" #define LIBDPF_HAS_ASIO #include "dpf.hpp" template void assign_leaf(DpfKey0 & dpf0, DpfKey1 & dpf1, const ConcreteT y_shr0, const ConcreteT y_shr1) { asio::io_context io_context; std::thread server([&io_context, &dpf0, &y_shr0]() { asio::ip::tcp::acceptor acceptor(io_context, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), 31337)); asio::ip::tcp::socket peer{io_context}; asio::ip::tcp::endpoint ep{}; acceptor.accept(peer, ep); auto future = dpf0.template async_assign_leaf(peer, y_shr0, asio::use_future); io_context.run(); future.wait(); }); std::thread client([&io_context, &dpf1, &y_shr1]() { asio::ip::tcp::socket peer(io_context); asio::ip::tcp::resolver resolver(io_context); asio::connect(peer, resolver.resolve("localhost", "31337")); auto future = dpf1.template async_assign_leaf(peer, y_shr1, asio::use_future); io_context.run(); future.wait(); }); client.join(); server.join(); } TEST(WildcardTest, SingleLeafFailEvalBeforeVernalization) { using input_type = uint8_t; using output_type = dpf::wildcard_value; using dpf_type = dpf::utils::dpf_type_t; input_type x = 0xAA; output_type y; auto [dpf0, dpf1] = dpf::make_dpf(x, y); ASSERT_THROW(dpf::eval_point(dpf0, x), std::runtime_error); ASSERT_THROW(dpf::eval_point(dpf1, x), std::runtime_error); input_type from = 0x33, to = 0xCC; ASSERT_THROW(dpf::eval_interval(dpf0, from, to), std::runtime_error); ASSERT_THROW(dpf::eval_interval(dpf1, from, to), std::runtime_error); ASSERT_THROW(dpf::eval_full(dpf0), std::runtime_error); ASSERT_THROW(dpf::eval_full(dpf1), std::runtime_error); std::array points{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; ASSERT_THROW(dpf::eval_sequence(dpf0, std::begin(points), std::end(points)), std::runtime_error); ASSERT_THROW(dpf::eval_sequence(dpf1, std::begin(points), std::end(points)), std::runtime_error); ASSERT_THROW(dpf::eval_sequence_breadth_first(dpf0, std::begin(points), std::end(points)), std::runtime_error); ASSERT_THROW(dpf::eval_sequence_breadth_first(dpf1, std::begin(points), std::end(points)), std::runtime_error); auto recipe0 = dpf::make_sequence_recipe(dpf0, std::begin(points), std::end(points)); auto recipe1 = dpf::make_sequence_recipe(dpf1, std::begin(points), std::end(points)); ASSERT_THROW(dpf::eval_sequence(dpf0, recipe0), std::runtime_error); ASSERT_THROW(dpf::eval_sequence(dpf1, recipe1), std::runtime_error); } TEST(WildcardTest, MultiLeafFailEvalBeforeVernalization) { using input_type = uint8_t; using output_type0 = uint32_t; using output_type1 = dpf::wildcard_value; using dpf_type = dpf::utils::dpf_type_t; input_type x = 0xAA; output_type0 y0 = 0xAAAAAAAA; output_type1 y1; auto [dpf0, dpf1] = dpf::make_dpf(x, y0, y1); ASSERT_NO_THROW((dpf::eval_point<0>(dpf0, x))); ASSERT_NO_THROW((dpf::eval_point<0>(dpf1, x))); ASSERT_THROW((dpf::eval_point<1>(dpf0, x)), std::runtime_error); ASSERT_THROW((dpf::eval_point<1>(dpf1, x)), std::runtime_error); ASSERT_THROW((dpf::eval_point<0, 1>(dpf0, x)), std::runtime_error); ASSERT_THROW((dpf::eval_point<0, 1>(dpf1, x)), std::runtime_error); input_type from = 0x33, to = 0xCC; ASSERT_NO_THROW((dpf::eval_interval<0>(dpf0, from, to))); ASSERT_NO_THROW((dpf::eval_interval<0>(dpf1, from, to))); ASSERT_THROW((dpf::eval_interval<1>(dpf0, from, to)), std::runtime_error); ASSERT_THROW((dpf::eval_interval<1>(dpf1, from, to)), std::runtime_error); ASSERT_THROW((dpf::eval_interval<0, 1>(dpf0, from, to)), std::runtime_error); ASSERT_THROW((dpf::eval_interval<0, 1>(dpf1, from, to)), std::runtime_error); ASSERT_NO_THROW((dpf::eval_full<0>(dpf0))); ASSERT_NO_THROW((dpf::eval_full<0>(dpf1))); ASSERT_THROW((dpf::eval_full<1>(dpf0)), std::runtime_error); ASSERT_THROW((dpf::eval_full<1>(dpf1)), std::runtime_error); ASSERT_THROW((dpf::eval_full<0, 1>(dpf0)), std::runtime_error); ASSERT_THROW((dpf::eval_full<0, 1>(dpf1)), std::runtime_error); std::array points{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; ASSERT_NO_THROW((dpf::eval_sequence<0>(dpf0, std::begin(points), std::end(points)))); ASSERT_NO_THROW((dpf::eval_sequence<0>(dpf1, std::begin(points), std::end(points)))); ASSERT_THROW((dpf::eval_sequence<1>(dpf0, std::begin(points), std::end(points))), std::runtime_error); ASSERT_THROW((dpf::eval_sequence<1>(dpf1, std::begin(points), std::end(points))), std::runtime_error); ASSERT_THROW((dpf::eval_sequence<0, 1>(dpf0, std::begin(points), std::end(points))), std::runtime_error); ASSERT_THROW((dpf::eval_sequence<0, 1>(dpf1, std::begin(points), std::end(points))), std::runtime_error); auto recipe0 = dpf::make_sequence_recipe(dpf0, std::begin(points), std::end(points)); auto recipe1 = dpf::make_sequence_recipe(dpf1, std::begin(points), std::end(points)); ASSERT_NO_THROW((dpf::eval_sequence<0>(dpf0, recipe0))); ASSERT_NO_THROW((dpf::eval_sequence<0>(dpf1, recipe1))); ASSERT_THROW((dpf::eval_sequence<1>(dpf0, recipe0)), std::runtime_error); ASSERT_THROW((dpf::eval_sequence<1>(dpf1, recipe1)), std::runtime_error); ASSERT_THROW((dpf::eval_sequence<0, 1>(dpf0, recipe0)), std::runtime_error); ASSERT_THROW((dpf::eval_sequence<0, 1>(dpf1, recipe1)), std::runtime_error); } TEST(WildcardTest, SingleLeafSuccess) { using input_type = uint8_t; using concrete_type = uint32_t; using output_type = dpf::wildcard_value; using dpf_type = dpf::utils::dpf_type_t; input_type x = 0xAA, from = 0x33, to = 0xCC; output_type y; concrete_type y_exp = 0xAAAAAAAA, y_shr0 = 0x12345678, y_shr1 = y_exp - y_shr0, zero_output = 0; auto [dpf0, dpf1] = dpf::make_dpf(x, y); assign_leaf(dpf0, dpf1, y_shr0, y_shr1); for (std::size_t i = std::numeric_limits::min(); i <= std::numeric_limits::max(); ++i) { auto y0 = *dpf::eval_point(dpf0, i); auto y1 = *dpf::eval_point(dpf1, i); if (i == x) { ASSERT_EQ(dpf::reconstruct(y0, y1), y_exp); } else { ASSERT_EQ(dpf::reconstruct(y0, y1), zero_output); } } auto [bufint0, iterint0] = dpf::eval_interval(dpf0, from, to); auto [bufint1, iterint1] = dpf::eval_interval(dpf1, from, to); auto itint0 = std::begin(iterint0); auto itint1 = std::begin(iterint1); for (std::size_t i = from; i <= to; ++i, ++itint0, ++itint1) { if (i == x) { ASSERT_EQ(dpf::reconstruct(*itint0, *itint1), y_exp); } else { ASSERT_EQ(dpf::reconstruct(*itint0, *itint1), zero_output); } } ASSERT_EQ(itint0, std::end(iterint0)); ASSERT_EQ(itint1, std::end(iterint1)); auto [bufful0, iterful0] = dpf::eval_full(dpf0); auto [bufful1, iterful1] = dpf::eval_full(dpf1); auto itful0 = std::begin(iterful0); auto itful1 = std::begin(iterful1); for (std::size_t i = std::numeric_limits::min(); i <= std::numeric_limits::max(); ++i, ++itful0, ++itful1) { if (i == x) { ASSERT_EQ(dpf::reconstruct(*itful0, *itful1), y_exp); } else { ASSERT_EQ(dpf::reconstruct(*itful0, *itful1), zero_output); } } ASSERT_EQ(itful0, std::end(iterful0)); ASSERT_EQ(itful1, std::end(iterful1)); std::array points{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; auto [bufseq0, iterseq0] = dpf::eval_sequence(dpf0, std::begin(points), std::end(points)); auto [bufseq1, iterseq1] = dpf::eval_sequence(dpf1, std::begin(points), std::end(points)); auto itseq0 = std::begin(iterseq0); auto itseq1 = std::begin(iterseq1); for (std::size_t i = 0; i < points.size(); ++i, ++itseq0, ++itseq1) { if (points[i] == x) { ASSERT_EQ(dpf::reconstruct(*itseq0, *itseq1), y_exp); } else { ASSERT_EQ(dpf::reconstruct(*itseq0, *itseq1), zero_output); } } ASSERT_EQ(itseq0, std::end(iterseq0)); ASSERT_EQ(itseq1, std::end(iterseq1)); auto [bufbre0, iterbre0] = dpf::eval_sequence_breadth_first(dpf0, std::begin(points), std::end(points)); auto [bufbre1, iterbre1] = dpf::eval_sequence_breadth_first(dpf1, std::begin(points), std::end(points)); auto itbre0 = std::begin(iterbre0); auto itbre1 = std::begin(iterbre1); for (std::size_t i = 0; i < points.size(); ++i, ++itbre0, ++itbre1) { if (points[i] == x) { ASSERT_EQ(dpf::reconstruct(*itbre0, *itbre1), y_exp); } else { ASSERT_EQ(dpf::reconstruct(*itbre0, *itbre1), zero_output); } } ASSERT_EQ(itbre0, std::end(iterbre0)); ASSERT_EQ(itbre1, std::end(iterbre1)); auto recipe0 = dpf::make_sequence_recipe(dpf0, std::begin(points), std::end(points)); auto recipe1 = dpf::make_sequence_recipe(dpf1, std::begin(points), std::end(points)); auto [bufrec0, iterrec0] = dpf::eval_sequence(dpf0, recipe0); auto [bufrec1, iterrec1] = dpf::eval_sequence(dpf1, recipe1); auto itrec0 = std::begin(iterrec0); auto itrec1 = std::begin(iterrec1); for (std::size_t i = 0; i < points.size(); ++i, ++itrec0, ++itrec1) { if (points[i] == x) { ASSERT_EQ(dpf::reconstruct(*itrec0, *itrec1), y_exp); } else { ASSERT_EQ(dpf::reconstruct(*itrec0, *itrec1), zero_output); } } ASSERT_EQ(itrec0, std::end(iterrec0)); ASSERT_EQ(itrec1, std::end(iterrec1)); } TEST(WildcardTest, MultiLeafSuccess) { using input_type = uint8_t; using output_type0 = uint32_t; using concrete_type = uint32_t; using output_type1 = dpf::wildcard_value; using dpf_type = dpf::utils::dpf_type_t; input_type x = 0xAA, from = 0x33, to = 0xCC; output_type0 y0 = 0x55555555, zero_output0 = 0; output_type1 y1; concrete_type y_exp = 0xAAAAAAAA, y_shr0 = 0x12345678, y_shr1 = y_exp - y_shr0, zero_output1 = 0; auto [dpf0, dpf1] = dpf::make_dpf(x, y0, y1); for (std::size_t i = std::numeric_limits::min(); i <= std::numeric_limits::max(); ++i) { auto ypoi0 = *dpf::eval_point<0>(dpf0, i); auto ypoi1 = *dpf::eval_point<0>(dpf1, i); if (i == x) { ASSERT_EQ(dpf::reconstruct(ypoi0, ypoi1), y0); } else { ASSERT_EQ(dpf::reconstruct(ypoi0, ypoi1), zero_output0); } } auto [bufint00, iterint00] = dpf::eval_interval<0>(dpf0, from, to); auto [bufint01, iterint01] = dpf::eval_interval<0>(dpf1, from, to); auto itint00 = std::begin(iterint00); auto itint01 = std::begin(iterint01); for (std::size_t i = from; i <= to; ++i, ++itint00, ++itint01) { if (i == x) { ASSERT_EQ(dpf::reconstruct(*itint00, *itint01), y0); } else { ASSERT_EQ(dpf::reconstruct(*itint00, *itint01), zero_output0); } } ASSERT_EQ(itint00, std::end(iterint00)); ASSERT_EQ(itint01, std::end(iterint01)); auto [bufful00, iterful00] = dpf::eval_full<0>(dpf0); auto [bufful01, iterful01] = dpf::eval_full<0>(dpf1); auto itful00 = std::begin(iterful00); auto itful01 = std::begin(iterful01); for (std::size_t i = std::numeric_limits::min(); i <= std::numeric_limits::max(); ++i, ++itful00, ++itful01) { if (i == x) { ASSERT_EQ(dpf::reconstruct(*itful00, *itful01), y0); } else { ASSERT_EQ(dpf::reconstruct(*itful00, *itful01), zero_output0); } } ASSERT_EQ(itful00, std::end(iterful00)); ASSERT_EQ(itful01, std::end(iterful01)); std::array points{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; auto [bufseq00, iterseq00] = dpf::eval_sequence<0>(dpf0, std::begin(points), std::end(points)); auto [bufseq01, iterseq01] = dpf::eval_sequence<0>(dpf1, std::begin(points), std::end(points)); auto itseq00 = std::begin(iterseq00); auto itseq01 = std::begin(iterseq01); for (std::size_t i = 0; i < points.size(); ++i, ++itseq00, ++itseq01) { if (points[i] == x) { ASSERT_EQ(dpf::reconstruct(*itseq00, *itseq01), y0); } else { ASSERT_EQ(dpf::reconstruct(*itseq00, *itseq01), zero_output0); } } ASSERT_EQ(itseq00, std::end(iterseq00)); ASSERT_EQ(itseq01, std::end(iterseq01)); auto [bufbre00, iterbre00] = dpf::eval_sequence_breadth_first<0>(dpf0, std::begin(points), std::end(points)); auto [bufbre01, iterbre01] = dpf::eval_sequence_breadth_first<0>(dpf1, std::begin(points), std::end(points)); auto itbre00 = std::begin(iterbre00); auto itbre01 = std::begin(iterbre01); for (std::size_t i = 0; i < points.size(); ++i, ++itbre00, ++itbre01) { if (points[i] == x) { ASSERT_EQ(dpf::reconstruct(*itbre00, *itbre01), y0); } else { ASSERT_EQ(dpf::reconstruct(*itbre00, *itbre01), zero_output0); } } ASSERT_EQ(itbre00, std::end(iterbre00)); ASSERT_EQ(itbre01, std::end(iterbre01)); auto recipe0 = dpf::make_sequence_recipe(dpf0, std::begin(points), std::end(points)); auto recipe1 = dpf::make_sequence_recipe(dpf1, std::begin(points), std::end(points)); auto [bufrec00, iterrec00] = dpf::eval_sequence<0>(dpf0, recipe0); auto [bufrec01, iterrec01] = dpf::eval_sequence<0>(dpf1, recipe1); auto itrec00 = std::begin(iterrec00); auto itrec01 = std::begin(iterrec01); for (std::size_t i = 0; i < points.size(); ++i, ++itrec00, ++itrec01) { if (points[i] == x) { ASSERT_EQ(dpf::reconstruct(*itrec00, *itrec01), y0); } else { ASSERT_EQ(dpf::reconstruct(*itrec00, *itrec01), zero_output0); } } ASSERT_EQ(itrec00, std::end(iterrec00)); ASSERT_EQ(itrec01, std::end(iterrec01)); assign_leaf<1>(dpf0, dpf1, y_shr0, y_shr1); for (std::size_t i = std::numeric_limits::min(); i <= std::numeric_limits::max(); ++i) { auto ypoi0 = *dpf::eval_point<1>(dpf0, i); auto ypoi1 = *dpf::eval_point<1>(dpf1, i); if (i == x) { ASSERT_EQ(dpf::reconstruct(ypoi0, ypoi1), y_exp); } else { ASSERT_EQ(dpf::reconstruct(ypoi0, ypoi1), zero_output1); } } auto [bufint10, iterint10] = dpf::eval_interval<1>(dpf0, from, to); auto [bufint11, iterint11] = dpf::eval_interval<1>(dpf1, from, to); auto itint10 = std::begin(iterint10); auto itint11 = std::begin(iterint11); for (std::size_t i = from; i <= to; ++i, ++itint10, ++itint11) { if (i == x) { ASSERT_EQ(dpf::reconstruct(*itint10, *itint11), y_exp); } else { ASSERT_EQ(dpf::reconstruct(*itint10, *itint11), zero_output1); } } ASSERT_EQ(itint10, std::end(iterint10)); ASSERT_EQ(itint11, std::end(iterint11)); auto [bufful10, iterful10] = dpf::eval_full<1>(dpf0); auto [bufful11, iterful11] = dpf::eval_full<1>(dpf1); auto itful10 = std::begin(iterful10); auto itful11 = std::begin(iterful11); for (std::size_t i = std::numeric_limits::min(); i <= std::numeric_limits::max(); ++i, ++itful10, ++itful11) { if (i == x) { ASSERT_EQ(dpf::reconstruct(*itful10, *itful11), y_exp); } else { ASSERT_EQ(dpf::reconstruct(*itful10, *itful11), zero_output1); } } ASSERT_EQ(itful10, std::end(iterful10)); ASSERT_EQ(itful11, std::end(iterful11)); auto [bufseq10, iterseq10] = dpf::eval_sequence<1>(dpf0, std::begin(points), std::end(points)); auto [bufseq11, iterseq11] = dpf::eval_sequence<1>(dpf1, std::begin(points), std::end(points)); auto itseq10 = std::begin(iterseq10); auto itseq11 = std::begin(iterseq11); for (std::size_t i = 0; i < points.size(); ++i, ++itseq10, ++itseq11) { if (points[i] == x) { ASSERT_EQ(dpf::reconstruct(*itseq10, *itseq11), y_exp); } else { ASSERT_EQ(dpf::reconstruct(*itseq10, *itseq11), zero_output1); } } ASSERT_EQ(itseq10, std::end(iterseq10)); ASSERT_EQ(itseq11, std::end(iterseq11)); auto [bufbre10, iterbre10] = dpf::eval_sequence_breadth_first<1>(dpf0, std::begin(points), std::end(points)); auto [bufbre11, iterbre11] = dpf::eval_sequence_breadth_first<1>(dpf1, std::begin(points), std::end(points)); auto itbre10 = std::begin(iterbre10); auto itbre11 = std::begin(iterbre11); for (std::size_t i = 0; i < points.size(); ++i, ++itbre10, ++itbre11) { if (points[i] == x) { ASSERT_EQ(dpf::reconstruct(*itbre10, *itbre11), y_exp); } else { ASSERT_EQ(dpf::reconstruct(*itbre10, *itbre11), zero_output1); } } ASSERT_EQ(itbre10, std::end(iterbre10)); ASSERT_EQ(itbre11, std::end(iterbre11)); auto [bufrec10, iterrec10] = dpf::eval_sequence<1>(dpf0, recipe0); auto [bufrec11, iterrec11] = dpf::eval_sequence<1>(dpf1, recipe1); auto itrec10 = std::begin(iterrec10); auto itrec11 = std::begin(iterrec11); for (std::size_t i = 0; i < points.size(); ++i, ++itrec10, ++itrec11) { if (points[i] == x) { ASSERT_EQ(dpf::reconstruct(*itrec10, *itrec11), y_exp); } else { ASSERT_EQ(dpf::reconstruct(*itrec10, *itrec11), zero_output1); } } ASSERT_EQ(itrec10, std::end(iterrec10)); ASSERT_EQ(itrec11, std::end(iterrec11)); for (std::size_t i = std::numeric_limits::min(); i <= std::numeric_limits::max(); ++i) { auto ypoi0 = dpf::eval_point<0, 1>(dpf0, i); auto ypoi1 = dpf::eval_point<0, 1>(dpf1, i); if (i == x) { ASSERT_EQ(static_cast(dpf::reconstruct(std::get<0>(ypoi0), std::get<0>(ypoi1))), y0); ASSERT_EQ(static_cast(dpf::reconstruct(std::get<1>(ypoi0), std::get<1>(ypoi1))), y_exp); } else { ASSERT_EQ(static_cast(dpf::reconstruct(std::get<0>(ypoi0), std::get<0>(ypoi1))), zero_output0); ASSERT_EQ(static_cast(dpf::reconstruct(std::get<1>(ypoi0), std::get<1>(ypoi1))), zero_output1); } } auto [bufintmul0, iterintmul0] = dpf::eval_interval<0, 1>(dpf0, from, to); auto [bufintmul1, iterintmul1] = dpf::eval_interval<0, 1>(dpf1, from, to); auto zipint0 = dpf::tuple_as_zip(iterintmul0); auto zipint1 = dpf::tuple_as_zip(iterintmul1); auto itintmul0 = std::begin(zipint0); auto itintmul1 = std::begin(zipint1); for (std::size_t i = from; i <= to; ++i, ++itintmul0, ++itintmul1) { if (i == x) { ASSERT_EQ(dpf::reconstruct(std::get<0>(*itintmul0), std::get<0>(*itintmul1)), y0); ASSERT_EQ(dpf::reconstruct(std::get<1>(*itintmul0), std::get<1>(*itintmul1)), y_exp); } else { ASSERT_EQ(dpf::reconstruct(std::get<0>(*itintmul0), std::get<0>(*itintmul1)), zero_output0); ASSERT_EQ(dpf::reconstruct(std::get<1>(*itintmul0), std::get<1>(*itintmul1)), zero_output1); } } ASSERT_EQ(itintmul0, std::end(zipint0)); ASSERT_EQ(itintmul1, std::end(zipint1)); auto [buffulmul0, iterfulmul0] = dpf::eval_full<0, 1>(dpf0); auto [buffulmul1, iterfulmul1] = dpf::eval_full<0, 1>(dpf1); auto zipful0 = dpf::tuple_as_zip(iterfulmul0); auto zipful1 = dpf::tuple_as_zip(iterfulmul1); auto itfulmul0 = std::begin(zipful0); auto itfulmul1 = std::begin(zipful1); for (std::size_t i = std::numeric_limits::min(); i <= std::numeric_limits::max(); ++i, ++itfulmul0, ++itfulmul1) { if (i == x) { ASSERT_EQ(dpf::reconstruct(std::get<0>(*itfulmul0), std::get<0>(*itfulmul1)), y0); ASSERT_EQ(dpf::reconstruct(std::get<1>(*itfulmul0), std::get<1>(*itfulmul1)), y_exp); } else { ASSERT_EQ(dpf::reconstruct(std::get<0>(*itfulmul0), std::get<0>(*itfulmul1)), zero_output0); ASSERT_EQ(dpf::reconstruct(std::get<1>(*itfulmul0), std::get<1>(*itfulmul1)), zero_output1); } } ASSERT_EQ(itfulmul0, std::end(zipful0)); ASSERT_EQ(itfulmul1, std::end(zipful1)); auto [bufseqmul0, iterseqmul0] = dpf::eval_sequence<0, 1>(dpf0, std::begin(points), std::end(points)); auto [bufseqmul1, iterseqmul1] = dpf::eval_sequence<0, 1>(dpf1, std::begin(points), std::end(points)); auto zipseq0 = dpf::tuple_as_zip(iterseqmul0); auto zipseq1 = dpf::tuple_as_zip(iterseqmul1); auto itseqmul0 = std::begin(zipseq0); auto itseqmul1 = std::begin(zipseq1); for (std::size_t i = 0; i < points.size(); ++i, ++itseqmul0, ++itseqmul1) { if (points[i] == x) { ASSERT_EQ(dpf::reconstruct(std::get<0>(*itseqmul0), std::get<0>(*itseqmul1)), y0); ASSERT_EQ(dpf::reconstruct(std::get<1>(*itseqmul0), std::get<1>(*itseqmul1)), y_exp); } else { ASSERT_EQ(dpf::reconstruct(std::get<0>(*itseqmul0), std::get<0>(*itseqmul1)), zero_output0); ASSERT_EQ(dpf::reconstruct(std::get<1>(*itseqmul0), std::get<1>(*itseqmul1)), zero_output1); } } ASSERT_EQ(itseqmul0, std::end(zipseq0)); ASSERT_EQ(itseqmul1, std::end(zipseq1)); auto [bufrecmul0, iterrecmul0] = dpf::eval_sequence<0, 1>(dpf0, recipe0); auto [bufrecmul1, iterrecmul1] = dpf::eval_sequence<0, 1>(dpf1, recipe1); auto ziprec0 = dpf::tuple_as_zip(iterrecmul0); auto ziprec1 = dpf::tuple_as_zip(iterrecmul1); auto itrecmul0 = std::begin(ziprec0); auto itrecmul1 = std::begin(ziprec1); for (std::size_t i = 0; i < points.size(); ++i, ++itrecmul0, ++itrecmul1) { if (points[i] == x) { ASSERT_EQ(dpf::reconstruct(std::get<0>(*itrecmul0), std::get<0>(*itrecmul1)), y0); ASSERT_EQ(dpf::reconstruct(std::get<1>(*itrecmul0), std::get<1>(*itrecmul1)), y_exp); } else { ASSERT_EQ(dpf::reconstruct(std::get<0>(*itrecmul0), std::get<0>(*itrecmul1)), zero_output0); ASSERT_EQ(dpf::reconstruct(std::get<1>(*itrecmul0), std::get<1>(*itrecmul1)), zero_output1); } } ASSERT_EQ(itrecmul0, std::end(ziprec0)); ASSERT_EQ(itrecmul1, std::end(ziprec1)); } TEST(WildcardTest, PackedSmallWildcardsAtNonTerminalAssignAll) { // Four packed uint8 wildcards at a non-terminal prefix, plus a concrete // deepest output. Assign every wildcard leaf through the asio path. using concrete_t = uint8_t; dpf::wildcard_value w; uint16_t x = 0x55aa; auto [dpf0, dpf1] = dpf::make_dpf(x, dpf::at<10>(w, w, w, w), uint16_t{99}); ASSERT_THROW(dpf::eval_point(dpf::out<0, 10>, dpf0, x), std::runtime_error); ASSERT_EQ(static_cast( dpf::reconstruct(*dpf::eval_point(dpf::out<4>, dpf0, x), *dpf::eval_point(dpf::out<4>, dpf1, x))), uint16_t{99}); const concrete_t want[4] = {1, 2, 3, 4}; const concrete_t shr0[4] = {10, 20, 30, 40}; assign_leaf<0>(dpf0, dpf1, shr0[0], static_cast(want[0] - shr0[0])); assign_leaf<1>(dpf0, dpf1, shr0[1], static_cast(want[1] - shr0[1])); assign_leaf<2>(dpf0, dpf1, shr0[2], static_cast(want[2] - shr0[2])); assign_leaf<3>(dpf0, dpf1, shr0[3], static_cast(want[3] - shr0[3])); ASSERT_EQ(static_cast( dpf::reconstruct(*dpf::eval_point(dpf::out<0, 10>, dpf0, x), *dpf::eval_point(dpf::out<0, 10>, dpf1, x))), want[0]); ASSERT_EQ(static_cast( dpf::reconstruct(*dpf::eval_point(dpf::out<3, 10>, dpf0, x), *dpf::eval_point(dpf::out<3, 10>, dpf1, x))), want[3]); ASSERT_EQ(static_cast( dpf::reconstruct( *dpf::eval_point(dpf::out<0, 10>, dpf0, static_cast(x ^ (1u << 6))), *dpf::eval_point(dpf::out<0, 10>, dpf1, static_cast(x ^ (1u << 6))))), concrete_t{0}); } namespace { template T bits_as(Bits bits) { T out{}; std::memcpy(&out, &bits, sizeof(T)); return out; } template auto bits_of(T v) { using bits_t = std::conditional_t; bits_t bits{}; std::memcpy(&bits, &v, sizeof(T)); return bits; } /// In-process assign for a wildcard leaf (same messages as asio, no socket). template void assign_leaf_local(DpfKey0 & dpf0, DpfKey1 & dpf1, const ShareT & shr0, const ShareT & shr1) { auto & w0 = std::get(dpf0.leaf_nodes); auto & w1 = std::get(dpf1.leaf_nodes); if (w0.is_ready()) w0.begin_update(); if (w1.is_ready()) w1.begin_update(); const auto b0 = w0.compute_and_get_blinded_output_share(shr0); const auto b1 = w1.compute_and_get_blinded_output_share(shr1); const auto l0 = w0.compute_and_get_leaf_share(b1); const auto l1 = w1.compute_and_get_leaf_share(b0); w0.reconstruct_correction_word(l1); w1.reconstruct_correction_word(l0); } } // namespace TEST(WildcardTest, FullWidthXorDoesNotRevealBeta) { using input_type = uint8_t; using concrete_type = dpf::xints::xint128_t; using output_type = dpf::wildcard_value; input_type x = 0x42; output_type y; auto [dpf0, dpf1] = dpf::make_dpf(x, y); auto & w0 = std::get<0>(dpf0.leaf_nodes); auto & w1 = std::get<0>(dpf1.leaf_nodes); // Scale Beaver must be planted even for a single full-width XOR lane. EXPECT_EQ((dpf::outputs_per_leaf_v::exterior_node>), 1u); EXPECT_TRUE(dpf::utils::has_characteristic_two_v); concrete_type y_exp = concrete_type{0x0123456789ABCDEFull}; concrete_type y_shr0 = concrete_type{0x1111111111111111ull}; concrete_type y_shr1 = y_exp + y_shr0; // XOR group const auto blinded0 = w0.compute_and_get_blinded_output_share(y_shr0); const auto blinded1 = w1.compute_and_get_blinded_output_share(y_shr1); // With a non-trivial output blind, the exchanged value is not the share. EXPECT_NE(blinded0, y_shr0); EXPECT_NE(blinded1, y_shr1); // Finish the assign after the privacy check above (state is already blinded). const auto l0 = w0.compute_and_get_leaf_share(blinded1); const auto l1 = w1.compute_and_get_leaf_share(blinded0); w0.reconstruct_correction_word(l1); w1.reconstruct_correction_word(l0); EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(dpf0, x), *dpf::eval_point(dpf1, x)), y_exp); EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(dpf0, static_cast(x ^ 1)), *dpf::eval_point(dpf1, static_cast(x ^ 1))), concrete_type{}); } TEST(WildcardTest, FloatWildcardRoundTrip) { using input_type = uint8_t; using concrete_type = float; using output_type = dpf::wildcard_value; input_type x = 0x55; output_type y; auto [dpf0, dpf1] = dpf::make_dpf(x, y); const concrete_type y_exp = 3.14159265f; const auto y_bits = bits_of(y_exp); const std::uint32_t shr0_bits = 0xA5A5A5A5u; const concrete_type y_shr0 = bits_as(shr0_bits); const concrete_type y_shr1 = bits_as(y_bits ^ shr0_bits); assign_leaf_local(dpf0, dpf1, y_shr0, y_shr1); const auto got = dpf::reconstruct(*dpf::eval_point(dpf0, x), *dpf::eval_point(dpf1, x)); EXPECT_EQ(bits_of(got), y_bits); EXPECT_EQ(bits_of(dpf::reconstruct( *dpf::eval_point(dpf0, static_cast(x ^ 1)), *dpf::eval_point(dpf1, static_cast(x ^ 1)))), 0u); } TEST(WildcardTest, SecondAssignUpdatesPayload) { using input_type = uint8_t; using concrete_type = uint32_t; using output_type = dpf::wildcard_value; input_type x = 0x11; auto [dpf0, dpf1] = dpf::make_dpf(x, output_type{}); const concrete_type beta = 0xAAAAAAAAu; const concrete_type beta2 = 0xBBBBBBBBu; const concrete_type s0 = 0x12345678u; assign_leaf_local(dpf0, dpf1, s0, static_cast(beta - s0)); EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(dpf0, x), *dpf::eval_point(dpf1, x)), beta); // Second assign installs (beta2 - beta) on top of the ready leaf. const concrete_type delta = static_cast(beta2 - beta); const concrete_type d0 = 0x01010101u; assign_leaf_local(dpf0, dpf1, d0, static_cast(delta - d0)); EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(dpf0, x), *dpf::eval_point(dpf1, x)), beta2); } TEST(WildcardTest, AssignWildcardInputOpensPublicShiftNotAlpha) { using input_type = uint8_t; using output_type = uint32_t; // Wildcard domain: dealer plants a random mask; parties later open (mask - alpha). auto [dpf0, dpf1] = dpf::make_dpf(dpf::wildcard_value{}, output_type{7}); const input_type mask = static_cast( dpf0.offset_x.raw() + dpf1.offset_x.raw()); const input_type alpha = 0xAAu; const input_type a0 = 0x12u; const input_type a1 = static_cast(alpha - a0); const auto sh0 = dpf0.offset_x.compute_and_get_share(a0); const auto sh1 = dpf1.offset_x.compute_and_get_share(a1); const auto open0 = dpf0.offset_x.reconstruct(sh1); const auto open1 = dpf1.offset_x.reconstruct(sh0); EXPECT_EQ(open0, open1); const input_type want_shift = static_cast(mask - alpha); EXPECT_EQ(open0, want_shift); EXPECT_NE(open0, alpha); // public value is the shift, not alpha EXPECT_EQ(static_cast(open0 + alpha), mask); EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(dpf0, alpha), *dpf::eval_point(dpf1, alpha)), output_type{7}); EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(dpf0, static_cast(alpha ^ 1)), *dpf::eval_point(dpf1, static_cast(alpha ^ 1))), output_type{0}); } TEST(WildcardTest, UpdatableTagAssignsThenRewrites) { const std::uint8_t alpha = 0x2a; auto [k0, k1] = dpf::make_dpf(alpha, std::uint64_t{7}, dpf::updatable{}); EXPECT_TRUE(k0.is_wildcard(0)); EXPECT_TRUE(k1.is_wildcard(0)); EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(k0, alpha), *dpf::eval_point(k1, alpha)), std::uint64_t{7}); EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(k0, std::uint8_t{0}), *dpf::eval_point(k1, std::uint8_t{0})), std::uint64_t{0}); auto & w0 = std::get<0>(k0.leaf_nodes); auto & w1 = std::get<0>(k1.leaf_nodes); w0.begin_update(); w1.begin_update(); // A second assign installs the difference β' − β, not the new absolute payload. const auto shares = dpf::additively_share(std::uint64_t{9} - std::uint64_t{7}); const auto b0 = w0.compute_and_get_blinded_output_share(shares.first.raw()); const auto b1 = w1.compute_and_get_blinded_output_share(shares.second.raw()); const auto l0 = w0.compute_and_get_leaf_share(b1); const auto l1 = w1.compute_and_get_leaf_share(b0); w0.reconstruct_correction_word(l1); w1.reconstruct_correction_word(l0); EXPECT_EQ(dpf::reconstruct(*dpf::eval_point(k0, alpha), *dpf::eval_point(k1, alpha)), std::uint64_t{9}); }