Skip to content

Commit f5fb79d

Browse files
committed
Update method that counts the number of hashes in merkle tree verification
1 parent cbb63e5 commit f5fb79d

File tree

4 files changed

+39
-27
lines changed

4 files changed

+39
-27
lines changed

libiop/bcs/bcs_common.tcc

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -711,7 +711,7 @@ void print_detailed_transcript_data(
711711

712712
const size_t digest_len_bytes = 2 * (params.security_parameter / 8);
713713
const size_t field_size = (libff::log_of_field_size_helper<FieldT>(FieldT::zero()) + 7) / 8;
714-
std::vector<size_t> two_to_one_hashes_by_round;
714+
std::vector<size_t> internal_hash_complexity_by_round;
715715
std::vector<size_t> leaf_hashes_by_round;
716716
std::vector<size_t> zk_hashes_by_round;
717717
std::vector<size_t> IOP_size_by_round;
@@ -743,10 +743,9 @@ void print_detailed_transcript_data(
743743
query_positions.emplace_back(MT_position);
744744
}
745745
}
746-
size_t num_two_to_one_hashes_in_round =
747-
MT.count_hashes_to_verify_set_membership_proof(
748-
query_positions);
749-
two_to_one_hashes_by_round.emplace_back(num_two_to_one_hashes_in_round);
746+
size_t internal_hash_complexity_in_round =
747+
MT.count_internal_hash_complexity_to_verify_set_membership(query_positions);
748+
internal_hash_complexity_by_round.emplace_back(internal_hash_complexity_in_round);
750749
const size_t num_values_per_leaf = transcript.query_responses_[round][0].size();
751750
const size_t num_leaves = transcript.query_responses_[round].size();
752751
leaf_hashes_by_round.emplace_back(num_values_per_leaf * num_leaves);
@@ -790,14 +789,14 @@ void print_detailed_transcript_data(
790789

791790
printf("\n");
792791
printf("total prover messages size: %lu\n", total_prover_message_size);
793-
const size_t total_two_to_one_hashes = std::accumulate(
794-
two_to_one_hashes_by_round.begin(), two_to_one_hashes_by_round.end(), 0);
792+
const size_t total_internal_hash_complexity = std::accumulate(
793+
internal_hash_complexity_by_round.begin(), internal_hash_complexity_by_round.end(), 0);
795794
const size_t total_leaves_hashed = std::accumulate(
796795
leaf_hashes_by_round.begin(), leaf_hashes_by_round.end(), 0);
797796
const size_t total_zk_hashes = std::accumulate(
798797
zk_hashes_by_round.begin(), zk_hashes_by_round.end(), 0);
799-
const size_t total_hashes = total_two_to_one_hashes + total_leaves_hashed + total_zk_hashes;
800-
printf("total two to one hashes: %lu\n", total_two_to_one_hashes);
798+
const size_t total_hashes = total_internal_hash_complexity + total_leaves_hashed + total_zk_hashes;
799+
printf("total internal hash complexity: %lu\n", total_internal_hash_complexity);
801800
printf("total leaves hashed: %lu\n", total_leaves_hashed);
802801
printf("total hashes: %lu\n", total_hashes);
803802
printf("\n");
@@ -810,7 +809,7 @@ void print_detailed_transcript_data(
810809
printf("MT_depth %lu\n", MT_depths[round]);
811810
printf("IOP size: %lu bytes\n", IOP_size_by_round[round]);
812811
printf("BCS size: %lu bytes\n", BCS_size_by_round[round]);
813-
printf("number of two to one hashes: %lu\n", two_to_one_hashes_by_round[round]);
812+
printf("internal hash complexity: %lu\n", internal_hash_complexity_by_round[round]);
814813
printf("number of leaves hashed: %lu\n", leaf_hashes_by_round[round]);
815814
if (make_zk[round])
816815
{

libiop/bcs/merkle_tree.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,10 @@ class merkle_tree {
136136
const std::vector<std::vector<FieldT>> &leaf_contents,
137137
const merkle_tree_set_membership_proof<hash_digest_type> &proof);
138138

139-
/* Returns number of two to one hashes */
140-
size_t count_hashes_to_verify_set_membership_proof(
139+
/** Returns a number that is proportional to the hashing runtime of verifying a set membership
140+
* proof. Each two-to-one hash is counted as 2 units, and each input of the cap hash is 1 unit.
141+
* Leaf hashes are not counted. */
142+
size_t count_internal_hash_complexity_to_verify_set_membership(
141143
const std::vector<std::size_t> &positions) const;
142144

143145
std::size_t num_leaves() const;

libiop/bcs/merkle_tree.tcc

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -548,35 +548,36 @@ bool merkle_tree<FieldT, hash_digest_type>::validate_set_membership_proof(
548548
}
549549

550550
template<typename FieldT, typename hash_digest_type>
551-
size_t merkle_tree<FieldT, hash_digest_type>::count_hashes_to_verify_set_membership_proof(
551+
size_t merkle_tree<FieldT, hash_digest_type>::count_internal_hash_complexity_to_verify_set_membership(
552552
const std::vector<size_t> &positions) const
553553
{
554554
/** This goes layer by layer,
555555
* and counts the number of hashes needed to be computed.
556556
* Essentially when moving up a layer in the verifier,
557557
* every unique parent is one hash that has to be computed. */
558-
size_t num_two_to_one_hashes = 0;
558+
size_t num_two_to_one_hashes = 0;
559559
std::vector<size_t> cur_pos_set = positions;
560560
sort(cur_pos_set.begin(), cur_pos_set.end());
561561
assert(cur_pos_set[cur_pos_set.size() - 1] < this->num_leaves());
562-
for (size_t cur_depth = this->depth(); cur_depth > 0; cur_depth--)
562+
563+
const size_t cap_depth = libff::log2(this->cap_size_);
564+
for (size_t cur_depth = this->depth(); cur_depth > cap_depth; cur_depth--)
563565
{
564566
// contains positions in range [0, 2^{cur_depth - 1})
565567
std::vector<size_t> next_pos_set;
566568
for (size_t i = 0; i < cur_pos_set.size(); i++)
567569
{
568570
size_t parent_pos = cur_pos_set[i] / 2;
569571
// Check that parent pos isn't already in next pos set
570-
if (next_pos_set.size() == 0
571-
|| next_pos_set[next_pos_set.size() - 1] != parent_pos)
572+
if (next_pos_set.size() == 0 || next_pos_set[next_pos_set.size() - 1] != parent_pos)
572573
{
573574
next_pos_set.emplace_back(parent_pos);
574575
}
575576
}
576577
num_two_to_one_hashes += next_pos_set.size();
577578
cur_pos_set = next_pos_set;
578579
}
579-
return num_two_to_one_hashes;
580+
return 2 * num_two_to_one_hashes + this->cap_size_;
580581
}
581582

582583
template<typename FieldT, typename hash_digest_type>

libiop/tests/bcs/test_merkle_tree.cpp

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,8 @@ TEST(MerkleTreeZKTest, RandomMultiTest) {
295295
run_random_multi_test(1ull << 16, digest_len_bytes, make_zk, security_parameter, 256, 100);
296296
}
297297

298+
/** Verify that count_internal_hash_complexity_to_verify_set_membership is correct for a fixed tree
299+
* size and query set, for various cap sizes. */
298300
TEST(MerkleTreeHashCountTest, SimpleTest)
299301
{
300302
typedef libff::gf64 FieldT;
@@ -304,16 +306,24 @@ TEST(MerkleTreeHashCountTest, SimpleTest)
304306
const size_t hash_length = 32;
305307
const bool algebraic_hash = false;
306308

307-
merkle_tree<FieldT, binary_hash_digest> tree = new_MT<FieldT, binary_hash_digest>(
308-
num_leaves,
309-
hash_length,
310-
make_zk,
311-
security_parameter);
309+
const std::vector<size_t> cap_sizes = {2, 4, 8};
310+
const std::vector<size_t> expected_num_hashes = {12, 10, 8};
311+
312+
const std::vector<size_t> positions = {1, 3, 6, 7};
312313

313-
std::vector<size_t> positions = {1, 3, 6, 7};
314-
size_t expected_num_hashes = 6;
315-
size_t actual_num_hashes = tree.count_hashes_to_verify_set_membership_proof(positions);
316-
ASSERT_EQ(expected_num_hashes, actual_num_hashes);
314+
for (size_t i = 0; i < cap_sizes.size(); i++)
315+
{
316+
merkle_tree<FieldT, binary_hash_digest> tree = new_MT<FieldT, binary_hash_digest>(
317+
num_leaves,
318+
hash_length,
319+
make_zk,
320+
security_parameter,
321+
cap_sizes[i]);
322+
323+
size_t actual_num_hashes = tree.count_internal_hash_complexity_to_verify_set_membership(
324+
positions);
325+
ASSERT_EQ(expected_num_hashes[i], actual_num_hashes);
326+
}
317327
}
318328

319329
}

0 commit comments

Comments
 (0)