#include #include #include "zcash/IncrementalMerkleTree.hpp" #include "crypto/sha256.h" #include "zerocash/utils/util.h" // TODO: remove these utilities namespace libzcash { SHA256Compress SHA256Compress::combine(const SHA256Compress& a, const SHA256Compress& b) { // This is a performance optimization. if (a.IsNull() && b.IsNull()) { return a; } SHA256Compress res = SHA256Compress(); CSHA256 hasher; hasher.Write(a.begin(), 32); hasher.Write(b.begin(), 32); hasher.FinalizeNoPadding(res.begin()); return res; } template class PathFiller { private: std::deque queue; public: PathFiller() : queue() { } PathFiller(std::deque queue) : queue(queue) { } Hash next() { if (queue.size() > 0) { Hash h = queue.front(); queue.pop_front(); return h; } else { return Hash(); } } }; template void IncrementalMerkleTree::wfcheck() const { if (parents.size() >= Depth) { throw std::ios_base::failure("tree has too many parents"); } // The last parent cannot be null. if (!(parents.empty()) && !(parents.back())) { throw std::ios_base::failure("tree has non-canonical representation of parent"); } // Left cannot be empty when right exists. if (!left && right) { throw std::ios_base::failure("tree has non-canonical representation of tree"); } // Left cannot be empty when parents is nonempty. if (!left && parents.size() > 0) { throw std::ios_base::failure("tree has non-canonical representation of tree"); } } template void IncrementalMerkleTree::append(Hash obj) { if (is_complete(Depth)) { throw std::runtime_error("tree is full"); } if (!left) { // Set the left leaf left = obj; } else if (!right) { // Set the right leaf right = obj; } else { // Combine the leaves and propagate it up the tree boost::optional combined = Hash::combine(*left, *right); // Set the "left" leaf to the object and make the "right" leaf none left = obj; right = boost::none; for (size_t i = 0; i < Depth; i++) { if (i < parents.size()) { if (parents[i]) { combined = Hash::combine(*parents[i], *combined); parents[i] = boost::none; } else { parents[i] = *combined; break; } } else { parents.push_back(combined); break; } } } } // This is for allowing the witness to determine if a subtree has filled // to a particular depth, or for append() to ensure we're not appending // to a full tree. template bool IncrementalMerkleTree::is_complete(size_t depth) const { if (!left || !right) { return false; } if (parents.size() != (depth - 1)) { return false; } BOOST_FOREACH(const boost::optional& parent, parents) { if (!parent) { return false; } } return true; } // This finds the next "depth" of an unfilled subtree, given that we've filled // `skip` uncles/subtrees. template size_t IncrementalMerkleTree::next_depth(size_t skip) const { if (!left) { if (skip) { skip--; } else { return 0; } } if (!right) { if (skip) { skip--; } else { return 0; } } size_t d = 1; BOOST_FOREACH(const boost::optional& parent, parents) { if (!parent) { if (skip) { skip--; } else { return d; } } d++; } return d + skip; } // This calculates the root of the tree. template Hash IncrementalMerkleTree::root(size_t depth, std::deque filler_hashes) const { PathFiller filler(filler_hashes); Hash combine_left = left ? *left : filler.next(); Hash combine_right = right ? *right : filler.next(); Hash root = Hash::combine(combine_left, combine_right); size_t d = 1; BOOST_FOREACH(const boost::optional& parent, parents) { if (parent) { root = Hash::combine(*parent, root); } else { root = Hash::combine(root, filler.next()); } d++; } // We may not have parents for ancestor trees, so we fill // the rest in here. while (d < depth) { root = Hash::combine(root, filler.next()); d++; } return root; } // This constructs an authentication path into the tree in the format that the circuit // wants. The caller provides `filler_hashes` to fill in the uncle subtrees. template MerklePath IncrementalMerkleTree::path(std::deque filler_hashes) const { if (!left) { throw std::runtime_error("can't create an authentication path for the beginning of the tree"); } PathFiller filler(filler_hashes); std::vector path; std::vector index; if (right) { index.push_back(true); path.push_back(*left); } else { index.push_back(false); path.push_back(filler.next()); } size_t d = 1; BOOST_FOREACH(const boost::optional& parent, parents) { if (parent) { index.push_back(true); path.push_back(*parent); } else { index.push_back(false); path.push_back(filler.next()); } d++; } while (d < Depth) { index.push_back(false); path.push_back(filler.next()); d++; } std::vector> merkle_path; BOOST_FOREACH(Hash b, path) { std::vector hashv(b.begin(), b.end()); std::vector tmp_b; libzerocash::convertBytesVectorToVector(hashv, tmp_b); merkle_path.push_back(tmp_b); } std::reverse(merkle_path.begin(), merkle_path.end()); std::reverse(index.begin(), index.end()); return MerklePath(merkle_path, index); } template std::deque IncrementalWitness::partial_path() const { std::deque uncles(filled.begin(), filled.end()); if (cursor) { uncles.push_back(cursor->root(cursor_depth)); } return uncles; } template void IncrementalWitness::append(Hash obj) { if (cursor) { cursor->append(obj); if (cursor->is_complete(cursor_depth)) { filled.push_back(cursor->root(cursor_depth)); cursor = boost::none; } } else { cursor_depth = tree.next_depth(filled.size()); if (cursor_depth >= Depth) { throw std::runtime_error("tree is full"); } if (cursor_depth == 0) { filled.push_back(obj); } else { cursor = IncrementalMerkleTree(); cursor->append(obj); } } } template class IncrementalMerkleTree; template class IncrementalMerkleTree; template class IncrementalWitness; template class IncrementalWitness; } // end namespace `libzcash`