|
@@ -9,7 +9,118 @@
|
|
#include "types.hpp"
|
|
#include "types.hpp"
|
|
#include "bitutils.hpp"
|
|
#include "bitutils.hpp"
|
|
|
|
|
|
|
|
+// Streaming evaluation, to avoid taking up enough memory to store
|
|
|
|
+// an entire evaluation. T can be RDPF, RDPFPair, or RDPFTriple.
|
|
|
|
+template <typename T>
|
|
|
|
+class StreamEval {
|
|
|
|
+ const T &rdpf;
|
|
|
|
+ size_t &op_counter;
|
|
|
|
+ bool use_expansion;
|
|
|
|
+ nbits_t depth;
|
|
|
|
+ address_t indexmask;
|
|
|
|
+ address_t pathindex;
|
|
|
|
+ address_t nextindex;
|
|
|
|
+ std::vector<typename T::node> path;
|
|
|
|
+public:
|
|
|
|
+ // Create an Eval object that will start its output at index start.
|
|
|
|
+ // It will wrap around to 0 when it hits 2^depth. If use_expansion
|
|
|
|
+ // is true, then if the DPF has been expanded, just output values
|
|
|
|
+ // from that. If use_expansion=false or if the DPF has not been
|
|
|
|
+ // expanded, compute the values on the fly.
|
|
|
|
+ StreamEval(const T &rdpf, address_t start, size_t &op_counter,
|
|
|
|
+ bool use_expansion = true);
|
|
|
|
+ // Get the next value (or tuple of values) from the evaluator
|
|
|
|
+ typename T::node next();
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+// Create a StreamEval object that will start its output at index start.
|
|
|
|
+// It will wrap around to 0 when it hits 2^depth. If use_expansion
|
|
|
|
+// is true, then if the DPF has been expanded, just output values
|
|
|
|
+// from that. If use_expansion=false or if the DPF has not been
|
|
|
|
+// expanded, compute the values on the fly.
|
|
|
|
+template <typename T>
|
|
|
|
+StreamEval<T>::StreamEval(const T &rdpf, address_t start,
|
|
|
|
+ size_t &op_counter, bool use_expansion) : rdpf(rdpf),
|
|
|
|
+ op_counter(op_counter), use_expansion(use_expansion)
|
|
|
|
+{
|
|
|
|
+ depth = rdpf.depth();
|
|
|
|
+ // Prevent overflow of 1<<depth
|
|
|
|
+ if (depth < ADDRESS_MAX_BITS) {
|
|
|
|
+ indexmask = (address_t(1)<<depth)-1;
|
|
|
|
+ } else {
|
|
|
|
+ indexmask = ~0;
|
|
|
|
+ }
|
|
|
|
+ // Record that we haven't actually output the leaf for index start
|
|
|
|
+ // itself yet
|
|
|
|
+ nextindex = start;
|
|
|
|
+ if (use_expansion && rdpf.has_expansion()) {
|
|
|
|
+ // We just need to keep the counter, not compute anything
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ path.resize(depth);
|
|
|
|
+ pathindex = start;
|
|
|
|
+ path[0] = rdpf.get_seed();
|
|
|
|
+ for (nbits_t i=1;i<depth;++i) {
|
|
|
|
+ bool dir = !!(pathindex & (address_t(1)<<(depth-i)));
|
|
|
|
+ path[i] = rdpf.descend(path[i-1], i-1, dir, op_counter);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+template <typename T>
|
|
|
|
+typename T::node StreamEval<T>::next()
|
|
|
|
+{
|
|
|
|
+ if (use_expansion && rdpf.has_expansion()) {
|
|
|
|
+ // Just use the precomputed values
|
|
|
|
+ typename T::node leaf = rdpf.get_expansion(nextindex);
|
|
|
|
+ nextindex = (nextindex + 1) & indexmask;
|
|
|
|
+ return leaf;
|
|
|
|
+ }
|
|
|
|
+ // Invariant: in the first call to next(), nextindex = pathindex.
|
|
|
|
+ // Otherwise, nextindex = pathindex+1.
|
|
|
|
+ // Get the XOR of nextindex and pathindex, and strip the low bit.
|
|
|
|
+ // If nextindex and pathindex are equal, or pathindex is even
|
|
|
|
+ // and nextindex is the consecutive odd number, index_xor will be 0,
|
|
|
|
+ // indicating that we don't have to update the path, but just
|
|
|
|
+ // compute the appropriate leaf given by the low bit of nextindex.
|
|
|
|
+ //
|
|
|
|
+ // Otherwise, say for example pathindex is 010010111 and nextindex
|
|
|
|
+ // is 010011000. Then their XOR is 000001111, and stripping the low
|
|
|
|
+ // bit yields 000001110, so how_many_1_bits will be 3.
|
|
|
|
+ // That indicates (typically) that path[depth-3] was a left child,
|
|
|
|
+ // and now we need to change it to a right child by descending right
|
|
|
|
+ // from path[depth-4], and then filling the path after that with
|
|
|
|
+ // left children.
|
|
|
|
+ //
|
|
|
|
+ // When we wrap around, however, index_xor will be 111111110 (after
|
|
|
|
+ // we strip the low bit), and how_many_1_bits will be depth-1, but
|
|
|
|
+ // the new top child (of the root seed) we have to compute will be a
|
|
|
|
+ // left, not a right, child.
|
|
|
|
+ uint64_t index_xor = (nextindex ^ pathindex) & ~1;
|
|
|
|
+ nbits_t how_many_1_bits = __builtin_popcount(index_xor);
|
|
|
|
+ if (how_many_1_bits > 0) {
|
|
|
|
+ // This will almost always be 1, unless we've just wrapped
|
|
|
|
+ // around from the right subtree back to the left, in which case
|
|
|
|
+ // it will be 0.
|
|
|
|
+ bool top_changed_bit =
|
|
|
|
+ nextindex & (address_t(1) << how_many_1_bits);
|
|
|
|
+ path[depth-how_many_1_bits] =
|
|
|
|
+ rdpf.descend(path[depth-how_many_1_bits-1],
|
|
|
|
+ depth-how_many_1_bits-1, top_changed_bit, op_counter);
|
|
|
|
+ for (nbits_t i = depth-how_many_1_bits; i < depth-1; ++i) {
|
|
|
|
+ path[i+1] = rdpf.descend(path[i], i, 0, op_counter);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ typename T::node leaf = rdpf.descend(path[depth-1], depth-1,
|
|
|
|
+ nextindex & 1, op_counter);
|
|
|
|
+ pathindex = nextindex;
|
|
|
|
+ nextindex = (nextindex + 1) & indexmask;
|
|
|
|
+ return leaf;
|
|
|
|
+}
|
|
|
|
+
|
|
struct RDPF {
|
|
struct RDPF {
|
|
|
|
+ // The type of nodes
|
|
|
|
+ using node = DPFnode;
|
|
|
|
+
|
|
// The 128-bit seed
|
|
// The 128-bit seed
|
|
DPFnode seed;
|
|
DPFnode seed;
|
|
// Which half of the DPF are we?
|
|
// Which half of the DPF are we?
|
|
@@ -58,12 +169,23 @@ struct RDPF {
|
|
// The depth
|
|
// The depth
|
|
inline nbits_t depth() const { return cw.size(); }
|
|
inline nbits_t depth() const { return cw.size(); }
|
|
|
|
|
|
|
|
+ // The seed
|
|
|
|
+ inline node get_seed() const { return seed; }
|
|
|
|
+
|
|
|
|
+ // Do we have a precomputed expansion?
|
|
|
|
+ inline bool has_expansion() const { return expansion.size() > 0; }
|
|
|
|
+
|
|
|
|
+ // Get an element of the expansion
|
|
|
|
+ inline node get_expansion(address_t index) const {
|
|
|
|
+ return expansion[index];
|
|
|
|
+ }
|
|
|
|
+
|
|
// Descend from a node at depth parentdepth to one of its children
|
|
// Descend from a node at depth parentdepth to one of its children
|
|
// whichchild = 0: left child
|
|
// whichchild = 0: left child
|
|
// whichchild = 1: right child
|
|
// whichchild = 1: right child
|
|
//
|
|
//
|
|
// Cost: 1 AES operation
|
|
// Cost: 1 AES operation
|
|
- DPFnode descend(const DPFnode parent, nbits_t parentdepth,
|
|
|
|
|
|
+ DPFnode descend(const DPFnode &parent, nbits_t parentdepth,
|
|
bit_t whichchild, size_t &op_counter) const;
|
|
bit_t whichchild, size_t &op_counter) const;
|
|
|
|
|
|
// Get the leaf node for the given input
|
|
// Get the leaf node for the given input
|
|
@@ -74,6 +196,7 @@ struct RDPF {
|
|
// Expand the DPF if it's not already expanded
|
|
// Expand the DPF if it's not already expanded
|
|
void expand(size_t &op_counter);
|
|
void expand(size_t &op_counter);
|
|
|
|
|
|
|
|
+#if 0
|
|
// Streaming evaluation, to avoid taking up enough memory to store
|
|
// Streaming evaluation, to avoid taking up enough memory to store
|
|
// an entire evaluation
|
|
// an entire evaluation
|
|
class Eval {
|
|
class Eval {
|
|
@@ -97,8 +220,9 @@ struct RDPF {
|
|
// is true, then if the DPF has been expanded, just output values
|
|
// is true, then if the DPF has been expanded, just output values
|
|
// from that. If use_expansion=false or if the DPF has not been
|
|
// from that. If use_expansion=false or if the DPF has not been
|
|
// expanded, compute the values on the fly.
|
|
// expanded, compute the values on the fly.
|
|
- Eval eval(address_t start, size_t &op_counter,
|
|
|
|
|
|
+ StreamEval<RDPF> eval(address_t start, size_t &op_counter,
|
|
bool use_expansion=true) const;
|
|
bool use_expansion=true) const;
|
|
|
|
+#endif
|
|
|
|
|
|
// Get the bit-shared unit vector entry from the leaf node
|
|
// Get the bit-shared unit vector entry from the leaf node
|
|
inline RegBS unit_bs(DPFnode leaf) const {
|
|
inline RegBS unit_bs(DPFnode leaf) const {
|
|
@@ -233,16 +357,43 @@ T& operator<<(T &os, const RDPF &rdpf)
|
|
// not).
|
|
// not).
|
|
|
|
|
|
struct RDPFTriple {
|
|
struct RDPFTriple {
|
|
|
|
+ // The type of node triples
|
|
|
|
+ using node = std::tuple<DPFnode, DPFnode, DPFnode>;
|
|
|
|
+
|
|
RegAS as_target;
|
|
RegAS as_target;
|
|
RegXS xs_target;
|
|
RegXS xs_target;
|
|
RDPF dpf[3];
|
|
RDPF dpf[3];
|
|
|
|
|
|
|
|
+ // The depth
|
|
|
|
+ inline nbits_t depth() const { return dpf[0].depth(); }
|
|
|
|
+
|
|
|
|
+ // The seed
|
|
|
|
+ inline node get_seed() const {
|
|
|
|
+ return std::make_tuple(dpf[0].get_seed(), dpf[1].get_seed(),
|
|
|
|
+ dpf[2].get_seed());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Do we have a precomputed expansion?
|
|
|
|
+ inline bool has_expansion() const {
|
|
|
|
+ return dpf[0].expansion.size() > 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Get an element of the expansion
|
|
|
|
+ inline node get_expansion(address_t index) const {
|
|
|
|
+ return std::make_tuple(dpf[0].get_expansion(index),
|
|
|
|
+ dpf[1].get_expansion(index), dpf[2].get_expansion(index));
|
|
|
|
+ }
|
|
|
|
+
|
|
RDPFTriple() {}
|
|
RDPFTriple() {}
|
|
|
|
|
|
// Construct three RDPFs of the given depth all with the same
|
|
// Construct three RDPFs of the given depth all with the same
|
|
// randomly generated target index.
|
|
// randomly generated target index.
|
|
RDPFTriple(MPCTIO &tio, yield_t &yield,
|
|
RDPFTriple(MPCTIO &tio, yield_t &yield,
|
|
nbits_t depth, bool save_expansion = false);
|
|
nbits_t depth, bool save_expansion = false);
|
|
|
|
+
|
|
|
|
+ // Descend the three RDPFs in lock step
|
|
|
|
+ node descend(const node &parent, nbits_t parentdepth,
|
|
|
|
+ bit_t whichchild, size_t &op_counter) const;
|
|
};
|
|
};
|
|
|
|
|
|
// I/O for RDPF Triples
|
|
// I/O for RDPF Triples
|
|
@@ -274,7 +425,33 @@ T& operator>>(T &is, RDPFTriple &rdpftrip)
|
|
}
|
|
}
|
|
|
|
|
|
struct RDPFPair {
|
|
struct RDPFPair {
|
|
|
|
+ // The type of node pairs
|
|
|
|
+ using node = std::tuple<DPFnode, DPFnode>;
|
|
|
|
+
|
|
RDPF dpf[2];
|
|
RDPF dpf[2];
|
|
|
|
+
|
|
|
|
+ // The depth
|
|
|
|
+ inline nbits_t depth() const { return dpf[0].depth(); }
|
|
|
|
+
|
|
|
|
+ // The seed
|
|
|
|
+ inline node get_seed() const {
|
|
|
|
+ return std::make_tuple(dpf[0].get_seed(), dpf[1].get_seed());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Do we have a precomputed expansion?
|
|
|
|
+ inline bool has_expansion() const {
|
|
|
|
+ return dpf[0].expansion.size() > 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Get an element of the expansion
|
|
|
|
+ inline node get_expansion(address_t index) const {
|
|
|
|
+ return std::make_tuple(dpf[0].get_expansion(index),
|
|
|
|
+ dpf[1].get_expansion(index));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Descend the two RDPFs in lock step
|
|
|
|
+ node descend(const node &parent, nbits_t parentdepth,
|
|
|
|
+ bit_t whichchild, size_t &op_counter) const;
|
|
};
|
|
};
|
|
|
|
|
|
// I/O for RDPF Pairs
|
|
// I/O for RDPF Pairs
|