Original HUSH source code based on ZEC 1.0.8 . For historical purposes only! https://hush.is
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.
 
 
 
 
 
 

196 lines
8.3 KiB

/** @file
*****************************************************************************
Implementation of interfaces for the Merkle tree check read.
See merkle_tree_check_read_gadget.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_CHECK_READ_GADGET_TCC_
#define MERKLE_TREE_CHECK_READ_GADGET_TCC_
namespace libsnark {
template<typename FieldT, typename HashT>
merkle_tree_check_read_gadget<FieldT, HashT>::merkle_tree_check_read_gadget(protoboard<FieldT> &pb,
const size_t tree_depth,
const pb_linear_combination_array<FieldT> &address_bits,
const digest_variable<FieldT> &leaf,
const digest_variable<FieldT> &root,
const merkle_authentication_path_variable<FieldT, HashT> &path,
const pb_linear_combination<FieldT> &read_successful,
const std::string &annotation_prefix) :
gadget<FieldT>(pb, annotation_prefix),
digest_size(HashT::get_digest_len()),
tree_depth(tree_depth),
address_bits(address_bits),
leaf(leaf),
root(root),
path(path),
read_successful(read_successful)
{
/*
The tricky part here is ordering. For Merkle tree
authentication paths, path[0] corresponds to one layer below
the root (and path[tree_depth-1] corresponds to the layer
containing the leaf), while address_bits has the reverse order:
address_bits[0] is LSB, and corresponds to layer containing the
leaf, and address_bits[tree_depth-1] is MSB, and corresponds to
the subtree directly under the root.
*/
assert(tree_depth > 0);
assert(tree_depth == address_bits.size());
for (size_t i = 0; i < tree_depth-1; ++i)
{
internal_output.emplace_back(digest_variable<FieldT>(pb, digest_size, FMT(this->annotation_prefix, " internal_output_%zu", i)));
}
computed_root.reset(new digest_variable<FieldT>(pb, digest_size, FMT(this->annotation_prefix, " computed_root")));
for (size_t i = 0; i < tree_depth; ++i)
{
block_variable<FieldT> inp(pb, path.left_digests[i], path.right_digests[i], FMT(this->annotation_prefix, " inp_%zu", i));
hasher_inputs.emplace_back(inp);
hashers.emplace_back(HashT(pb, 2*digest_size, inp, (i == 0 ? *computed_root : internal_output[i-1]),
FMT(this->annotation_prefix, " load_hashers_%zu", i)));
}
for (size_t i = 0; i < tree_depth; ++i)
{
/*
The propagators take a computed hash value (or leaf in the
base case) and propagate it one layer up, either in the left
or the right slot of authentication_path_variable.
*/
propagators.emplace_back(digest_selector_gadget<FieldT>(pb, digest_size, i < tree_depth - 1 ? internal_output[i] : leaf,
address_bits[tree_depth-1-i], path.left_digests[i], path.right_digests[i],
FMT(this->annotation_prefix, " digest_selector_%zu", i)));
}
check_root.reset(new bit_vector_copy_gadget<FieldT>(pb, computed_root->bits, root.bits, read_successful, FieldT::capacity(), FMT(annotation_prefix, " check_root")));
}
template<typename FieldT, typename HashT>
void merkle_tree_check_read_gadget<FieldT, HashT>::generate_r1cs_constraints()
{
/* ensure correct hash computations */
for (size_t i = 0; i < tree_depth; ++i)
{
// Note that we check root outside and have enforced booleanity of path.left_digests/path.right_digests outside in path.generate_r1cs_constraints
hashers[i].generate_r1cs_constraints(false);
}
/* ensure consistency of path.left_digests/path.right_digests with internal_output */
for (size_t i = 0; i < tree_depth; ++i)
{
propagators[i].generate_r1cs_constraints();
}
check_root->generate_r1cs_constraints(false, false);
}
template<typename FieldT, typename HashT>
void merkle_tree_check_read_gadget<FieldT, HashT>::generate_r1cs_witness()
{
/* do the hash computations bottom-up */
for (int i = tree_depth-1; i >= 0; --i)
{
/* propagate previous input */
propagators[i].generate_r1cs_witness();
/* compute hash */
hashers[i].generate_r1cs_witness();
}
check_root->generate_r1cs_witness();
}
template<typename FieldT, typename HashT>
size_t merkle_tree_check_read_gadget<FieldT, HashT>::root_size_in_bits()
{
return HashT::get_digest_len();
}
template<typename FieldT, typename HashT>
size_t merkle_tree_check_read_gadget<FieldT, HashT>::expected_constraints(const size_t tree_depth)
{
/* NB: this includes path constraints */
const size_t hasher_constraints = tree_depth * HashT::expected_constraints(false);
const size_t propagator_constraints = tree_depth * HashT::get_digest_len();
const size_t authentication_path_constraints = 2 * tree_depth * HashT::get_digest_len();
const size_t check_root_constraints = 3 * div_ceil(HashT::get_digest_len(), FieldT::capacity());
return hasher_constraints + propagator_constraints + authentication_path_constraints + check_root_constraints;
}
template<typename FieldT, typename HashT>
void test_merkle_tree_check_read_gadget()
{
/* prepare test */
const size_t digest_len = HashT::get_digest_len();
const size_t tree_depth = 16;
std::vector<merkle_authentication_node> path(tree_depth);
bit_vector prev_hash(digest_len);
std::generate(prev_hash.begin(), prev_hash.end(), [&]() { return std::rand() % 2; });
bit_vector leaf = prev_hash;
bit_vector address_bits;
size_t address = 0;
for (long level = tree_depth-1; level >= 0; --level)
{
const bool computed_is_right = (std::rand() % 2);
address |= (computed_is_right ? 1ul << (tree_depth-1-level) : 0);
address_bits.push_back(computed_is_right);
bit_vector other(digest_len);
std::generate(other.begin(), other.end(), [&]() { return std::rand() % 2; });
bit_vector block = prev_hash;
block.insert(computed_is_right ? block.begin() : block.end(), other.begin(), other.end());
bit_vector h = HashT::get_hash(block);
path[level] = other;
prev_hash = h;
}
bit_vector root = prev_hash;
/* execute test */
protoboard<FieldT> pb;
pb_variable_array<FieldT> address_bits_va;
address_bits_va.allocate(pb, tree_depth, "address_bits");
digest_variable<FieldT> leaf_digest(pb, digest_len, "input_block");
digest_variable<FieldT> root_digest(pb, digest_len, "output_digest");
merkle_authentication_path_variable<FieldT, HashT> path_var(pb, tree_depth, "path_var");
merkle_tree_check_read_gadget<FieldT, HashT> ml(pb, tree_depth, address_bits_va, leaf_digest, root_digest, path_var, ONE, "ml");
path_var.generate_r1cs_constraints();
ml.generate_r1cs_constraints();
address_bits_va.fill_with_bits(pb, address_bits);
assert(address_bits_va.get_field_element_from_bits(pb).as_ulong() == address);
leaf_digest.generate_r1cs_witness(leaf);
path_var.generate_r1cs_witness(address, path);
ml.generate_r1cs_witness();
/* make sure that read checker didn't accidentally overwrite anything */
address_bits_va.fill_with_bits(pb, address_bits);
leaf_digest.generate_r1cs_witness(leaf);
root_digest.generate_r1cs_witness(root);
assert(pb.is_satisfied());
const size_t num_constraints = pb.num_constraints();
const size_t expected_constraints = merkle_tree_check_read_gadget<FieldT, HashT>::expected_constraints(tree_depth);
assert(num_constraints == expected_constraints);
}
} // libsnark
#endif // MERKLE_TREE_CHECK_READ_GADGET_TCC_