2016-03-28 01:40:21 -07:00
|
|
|
#include <stdexcept>
|
|
|
|
|
|
|
|
#include <boost/foreach.hpp>
|
|
|
|
|
|
|
|
#include "zcash/IncrementalMerkleTree.hpp"
|
|
|
|
#include "crypto/sha256.h"
|
2016-06-28 09:08:50 -07:00
|
|
|
#include "zcash/util.h"
|
2016-03-28 01:40:21 -07:00
|
|
|
|
|
|
|
namespace libzcash {
|
|
|
|
|
2018-04-12 16:10:04 -07:00
|
|
|
SHA256Compress SHA256Compress::combine(
|
|
|
|
const SHA256Compress& a,
|
|
|
|
const SHA256Compress& b,
|
|
|
|
size_t depth
|
|
|
|
)
|
2016-03-28 01:40:21 -07:00
|
|
|
{
|
|
|
|
SHA256Compress res = SHA256Compress();
|
|
|
|
|
|
|
|
CSHA256 hasher;
|
|
|
|
hasher.Write(a.begin(), 32);
|
|
|
|
hasher.Write(b.begin(), 32);
|
|
|
|
hasher.FinalizeNoPadding(res.begin());
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2016-05-04 08:15:14 -07:00
|
|
|
template <size_t Depth, typename Hash>
|
2016-03-28 01:40:21 -07:00
|
|
|
class PathFiller {
|
|
|
|
private:
|
|
|
|
std::deque<Hash> queue;
|
2016-05-04 08:15:14 -07:00
|
|
|
static EmptyMerkleRoots<Depth, Hash> emptyroots;
|
2016-03-28 01:40:21 -07:00
|
|
|
public:
|
|
|
|
PathFiller() : queue() { }
|
|
|
|
PathFiller(std::deque<Hash> queue) : queue(queue) { }
|
|
|
|
|
2016-05-04 08:15:14 -07:00
|
|
|
Hash next(size_t depth) {
|
2016-03-28 01:40:21 -07:00
|
|
|
if (queue.size() > 0) {
|
|
|
|
Hash h = queue.front();
|
|
|
|
queue.pop_front();
|
|
|
|
|
|
|
|
return h;
|
|
|
|
} else {
|
2016-05-04 08:15:14 -07:00
|
|
|
return emptyroots.empty_root(depth);
|
2016-03-28 01:40:21 -07:00
|
|
|
}
|
|
|
|
}
|
2016-05-04 08:15:14 -07:00
|
|
|
|
2016-03-28 01:40:21 -07:00
|
|
|
};
|
|
|
|
|
2016-05-04 08:15:14 -07:00
|
|
|
template<size_t Depth, typename Hash>
|
|
|
|
EmptyMerkleRoots<Depth, Hash> PathFiller<Depth, Hash>::emptyroots;
|
|
|
|
|
2016-03-28 01:40:21 -07:00
|
|
|
template<size_t Depth, typename Hash>
|
|
|
|
void IncrementalMerkleTree<Depth, Hash>::wfcheck() const {
|
|
|
|
if (parents.size() >= Depth) {
|
|
|
|
throw std::ios_base::failure("tree has too many parents");
|
|
|
|
}
|
2016-04-12 13:58:54 -07:00
|
|
|
|
|
|
|
// The last parent cannot be null.
|
2016-04-29 09:05:06 -07:00
|
|
|
if (!(parents.empty()) && !(parents.back())) {
|
2016-04-12 13:58:54 -07:00
|
|
|
throw std::ios_base::failure("tree has non-canonical representation of parent");
|
|
|
|
}
|
2016-04-21 10:44:16 -07:00
|
|
|
|
|
|
|
// Left cannot be empty when right exists.
|
|
|
|
if (!left && right) {
|
2016-05-03 10:23:54 -07:00
|
|
|
throw std::ios_base::failure("tree has non-canonical representation; right should not exist");
|
2016-04-21 10:44:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Left cannot be empty when parents is nonempty.
|
|
|
|
if (!left && parents.size() > 0) {
|
2016-05-03 10:23:54 -07:00
|
|
|
throw std::ios_base::failure("tree has non-canonical representation; parents should not be unempty");
|
2016-04-21 10:44:16 -07:00
|
|
|
}
|
2016-03-28 01:40:21 -07:00
|
|
|
}
|
|
|
|
|
2016-11-05 10:27:23 -07:00
|
|
|
template<size_t Depth, typename Hash>
|
|
|
|
Hash IncrementalMerkleTree<Depth, Hash>::last() const {
|
|
|
|
if (right) {
|
|
|
|
return *right;
|
|
|
|
} else if (left) {
|
|
|
|
return *left;
|
|
|
|
} else {
|
|
|
|
throw std::runtime_error("tree has no cursor");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-14 18:50:26 -08:00
|
|
|
template<size_t Depth, typename Hash>
|
|
|
|
size_t IncrementalMerkleTree<Depth, Hash>::size() const {
|
|
|
|
size_t ret = 0;
|
|
|
|
if (left) {
|
|
|
|
ret++;
|
|
|
|
}
|
|
|
|
if (right) {
|
|
|
|
ret++;
|
|
|
|
}
|
|
|
|
// Treat occupation of parents array as a binary number
|
|
|
|
// (right-shifted by 1)
|
|
|
|
for (size_t i = 0; i < parents.size(); i++) {
|
|
|
|
if (parents[i]) {
|
|
|
|
ret += (1 << (i+1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-03-28 01:40:21 -07:00
|
|
|
template<size_t Depth, typename Hash>
|
|
|
|
void IncrementalMerkleTree<Depth, Hash>::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
|
2018-04-12 16:10:04 -07:00
|
|
|
boost::optional<Hash> combined = Hash::combine(*left, *right, 0);
|
2016-03-28 01:40:21 -07:00
|
|
|
|
2016-04-29 09:05:06 -07:00
|
|
|
// Set the "left" leaf to the object and make the "right" leaf none
|
2016-03-28 01:40:21 -07:00
|
|
|
left = obj;
|
|
|
|
right = boost::none;
|
|
|
|
|
2016-04-21 11:02:43 -07:00
|
|
|
for (size_t i = 0; i < Depth; i++) {
|
|
|
|
if (i < parents.size()) {
|
|
|
|
if (parents[i]) {
|
2018-04-12 16:10:04 -07:00
|
|
|
combined = Hash::combine(*parents[i], *combined, i+1);
|
2016-04-21 11:02:43 -07:00
|
|
|
parents[i] = boost::none;
|
|
|
|
} else {
|
|
|
|
parents[i] = *combined;
|
|
|
|
break;
|
|
|
|
}
|
2016-03-28 01:40:21 -07:00
|
|
|
} else {
|
2016-04-21 11:02:43 -07:00
|
|
|
parents.push_back(combined);
|
2016-03-28 01:40:21 -07:00
|
|
|
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<size_t Depth, typename Hash>
|
|
|
|
bool IncrementalMerkleTree<Depth, Hash>::is_complete(size_t depth) const {
|
|
|
|
if (!left || !right) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parents.size() != (depth - 1)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOST_FOREACH(const boost::optional<Hash>& 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 Depth, typename Hash>
|
|
|
|
size_t IncrementalMerkleTree<Depth, Hash>::next_depth(size_t skip) const {
|
|
|
|
if (!left) {
|
2016-05-04 08:15:14 -07:00
|
|
|
if (skip) {
|
2016-03-28 01:40:21 -07:00
|
|
|
skip--;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!right) {
|
2016-05-04 08:15:14 -07:00
|
|
|
if (skip) {
|
2016-03-28 01:40:21 -07:00
|
|
|
skip--;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t d = 1;
|
|
|
|
|
|
|
|
BOOST_FOREACH(const boost::optional<Hash>& parent, parents) {
|
|
|
|
if (!parent) {
|
|
|
|
if (skip) {
|
|
|
|
skip--;
|
|
|
|
} else {
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
d++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return d + skip;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This calculates the root of the tree.
|
|
|
|
template<size_t Depth, typename Hash>
|
|
|
|
Hash IncrementalMerkleTree<Depth, Hash>::root(size_t depth,
|
|
|
|
std::deque<Hash> filler_hashes) const {
|
2016-05-04 08:15:14 -07:00
|
|
|
PathFiller<Depth, Hash> filler(filler_hashes);
|
2016-03-28 01:40:21 -07:00
|
|
|
|
2016-05-04 08:15:14 -07:00
|
|
|
Hash combine_left = left ? *left : filler.next(0);
|
|
|
|
Hash combine_right = right ? *right : filler.next(0);
|
2016-03-28 01:40:21 -07:00
|
|
|
|
2018-04-12 16:10:04 -07:00
|
|
|
Hash root = Hash::combine(combine_left, combine_right, 0);
|
2016-03-28 01:40:21 -07:00
|
|
|
|
|
|
|
size_t d = 1;
|
|
|
|
|
|
|
|
BOOST_FOREACH(const boost::optional<Hash>& parent, parents) {
|
|
|
|
if (parent) {
|
2018-04-12 16:10:04 -07:00
|
|
|
root = Hash::combine(*parent, root, d);
|
2016-03-28 01:40:21 -07:00
|
|
|
} else {
|
2018-04-12 16:10:04 -07:00
|
|
|
root = Hash::combine(root, filler.next(d), d);
|
2016-03-28 01:40:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
d++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We may not have parents for ancestor trees, so we fill
|
|
|
|
// the rest in here.
|
|
|
|
while (d < depth) {
|
2018-04-12 16:10:04 -07:00
|
|
|
root = Hash::combine(root, filler.next(d), d);
|
2016-03-28 01:40:21 -07:00
|
|
|
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<size_t Depth, typename Hash>
|
|
|
|
MerklePath IncrementalMerkleTree<Depth, Hash>::path(std::deque<Hash> filler_hashes) const {
|
|
|
|
if (!left) {
|
|
|
|
throw std::runtime_error("can't create an authentication path for the beginning of the tree");
|
|
|
|
}
|
|
|
|
|
2016-05-04 08:15:14 -07:00
|
|
|
PathFiller<Depth, Hash> filler(filler_hashes);
|
2016-03-28 01:40:21 -07:00
|
|
|
|
|
|
|
std::vector<Hash> path;
|
|
|
|
std::vector<bool> index;
|
|
|
|
|
|
|
|
if (right) {
|
|
|
|
index.push_back(true);
|
|
|
|
path.push_back(*left);
|
|
|
|
} else {
|
|
|
|
index.push_back(false);
|
2016-05-04 08:15:14 -07:00
|
|
|
path.push_back(filler.next(0));
|
2016-03-28 01:40:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t d = 1;
|
|
|
|
|
|
|
|
BOOST_FOREACH(const boost::optional<Hash>& parent, parents) {
|
|
|
|
if (parent) {
|
|
|
|
index.push_back(true);
|
|
|
|
path.push_back(*parent);
|
|
|
|
} else {
|
|
|
|
index.push_back(false);
|
2016-05-04 08:15:14 -07:00
|
|
|
path.push_back(filler.next(d));
|
2016-03-28 01:40:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
d++;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (d < Depth) {
|
|
|
|
index.push_back(false);
|
2016-05-04 08:15:14 -07:00
|
|
|
path.push_back(filler.next(d));
|
2016-03-28 01:40:21 -07:00
|
|
|
d++;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::vector<bool>> merkle_path;
|
|
|
|
BOOST_FOREACH(Hash b, path)
|
|
|
|
{
|
|
|
|
std::vector<unsigned char> hashv(b.begin(), b.end());
|
|
|
|
|
2016-06-28 09:08:50 -07:00
|
|
|
merkle_path.push_back(convertBytesVectorToVector(hashv));
|
2016-03-28 01:40:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
std::reverse(merkle_path.begin(), merkle_path.end());
|
|
|
|
std::reverse(index.begin(), index.end());
|
|
|
|
|
|
|
|
return MerklePath(merkle_path, index);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<size_t Depth, typename Hash>
|
2016-04-29 09:05:06 -07:00
|
|
|
std::deque<Hash> IncrementalWitness<Depth, Hash>::partial_path() const {
|
2016-03-28 01:40:21 -07:00
|
|
|
std::deque<Hash> uncles(filled.begin(), filled.end());
|
|
|
|
|
|
|
|
if (cursor) {
|
|
|
|
uncles.push_back(cursor->root(cursor_depth));
|
|
|
|
}
|
|
|
|
|
|
|
|
return uncles;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<size_t Depth, typename Hash>
|
|
|
|
void IncrementalWitness<Depth, Hash>::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<Depth, Hash>();
|
|
|
|
cursor->append(obj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template class IncrementalMerkleTree<INCREMENTAL_MERKLE_TREE_DEPTH, SHA256Compress>;
|
|
|
|
template class IncrementalMerkleTree<INCREMENTAL_MERKLE_TREE_DEPTH_TESTING, SHA256Compress>;
|
|
|
|
|
|
|
|
template class IncrementalWitness<INCREMENTAL_MERKLE_TREE_DEPTH, SHA256Compress>;
|
|
|
|
template class IncrementalWitness<INCREMENTAL_MERKLE_TREE_DEPTH_TESTING, SHA256Compress>;
|
|
|
|
|
|
|
|
} // end namespace `libzcash`
|