Files
hush3/src/zerocash/IncrementalMerkleTree.cpp
2016-03-31 15:59:06 -06:00

607 lines
20 KiB
C++

/** @file
*****************************************************************************
Implementation of interfaces for the classes IncrementalMerkleTreeCompact,
IncrementalMerkleNode, and IncrementalMerkleTree.
See IncrementalMerkleTree.h .
*****************************************************************************
* @author This file is part of libzerocash, developed by the Zerocash
* project and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include "IncrementalMerkleTree.h"
#include "Zerocash.h"
#include <cmath>
#include <iostream>
#include <vector>
namespace libzerocash {
/////////////////////////////////////////////
// IncrementalMerkleTreeCompact class
/////////////////////////////////////////////
std::vector<unsigned char> IncrementalMerkleTreeCompact::serialize() const {
/* Serialization format:
* treeHeight (4 bytes, big endian)
* hashList (ceil(treeHeight / 8) bytes)
* hashVec (32 bytes for every 1 bit in hashList)
*/
std::vector<unsigned char> serialized;
/* treeHeight (4 bytes, big endian) */
std::vector<unsigned char> treeHeightBytes(4);
convertIntToBytesVector((uint64_t)this->treeHeight, treeHeightBytes);
serialized.insert(serialized.end(), treeHeightBytes.begin(), treeHeightBytes.end());
/* hashList */
assert(this->hashList.size() == this->treeHeight);
/* Pad it out to a multiple of 8 bits. */
std::vector<bool> hashList = this->hashList;
if (hashList.size() % 8 != 0) {
hashList.insert(hashList.begin(), 8 - (hashList.size() % 8), false);
}
assert(hashList.size() % 8 == 0);
/* Convert it to a byte vector. */
std::vector<unsigned char> hashListBytes(hashList.size() / 8);
convertVectorToBytesVector(hashList, hashListBytes);
serialized.insert(serialized.end(), hashListBytes.begin(), hashListBytes.end());
/* hashVec */
assert(this->hashVec.size() == countOnes(this->hashList));
for (uint32_t i = 0; i < this->hashVec.size(); i++) {
assert(this->hashVec.at(i).size() == 32);
serialized.insert(serialized.end(), this->hashVec.at(i).begin(), this->hashVec.at(i).end());
}
return serialized;
}
IncrementalMerkleTreeCompact IncrementalMerkleTreeCompact::deserialize(const std::vector<unsigned char>& serialized) {
IncrementalMerkleTreeCompact deserialized;
size_t currentPos = 0;
/* treeHeight */
std::vector<unsigned char> treeHeightBytes = vectorSlice(serialized, 0, 4);
currentPos += 4;
deserialized.treeHeight = convertBytesVectorToInt(treeHeightBytes);
/* hashList */
uint32_t hashListBytesLength = ceil(deserialized.treeHeight / 8.0);
std::vector<unsigned char> hashListBytes = vectorSlice(serialized, currentPos, hashListBytesLength);
currentPos += hashListBytesLength;
convertBytesVectorToVector(hashListBytes, deserialized.hashList);
/* Remove the multiple-of-8-bits padding. */
deserialized.hashList.erase(deserialized.hashList.begin(),
deserialized.hashList.end() - deserialized.treeHeight
);
/* hashVec */
size_t hashVecSize = countOnes(deserialized.hashList);
for (size_t i = 0; i < hashVecSize; i++) {
std::vector<unsigned char> hashVecElement = vectorSlice(serialized, currentPos, 32);
currentPos += 32;
deserialized.hashVec.push_back(hashVecElement);
}
if (currentPos != serialized.size()) {
throw std::runtime_error("Serialized vector is longer than expected.");
}
return deserialized;
}
/////////////////////////////////////////////
// IncrementalMerkleTree class
/////////////////////////////////////////////
// Custom tree constructor (initialize tree of specified height)
IncrementalMerkleTree::IncrementalMerkleTree(uint32_t height) : root(0, height) {
treeHeight = height;
}
// Vector constructor. Initializes and inserts a list of elements.
IncrementalMerkleTree::IncrementalMerkleTree(std::vector< std::vector<bool> > &valueVector, uint32_t height) : root(0, height)
{
// Initialize the tree
treeHeight = height;
// Load the tree with all the given values
if (this->insertVector(valueVector) == false) {
throw std::runtime_error("Could not insert vector into Merkle Tree: too many elements");
}
}
// Custom tree constructor (initialize tree from compact representation)
//
IncrementalMerkleTree::IncrementalMerkleTree(IncrementalMerkleTreeCompact &compact) : root(0, 0)
{
// Initialize the tree
this->treeHeight = compact.getHeight();
root.treeHeight = treeHeight;
// Reconstitute tree from compact representation
this->fromCompactRepresentation(compact);
}
bool
IncrementalMerkleTree::insertElement(const std::vector<bool> &hashV, std::vector<bool> &index) {
// Resize the index vector
index.resize(this->treeHeight);
// Insert the element
return this->root.insertElement(hashV, index);
}
bool
IncrementalMerkleTree::insertElement(const std::vector<unsigned char> &hashV, std::vector<unsigned char> &index) {
// Create a temporary vector to hold hashV
std::vector<bool> hashVBool(hashV.size() * 8);
convertBytesVectorToVector(hashV, hashVBool);
// Create a temporary vector to hold the index
std::vector<bool> indexBool(this->treeHeight, 0);
// Insert the element
bool result = this->insertElement(hashVBool, indexBool);
// Convert the returned vector
index.resize(index.size() / 8); // this might need to include a ceil
convertVectorToBytesVector(indexBool, index);
return result;
}
bool
IncrementalMerkleTree::getWitness(const std::vector<bool> &index, merkle_authentication_path &witness) {
// Resize the witness if necessary
if (witness.size() < this->treeHeight) {
witness.resize(treeHeight);
}
std::vector<bool> indexPadded = index;
// Discard leading bits of the index if necessary
if (indexPadded.size() > this->treeHeight) {
indexPadded.erase(indexPadded.begin(), indexPadded.begin() + (indexPadded.size() - this->treeHeight));
}
// If the index vector is less than 'treeHeight' bits, pad the leftmost bits with 0 (false)
// This is to deal with the situation where somebody encodes e.g., a 32-bit integer as an index
// into a 64 height tree and does not explicitly pad to length.
if (indexPadded.size() < this->treeHeight) {
indexPadded.insert(indexPadded.begin(), (this->treeHeight - 1) - indexPadded.size(), false);
}
return this->root.getWitness(indexPadded, witness);
}
bool
IncrementalMerkleTree::insertVector(std::vector< std::vector<bool> > &valueVector)
{
std::vector<bool> index;
for (std::vector< std::vector<bool> >::iterator iter = valueVector.begin();
iter != valueVector.end(); ++iter) {
if (this->insertElement(*iter, index) == false) {
return false;
}
}
return true;
}
bool
IncrementalMerkleTree::getRootValue(std::vector<bool>& r) const {
// Query the root for its hash
this->root.getValue(r);
return true;
}
bool
IncrementalMerkleTree::getRootValue(std::vector<unsigned char>& r) const {
// Create a temporary byte vector
std::vector<bool> tempR(r.size() * 8, 0);
// Query the root for its hash
this->root.getValue(tempR);
// Convert the result back into the given vector
convertVectorToBytesVector(tempR, r);
return true;
}
std::vector<unsigned char>
IncrementalMerkleTree::getRoot(){
std::vector<unsigned char> temp(8);
this->getRootValue(temp);
return temp;
}
bool
IncrementalMerkleTree::prune()
{
return this->root.prune();
}
IncrementalMerkleTreeCompact
IncrementalMerkleTree::getCompactRepresentation() const
{
IncrementalMerkleTreeCompact rep;
rep.hashList.resize(this->treeHeight);
rep.treeHeight = this->treeHeight;
std::fill (rep.hashList.begin(), rep.hashList.end(), false);
this->root.getCompactRepresentation(rep);
return rep;
}
bool
IncrementalMerkleTree::fromCompactRepresentation(IncrementalMerkleTreeCompact &rep)
{
return this->root.fromCompactRepresentation(rep, 0);
}
/////////////////////////////////////////////
// IncrementalMerkleNode class
/////////////////////////////////////////////
// Standard constructor
//
IncrementalMerkleNode::IncrementalMerkleNode(uint32_t depth, uint32_t height) : left(NULL), right(NULL), value(CSHA256::OUTPUT_SIZE * 8, 0), nodeDepth(depth), treeHeight(height),
subtreeFull(false), subtreePruned(false)
{
}
// Copy constructor
//
IncrementalMerkleNode::IncrementalMerkleNode(const IncrementalMerkleNode& toCopy) : left(NULL), right(NULL), value(CSHA256::OUTPUT_SIZE * 8, 0)
{
this->nodeDepth = toCopy.nodeDepth;
this->subtreePruned = toCopy.subtreePruned;
this->subtreeFull = toCopy.subtreeFull;
this->value = toCopy.value;
this->treeHeight = toCopy.treeHeight;
// Recursively copy the subtrees
if (toCopy.left) {
this->left = new IncrementalMerkleNode(toCopy.left->nodeDepth, toCopy.left->treeHeight);
*(this->left) = *(toCopy.left);
}
if (toCopy.right) {
this->right = new IncrementalMerkleNode(toCopy.right->nodeDepth, toCopy.right->treeHeight);
*(this->right) = *(toCopy.right);
}
}
IncrementalMerkleNode::~IncrementalMerkleNode()
{
if (this->left) {
delete this->left;
this->left = NULL;
}
if (this->right) {
delete this->right;
this->right = NULL;
}
}
bool
IncrementalMerkleNode::insertElement(const std::vector<bool> &hashV, std::vector<bool> &index)
{
bool result = false;
// Check if we have any free leaves. If not, bail.
if (this->subtreeFull == true) {
return false;
}
// Are we a leaf? If so, store the hash value.
if (this->isLeaf()) {
// Store the given hash value here and return success.
this->value = hashV;
this->subtreeFull = true;
return true;
}
// We're not a leaf. Try to insert into subtrees, creating them if necessary.
// Try to recurse on left subtree
if (!this->left) {
this->left = new IncrementalMerkleNode(this->nodeDepth + 1, this->treeHeight);
}
result = this->left->insertElement(hashV, index);
if (result == true) {
// Update the index value to indicate where the new node went
index.at(this->nodeDepth) = false;
}
// If that failed, try to recurse on right subtree.
if (result == false) {
if (!this->right) {
this->right = new IncrementalMerkleNode(this->nodeDepth + 1, this->treeHeight);
}
result = this->right->insertElement(hashV, index);
if (result == true) {
index.at(this->nodeDepth) = true;
}
}
// If one of the inserts succeeded, update our 'fullness' status.
if (result == true) {
this->updateHashValue();
if (this->isLeaf()) { this->subtreeFull = true; }
else {
this->subtreeFull = this->checkIfNodeFull();
}
}
// Return the result
return result;
}
bool
IncrementalMerkleNode::getWitness(const std::vector<bool> &index, merkle_authentication_path &witness)
{
bool result = false;
// If this node is a leaf: do nothing and return success
if (this->isLeaf()) {
return true;
}
// If this node is pruned, we can't fetch a witness. Return failure.
if (this->isPruned()) {
return false;
}
// If the index path leads to the left, we grab the hash value on the
// right -- then recurse on the left node.
if (index.at(nodeDepth) == false) {
// Make sure there is a value on the right. If not we put the 'null' hash (0) into that element.
if (this->right == NULL) {
witness.at(nodeDepth).resize(CSHA256::OUTPUT_SIZE * 8);
std::fill (witness.at(nodeDepth).begin(), witness.at(nodeDepth).end(), false);
} else {
this->right->getValue(witness.at(nodeDepth));
//printVectorAsHex(witness.at(nodeDepth));
}
// Recurse on the left node
if (this->left) {
result = this->left->getWitness(index, witness);
}
}
// If the index path leads to the right, we grab the hash value on the
// left -- then recurse on the right node.
if (index.at(nodeDepth) == true) {
this->left->getValue(witness.at(nodeDepth));
// Recurse on the right node
if (this->right) {
result = this->right->getWitness(index, witness);
}
}
return result;
}
bool
IncrementalMerkleNode::prune()
{
bool result = true;
// If we're already pruned, return.
if (this->isPruned() == true) {
return true;
}
// Check to see if this node is full. If so, delete the subtrees.
if (this->subtreeFull == true) {
if (this->left) {
delete this->left;
this->left = NULL;
}
if (this->right) {
delete this->right;
this->right = NULL;
}
this->subtreePruned = true;
} else {
// Node is not full. Recurse on left and right.
if (this->left) {
result &= this->left->prune();
}
if (this->right) {
result &= this->right->prune();
}
}
return result;
}
void
IncrementalMerkleNode::updateHashValue()
{
// Take no action on leaves or pruned nodes.
if (this->isLeaf() || this->isPruned()) {
return;
}
// Obtain the hash of the two subtrees and hash the
// concatenation of the two.
std::vector<bool> hash(CSHA256::OUTPUT_SIZE * 8);
std::vector<bool> zero(CSHA256::OUTPUT_SIZE * 8);
std::fill (zero.begin(), zero.end(), false);
// The following code is ugly and should be refactored. It runs
// four special cases depending on whether left/right is NULL.
// It also ensures that the "hash" of (0 || 0) is 0.
if (this->left && !(this->right)) {
if (VectorIsZero(this->left->getValue())) {
hash = zero;
} else {
hashVectors(this->left->getValue(), zero, hash);
}
} else if (!(this->left) && this->right) {
if (VectorIsZero(this->right->getValue())) {
hash = zero;
} else {
hashVectors(zero, this->left->getValue(), hash);
}
} else if (this->left && this->right) {
if (VectorIsZero(this->left->getValue()) && VectorIsZero(this->right->getValue())) {
hash = zero;
} else {
hashVectors(this->left->getValue(), this->right->getValue(), hash);
}
} else {
hash = zero;
}
this->value = hash;
}
bool
IncrementalMerkleNode::checkIfNodeFull()
{
if (this->isPruned()) {
return true;
}
if (this->left == NULL || this->right == NULL) {
return false;
}
return (this->left->subtreeFull && this->right->subtreeFull);
}
void
IncrementalMerkleNode::getCompactRepresentation(IncrementalMerkleTreeCompact &rep) const
{
// Do nothing at the bottom level
if (this->isLeaf()) {
return;
}
// There's no content below us. We're done
if (!this->left) {
return;
}
// If we have no right elements, don't include any hashes. Recurse to the left.
if (this->hasRightChildren() == false && this->left->isLeaf() == false && this->left->subtreeFull == false) {
rep.hashList.at(this->nodeDepth) = false;
this->left->getCompactRepresentation(rep);
return;
}
// Otherwise: Add our left child hash to the tree.
rep.hashList.at(this->nodeDepth) = true;
std::vector<unsigned char> hash(CSHA256::OUTPUT_SIZE, 0);
convertVectorToBytesVector(this->left->getValue(), hash);
rep.hashVec.push_back(hash);
// If we have a right child, recurse to the right
if (this->hasRightChildren()) {
this->right->getCompactRepresentation(rep);
return;
}
// We get here in one of the following cases:
// 1. Our left child is a leaf, and there's no right child.
// 2. Our left child is a full tree, and there's no right child.
// We've gone right for the last time, now we go left until we reach the
// bottom.
for (uint32_t i = this->nodeDepth + 1; i < this->treeHeight; i++) {
rep.hashList.at(i) = false;
}
}
bool
IncrementalMerkleNode::fromCompactRepresentation(IncrementalMerkleTreeCompact &rep, uint32_t pos)
{
bool result = false;
// Do nothing at the bottom level
if (this->isLeaf()) {
return true;
}
// If we have any subtrees (or this tree already has stuff in it), clean it out.
if (this->left) {
// XXX memory leak: left might have the only pointers to its heap
// allocated children!
delete this->left;
this->left = NULL;
}
if (this->right) {
// XXX memory leak: right might have the only pointers to its heap
// allocated children!
delete this->right;
this->right = NULL;
}
this->subtreeFull = this->subtreePruned = false;
// If the hashList[nodeDepth] is true, insert the next hash into the left tree
// and mark it full AND pruned. Then recurse to the right.
if (rep.hashList.at(this->nodeDepth) == true) {
// Create a left node
this->left = new IncrementalMerkleNode(this->nodeDepth + 1, this->treeHeight);
// Fill the left node with the value and mark it full/pruned
std::vector<bool> hash(CSHA256::OUTPUT_SIZE * 8, 0);
convertBytesVectorToVector(rep.hashVec.at(pos), hash);
this->left->value = hash;
this->left->subtreePruned = this->left->subtreeFull = true;
// Create a right node and recurse on it (incrementing pos)
this->right = new IncrementalMerkleNode(this->nodeDepth + 1, this->treeHeight);
result = this->right->fromCompactRepresentation(rep, pos + 1);
} else if (this->nodeDepth < (this->treeHeight - 1)) {
// Otherwise --
// * If we're about to create a leaf level, do nothing.
// * Else create a left node and recurse on it.
this->left = new IncrementalMerkleNode(this->nodeDepth + 1, this->treeHeight);
// Otherwise recurse on the left node. Do not increment pos.
result = this->left->fromCompactRepresentation(rep, pos);
}
// Update the hash value of this node
this->updateHashValue();
return result;
}
IncrementalMerkleNode
IncrementalMerkleNode::operator=(const IncrementalMerkleNode &rhs) {
IncrementalMerkleNode dup(rhs);
return dup;
}
} /* namespace libzerocash */