From 157228931ec23942bf7eb8f07833fea0d4b5e498 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Fri, 26 May 2023 18:47:12 +0000 Subject: [PATCH 1/4] feat: add initial batch insertion initial commit --- .../nullifier_tree/nullifier_leaf.hpp | 38 ++++- .../nullifier_tree/nullifier_memory_tree.cpp | 154 ++++++++++++++++++ .../nullifier_tree/nullifier_memory_tree.hpp | 14 +- .../nullifier_memory_tree.test.cpp | 121 ++++++++++++++ 4 files changed, 318 insertions(+), 9 deletions(-) diff --git a/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_leaf.hpp b/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_leaf.hpp index d7080b48bb..0fd196e484 100644 --- a/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_leaf.hpp +++ b/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_leaf.hpp @@ -13,13 +13,9 @@ struct nullifier_leaf { index_t nextIndex; fr nextValue; - bool operator==(nullifier_leaf const&) const = default; + static nullifier_leaf empty() { return nullifier_leaf{ fr::zero(), 0, fr::zero() }; } - std::ostream& operator<<(std::ostream& os) - { - os << "value = " << value << "\nnextIdx = " << nextIndex << "\nnextVal = " << nextValue; - return os; - } + bool operator==(nullifier_leaf const&) const = default; void read(uint8_t const*& it) { @@ -38,8 +34,17 @@ struct nullifier_leaf { } barretenberg::fr hash() const { return stdlib::merkle_tree::hash_multiple_native({ value, nextIndex, nextValue }); } + + bool is_empty() const { return value == 0 && nextIndex == 0 && nextValue == 0; } }; +inline std::ostream& operator<<(std::ostream& os, nullifier_leaf const& leaf) +{ + return os << "value = " << leaf.value << "\n" + << "nextIdx = " << leaf.nextIndex << "\n" + << "nextVal = " << leaf.nextValue; +} + /** * @brief Wrapper for the Nullifier leaf class that allows for 0 values * @@ -78,7 +83,14 @@ class WrappedNullifierLeaf { * * @param value */ - void set(nullifier_leaf value) { data.emplace(value); } + void set(nullifier_leaf value) + { + if (value.is_empty()) { + data = std::nullopt; + } else { + data.emplace(value); + } + } /** * @brief Return the hash of the wrapped object, other return the zero hash of 0 @@ -99,6 +111,18 @@ class WrappedNullifierLeaf { std::optional data; }; +// TODO(SEAN): Come back to +inline std::ostream& operator<<(std::ostream& os, WrappedNullifierLeaf const& leaf) +{ + if (!leaf.has_value()) { + return os << "value = 0\n" + << "nextIdx = 0\n" + << "nextVal = 0"; + } else { + return os << leaf.unwrap(); + } +} + inline std::pair find_closest_leaf(std::vector const& leaves_, fr const& new_value) { std::vector diff; diff --git a/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.cpp b/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.cpp index f070ab2ded..9be872a740 100644 --- a/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.cpp +++ b/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.cpp @@ -72,6 +72,160 @@ fr NullifierMemoryTree::update_element(fr const& value) return root; } +// Check for a larger value in an array +bool check_has_less_than(std::vector const& values, fr const& value) +{ + // Must perform comparisons on integers + auto const value_as_uint = uint256_t(value); + for (auto const& v : values) { + if (uint256_t(v) < value_as_uint) { + return true; + } + } + return false; +} + +// handle synthetic membership assertions +LowLeafWitnessData NullifierMemoryTree::batch_insert(std::vector const& values) +{ + // Start insertion index + fr const start_insertion_index = this->size(); + + // Low nullifiers + auto values_size = values.size(); + std::vector low_nullifiers(values_size); + std::vector pending_insertion_tree(values_size); + + // Low nullifier sibling paths + std::vector> sibling_paths(values_size); + + // Low nullifier indexes + std::vector low_nullifier_indexes(values_size); + + // Keep track of the currently touched nodes while updating + std::map> touched_nodes; + + // Keep track of 0 values + std::vector const empty_sp(depth_, 0); + auto const empty_leaf = nullifier_leaf::empty(); + uint32_t const empty_index = 0; + + // Find the leaf with the value closest and less than `value` for each value + for (size_t i = 0; i < values.size(); ++i) { + + auto new_value = values[i]; + auto insertion_index = start_insertion_index + i; + + size_t current = 0; + bool is_already_present = false; + std::tie(current, is_already_present) = find_closest_leaf(leaves_, new_value); + + // If the inserted value is 0, then we ignore and provide a dummy low nullifier + if (new_value == 0) { + sibling_paths[i] = empty_sp; + low_nullifier_indexes[i] = empty_index; + low_nullifiers[i] = empty_leaf; + pending_insertion_tree[i] = empty_leaf; + continue; + } + + // If the low_nullifier node has been touched this sub tree insertion, we provide a dummy sibling path + // It will be up to the circuit to check if the included node is valid vs the other nodes that have been + // inserted before it If it has not been touched, we provide a sibling path then update the nodes pointers + auto prev_nodes = touched_nodes.find(current); + + bool has_less_than = false; + if (prev_nodes != touched_nodes.end()) { + has_less_than = check_has_less_than(prev_nodes->second, new_value); + } + // If there is a lower value in the tree, we need to check the current low nullifiers for one that can be used + if (has_less_than) { + for (size_t j = 0; j < pending_insertion_tree.size(); ++j) { + + nullifier_leaf& pending = pending_insertion_tree[j]; + // Skip checking empty values + if (pending.is_empty()) { + continue; + } + + if (uint256_t(pending.value) < uint256_t(new_value) && + (uint256_t(pending.nextValue) > uint256_t(new_value) || pending.nextValue == fr::zero())) { + + // Add a new pending low nullifier for this value + nullifier_leaf const current_low_leaf = { .value = new_value, + .nextIndex = pending_insertion_tree[j].nextIndex, + .nextValue = pending_insertion_tree[j].nextValue }; + + pending_insertion_tree[i] = current_low_leaf; + + // Update the pending low nullifier to point at the new value + pending.nextValue = new_value; + pending.nextIndex = insertion_index; + + break; + } + } + + // add empty low nullifier + sibling_paths[i] = empty_sp; + low_nullifier_indexes[i] = empty_index; + low_nullifiers[i] = empty_leaf; + } else { + // Update the touched mapping + if (prev_nodes == touched_nodes.end()) { + std::vector const new_touched_values = { new_value }; + touched_nodes[current] = new_touched_values; + } else { + prev_nodes->second.push_back(new_value); + } + + nullifier_leaf const low_nullifier = leaves_[current].unwrap(); + std::vector const sibling_path = this->get_sibling_path(current); + + sibling_paths[i] = sibling_path; + low_nullifier_indexes[i] = static_cast(current); + low_nullifiers[i] = low_nullifier; + + // TODO(SEAN): rename this and new leaf + nullifier_leaf insertion_leaf = { .value = new_value, + .nextIndex = low_nullifier.nextIndex, + .nextValue = low_nullifier.nextValue }; + pending_insertion_tree[i] = insertion_leaf; + + // Update the current low nullifier + nullifier_leaf const new_leaf = { .value = low_nullifier.value, + .nextIndex = insertion_index, + .nextValue = new_value }; + + // Update the old leaf in the tree + // update old value in tree + update_element_in_place(current, new_leaf); + } + + info("pending at index ", i, " ", pending_insertion_tree); + } + + // resize leaves array + this->leaves_.resize(this->leaves_.size() + pending_insertion_tree.size()); + for (size_t i = 0; i < pending_insertion_tree.size(); ++i) { + nullifier_leaf const pending = pending_insertion_tree[i]; + + // Update the old leaf in the tree + // update old value in tree + update_element_in_place(size_t(start_insertion_index) + i, pending); + } + + // Return tuple of low nullifiers and sibling paths + return std::make_tuple(low_nullifiers, sibling_paths, low_nullifier_indexes); +} + +// Update the value of a leaf in place +fr NullifierMemoryTree::update_element_in_place(size_t index, const nullifier_leaf& leaf) +{ + this->leaves_[index].set(leaf); + return update_element(index, leaf.hash()); +} + } // namespace merkle_tree } // namespace stdlib } // namespace proof_system::plonk \ No newline at end of file diff --git a/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.hpp b/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.hpp index d40b2ea2bc..9cc3045ba1 100644 --- a/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.hpp +++ b/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.hpp @@ -7,6 +7,9 @@ namespace proof_system::plonk { namespace stdlib { namespace merkle_tree { +// tuple(nullifier_leaf, sibling_path, index) // utility alias +using LowLeafWitnessData = std::tuple, std::vector>, std::vector>; + using namespace barretenberg; /** @@ -76,8 +79,10 @@ class NullifierMemoryTree : public MemoryTree { using MemoryTree::root; using MemoryTree::update_element; - fr update_element(fr const& value); - + // Inspectors + fr size() { return leaves_.size(); } + fr total_size() const { return total_size_; } + fr depth() const { return depth_; } const std::vector& get_hashes() { return hashes_; } const WrappedNullifierLeaf get_leaf(size_t index) { @@ -85,6 +90,11 @@ class NullifierMemoryTree : public MemoryTree { } const std::vector& get_leaves() { return leaves_; } + // Mutators + fr update_element(fr const& value); + fr update_element_in_place(size_t index, const nullifier_leaf& value); + LowLeafWitnessData batch_insert(std::vector const& values); + protected: using MemoryTree::depth_; using MemoryTree::hashes_; diff --git a/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.test.cpp b/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.test.cpp index 1a5b5bfae8..9890706307 100644 --- a/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.test.cpp +++ b/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.test.cpp @@ -327,6 +327,127 @@ TEST(crypto_nullifier_tree, test_nullifier_memory_appending_zero) EXPECT_EQ(tree.get_hash_path(6), expected); EXPECT_EQ(tree.get_hash_path(7), expected); } + +TEST(crypto_nullifier_tree, test_nullifier_memory_tree_batch_insert) +{ + // Create a depth-3 indexed merkle tree + constexpr size_t depth = 3; + NullifierMemoryTree tree(depth); + + /** + * Initial State: + * + * index 0 1 2 3 4 5 6 7 + * --------------------------------------------------------------------- + * val 0 30 10 20 0 0 0 0 + * nextIdx 2 0 3 1 0 0 0 0 + * nextVal 10 0 20 30 0 0 0 0 + */ + tree.update_element(30); + tree.update_element(10); + tree.update_element(20); + + EXPECT_EQ(tree.get_leaves().size(), 4); + EXPECT_EQ(tree.get_leaves()[0].hash(), WrappedNullifierLeaf({ 0, 2, 10 }).hash()); + EXPECT_EQ(tree.get_leaves()[1].hash(), WrappedNullifierLeaf({ 30, 0, 0 }).hash()); + EXPECT_EQ(tree.get_leaves()[2].hash(), WrappedNullifierLeaf({ 10, 3, 20 }).hash()); + EXPECT_EQ(tree.get_leaves()[3].hash(), WrappedNullifierLeaf({ 20, 1, 30 }).hash()); + + // Perform batch insertion + // What should we expect? + // 50's low nullifier should be the value 30, index 1 + // 25's low nullifier should be the value 20, index 3, sibling path will be such that the low nullifier for 30 has + // been updated! 80's low nullifier should be the value 30 - this is already used, so it will return 0 75's low + // nullifier should be the value 30 - this is already used, so it will return 0 + std::vector batch_values = { 50, 25, 80, 75 }; + LowLeafWitnessData low_leaf_witnesses = tree.batch_insert(batch_values); + + // Destructure return type + std::vector low_leaves = std::get<0>(low_leaf_witnesses); + std::vector> low_leaf_sibling_paths = std::get<1>(low_leaf_witnesses); + std::vector low_leaf_witness_indexes = std::get<2>(low_leaf_witnesses); + + /** + * State after batch insertion: + * + * index 0 1 2 3 4 5 6 7 + * --------------------------------------------------------------------- + * val 0 30 10 20 50 25 80 75 + * nextIdx 2 4 3 5 7 1 0 6 + * nextVal 10 50 20 25 75 30 0 80 + */ + // Check that insertions have been performed correctly + auto leaves = tree.get_leaves(); + + info("\n\n"); + info(leaves); + + info("\n\n"); + EXPECT_EQ(leaves.size(), 8); + EXPECT_EQ(leaves[0].hash(), WrappedNullifierLeaf({ 0, 2, 10 }).hash()); + EXPECT_EQ(leaves[1].hash(), WrappedNullifierLeaf({ 30, 4, 50 }).hash()); + EXPECT_EQ(leaves[2].hash(), WrappedNullifierLeaf({ 10, 3, 20 }).hash()); + EXPECT_EQ(leaves[3].hash(), WrappedNullifierLeaf({ 20, 5, 25 }).hash()); + EXPECT_EQ(leaves[4].hash(), WrappedNullifierLeaf({ 50, 7, 75 }).hash()); + EXPECT_EQ(leaves[5].hash(), WrappedNullifierLeaf({ 25, 1, 30 }).hash()); + EXPECT_EQ(leaves[6].hash(), WrappedNullifierLeaf({ 80, 0, 0 }).hash()); + EXPECT_EQ(leaves[7].hash(), WrappedNullifierLeaf({ 75, 6, 80 }).hash()); + + // Compute tree node values for each insertion + + // auto e00 = hash_pair_native(e000, e001); + // auto e01 = hash_pair_native(e010, e011); + // auto e10 = hash_pair_native(e100, e101); + // auto e11 = hash_pair_native(e110, e111); + + // auto e0 = hash_pair_native(e00, e01); + // auto e1 = hash_pair_native(e10, e11); + // auto root = hash_pair_native(e0, e1); + + // info(low_leaves); + // info(low_leaf_sibling_paths); + // info(low_leaf_witness_indexes); + + // // Manually compute the node values + // auto e000 = tree.get_leaf(0).hash(); + // auto e001 = tree.get_leaf(1).hash(); + // auto e010 = tree.get_leaf(2).hash(); + // auto e011 = tree.get_leaf(3).hash(); + // auto e100 = tree.get_leaf(4).hash(); + // auto e101 = tree.get_leaf(5).hash(); + // auto e110 = tree.get_leaf(6).hash(); + // auto e111 = tree.get_leaf(7).hash(); + + // auto e00 = hash_pair_native(e000, e001); + // auto e01 = hash_pair_native(e010, e011); + // auto e10 = hash_pair_native(e100, e101); + // auto e11 = hash_pair_native(e110, e111); + + // auto e0 = hash_pair_native(e00, e01); + // auto e1 = hash_pair_native(e10, e11); + // auto root = hash_pair_native(e0, e1); + + // // Check the hash path at index 2 and 3 + // // Note: This merkle proof would also serve as a non-membership proof of values in (10, 20) and (20, 30) + // fr_hash_path expected = { + // std::make_pair(e010, e011), + // std::make_pair(e00, e01), + // std::make_pair(e0, e1), + // }; + // EXPECT_EQ(tree.get_hash_path(2), expected); + // EXPECT_EQ(tree.get_hash_path(3), expected); + // EXPECT_EQ(tree.root(), root); + + // // Check the hash path at index 6 and 7 + // expected = { + // std::make_pair(e110, e111), + // std::make_pair(e10, e11), + // std::make_pair(e0, e1), + // }; + // EXPECT_EQ(tree.get_hash_path(6), expected); + // EXPECT_EQ(tree.get_hash_path(7), expected); +} + TEST(crypto_nullifier_tree, test_nullifier_tree) { // Create a depth-8 indexed merkle tree From 8f03340d2beea72fa610c324b2caac4e9327a390 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Sun, 25 Jun 2023 23:55:50 +0000 Subject: [PATCH 2/4] feat: batch insertion test --- .../nullifier_tree/nullifier_leaf.hpp | 1 - .../nullifier_memory_tree.test.cpp | 97 ++++++++----------- 2 files changed, 40 insertions(+), 58 deletions(-) diff --git a/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_leaf.hpp b/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_leaf.hpp index 0fd196e484..8c88cc9ca2 100644 --- a/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_leaf.hpp +++ b/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_leaf.hpp @@ -111,7 +111,6 @@ class WrappedNullifierLeaf { std::optional data; }; -// TODO(SEAN): Come back to inline std::ostream& operator<<(std::ostream& os, WrappedNullifierLeaf const& leaf) { if (!leaf.has_value()) { diff --git a/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.test.cpp b/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.test.cpp index 9890706307..ffec436c75 100644 --- a/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.test.cpp +++ b/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.test.cpp @@ -379,10 +379,6 @@ TEST(crypto_nullifier_tree, test_nullifier_memory_tree_batch_insert) // Check that insertions have been performed correctly auto leaves = tree.get_leaves(); - info("\n\n"); - info(leaves); - - info("\n\n"); EXPECT_EQ(leaves.size(), 8); EXPECT_EQ(leaves[0].hash(), WrappedNullifierLeaf({ 0, 2, 10 }).hash()); EXPECT_EQ(leaves[1].hash(), WrappedNullifierLeaf({ 30, 4, 50 }).hash()); @@ -393,59 +389,46 @@ TEST(crypto_nullifier_tree, test_nullifier_memory_tree_batch_insert) EXPECT_EQ(leaves[6].hash(), WrappedNullifierLeaf({ 80, 0, 0 }).hash()); EXPECT_EQ(leaves[7].hash(), WrappedNullifierLeaf({ 75, 6, 80 }).hash()); - // Compute tree node values for each insertion - - // auto e00 = hash_pair_native(e000, e001); - // auto e01 = hash_pair_native(e010, e011); - // auto e10 = hash_pair_native(e100, e101); - // auto e11 = hash_pair_native(e110, e111); - - // auto e0 = hash_pair_native(e00, e01); - // auto e1 = hash_pair_native(e10, e11); - // auto root = hash_pair_native(e0, e1); - - // info(low_leaves); - // info(low_leaf_sibling_paths); - // info(low_leaf_witness_indexes); - - // // Manually compute the node values - // auto e000 = tree.get_leaf(0).hash(); - // auto e001 = tree.get_leaf(1).hash(); - // auto e010 = tree.get_leaf(2).hash(); - // auto e011 = tree.get_leaf(3).hash(); - // auto e100 = tree.get_leaf(4).hash(); - // auto e101 = tree.get_leaf(5).hash(); - // auto e110 = tree.get_leaf(6).hash(); - // auto e111 = tree.get_leaf(7).hash(); - - // auto e00 = hash_pair_native(e000, e001); - // auto e01 = hash_pair_native(e010, e011); - // auto e10 = hash_pair_native(e100, e101); - // auto e11 = hash_pair_native(e110, e111); - - // auto e0 = hash_pair_native(e00, e01); - // auto e1 = hash_pair_native(e10, e11); - // auto root = hash_pair_native(e0, e1); - - // // Check the hash path at index 2 and 3 - // // Note: This merkle proof would also serve as a non-membership proof of values in (10, 20) and (20, 30) - // fr_hash_path expected = { - // std::make_pair(e010, e011), - // std::make_pair(e00, e01), - // std::make_pair(e0, e1), - // }; - // EXPECT_EQ(tree.get_hash_path(2), expected); - // EXPECT_EQ(tree.get_hash_path(3), expected); - // EXPECT_EQ(tree.root(), root); - - // // Check the hash path at index 6 and 7 - // expected = { - // std::make_pair(e110, e111), - // std::make_pair(e10, e11), - // std::make_pair(e0, e1), - // }; - // EXPECT_EQ(tree.get_hash_path(6), expected); - // EXPECT_EQ(tree.get_hash_path(7), expected); + // Check the correct low leaf witness indexes have been returned. + // 50's low index is 30, 25's low index is 20, 80's low index is 30 (as it is being revisited it is 0), + // 75's low index is 30 (as it being revisited it is 0 ) + auto expected_low_leaf_witness_indexes = std::vector{ 1, 3, 0, 0 }; + EXPECT_EQ(low_leaf_witness_indexes, expected_low_leaf_witness_indexes); + + // Check the low nullifier for the first insertion + EXPECT_EQ(low_leaves[0], WrappedNullifierLeaf({ 30, 0, 0 })); + + // Below, nodes are indexed by their layer, then their index e.g. e12 -> layer 1, index 2 + auto zero_leaf_hash = WrappedNullifierLeaf().hash(); + auto e00 = WrappedNullifierLeaf({ 0, 2, 10 }).hash(); + auto e02 = WrappedNullifierLeaf({ 10, 3, 20 }).hash(); + auto e03 = WrappedNullifierLeaf({ 20, 1, 30 }).hash(); + + auto e11 = hash_pair_native(e02, e03); + auto e12 = hash_pair_native(zero_leaf_hash, zero_leaf_hash); + auto e13 = hash_pair_native(zero_leaf_hash, zero_leaf_hash); + + auto e21 = hash_pair_native(e12, e13); + + auto expected_low_leaf_sibling_path = std::vector{ e00, e11, e21 }; + EXPECT_EQ(low_leaf_sibling_paths[0], expected_low_leaf_sibling_path); + + // Check the low nullifier for the second insertion + EXPECT_EQ(low_leaves[1], nullifier_leaf({ 20, 1, 30 })); + + // Update sibling paths after first low nullifier update below. + auto updated_e01 = WrappedNullifierLeaf({ 30, 4, 50 }).hash(); + auto updated_e10 = hash_pair_native(e00, updated_e01); + + auto expected_second_low_leaf_sibling_path = std::vector{ e02, updated_e10, e21 }; + EXPECT_EQ(low_leaf_sibling_paths[1], expected_second_low_leaf_sibling_path); + + // Check the remaining insertions are zero paths + auto zero_path = std::vector{ fr::zero(), fr::zero(), fr::zero() }; + EXPECT_EQ(low_leaves[2], nullifier_leaf()); + EXPECT_EQ(low_leaf_sibling_paths[2], zero_path); + EXPECT_EQ(low_leaves[3], nullifier_leaf()); + EXPECT_EQ(low_leaf_sibling_paths[3], zero_path); } TEST(crypto_nullifier_tree, test_nullifier_tree) From 9417bae653cdfefbb28f9db8b5280cc02b7abfda Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Mon, 26 Jun 2023 00:27:51 +0000 Subject: [PATCH 3/4] fix: change size signature to size_t --- .../merkle_tree/nullifier_tree/nullifier_memory_tree.cpp | 6 ++---- .../merkle_tree/nullifier_tree/nullifier_memory_tree.hpp | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.cpp b/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.cpp index 9be872a740..d83bf54a6b 100644 --- a/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.cpp +++ b/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.cpp @@ -89,7 +89,7 @@ bool check_has_less_than(std::vector const& values, fr const& value) LowLeafWitnessData NullifierMemoryTree::batch_insert(std::vector const& values) { // Start insertion index - fr const start_insertion_index = this->size(); + size_t const start_insertion_index = this->size(); // Low nullifiers auto values_size = values.size(); @@ -201,8 +201,6 @@ LowLeafWitnessData NullifierMemoryTree::batch_insert(std::vector const& valu // update old value in tree update_element_in_place(current, new_leaf); } - - info("pending at index ", i, " ", pending_insertion_tree); } // resize leaves array @@ -212,7 +210,7 @@ LowLeafWitnessData NullifierMemoryTree::batch_insert(std::vector const& valu // Update the old leaf in the tree // update old value in tree - update_element_in_place(size_t(start_insertion_index) + i, pending); + update_element_in_place(start_insertion_index + i, pending); } // Return tuple of low nullifiers and sibling paths diff --git a/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.hpp b/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.hpp index 9cc3045ba1..178779cc3d 100644 --- a/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.hpp +++ b/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.hpp @@ -80,7 +80,7 @@ class NullifierMemoryTree : public MemoryTree { using MemoryTree::update_element; // Inspectors - fr size() { return leaves_.size(); } + size_t size() { return leaves_.size(); } fr total_size() const { return total_size_; } fr depth() const { return depth_; } const std::vector& get_hashes() { return hashes_; } From 0bc31f0fa1d8b6979757f96feebc8d38ce306e07 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Wed, 28 Jun 2023 13:05:43 +0000 Subject: [PATCH 4/4] docs: add doc comment --- .../nullifier_tree/nullifier_memory_tree.cpp | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.cpp b/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.cpp index d83bf54a6b..9dd48c6014 100644 --- a/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.cpp +++ b/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.cpp @@ -85,7 +85,29 @@ bool check_has_less_than(std::vector const& values, fr const& value) return false; } -// handle synthetic membership assertions +/** + * @brief Insert a batch of values into the tree, returning the low nullifiers membership information (leaf, sibling + * path, index) + * + * Special Considerations + * + * Short algorithm description: + * When batch inserting values into the tree, we first update their "low_nullifiers" (the node that will insert to the + * inserted value). + * - For each low nullifier that we update, we need to perform a membership check against the current root of the tree. + * - Once membership is confirmed, we can update the leaf, then use the same sibling path to update the root. + * - The next membership check will be against the new root. + * + * If the low nullifier for a value exists within the batch being inserted, then we cannot perform a membership check, + * as it has not yet been inserted! In this case we provide an all 0 sibling path and all 0 low nullifier, all + * corresponding aztec circuits account for this case. + * + * A description of the algorithm can be found here: + * https://colab.research.google.com/drive/1A0gizduSi4FIiIJZ8OylwIpO9-OTqV-R + * + * @param values - An array of values to insert into the tree. + * @return std::vector + */ LowLeafWitnessData NullifierMemoryTree::batch_insert(std::vector const& values) { // Start insertion index