From 17ed7b6bcc7b09087d643c7268b6f19cf91615f1 Mon Sep 17 00:00:00 2001 From: Suyash Bagad Date: Wed, 26 Apr 2023 00:25:41 +0530 Subject: [PATCH] feat(nullifier_tree): make empty nullifier tree leaves hash be 0 (https://github.com/AztecProtocol/barretenberg/pull/360) (https://github.com/AztecProtocol/barretenberg/pull/382) * feat(nullifier_tree): make empty nullifier tree leaves be 0 * fix: add append zero behaviour and test * fix: explicit type name * clean: update class semantics --------- Co-authored-by: Maddiaa <47148561+cheethas@users.noreply.github.com> Co-authored-by: cheethas Co-authored-by: cheethas --- .../nullifier_tree/nullifier_leaf.hpp | 69 +++++- .../nullifier_tree/nullifier_memory_tree.cpp | 34 ++- .../nullifier_tree/nullifier_memory_tree.hpp | 10 +- .../nullifier_memory_tree.test.cpp | 216 ++++++++++++++++-- .../nullifier_tree/nullifier_tree.cpp | 23 +- .../nullifier_tree/nullifier_tree.hpp | 2 +- 6 files changed, 304 insertions(+), 50 deletions(-) diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_leaf.hpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_leaf.hpp index bac3478e6a8..d7080b48bbe 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_leaf.hpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_leaf.hpp @@ -40,14 +40,79 @@ struct nullifier_leaf { barretenberg::fr hash() const { return stdlib::merkle_tree::hash_multiple_native({ value, nextIndex, nextValue }); } }; -inline std::pair find_closest_leaf(std::vector const& leaves_, fr const& new_value) +/** + * @brief Wrapper for the Nullifier leaf class that allows for 0 values + * + */ +class WrappedNullifierLeaf { + + public: + // Initialise with a nullifier leaf + WrappedNullifierLeaf(nullifier_leaf value) + : data(value) + {} + // Initialise an empty leaf + WrappedNullifierLeaf() + : data(std::nullopt) + {} + + bool operator==(WrappedNullifierLeaf const&) const = default; + + /** + * @brief Pass through the underlying std::optional method + * + * @return true + * @return false + */ + bool has_value() const { return data.has_value(); } + + /** + * @brief Return the wrapped nullifier_leaf object + * + * @return nullifier_leaf + */ + nullifier_leaf unwrap() const { return data.value(); } + + /** + * @brief Set the wrapped nullifier_leaf object value + * + * @param value + */ + void set(nullifier_leaf value) { data.emplace(value); } + + /** + * @brief Return the hash of the wrapped object, other return the zero hash of 0 + * + * @return barretenberg::fr + */ + barretenberg::fr hash() const { return data.has_value() ? data.value().hash() : barretenberg::fr::zero(); } + + /** + * @brief Generate a zero leaf (call the constructor with no arguments) + * + * @return NullifierLeaf + */ + static WrappedNullifierLeaf zero() { return WrappedNullifierLeaf(); } + + private: + // Underlying data + std::optional data; +}; + +inline std::pair find_closest_leaf(std::vector const& leaves_, fr const& new_value) { std::vector diff; bool repeated = false; auto new_value_ = uint256_t(new_value); for (size_t i = 0; i < leaves_.size(); i++) { - auto leaf_value_ = uint256_t(leaves_[i].value); + + if (!leaves_[i].has_value()) { + diff.push_back(new_value_); + continue; + } + + auto leaf_value_ = uint256_t(leaves_[i].unwrap().value); if (leaf_value_ > new_value_) { diff.push_back(leaf_value_); } else if (leaf_value_ == new_value_) { diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.cpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.cpp index 09398f5a5a9..f070ab2dedb 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.cpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.cpp @@ -12,11 +12,8 @@ NullifierMemoryTree::NullifierMemoryTree(size_t depth) total_size_ = 1UL << depth_; hashes_.resize(total_size_ * 2 - 2); - // Build the entire tree. - nullifier_leaf zero_leaf = { 0, 0, 0 }; - leaves_.push_back(zero_leaf); - auto current = zero_leaf.hash(); - update_element(0, current); + // Build the entire tree and fill with 0 hashes. + auto current = WrappedNullifierLeaf::zero().hash(); size_t layer_size = total_size_; for (size_t offset = 0; offset < hashes_.size(); offset += layer_size, layer_size /= 2) { for (size_t i = 0; i < layer_size; ++i) { @@ -25,30 +22,45 @@ NullifierMemoryTree::NullifierMemoryTree(size_t depth) current = hash_pair_native(current, current); } - root_ = current; + // Insert the initial leaf at index 0 + auto initial_leaf = WrappedNullifierLeaf(nullifier_leaf{ .value = 0, .nextIndex = 0, .nextValue = 0 }); + leaves_.push_back(initial_leaf); + root_ = update_element(0, initial_leaf.hash()); } fr NullifierMemoryTree::update_element(fr const& value) { // Find the leaf with the value closest and less than `value` + + // If value is 0 we simply append 0 a null NullifierLeaf to the tree + if (value == 0) { + auto zero_leaf = WrappedNullifierLeaf::zero(); + leaves_.push_back(zero_leaf); + return update_element(leaves_.size() - 1, zero_leaf.hash()); + } + size_t current; bool is_already_present; std::tie(current, is_already_present) = find_closest_leaf(leaves_, value); + nullifier_leaf current_leaf = leaves_[current].unwrap(); nullifier_leaf new_leaf = { .value = value, - .nextIndex = leaves_[current].nextIndex, - .nextValue = leaves_[current].nextValue }; + .nextIndex = current_leaf.nextIndex, + .nextValue = current_leaf.nextValue }; + if (!is_already_present) { // Update the current leaf to point it to the new leaf - leaves_[current].nextIndex = leaves_.size(); - leaves_[current].nextValue = value; + current_leaf.nextIndex = leaves_.size(); + current_leaf.nextValue = value; + + leaves_[current].set(current_leaf); // Insert the new leaf with (nextIndex, nextValue) of the current leaf leaves_.push_back(new_leaf); } // Update the old leaf in the tree - auto old_leaf_hash = leaves_[current].hash(); + auto old_leaf_hash = current_leaf.hash(); size_t old_leaf_index = current; auto root = update_element(old_leaf_index, old_leaf_hash); diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.hpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.hpp index 3a41d691944..d40b2ea2bc5 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.hpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.hpp @@ -68,6 +68,7 @@ using namespace barretenberg; * nextVal 10 50 20 30 0 0 0 0 */ class NullifierMemoryTree : public MemoryTree { + public: NullifierMemoryTree(size_t depth); @@ -78,15 +79,18 @@ class NullifierMemoryTree : public MemoryTree { fr update_element(fr const& value); const std::vector& get_hashes() { return hashes_; } - const std::vector& get_leaves() { return leaves_; } - const nullifier_leaf& get_leaf(size_t index) { return leaves_[index]; } + const WrappedNullifierLeaf get_leaf(size_t index) + { + return (index < leaves_.size()) ? leaves_[index] : WrappedNullifierLeaf::zero(); + } + const std::vector& get_leaves() { return leaves_; } protected: using MemoryTree::depth_; using MemoryTree::hashes_; using MemoryTree::root_; using MemoryTree::total_size_; - std::vector leaves_; + std::vector leaves_; }; } // namespace merkle_tree diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.test.cpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.test.cpp index b404519fd4d..1a5b5bfae83 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.test.cpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_memory_tree.test.cpp @@ -62,8 +62,8 @@ TEST(crypto_nullifier_tree, test_nullifier_memory) */ tree.update_element(30); EXPECT_EQ(tree.get_leaves().size(), 2); - EXPECT_EQ(tree.get_leaves()[0].hash(), nullifier_leaf({ 0, 1, 30 }).hash()); - EXPECT_EQ(tree.get_leaves()[1].hash(), nullifier_leaf({ 30, 0, 0 }).hash()); + EXPECT_EQ(tree.get_leaves()[0].hash(), WrappedNullifierLeaf({ 0, 1, 30 }).hash()); + EXPECT_EQ(tree.get_leaves()[1].hash(), WrappedNullifierLeaf({ 30, 0, 0 }).hash()); /** * Add new value 10: @@ -76,9 +76,9 @@ TEST(crypto_nullifier_tree, test_nullifier_memory) */ tree.update_element(10); EXPECT_EQ(tree.get_leaves().size(), 3); - EXPECT_EQ(tree.get_leaves()[0].hash(), nullifier_leaf({ 0, 2, 10 }).hash()); - EXPECT_EQ(tree.get_leaves()[1].hash(), nullifier_leaf({ 30, 0, 0 }).hash()); - EXPECT_EQ(tree.get_leaves()[2].hash(), nullifier_leaf({ 10, 1, 30 }).hash()); + 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, 1, 30 }).hash()); /** * Add new value 20: @@ -91,18 +91,18 @@ TEST(crypto_nullifier_tree, test_nullifier_memory) */ tree.update_element(20); EXPECT_EQ(tree.get_leaves().size(), 4); - EXPECT_EQ(tree.get_leaves()[0].hash(), nullifier_leaf({ 0, 2, 10 }).hash()); - EXPECT_EQ(tree.get_leaves()[1].hash(), nullifier_leaf({ 30, 0, 0 }).hash()); - EXPECT_EQ(tree.get_leaves()[2].hash(), nullifier_leaf({ 10, 3, 20 }).hash()); - EXPECT_EQ(tree.get_leaves()[3].hash(), nullifier_leaf({ 20, 1, 30 }).hash()); + 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()); // Adding the same value must not affect anything tree.update_element(20); EXPECT_EQ(tree.get_leaves().size(), 4); - EXPECT_EQ(tree.get_leaves()[0].hash(), nullifier_leaf({ 0, 2, 10 }).hash()); - EXPECT_EQ(tree.get_leaves()[1].hash(), nullifier_leaf({ 30, 0, 0 }).hash()); - EXPECT_EQ(tree.get_leaves()[2].hash(), nullifier_leaf({ 10, 3, 20 }).hash()); - EXPECT_EQ(tree.get_leaves()[3].hash(), nullifier_leaf({ 20, 1, 30 }).hash()); + 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()); /** * Add new value 50: @@ -115,11 +115,11 @@ TEST(crypto_nullifier_tree, test_nullifier_memory) */ tree.update_element(50); EXPECT_EQ(tree.get_leaves().size(), 5); - EXPECT_EQ(tree.get_leaves()[0].hash(), nullifier_leaf({ 0, 2, 10 }).hash()); - EXPECT_EQ(tree.get_leaves()[1].hash(), nullifier_leaf({ 30, 4, 50 }).hash()); - EXPECT_EQ(tree.get_leaves()[2].hash(), nullifier_leaf({ 10, 3, 20 }).hash()); - EXPECT_EQ(tree.get_leaves()[3].hash(), nullifier_leaf({ 20, 1, 30 }).hash()); - EXPECT_EQ(tree.get_leaves()[4].hash(), nullifier_leaf({ 50, 0, 0 }).hash()); + EXPECT_EQ(tree.get_leaves()[0].hash(), WrappedNullifierLeaf({ 0, 2, 10 }).hash()); + EXPECT_EQ(tree.get_leaves()[1].hash(), WrappedNullifierLeaf({ 30, 4, 50 }).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()); + EXPECT_EQ(tree.get_leaves()[4].hash(), WrappedNullifierLeaf({ 50, 0, 0 }).hash()); // Manually compute the node values auto e000 = tree.get_leaves()[0].hash(); @@ -127,9 +127,9 @@ TEST(crypto_nullifier_tree, test_nullifier_memory) auto e010 = tree.get_leaves()[2].hash(); auto e011 = tree.get_leaves()[3].hash(); auto e100 = tree.get_leaves()[4].hash(); - auto e101 = nullifier_leaf({ 0, 0, 0 }).hash(); - auto e110 = nullifier_leaf({ 0, 0, 0 }).hash(); - auto e111 = nullifier_leaf({ 0, 0, 0 }).hash(); + auto e101 = WrappedNullifierLeaf::zero().hash(); + auto e110 = WrappedNullifierLeaf::zero().hash(); + auto e111 = WrappedNullifierLeaf::zero().hash(); auto e00 = hash_pair_native(e000, e001); auto e01 = hash_pair_native(e010, e011); @@ -161,6 +161,172 @@ TEST(crypto_nullifier_tree, test_nullifier_memory) EXPECT_EQ(tree.get_hash_path(7), expected); } +TEST(crypto_nullifier_tree, test_nullifier_memory_appending_zero) +{ + // Create a depth-3 indexed merkle tree + constexpr size_t depth = 3; + NullifierMemoryTree tree(depth); + + /** + * Intial state: + * + * index 0 1 2 3 4 5 6 7 + * --------------------------------------------------------------------- + * val 0 0 0 0 0 0 0 0 + * nextIdx 0 0 0 0 0 0 0 0 + * nextVal 0 0 0 0 0 0 0 0 + */ + WrappedNullifierLeaf zero_leaf = WrappedNullifierLeaf({ 0, 0, 0 }); + EXPECT_EQ(tree.get_leaves().size(), 1); + EXPECT_EQ(tree.get_leaves()[0], zero_leaf); + + /** + * Add new value 30: + * + * index 0 1 2 3 4 5 6 7 + * --------------------------------------------------------------------- + * val 0 30 0 0 0 0 0 0 + * nextIdx 1 0 0 0 0 0 0 0 + * nextVal 30 0 0 0 0 0 0 0 + */ + tree.update_element(30); + EXPECT_EQ(tree.get_leaves().size(), 2); + EXPECT_EQ(tree.get_leaves()[0].hash(), WrappedNullifierLeaf({ 0, 1, 30 }).hash()); + EXPECT_EQ(tree.get_leaves()[1].hash(), WrappedNullifierLeaf({ 30, 0, 0 }).hash()); + + /** + * Add new value 10: + * + * index 0 1 2 3 4 5 6 7 + * --------------------------------------------------------------------- + * val 0 30 10 0 0 0 0 0 + * nextIdx 2 0 1 0 0 0 0 0 + * nextVal 10 0 30 0 0 0 0 0 + */ + tree.update_element(10); + EXPECT_EQ(tree.get_leaves().size(), 3); + 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, 1, 30 }).hash()); + + /** + * Add new value 20: + * + * 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(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()); + + // Adding the same value must not affect anything + 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()); + + /** + * Add new value 0: + * + * 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(0); + EXPECT_EQ(tree.get_leaves().size(), 5); + 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()); + EXPECT_EQ(tree.get_leaves()[4].hash(), WrappedNullifierLeaf::zero().hash()); + + /* + * Add new value 0: + * + * 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(0); + EXPECT_EQ(tree.get_leaves().size(), 6); + 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()); + EXPECT_EQ(tree.get_leaves()[4].hash(), WrappedNullifierLeaf::zero().hash()); + EXPECT_EQ(tree.get_leaves()[5].hash(), WrappedNullifierLeaf::zero().hash()); + + /** + * Add new value 50: + * + * index 0 1 2 3 4 5 6 7 + * --------------------------------------------------------------------- + * val 0 30 10 20 0 0 50 0 + * nextIdx 2 6 3 1 0 0 0 0 + * nextVal 10 50 20 30 0 0 0 0 + */ + tree.update_element(50); + EXPECT_EQ(tree.get_leaves().size(), 7); + EXPECT_EQ(tree.get_leaf(0).hash(), WrappedNullifierLeaf({ 0, 2, 10 }).hash()); + EXPECT_EQ(tree.get_leaf(1).hash(), WrappedNullifierLeaf({ 30, 6, 50 }).hash()); + EXPECT_EQ(tree.get_leaf(2).hash(), WrappedNullifierLeaf({ 10, 3, 20 }).hash()); + EXPECT_EQ(tree.get_leaf(3).hash(), WrappedNullifierLeaf({ 20, 1, 30 }).hash()); + EXPECT_EQ(tree.get_leaf(4).hash(), WrappedNullifierLeaf::zero().hash()); + EXPECT_EQ(tree.get_leaf(5).hash(), WrappedNullifierLeaf::zero().hash()); + EXPECT_EQ(tree.get_leaf(6).hash(), WrappedNullifierLeaf({ 50, 0, 0 }).hash()); + EXPECT_EQ(tree.get_leaf(7).hash(), WrappedNullifierLeaf::zero().hash()); + + // 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 @@ -190,8 +356,10 @@ TEST(crypto_nullifier_tree, test_nullifier_tree) const auto& leaves = tree.get_leaves(); std::vector differences; for (size_t i = 0; i < leaves.size(); i++) { - uint256_t diff_hi = abs_diff(uint256_t(new_member), uint256_t(leaves[i].value)); - uint256_t diff_lo = abs_diff(uint256_t(new_member), uint256_t(leaves[i].nextValue)); + uint256_t diff_hi = + abs_diff(uint256_t(new_member), uint256_t(leaves[i].has_value() ? leaves[i].unwrap().value : 0)); + uint256_t diff_lo = + abs_diff(uint256_t(new_member), uint256_t(leaves[i].has_value() ? leaves[i].unwrap().nextValue : 0)); differences.push_back(diff_hi + diff_lo); } auto it = std::min_element(differences.begin(), differences.end()); @@ -199,5 +367,5 @@ TEST(crypto_nullifier_tree, test_nullifier_tree) // Merkle proof at `index` proves non-membership of `new_member` auto hash_path = tree.get_hash_path(index); - EXPECT_TRUE(check_hash_path(tree.root(), hash_path, leaves[index], index)); + EXPECT_TRUE(check_hash_path(tree.root(), hash_path, leaves[index].unwrap(), index)); } \ No newline at end of file diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_tree.cpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_tree.cpp index 0a36b8020cf..0280670f151 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_tree.cpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_tree.cpp @@ -29,10 +29,13 @@ NullifierTree::NullifierTree(Store& store, size_t depth, uint8_t tree_id) // Compute the zero values at each layer. // Insert the zero leaf to the `leaves` and also to the tree at index 0. - auto zero_leaf = nullifier_leaf{ .value = 0, .nextIndex = 0, .nextValue = 0 }; - leaves.push_back(zero_leaf); - auto current = zero_leaf.hash(); - update_element(0, current); + WrappedNullifierLeaf initial_leaf = + WrappedNullifierLeaf(nullifier_leaf{ .value = 0, .nextIndex = 0, .nextValue = 0 }); + leaves.push_back(initial_leaf); + update_element(0, initial_leaf.hash()); + + // Create the zero hashes for the tree + auto current = WrappedNullifierLeaf::zero().hash(); for (size_t i = 0; i < depth; ++i) { zero_hashes_[i] = current; current = hash_pair_native(current, current); @@ -53,13 +56,15 @@ template fr NullifierTree::update_element(fr const& valu bool is_already_present; std::tie(current, is_already_present) = find_closest_leaf(leaves, value); - nullifier_leaf new_leaf = { .value = value, - .nextIndex = leaves[current].nextIndex, - .nextValue = leaves[current].nextValue }; + nullifier_leaf current_leaf = leaves[current].unwrap(); + WrappedNullifierLeaf new_leaf = WrappedNullifierLeaf( + { .value = value, .nextIndex = current_leaf.nextIndex, .nextValue = current_leaf.nextValue }); if (!is_already_present) { // Update the current leaf to point it to the new leaf - leaves[current].nextIndex = leaves.size(); - leaves[current].nextValue = value; + current_leaf.nextIndex = leaves.size(); + current_leaf.nextValue = value; + + leaves[current].set(current_leaf); // Insert the new leaf with (nextIndex, nextValue) of the current leaf leaves.push_back(new_leaf); diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_tree.hpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_tree.hpp index aa5af2771a6..4e8155937ab 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_tree.hpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/nullifier_tree/nullifier_tree.hpp @@ -35,7 +35,7 @@ template class NullifierTree : public MerkleTree { using MerkleTree::zero_hashes_; using MerkleTree::depth_; using MerkleTree::tree_id_; - std::vector leaves; + std::vector leaves; }; extern template class NullifierTree;