You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
246 lines
7.5 KiB
246 lines
7.5 KiB
/** @file
|
|
*****************************************************************************
|
|
|
|
Implementation of interfaces for Merkle tree.
|
|
|
|
See merkle_tree.hpp .
|
|
|
|
*****************************************************************************
|
|
* @author This file is part of libsnark, developed by SCIPR Lab
|
|
* and contributors (see AUTHORS).
|
|
* @copyright MIT license (see LICENSE file)
|
|
*****************************************************************************/
|
|
|
|
#ifndef MERKLE_TREE_TCC
|
|
#define MERKLE_TREE_TCC
|
|
|
|
#include <algorithm>
|
|
|
|
#include "common/profiling.hpp"
|
|
#include "common/utils.hpp"
|
|
|
|
namespace libsnark {
|
|
|
|
template<typename HashT>
|
|
typename HashT::hash_value_type two_to_one_CRH(const typename HashT::hash_value_type &l,
|
|
const typename HashT::hash_value_type &r)
|
|
{
|
|
typename HashT::hash_value_type new_input;
|
|
new_input.insert(new_input.end(), l.begin(), l.end());
|
|
new_input.insert(new_input.end(), r.begin(), r.end());
|
|
|
|
const size_t digest_size = HashT::get_digest_len();
|
|
assert(l.size() == digest_size);
|
|
assert(r.size() == digest_size);
|
|
|
|
return HashT::get_hash(new_input);
|
|
}
|
|
|
|
template<typename HashT>
|
|
merkle_tree<HashT>::merkle_tree(const size_t depth, const size_t value_size) :
|
|
depth(depth), value_size(value_size)
|
|
{
|
|
assert(depth < sizeof(size_t) * 8);
|
|
|
|
digest_size = HashT::get_digest_len();
|
|
assert(value_size <= digest_size);
|
|
|
|
hash_value_type last(digest_size);
|
|
hash_defaults.reserve(depth+1);
|
|
hash_defaults.emplace_back(last);
|
|
for (size_t i = 0; i < depth; ++i)
|
|
{
|
|
last = two_to_one_CRH<HashT>(last, last);
|
|
hash_defaults.emplace_back(last);
|
|
}
|
|
|
|
std::reverse(hash_defaults.begin(), hash_defaults.end());
|
|
}
|
|
|
|
template<typename HashT>
|
|
merkle_tree<HashT>::merkle_tree(const size_t depth,
|
|
const size_t value_size,
|
|
const std::vector<bit_vector> &contents_as_vector) :
|
|
merkle_tree<HashT>(depth, value_size)
|
|
{
|
|
assert(log2(contents_as_vector.size()) <= depth);
|
|
for (size_t address = 0; address < contents_as_vector.size(); ++address)
|
|
{
|
|
const size_t idx = address + (1ul<<depth) - 1;
|
|
values[idx] = contents_as_vector[address];
|
|
hashes[idx] = contents_as_vector[address];
|
|
hashes[idx].resize(digest_size);
|
|
}
|
|
|
|
size_t idx_begin = (1ul<<depth) - 1;
|
|
size_t idx_end = contents_as_vector.size() + ((1ul<<depth) - 1);
|
|
|
|
for (int layer = depth; layer > 0; --layer)
|
|
{
|
|
for (size_t idx = idx_begin; idx < idx_end; idx += 2)
|
|
{
|
|
hash_value_type l = hashes[idx]; // this is sound, because idx_begin is always a left child
|
|
hash_value_type r = (idx + 1 < idx_end ? hashes[idx+1] : hash_defaults[layer]);
|
|
|
|
hash_value_type h = two_to_one_CRH<HashT>(l, r);
|
|
hashes[(idx-1)/2] = h;
|
|
}
|
|
|
|
idx_begin = (idx_begin-1)/2;
|
|
idx_end = (idx_end-1)/2;
|
|
}
|
|
}
|
|
|
|
template<typename HashT>
|
|
merkle_tree<HashT>::merkle_tree(const size_t depth,
|
|
const size_t value_size,
|
|
const std::map<size_t, bit_vector> &contents) :
|
|
merkle_tree<HashT>(depth, value_size)
|
|
{
|
|
|
|
if (!contents.empty())
|
|
{
|
|
assert(contents.rbegin()->first < 1ul<<depth);
|
|
|
|
for (auto it = contents.begin(); it != contents.end(); ++it)
|
|
{
|
|
const size_t address = it->first;
|
|
const bit_vector value = it->second;
|
|
const size_t idx = address + (1ul<<depth) - 1;
|
|
|
|
values[address] = value;
|
|
hashes[idx] = value;
|
|
hashes[idx].resize(digest_size);
|
|
}
|
|
|
|
auto last_it = hashes.end();
|
|
|
|
for (int layer = depth; layer > 0; --layer)
|
|
{
|
|
auto next_last_it = hashes.begin();
|
|
|
|
for (auto it = hashes.begin(); it != last_it; ++it)
|
|
{
|
|
const size_t idx = it->first;
|
|
const hash_value_type hash = it->second;
|
|
|
|
if (idx % 2 == 0)
|
|
{
|
|
// this is the right child of its parent and by invariant we are missing the left child
|
|
hashes[(idx-1)/2] = two_to_one_CRH<HashT>(hash_defaults[layer], hash);
|
|
}
|
|
else
|
|
{
|
|
if (std::next(it) == last_it || std::next(it)->first != idx + 1)
|
|
{
|
|
// this is the left child of its parent and is missing its right child
|
|
hashes[(idx-1)/2] = two_to_one_CRH<HashT>(hash, hash_defaults[layer]);
|
|
}
|
|
else
|
|
{
|
|
// typical case: this is the left child of the parent and adjecent to it there is a right child
|
|
hashes[(idx-1)/2] = two_to_one_CRH<HashT>(hash, std::next(it)->second);
|
|
++it;
|
|
}
|
|
}
|
|
}
|
|
|
|
last_it = next_last_it;
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename HashT>
|
|
bit_vector merkle_tree<HashT>::get_value(const size_t address) const
|
|
{
|
|
assert(log2(address) <= depth);
|
|
|
|
auto it = values.find(address);
|
|
bit_vector padded_result = (it == values.end() ? bit_vector(digest_size) : it->second);
|
|
padded_result.resize(value_size);
|
|
|
|
return padded_result;
|
|
}
|
|
|
|
template<typename HashT>
|
|
void merkle_tree<HashT>::set_value(const size_t address,
|
|
const bit_vector &value)
|
|
{
|
|
assert(log2(address) <= depth);
|
|
size_t idx = address + (1ul<<depth) - 1;
|
|
|
|
assert(value.size() == value_size);
|
|
values[address] = value;
|
|
hashes[idx] = value;
|
|
hashes[idx].resize(digest_size);
|
|
|
|
for (int layer = depth-1; layer >=0; --layer)
|
|
{
|
|
idx = (idx-1)/2;
|
|
|
|
auto it = hashes.find(2*idx+1);
|
|
hash_value_type l = (it == hashes.end() ? hash_defaults[layer+1] : it->second);
|
|
|
|
it = hashes.find(2*idx+2);
|
|
hash_value_type r = (it == hashes.end() ? hash_defaults[layer+1] : it->second);
|
|
|
|
hash_value_type h = two_to_one_CRH<HashT>(l, r);
|
|
hashes[idx] = h;
|
|
}
|
|
}
|
|
|
|
template<typename HashT>
|
|
typename HashT::hash_value_type merkle_tree<HashT>::get_root() const
|
|
{
|
|
auto it = hashes.find(0);
|
|
return (it == hashes.end() ? hash_defaults[0] : it->second);
|
|
}
|
|
|
|
template<typename HashT>
|
|
typename HashT::merkle_authentication_path_type merkle_tree<HashT>::get_path(const size_t address) const
|
|
{
|
|
typename HashT::merkle_authentication_path_type result(depth);
|
|
assert(log2(address) <= depth);
|
|
size_t idx = address + (1ul<<depth) - 1;
|
|
|
|
for (size_t layer = depth; layer > 0; --layer)
|
|
{
|
|
size_t sibling_idx = ((idx + 1) ^ 1) - 1;
|
|
auto it = hashes.find(sibling_idx);
|
|
if (layer == depth)
|
|
{
|
|
auto it2 = values.find(sibling_idx - ((1ul<<depth) - 1));
|
|
result[layer-1] = (it2 == values.end() ? bit_vector(value_size, false) : it2->second);
|
|
result[layer-1].resize(digest_size);
|
|
}
|
|
else
|
|
{
|
|
result[layer-1] = (it == hashes.end() ? hash_defaults[layer] : it->second);
|
|
}
|
|
|
|
idx = (idx-1)/2;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
template<typename HashT>
|
|
void merkle_tree<HashT>::dump() const
|
|
{
|
|
for (size_t i = 0; i < 1ul<<depth; ++i)
|
|
{
|
|
auto it = values.find(i);
|
|
printf("[%zu] -> ", i);
|
|
const bit_vector value = (it == values.end() ? bit_vector(value_size) : it->second);
|
|
for (bool b : value)
|
|
{
|
|
printf("%d", b ? 1 : 0);
|
|
}
|
|
printf("\n");
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
} // libsnark
|
|
|
|
#endif // MERKLE_TREE_TCC
|
|
|