libdpf/party/flows_gadget.cpp

643 lines
24 KiB
C++
Raw Permalink Normal View History

/// @file party/flows_gadget.cpp
/// @brief Bench flows for word garbling, stacked Yao, FLUTE, and hidden shuffle.
/// @details Amalgamated into run.cpp. Every cross-party byte goes through
/// `net::trio` (the same local mesh `party_bench` already dials).
/// Two-party gadgets use the p0–p1 link. FLUTE's dealer is p2.
/// The hidden shuffle is the three ring passes, and the receiver
/// feeds the arrived array into the next party step.
#include "cases.hpp"
#include <cstdint>
#include <cstring>
#include <stdexcept>
#include <vector>
#include "dpf/arith_garble.hpp"
#include "dpf/flute.hpp"
#include "dpf/shuffle.hpp"
#include "dpf/yao.hpp"
#include "dpf/yao_stack.hpp"
namespace dpf
{
namespace party
{
namespace gadget
{
using net::role;
using net::trio;
void require(bool cond, const char * msg)
{
if (!cond)
throw std::runtime_error(msg);
}
void put_lab(std::vector<std::uint16_t> & blob, const arith_garble::lab & lab)
{
blob.insert(blob.end(), lab.d.begin(), lab.d.end());
}
arith_garble::lab take_lab(const std::uint16_t *& p, const std::uint16_t * end,
std::uint16_t mod)
{
if (static_cast<std::size_t>(end - p) < arith_garble::k_digits)
throw std::runtime_error("arith garble: short label");
arith_garble::lab lab;
lab.mod = mod;
for (std::size_t i = 0; i < arith_garble::k_digits; ++i)
lab.d[i] = *p++;
return lab;
}
std::uint16_t take_u16(const std::uint16_t *& p, const std::uint16_t * end)
{
if (p == end)
throw std::runtime_error("arith garble: short frame");
return *p++;
}
/// @brief p0 garbles and ships the evaluator view. p1 evaluates that view.
void arith_exchange(trio & net, role self, const arith_garble::circuit & c,
const std::uint16_t * semantic)
{
const auto plain = arith_garble::eval_plain(c, semantic);
if (self == role::p0)
{
auto st = arith_garble::detail::garble(c);
auto active = arith_garble::detail::evaluate(c, st, semantic);
std::size_t out_i = 0;
for (std::uint32_t id : c.outputs())
{
const std::uint16_t mod = c.nodes()[id].mod;
const std::uint16_t got = arith_garble::open_shares(
mod, st.zero[id].d[0], active[id].d[0]);
require(got == plain[id], "arith garble: garbler open");
++out_i;
}
require(out_i == c.outputs().size(), "arith garble: outputs");
std::vector<std::uint16_t> blob;
const auto & nodes = c.nodes();
for (std::uint32_t i = 0; i < nodes.size(); ++i)
{
const auto & nd = nodes[i];
if (nd.code == arith_garble::circuit::op::in)
put_lab(blob, active[i]);
else if (nd.code == arith_garble::circuit::op::proj)
{
for (const auto & row : st.proj[i].row)
put_lab(blob, row);
}
else if (nd.code == arith_garble::circuit::op::pass)
{
put_lab(blob, st.pass[i].payload[0]);
put_lab(blob, st.pass[i].payload[1]);
blob.push_back(st.pass[i].flag_ct[0]);
blob.push_back(st.pass[i].flag_ct[1]);
}
}
for (std::uint32_t id : c.outputs())
blob.push_back(st.zero[id].d[0]);
net.send_vec_to(role::p1, net::msg::bytes, blob);
return;
}
require(self == role::p1, "arith garble: party");
auto blob = net.recv_vec_from<std::uint16_t>(role::p0, net::msg::bytes);
const std::uint16_t * p = blob.data();
const std::uint16_t * end = p + blob.size();
arith_garble::detail::garble_state st;
const auto & nodes = c.nodes();
st.zero.resize(nodes.size());
st.delta.assign(static_cast<std::size_t>(arith_garble::k_max_mod) + 1,
arith_garble::lab{});
for (std::uint16_t m = 1; m <= arith_garble::k_max_mod; ++m)
st.delta[m].mod = m;
st.proj.resize(nodes.size());
st.pass.resize(nodes.size());
std::size_t n_in = 0;
for (const auto & nd : nodes)
if (nd.code == arith_garble::circuit::op::in)
++n_in;
for (std::uint32_t i = 0; i < nodes.size(); ++i)
{
const auto & nd = nodes[i];
if (nd.code == arith_garble::circuit::op::in)
st.zero[i] = take_lab(p, end, nd.mod);
else if (nd.code == arith_garble::circuit::op::proj)
{
const std::uint16_t m = nodes[nd.a].mod;
require(m > 0, "arith garble: projection modulus");
st.proj[i].row.resize(static_cast<std::size_t>(m - 1));
for (std::uint16_t color = 1; color < m; ++color)
st.proj[i].row[static_cast<std::size_t>(color - 1)] =
take_lab(p, end, nd.mod);
}
else if (nd.code == arith_garble::circuit::op::pass)
{
st.pass[i].payload[0] = take_lab(p, end, nd.mod);
st.pass[i].payload[1] = take_lab(p, end, nd.mod);
st.pass[i].flag_ct[0] = take_u16(p, end);
st.pass[i].flag_ct[1] = take_u16(p, end);
}
}
std::vector<std::uint16_t> masks;
masks.reserve(c.outputs().size());
for (std::size_t i = 0; i < c.outputs().size(); ++i)
masks.push_back(take_u16(p, end));
require(p == end, "arith garble: trailing bytes");
std::vector<std::uint16_t> zeros(n_in, 0);
auto active = arith_garble::detail::evaluate(c, st, zeros.data());
std::size_t out_i = 0;
for (std::uint32_t id : c.outputs())
{
const std::uint16_t mod = nodes[id].mod;
const std::uint16_t got = arith_garble::open_shares(
mod, masks[out_i], active[id].d[0]);
require(got == plain[id], "arith garble: evaluator open");
++out_i;
}
}
int run_proj(role self, trio & net, std::uint16_t mod)
{
if (self == role::p2)
return 0;
arith_garble::circuit c;
auto x = c.input(mod);
std::vector<std::uint16_t> phi(mod);
for (std::uint16_t i = 0; i < mod; ++i)
phi[i] = static_cast<std::uint16_t>((i * 3) % mod);
c.out(c.project(x, mod, std::move(phi)));
const std::uint16_t in = static_cast<std::uint16_t>(mod / 2);
arith_exchange(net, self, c, &in);
return 0;
}
int run_mul(role self, trio & net, std::uint16_t p)
{
if (self == role::p2)
return 0;
arith_garble::circuit c;
auto x = c.input(p);
auto y = c.input(p);
c.out(c.mul(x, y));
const std::uint16_t in[2] = {static_cast<std::uint16_t>(p - 1), 2};
arith_exchange(net, self, c, in);
return 0;
}
int run_thresh(role self, trio & net, unsigned b)
{
if (self == role::p2)
return 0;
arith_garble::circuit c;
const auto mod = static_cast<std::uint16_t>(b + 1);
std::vector<arith_garble::wire> bits;
bits.reserve(b);
for (unsigned i = 0; i < b; ++i)
bits.push_back(c.input(mod));
c.out(c.threshold(bits, static_cast<std::uint16_t>(b / 2)));
std::vector<std::uint16_t> in(b, 0);
for (unsigned i = 0; i < b; i += 2)
in[i] = 1;
arith_exchange(net, self, c, in.data());
return 0;
}
int run_chain(role self, trio & net)
{
if (self == role::p2)
return 0;
arith_garble::circuit c;
auto x = c.input(7);
auto y = c.input(7);
auto z = c.mul(x, y);
for (int i = 0; i < 3; ++i)
z = c.mul(z, x);
c.out(z);
const std::uint16_t in[2] = {2, 3};
arith_exchange(net, self, c, in);
return 0;
}
yao::netlist and_n(unsigned n)
{
yao::netlist nl;
std::vector<yao::bit> in;
in.reserve(n);
for (unsigned i = 0; i < n; ++i)
in.push_back(nl.shared_in());
auto acc = in[0];
for (unsigned i = 1; i < n; ++i)
acc = nl.and_(acc, in[i]);
nl.out(acc);
return nl;
}
yao::netlist xor2()
{
yao::netlist nl;
auto a = nl.shared_in();
auto b = nl.shared_in();
nl.out(nl.xor_(a, b));
return nl;
}
/// @brief Garbled tables and the evaluator's labels on the p0–p1 channel.
/// @details Party 1 runs `eval_party1` on the bytes that arrived. The choice
/// labels are the OT output for party 1's bits; base OT is its own
/// bench (`iknp`), not this garble row.
void yao_ship(trio & net, role self, const yao::netlist & nl,
const std::uint8_t * p0_bits, const std::uint8_t * p1_bits)
{
const role peer = self == role::p0 ? role::p1 : role::p0;
auto & link = net.to(peer);
std::size_t nchoice = 0;
std::size_t ndirect = 0;
for (std::uint32_t i = 0; i < nl.n_in(); ++i)
{
const auto kind = nl.kind_at(i);
if (kind == yao::input::shared || kind == yao::input::priv1)
++nchoice;
if (kind == yao::input::shared || kind == yao::input::priv0)
++ndirect;
}
const std::size_t ntable = static_cast<std::size_t>(nl.n_and()) * 2u;
std::vector<std::uint8_t> share;
if (self == role::p0)
{
auto g = yao::detail::garble_party0(nl, p0_bits);
require(g.tables.size() == ntable, "yao: table size");
require(g.direct.size() == ndirect, "yao: direct labels");
require(g.ot0.size() == nchoice && g.ot1.size() == nchoice, "yao: ot labels");
std::vector<yao::block> chosen(nchoice);
std::size_t oi = 0;
for (std::uint32_t i = 0; i < nl.n_in(); ++i)
{
const auto kind = nl.kind_at(i);
if (kind != yao::input::shared && kind != yao::input::priv1)
continue;
chosen[oi] = (p1_bits[i] & 1u) ? g.ot1[oi] : g.ot0[oi];
++oi;
}
std::vector<yao::block> blob;
blob.reserve(ntable + ndirect + nchoice);
blob.insert(blob.end(), g.tables.begin(), g.tables.end());
blob.insert(blob.end(), g.direct.begin(), g.direct.end());
blob.insert(blob.end(), chosen.begin(), chosen.end());
link.send_vec(blob, net::msg::bytes);
share = std::move(g.share);
}
else
{
const auto blob = link.recv_vec<yao::block>(net::msg::bytes);
require(blob.size() == ntable + ndirect + nchoice, "yao: payload size");
std::vector<yao::block> tables(ntable);
std::vector<yao::block> direct(ndirect);
std::vector<yao::block> chosen(nchoice);
if (ntable != 0)
std::memcpy(tables.data(), blob.data(), ntable * sizeof(yao::block));
if (ndirect != 0)
std::memcpy(direct.data(), blob.data() + ntable,
ndirect * sizeof(yao::block));
if (nchoice != 0)
std::memcpy(chosen.data(), blob.data() + ntable + ndirect,
nchoice * sizeof(yao::block));
share = yao::detail::eval_party1(nl, tables, direct, chosen);
}
auto other = net.exchange_vec_with(peer, share, net::msg::delta);
require(other.size() == share.size(), "yao: share length");
std::vector<std::uint8_t> semantic(nl.n_in());
for (std::uint32_t i = 0; i < nl.n_in(); ++i)
semantic[i] = static_cast<std::uint8_t>(p0_bits[i] ^ p1_bits[i]);
auto plain = yao::eval_plain(nl, semantic.data());
require(plain.size() == share.size(), "yao: output length");
for (std::size_t i = 0; i < plain.size(); ++i)
{
const auto opened = static_cast<std::uint8_t>(share[i] ^ other[i]);
require(opened == plain[i], "yao: open");
}
}
int run_if(role self, trio & net, unsigned heavy, unsigned light)
{
if (self == role::p2)
return 0;
auto then_nl = and_n(heavy);
std::vector<std::uint8_t> h0(heavy, 1);
std::vector<std::uint8_t> h1(heavy, 0);
if (self == role::p0)
{
auto else_nl = and_n(light);
std::vector<std::uint8_t> l0(light, 1);
std::vector<std::uint8_t> l1(light, 1);
auto stacked = yao::eval_if(then_nl, else_nl, 0, 0, h0.data(), h1.data(),
l0.data(), l1.data());
std::vector<std::uint8_t> semantic(heavy, 1);
auto plain = yao::eval_plain(then_nl, semantic.data());
require(stacked.share0.size() == plain.size(), "yao stack: outputs");
for (std::size_t i = 0; i < plain.size(); ++i)
{
const auto opened = static_cast<std::uint8_t>(
stacked.share0[i] ^ stacked.share1[i]);
require(opened == plain[i], "yao stack: if");
}
require(stacked.stack_blocks > 0, "yao stack: empty");
}
yao_ship(net, self, then_nl, h0.data(), h1.data());
return 0;
}
int run_hot(role self, trio & net, unsigned k)
{
if (self == role::p2)
return 0;
auto active = and_n(2);
const std::uint8_t bits0[2] = {1, 0};
const std::uint8_t bits1[2] = {0, 1};
if (self == role::p0)
{
std::vector<yao::netlist> branches;
std::vector<std::vector<std::uint8_t>> p0;
std::vector<std::vector<std::uint8_t>> p1;
branches.reserve(k);
for (unsigned i = 0; i < k; ++i)
{
branches.push_back(i % 2 == 0 ? and_n(2) : xor2());
p0.push_back({1, static_cast<std::uint8_t>(i & 1u)});
p1.push_back({0, 1});
}
auto stacked = yao::eval_one_hot(branches, 0, 0, p0, p1);
const std::uint8_t semantic[2] = {1, 1};
auto plain = yao::eval_plain(active, semantic);
require(stacked.share0.size() == plain.size(), "yao stack: one-hot outputs");
for (std::size_t i = 0; i < plain.size(); ++i)
{
const auto opened = static_cast<std::uint8_t>(
stacked.share0[i] ^ stacked.share1[i]);
require(opened == plain[i], "yao stack: one-hot");
}
require(stacked.stack_blocks > 0, "yao stack: one-hot empty");
}
yao_ship(net, self, active, bits0, bits1);
return 0;
}
std::vector<std::uint8_t> flute_columns(unsigned delta, unsigned n_out)
{
const unsigned rows = 1u << delta;
std::vector<std::uint8_t> columns(static_cast<std::size_t>(n_out) * rows);
for (unsigned w = 0; w < n_out; ++w)
for (unsigned j = 0; j < rows; ++j)
columns[static_cast<std::size_t>(w) * rows + j] =
static_cast<std::uint8_t>(((j >> (w % delta)) ^ w) & 1u);
return columns;
}
std::vector<std::uint8_t> flute_bits(unsigned delta)
{
std::vector<std::uint8_t> bits(delta, 0);
bits[0] = 1;
if (delta > 2)
bits[2] = 1;
return bits;
}
std::vector<std::uint8_t> flute_pack(const flute::detail::setup & s, unsigned party)
{
std::vector<std::uint8_t> out;
out.insert(out.end(), s.m.begin(), s.m.end());
out.insert(out.end(), s.share[party].begin(), s.share[party].end());
out.insert(out.end(), s.lamz[party].begin(), s.lamz[party].end());
return out;
}
int run_flute(role self, trio & net, unsigned delta, unsigned n_out)
{
const auto columns = flute_columns(delta, n_out);
const auto x = flute_bits(delta);
const unsigned rows = 1u << delta;
const std::uint32_t full = rows - 1u;
if (self == role::p2)
{
auto setup = flute::detail::make_setup(delta, n_out, 2, x.data());
auto to0 = flute_pack(setup, 0);
auto to1 = flute_pack(setup, 1);
net.send_bytes_to(role::p0, net::msg::bytes, to0.data(), to0.size());
net.send_bytes_to(role::p1, net::msg::bytes, to1.data(), to1.size());
return 0;
}
const unsigned party = self == role::p0 ? 0u : 1u;
auto bytes = net.recv_bytes_from(role::p2, net::msg::bytes);
const std::size_t need = static_cast<std::size_t>(delta) + rows + n_out;
require(bytes.size() == need, "flute: deal size");
flute::detail::setup setup;
setup.m.assign(bytes.begin(), bytes.begin() + delta);
setup.share.assign(2, {});
setup.share[party].assign(bytes.begin() + delta, bytes.begin() + delta + rows);
setup.lamz.assign(2, {});
setup.lamz[party].assign(bytes.end() - static_cast<std::ptrdiff_t>(n_out),
bytes.end());
std::vector<std::uint8_t> mine(static_cast<std::size_t>(n_out) * 2u);
for (unsigned w = 0; w < n_out; ++w)
{
const std::uint8_t * column =
columns.data() + static_cast<std::size_t>(w) * rows;
mine[w] = flute::detail::party_v(setup, party, delta, full, column,
setup.lamz[party][w]);
mine[n_out + w] = setup.lamz[party][w];
}
const role peer = self == role::p0 ? role::p1 : role::p0;
auto theirs = net.exchange_vec_with(peer, mine, net::msg::bytes);
auto expect = flute::eval_plain(delta, n_out, columns.data(), x.data());
for (unsigned w = 0; w < n_out; ++w)
{
const std::uint8_t * column =
columns.data() + static_cast<std::size_t>(w) * rows;
const std::uint8_t t = flute::detail::dot_column(
full, delta, setup.m.data(), column);
const auto opened = static_cast<std::uint8_t>(
mine[w] ^ theirs[w] ^ t ^ mine[n_out + w] ^ theirs[n_out + w]);
require(opened == expect[w], "flute: open");
}
return 0;
}
rss::seed_bundle shuffle_bundle()
{
rss::seed_bundle bundle{};
auto fill = [](rss::seed_block & seed, std::uint8_t tag) {
auto * p = reinterpret_cast<std::uint8_t *>(&seed);
for (std::size_t i = 0; i < sizeof(seed); ++i)
p[i] = static_cast<std::uint8_t>(tag + i * 17u);
};
fill(bundle.k01, 1);
fill(bundle.k12, 2);
fill(bundle.k20, 3);
return bundle;
}
shuffle::shuffle_party_view<std::uint64_t> shuffle_view(unsigned me, std::size_t n)
{
std::uint64_t rng = 0xA5A5A5A5A5A5A5A5ull;
auto draw = [&] {
rng = rng * 6364136223846793005ull + 1u;
return rng;
};
shuffle::shuffle_party_view<std::uint64_t> view;
view.own.resize(n);
view.next.resize(n);
for (std::size_t i = 0; i < n; ++i)
{
const auto a = draw();
const auto b = draw();
const auto c = static_cast<std::uint64_t>(i) - a - b;
if (me == 0)
{
view.own[i] = a;
view.next[i] = b;
}
else if (me == 1)
{
view.own[i] = b;
view.next[i] = c;
}
else
{
view.own[i] = c;
view.next[i] = a;
}
}
return view;
}
int run_shuffle(role self, trio & net, std::size_t n)
{
const unsigned me = static_cast<unsigned>(self);
const auto bundle = shuffle_bundle();
const std::uint64_t index = 1;
auto seeds = rss::party_seeds::from_bundle(bundle, me);
auto view = shuffle_view(me, n);
const unsigned order[3] = {2u, 0u, 1u};
for (unsigned left : order)
{
const unsigned u = shuffle::hidden_u_party(left);
const unsigned side = shuffle::hidden_side_party(left);
if (me == u)
{
auto step = shuffle::shuffle_hidden_pass<std::uint64_t>(
me, seeds, view, index, left, nullptr);
require(step.out.sends, "shuffle: u sends");
require(step.out.to == side, "shuffle: u recipient");
net.send_vec_to(static_cast<role>(step.out.to), net::msg::ring_vector,
step.out.data);
view = std::move(step.view);
}
else if (me == side)
{
auto inbound = net.recv_vec_from<std::uint64_t>(
static_cast<role>(u), net::msg::ring_vector);
auto step = shuffle::shuffle_hidden_pass<std::uint64_t>(
me, seeds, view, index, left, &inbound);
require(step.out.sends, "shuffle: side sends");
require(step.out.to == left, "shuffle: side recipient");
net.send_vec_to(static_cast<role>(step.out.to), net::msg::ring_vector,
step.out.data);
view = std::move(step.view);
}
else
{
auto inbound = net.recv_vec_from<std::uint64_t>(
static_cast<role>(side), net::msg::ring_vector);
auto step = shuffle::shuffle_hidden_pass<std::uint64_t>(
me, seeds, view, index, left, &inbound);
require(!step.out.sends, "shuffle: left-out is silent");
view = std::move(step.view);
}
}
if (self == role::p0)
{
auto from1 = net.recv_vec_from<std::uint64_t>(role::p1, net::msg::ring_vector);
auto from2 = net.recv_vec_from<std::uint64_t>(role::p2, net::msg::ring_vector);
require(from1.size() == n && from2.size() == n, "shuffle: open size");
std::vector<std::uint64_t> clear(n);
for (std::size_t i = 0; i < n; ++i)
clear[i] = i;
auto expect = shuffle::permute(clear,
shuffle::permutation_from_seed(bundle.k01, n, index));
expect = shuffle::permute(expect,
shuffle::permutation_from_seed(bundle.k12, n, index));
expect = shuffle::permute(expect,
shuffle::permutation_from_seed(bundle.k20, n, index));
for (std::size_t i = 0; i < n; ++i)
{
const auto got = view.own[i] + from1[i] + from2[i];
require(got == expect[i], "shuffle: open");
}
}
else
{
net.send_vec_to(role::p0, net::msg::ring_vector, view.own);
}
return 0;
}
int arith_proj_m5(role self, trio & net) { return run_proj(self, net, 5); }
int arith_proj_m17(role self, trio & net) { return run_proj(self, net, 17); }
int arith_proj_m64(role self, trio & net) { return run_proj(self, net, 64); }
int arith_mul_p5(role self, trio & net) { return run_mul(self, net, 5); }
int arith_mul_p7(role self, trio & net) { return run_mul(self, net, 7); }
int arith_mul_p11(role self, trio & net) { return run_mul(self, net, 11); }
int arith_thresh_b8(role self, trio & net) { return run_thresh(self, net, 8); }
int arith_thresh_b16(role self, trio & net) { return run_thresh(self, net, 16); }
int arith_chain_mul4(role self, trio & net) { return run_chain(self, net); }
int yao_if_4_2(role self, trio & net) { return run_if(self, net, 4, 2); }
int yao_if_16_8(role self, trio & net) { return run_if(self, net, 16, 8); }
int yao_onehot_k4(role self, trio & net) { return run_hot(self, net, 4); }
int yao_onehot_k8(role self, trio & net) { return run_hot(self, net, 8); }
int flute_d2(role self, trio & net) { return run_flute(self, net, 2, 1); }
int flute_d4(role self, trio & net) { return run_flute(self, net, 4, 1); }
int flute_d8(role self, trio & net) { return run_flute(self, net, 8, 1); }
int flute_d4_o8(role self, trio & net) { return run_flute(self, net, 4, 8); }
int shuffle_n16(role self, trio & net) { return run_shuffle(self, net, 16); }
int shuffle_n64(role self, trio & net) { return run_shuffle(self, net, 64); }
int shuffle_n256(role self, trio & net) { return run_shuffle(self, net, 256); }
#define REG(name, tags, fn) \
register_flow(flow{#name, tags, fn, true})
} // namespace gadget
void register_gadget_flows()
{
using namespace gadget;
REG(arith_proj_m5, "bench arith garble", arith_proj_m5);
REG(arith_proj_m17, "bench arith garble", arith_proj_m17);
REG(arith_proj_m64, "bench arith garble", arith_proj_m64);
REG(arith_mul_p5, "bench arith garble", arith_mul_p5);
REG(arith_mul_p7, "bench arith garble", arith_mul_p7);
REG(arith_mul_p11, "bench arith garble", arith_mul_p11);
REG(arith_thresh_b8, "bench arith garble", arith_thresh_b8);
REG(arith_thresh_b16, "bench arith garble", arith_thresh_b16);
REG(arith_chain_mul4, "bench arith garble", arith_chain_mul4);
REG(yao_if_4_2, "bench yao stack", yao_if_4_2);
REG(yao_if_16_8, "bench yao stack", yao_if_16_8);
REG(yao_onehot_k4, "bench yao stack", yao_onehot_k4);
REG(yao_onehot_k8, "bench yao stack", yao_onehot_k8);
REG(flute_d2, "bench flute", flute_d2);
REG(flute_d4, "bench flute", flute_d4);
REG(flute_d8, "bench flute", flute_d8);
REG(flute_d4_o8, "bench flute", flute_d4_o8);
REG(shuffle_n16, "bench shuffle rss", shuffle_n16);
REG(shuffle_n64, "bench shuffle rss", shuffle_n64);
REG(shuffle_n256, "bench shuffle rss", shuffle_n256);
}
} // namespace party
} // namespace dpf