Skip to content

Commit 3476dc3

Browse files
PhilWindleAztecBot
authored and
AztecBot
committed
fix: Don't store indices of zero leaves. (#10270)
This PR ensures we don't store indices of zero leaves as this could be misleading. Requesting the index of a zero leaf is deemed invalid. It also adds tests that ensure we can unwind blocks of zero leaves
1 parent 795057f commit 3476dc3

File tree

4 files changed

+116
-7
lines changed

4 files changed

+116
-7
lines changed

cpp/src/barretenberg/crypto/merkle_tree/append_only_tree/content_addressed_append_only_tree.hpp

+10
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,9 @@ void ContentAddressedAppendOnlyTree<Store, HashingPolicy>::find_leaf_index_from(
671671
auto job = [=, this]() -> void {
672672
execute_and_report<FindLeafIndexResponse>(
673673
[=, this](TypedResponse<FindLeafIndexResponse>& response) {
674+
if (leaf == fr::zero()) {
675+
throw std::runtime_error("Requesting indices for zero leaves is prohibited");
676+
}
674677
ReadTransactionPtr tx = store_->create_read_transaction();
675678
RequestContext requestContext;
676679
requestContext.includeUncommitted = includeUncommitted;
@@ -703,6 +706,9 @@ void ContentAddressedAppendOnlyTree<Store, HashingPolicy>::find_leaf_index_from(
703706
if (blockNumber == 0) {
704707
throw std::runtime_error("Unable to find leaf index for block number 0");
705708
}
709+
if (leaf == fr::zero()) {
710+
throw std::runtime_error("Requesting indices for zero leaves is prohibited");
711+
}
706712
ReadTransactionPtr tx = store_->create_read_transaction();
707713
BlockPayload blockData;
708714
if (!store_->get_block_data(blockNumber, blockData, *tx)) {
@@ -909,6 +915,10 @@ void ContentAddressedAppendOnlyTree<Store, HashingPolicy>::add_batch_internal(
909915
// If we have been told to add these leaves to the index then do so now
910916
if (update_index) {
911917
for (uint32_t i = 0; i < number_to_insert; ++i) {
918+
// We don't store indices of zero leaves
919+
if (hashes_local[i] == fr::zero()) {
920+
continue;
921+
}
912922
// std::cout << "Updating index " << index + i << " : " << hashes_local[i] << std::endl;
913923
store_->update_index(index + i, hashes_local[i]);
914924
}

cpp/src/barretenberg/crypto/merkle_tree/append_only_tree/content_addressed_append_only_tree.test.cpp

+97-4
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,57 @@ TEST_F(PersistedContentAddressedAppendOnlyTreeTest, can_add_multiple_values_in_a
786786
check_sibling_path(tree, 4 - 1, memdb.get_sibling_path(4 - 1));
787787
}
788788

789+
TEST_F(PersistedContentAddressedAppendOnlyTreeTest, can_pad_with_zero_leaves)
790+
{
791+
constexpr size_t depth = 10;
792+
std::string name = random_string();
793+
LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
794+
std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
795+
ThreadPoolPtr pool = make_thread_pool(1);
796+
TreeType tree(std::move(store), pool);
797+
MemoryTree<Poseidon2HashPolicy> memdb(depth);
798+
799+
std::vector<fr> to_add(32, fr::zero());
800+
to_add[0] = VALUES[0];
801+
802+
for (size_t i = 0; i < 32; ++i) {
803+
memdb.update_element(i, to_add[i]);
804+
}
805+
add_values(tree, to_add);
806+
check_size(tree, 32);
807+
check_root(tree, memdb.root());
808+
809+
commit_tree(tree, true);
810+
811+
check_size(tree, 32);
812+
check_root(tree, memdb.root());
813+
}
814+
815+
TEST_F(PersistedContentAddressedAppendOnlyTreeTest, can_not_retrieve_zero_leaf_indices)
816+
{
817+
constexpr size_t depth = 8;
818+
std::string name = random_string();
819+
LMDBTreeStore::SharedPtr db = std::make_shared<LMDBTreeStore>(_directory, name, _mapSize, _maxReaders);
820+
std::unique_ptr<Store> store = std::make_unique<Store>(name, depth, db);
821+
ThreadPoolPtr pool = make_thread_pool(1);
822+
TreeType tree(std::move(store), pool);
823+
MemoryTree<Poseidon2HashPolicy> memdb(depth);
824+
825+
std::vector<fr> to_add(32, fr::zero());
826+
to_add[0] = VALUES[0];
827+
828+
for (size_t i = 0; i < 32; ++i) {
829+
memdb.update_element(i, VALUES[i]);
830+
}
831+
add_values(tree, to_add);
832+
commit_tree(tree);
833+
fr leaf = fr::zero();
834+
check_find_leaf_index(tree, leaf, 0, false);
835+
check_find_historic_leaf_index(tree, 1, leaf, 0, false);
836+
check_find_leaf_index_from(tree, leaf, 0, 0, false);
837+
check_find_historic_leaf_index_from(tree, 1, leaf, 0, 0, false);
838+
}
839+
789840
TEST_F(PersistedContentAddressedAppendOnlyTreeTest, can_commit_multiple_blocks)
790841
{
791842
constexpr size_t depth = 10;
@@ -1354,7 +1405,12 @@ void test_unwind(std::string directory,
13541405
// attempting to unwind a block that is not the tip should fail
13551406
unwind_block(tree, blockNumber + 1, false);
13561407
unwind_block(tree, blockNumber);
1357-
check_block_and_root_data(db, blockNumber, roots[blockNumber - 1], false);
1408+
1409+
// the root should now only exist if there are other blocks with same root
1410+
const auto last = roots.begin() + long(blockNumber - 1);
1411+
const auto it =
1412+
std::find_if(roots.begin(), last, [=](const fr& r) -> bool { return r == roots[blockNumber - 1]; });
1413+
check_block_and_root_data(db, blockNumber, roots[blockNumber - 1], false, it != last);
13581414

13591415
const index_t previousValidBlock = blockNumber - 1;
13601416
index_t deletedBlockStartIndex = previousValidBlock * batchSize;
@@ -1384,9 +1440,19 @@ void test_unwind(std::string directory,
13841440

13851441
const index_t leafIndex = 1;
13861442
check_historic_leaf(tree, historicBlockNumber, values[leafIndex], leafIndex, expectedSuccess);
1387-
check_find_historic_leaf_index(tree, historicBlockNumber, values[leafIndex], leafIndex, expectedSuccess);
1388-
check_find_historic_leaf_index_from(
1389-
tree, historicBlockNumber, values[leafIndex], 0, leafIndex, expectedSuccess);
1443+
1444+
// find historic leaves, provided they are not zero leaves
1445+
check_find_historic_leaf_index(tree,
1446+
historicBlockNumber,
1447+
values[leafIndex],
1448+
leafIndex,
1449+
expectedSuccess && values[leafIndex] != fr::zero());
1450+
check_find_historic_leaf_index_from(tree,
1451+
historicBlockNumber,
1452+
values[leafIndex],
1453+
0,
1454+
leafIndex,
1455+
expectedSuccess && values[leafIndex] != fr::zero());
13901456
}
13911457
}
13921458
}
@@ -1405,6 +1471,33 @@ TEST_F(PersistedContentAddressedAppendOnlyTreeTest, can_unwind_all_blocks)
14051471
test_unwind(_directory, "DB", _mapSize, _maxReaders, 10, 16, 16, 16, second);
14061472
}
14071473

1474+
TEST_F(PersistedContentAddressedAppendOnlyTreeTest, can_unwind_initial_blocks_that_are_empty)
1475+
{
1476+
const size_t block_size = 16;
1477+
// First we add 16 blocks worth pf zero leaves and unwind them all
1478+
std::vector<fr> first(1024, fr::zero());
1479+
test_unwind(_directory, "DB", _mapSize, _maxReaders, 10, block_size, 16, 16, first);
1480+
// now we add 1 block of zero leaves and the other blocks non-zero leaves and unwind them all
1481+
std::vector<fr> second = create_values(1024);
1482+
// set the first 16 values to be zeros
1483+
for (size_t i = 0; i < block_size; i++) {
1484+
second[i] = fr::zero();
1485+
}
1486+
test_unwind(_directory, "DB", _mapSize, _maxReaders, 10, block_size, 16, 16, second);
1487+
1488+
// now we add 2 block of zero leaves in the middle and the other blocks non-zero leaves and unwind them all
1489+
std::vector<fr> third = create_values(1024);
1490+
size_t offset = block_size * 2;
1491+
for (size_t i = 0; i < block_size * 2; i++) {
1492+
third[i + offset] = fr::zero();
1493+
}
1494+
test_unwind(_directory, "DB", _mapSize, _maxReaders, 10, block_size, 16, 16, third);
1495+
1496+
// Now we add a number of regular blocks and unwind
1497+
std::vector<fr> fourth = create_values(1024);
1498+
test_unwind(_directory, "DB", _mapSize, _maxReaders, 10, block_size, 16, 16, fourth);
1499+
}
1500+
14081501
TEST_F(PersistedContentAddressedAppendOnlyTreeTest, can_sync_and_unwind_large_blocks)
14091502
{
14101503

cpp/src/barretenberg/crypto/merkle_tree/node_store/cached_content_addressed_tree_store.hpp

-2
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,6 @@ void ContentAddressedCachedTreeStore<LeafValueType>::put_cached_node_by_index(ui
512512
return;
513513
}
514514
}
515-
516515
nodes_by_index_[level][index] = data;
517516
}
518517

@@ -695,7 +694,6 @@ void ContentAddressedCachedTreeStore<LeafValueType>::persist_leaf_pre_image(cons
695694
if (leafPreImageIter == leaves_.end()) {
696695
return;
697696
}
698-
// std::cout << "Persisting leaf preimage " << leafPreImageIter->second << std::endl;
699697
dataStore_->write_leaf_by_hash(hash, leafPreImageIter->second, tx);
700698
}
701699

cpp/src/barretenberg/world_state/world_state.cpp

+9-1
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,15 @@ WorldStateStatusFull WorldState::sync_block(const StateReference& block_state_re
505505

506506
{
507507
auto& wrapper = std::get<TreeWithStore<NullifierTree>>(fork->_trees.at(MerkleTreeId::NULLIFIER_TREE));
508-
NullifierTree::AddCompletionCallback completion = [&](const auto&) -> void { signal.signal_decrement(); };
508+
NullifierTree::AddCompletionCallback completion = [&](const auto& resp) -> void {
509+
// take the first error
510+
bool expected = true;
511+
if (!resp.success && success.compare_exchange_strong(expected, false)) {
512+
err_message = resp.message;
513+
}
514+
515+
signal.signal_decrement();
516+
};
509517
wrapper.tree->add_or_update_values(nullifiers, 0, completion);
510518
}
511519

0 commit comments

Comments
 (0)