Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
world_state.cpp
Go to the documentation of this file.
17#include <array>
18#include <atomic>
19#include <cstddef>
20#include <cstdint>
21#include <filesystem>
22#include <memory>
23#include <mutex>
24#include <optional>
25#include <ostream>
26#include <stdexcept>
27#include <tuple>
28#include <unordered_map>
29#include <utility>
30#include <variant>
31
32namespace bb::world_state {
33
34using namespace bb::crypto::merkle_tree;
35
36WorldState::WorldState(uint64_t thread_pool_size,
37 const std::string& data_dir,
39 const std::unordered_map<MerkleTreeId, uint32_t>& tree_heights,
40 const std::unordered_map<MerkleTreeId, index_t>& tree_prefill,
41 const std::vector<PublicDataLeafValue>& prefilled_public_data,
42 uint32_t initial_header_generator_point)
43 : _workers(std::make_shared<ThreadPool>(thread_pool_size))
44 , _tree_heights(tree_heights)
45 , _initial_tree_size(tree_prefill)
46 , _forkId(CANONICAL_FORK_ID)
47 , _initial_header_generator_point(initial_header_generator_point)
48{
49 // We set the max readers to be high, at least the number of given threads or the default if higher
50 uint64_t maxReaders = std::max(thread_pool_size, DEFAULT_MIN_NUMBER_OF_READERS);
51 create_canonical_fork(data_dir, map_size, prefilled_public_data, maxReaders);
52 try {
54 } catch (std::exception& e) {
55 // We don't do anything with this. If any attept to re-sync failed it will be picked up later in TS land
56 }
57}
58
59WorldState::WorldState(uint64_t thread_pool_size,
60 const std::string& data_dir,
62 const std::unordered_map<MerkleTreeId, uint32_t>& tree_heights,
63 const std::unordered_map<MerkleTreeId, index_t>& tree_prefill,
64 uint32_t initial_header_generator_point)
65 : WorldState::WorldState(thread_pool_size,
66 data_dir,
67 map_size,
68 tree_heights,
69 tree_prefill,
70 std::vector<PublicDataLeafValue>(),
71 initial_header_generator_point)
72{}
73
74WorldState::WorldState(uint64_t thread_pool_size,
75 const std::string& data_dir,
76 uint64_t map_size,
77 const std::unordered_map<MerkleTreeId, uint32_t>& tree_heights,
78 const std::unordered_map<MerkleTreeId, index_t>& tree_prefill,
79 const std::vector<PublicDataLeafValue>& prefilled_public_data,
80 uint32_t initial_header_generator_point)
81 : WorldState(thread_pool_size,
82 data_dir,
83 {
84 { MerkleTreeId::NULLIFIER_TREE, map_size },
86 { MerkleTreeId::ARCHIVE, map_size },
87 { MerkleTreeId::NOTE_HASH_TREE, map_size },
89 },
90 tree_heights,
91 tree_prefill,
92 prefilled_public_data,
93 initial_header_generator_point)
94{}
95
96WorldState::WorldState(uint64_t thread_pool_size,
97 const std::string& data_dir,
98 uint64_t map_size,
99 const std::unordered_map<MerkleTreeId, uint32_t>& tree_heights,
100 const std::unordered_map<MerkleTreeId, index_t>& tree_prefill,
101 uint32_t initial_header_generator_point)
102 : WorldState(thread_pool_size,
103 data_dir,
104 map_size,
105 tree_heights,
106 tree_prefill,
107 std::vector<PublicDataLeafValue>(),
108 initial_header_generator_point)
109{}
110
111void WorldState::create_canonical_fork(const std::string& dataDir,
113 const std::vector<PublicDataLeafValue>& prefilled_public_data,
114 uint64_t maxReaders)
115{
116 // create the underlying stores
117 auto createStore = [&](MerkleTreeId id) {
118 auto name = getMerkleTreeName(id);
119 std::filesystem::path directory = dataDir;
120 directory /= name;
121 std::filesystem::create_directories(directory);
122 return std::make_shared<LMDBTreeStore>(directory, name, dbSize.at(id), maxReaders);
123 };
126 createStore(MerkleTreeId::ARCHIVE),
127 createStore(MerkleTreeId::NOTE_HASH_TREE),
129
131 fork->_forkId = _forkId++;
132 {
133 uint32_t levels = _tree_heights.at(MerkleTreeId::NULLIFIER_TREE);
137 auto tree = std::make_unique<NullifierTree>(std::move(store), _workers, initial_size);
138 fork->_trees.insert({ MerkleTreeId::NULLIFIER_TREE, TreeWithStore(std::move(tree)) });
139 }
140 {
141 uint32_t levels = _tree_heights.at(MerkleTreeId::NOTE_HASH_TREE);
142 auto store = std::make_unique<FrStore>(
144 auto tree = std::make_unique<FrTree>(std::move(store), _workers);
145 fork->_trees.insert({ MerkleTreeId::NOTE_HASH_TREE, TreeWithStore(std::move(tree)) });
146 }
147 {
148 uint32_t levels = _tree_heights.at(MerkleTreeId::PUBLIC_DATA_TREE);
152 auto tree = std::make_unique<PublicDataTree>(std::move(store), _workers, initial_size, prefilled_public_data);
153 fork->_trees.insert({ MerkleTreeId::PUBLIC_DATA_TREE, TreeWithStore(std::move(tree)) });
154 }
155 {
157 auto store = std::make_unique<FrStore>(
159 auto tree = std::make_unique<FrTree>(std::move(store), _workers);
160 fork->_trees.insert({ MerkleTreeId::L1_TO_L2_MESSAGE_TREE, TreeWithStore(std::move(tree)) });
161 }
162 {
163 uint32_t levels = _tree_heights.at(MerkleTreeId::ARCHIVE);
166 auto store = std::make_unique<FrStore>(
168 auto tree = std::make_unique<FrTree>(std::move(store), _workers, initial_values);
169 fork->_trees.insert({ MerkleTreeId::ARCHIVE, TreeWithStore(std::move(tree)) });
170 }
171 _forks[fork->_forkId] = fork;
172}
173
174void WorldState::copy_stores(const std::string& dstPath, bool compact) const
175{
176 auto copyStore = [&](const LMDBTreeStore::SharedPtr& store) {
177 std::filesystem::path directory = dstPath;
178 directory /= store->get_name();
179 std::filesystem::create_directories(directory);
180 store->copy_store(directory, compact);
181 };
182
183 std::for_each(_persistentStores->begin(), _persistentStores->end(), copyStore);
184}
185
186Fork::SharedPtr WorldState::retrieve_fork(const uint64_t& forkId) const
187{
188 std::unique_lock lock(mtx);
189 auto it = _forks.find(forkId);
190 if (it == _forks.end()) {
191 throw std::runtime_error("Fork not found");
192 }
193 return it->second;
194}
196{
197 block_number_t blockNumberForFork = 0;
198 if (!blockNumber.has_value()) {
199 // we are forking at latest
200 WorldStateRevision revision{ .forkId = CANONICAL_FORK_ID, .blockNumber = 0, .includeUncommitted = false };
202 blockNumberForFork = archiveMeta.meta.unfinalizedBlockHeight;
203 } else {
204 blockNumberForFork = blockNumber.value();
205 }
206 Fork::SharedPtr fork = create_new_fork(blockNumberForFork);
207 std::unique_lock lock(mtx);
208 uint64_t forkId = _forkId++;
209 fork->_forkId = forkId;
210 _forks[forkId] = fork;
211 return forkId;
212}
213
215{
216 // capture the shared pointers outside of the lock scope so we are not under the lock when the objects are destroyed
218 {
219 std::unique_lock lock(mtx);
220 for (auto it = _forks.begin(); it != _forks.end();) {
221 if (it->second->_blockNumber == blockNumber) {
222 forks.push_back(it->second);
223 it = _forks.erase(it);
224
225 } else {
226 it++;
227 }
228 }
229 }
230}
231
232void WorldState::delete_fork(const uint64_t& forkId)
233{
234 if (forkId == 0) {
235 throw std::runtime_error("Unable to delete canonical fork");
236 }
237 // Retrieving the shared pointer here means we throw if the fork is not available, it also means we are not under a
238 // lock when we destroy the object
239 Fork::SharedPtr fork = retrieve_fork(forkId);
240 {
241 std::unique_lock lock(mtx);
242 _forks.erase(forkId);
243 }
244}
245
247{
249 fork->_blockNumber = blockNumber;
250 {
251 uint32_t levels = _tree_heights.at(MerkleTreeId::NULLIFIER_TREE);
254 getMerkleTreeName(MerkleTreeId::NULLIFIER_TREE), levels, blockNumber, _persistentStores->nullifierStore);
255 auto tree = std::make_unique<NullifierTree>(std::move(store), _workers, initial_size);
256 fork->_trees.insert({ MerkleTreeId::NULLIFIER_TREE, TreeWithStore(std::move(tree)) });
257 }
258 {
259 uint32_t levels = _tree_heights.at(MerkleTreeId::NOTE_HASH_TREE);
260 auto store = std::make_unique<FrStore>(
261 getMerkleTreeName(MerkleTreeId::NOTE_HASH_TREE), levels, blockNumber, _persistentStores->noteHashStore);
262 auto tree = std::make_unique<FrTree>(std::move(store), _workers);
263 fork->_trees.insert({ MerkleTreeId::NOTE_HASH_TREE, TreeWithStore(std::move(tree)) });
264 }
265 {
266 uint32_t levels = _tree_heights.at(MerkleTreeId::PUBLIC_DATA_TREE);
269 getMerkleTreeName(MerkleTreeId::PUBLIC_DATA_TREE), levels, blockNumber, _persistentStores->publicDataStore);
270 auto tree = std::make_unique<PublicDataTree>(std::move(store), _workers, initial_size);
271 fork->_trees.insert({ MerkleTreeId::PUBLIC_DATA_TREE, TreeWithStore(std::move(tree)) });
272 }
273 {
276 levels,
277 blockNumber,
278 _persistentStores->messageStore);
279 auto tree = std::make_unique<FrTree>(std::move(store), _workers);
280 fork->_trees.insert({ MerkleTreeId::L1_TO_L2_MESSAGE_TREE, TreeWithStore(std::move(tree)) });
281 }
282 {
283 uint32_t levels = _tree_heights.at(MerkleTreeId::ARCHIVE);
284 auto store = std::make_unique<FrStore>(
285 getMerkleTreeName(MerkleTreeId::ARCHIVE), levels, blockNumber, _persistentStores->archiveStore);
286 auto tree = std::make_unique<FrTree>(std::move(store), _workers);
287 fork->_trees.insert({ MerkleTreeId::ARCHIVE, TreeWithStore(std::move(tree)) });
288 }
289 return fork;
290}
291
293{
294 Fork::SharedPtr fork = retrieve_fork(revision.forkId);
295 return std::visit(
296 [=](auto&& wrapper) {
297 Signal signal(1);
299
300 auto callback = [&](TypedResponse<TreeMetaResponse>& meta) {
301 local = std::move(meta);
302 signal.signal_level(0);
303 };
304
305 if (revision.blockNumber) {
306 wrapper.tree->get_meta_data(revision.blockNumber, revision.includeUncommitted, callback);
307 } else {
308 wrapper.tree->get_meta_data(revision.includeUncommitted, callback);
309 }
310 signal.wait_for_level(0);
311
312 if (!local.success) {
313 throw std::runtime_error(local.message);
314 }
315 return local.inner;
316 },
317 fork->_trees.at(tree_id));
318}
319
321{
322 Fork::SharedPtr fork = retrieve_fork(revision.forkId);
323
327 };
328
329 Signal signal(static_cast<uint32_t>(tree_ids.size()));
330 std::mutex mutex;
332
333 for (auto id : tree_ids) {
334 const auto& tree = fork->_trees.at(id);
335 auto callback = [&signal, &local, &mutex, id](TypedResponse<TreeMetaResponse>& meta) {
336 {
337 std::lock_guard<std::mutex> lock(mutex);
338 local[id] = std::move(meta);
339 }
340 signal.signal_decrement();
341 };
342 std::visit(
343 [&callback, &revision](auto&& wrapper) {
344 if (revision.blockNumber) {
345 wrapper.tree->get_meta_data(revision.blockNumber, revision.includeUncommitted, callback);
346 } else {
347 wrapper.tree->get_meta_data(revision.includeUncommitted, callback);
348 }
349 },
350 tree);
351 }
352
353 signal.wait_for_level(0);
354
355 for (auto tree_id : tree_ids) {
356 auto& m = local[tree_id];
357 if (!m.success) {
358 throw std::runtime_error(m.message);
359 }
360 responses[tree_id] = std::move(m.inner.meta);
361 }
362}
363
365{
366 return get_state_reference(revision, retrieve_fork(revision.forkId));
367}
368
375
377 Fork::SharedPtr fork,
378 bool initial_state)
379{
380 if (fork->_forkId != revision.forkId) {
381 throw std::runtime_error("Fork does not match revision");
382 }
383
389 };
390
391 Signal signal(static_cast<uint32_t>(tree_ids.size()));
392 StateReference state_reference;
394 std::mutex state_ref_mutex;
395
396 for (auto id : tree_ids) {
397 const auto& tree = fork->_trees.at(id);
398 auto callback = [&signal, &local, &state_ref_mutex, id](TypedResponse<TreeMetaResponse>& meta) {
399 {
400 std::lock_guard<std::mutex> lock(state_ref_mutex);
401 local[id] = std::move(meta);
402 }
403 signal.signal_decrement();
404 };
405 std::visit(
406 [&callback, &revision](auto&& wrapper) {
407 if (revision.blockNumber) {
408 wrapper.tree->get_meta_data(revision.blockNumber, revision.includeUncommitted, callback);
409 } else {
410 wrapper.tree->get_meta_data(revision.includeUncommitted, callback);
411 }
412 },
413 tree);
414 }
415
416 signal.wait_for_level(0);
417
418 for (auto tree_id : tree_ids) {
419 auto& m = local[tree_id];
420 if (!m.success) {
421 throw std::runtime_error(m.message);
422 }
423 if (initial_state) {
424 state_reference[tree_id] = std::make_pair(m.inner.meta.initialRoot, m.inner.meta.initialSize);
425 continue;
426 }
427 state_reference[tree_id] = std::make_pair(m.inner.meta.root, m.inner.meta.size);
428 }
429
430 return state_reference;
431}
432
434 MerkleTreeId tree_id,
435 index_t leaf_index) const
436{
437 Fork::SharedPtr fork = retrieve_fork(revision.forkId);
438
439 return std::visit(
440 [leaf_index, revision](auto&& wrapper) {
441 Signal signal(1);
443
444 auto callback = [&signal, &local](TypedResponse<GetSiblingPathResponse>& response) {
445 local = std::move(response);
446 signal.signal_level(0);
447 };
448
449 if (revision.blockNumber) {
450 wrapper.tree->get_sibling_path(leaf_index, revision.blockNumber, callback, revision.includeUncommitted);
451 } else {
452 wrapper.tree->get_sibling_path(leaf_index, callback, revision.includeUncommitted);
453 }
454 signal.wait_for_level(0);
455
456 if (!local.success) {
457 throw std::runtime_error(local.message);
458 }
459 return local.inner.path;
460 },
461 fork->_trees.at(tree_id));
462}
463
465 MerkleTreeId tree_id,
466 const std::vector<index_t>& leafIndices,
467 std::vector<std::optional<block_number_t>>& blockNumbers) const
468{
469 Fork::SharedPtr fork = retrieve_fork(revision.forkId);
470
471 std::visit(
472 [&leafIndices, revision, &blockNumbers](auto&& wrapper) {
473 Signal signal(1);
475
476 auto callback = [&signal, &local](TypedResponse<BlockForIndexResponse>& response) {
477 local = std::move(response);
478 signal.signal_level();
479 };
480
481 if (revision.blockNumber) {
482 wrapper.tree->find_block_numbers(leafIndices, revision.blockNumber, callback);
483 } else {
484 wrapper.tree->find_block_numbers(leafIndices, callback);
485 }
486 signal.wait_for_level(0);
487
488 if (!local.success) {
489 throw std::runtime_error(local.message);
490 }
491 blockNumbers = std::move(local.inner.blockNumbers);
492 },
493 fork->_trees.at(tree_id));
494}
495
497{
498 Fork::SharedPtr fork = retrieve_fork(fork_id);
499 if (const auto* wrapper =
501 Signal signal;
502 wrapper->tree->add_or_update_value(
503 new_value,
505 signal.wait_for_level();
506 } else {
507 throw std::runtime_error("Invalid tree type for PublicDataTree");
508 }
509}
510
511void WorldState::update_archive(const StateReference& block_state_ref,
512 const bb::fr& block_header_hash,
513 Fork::Id fork_id)
514{
515 if (is_same_state_reference(WorldStateRevision{ .forkId = fork_id, .includeUncommitted = true }, block_state_ref)) {
516 append_leaves<fr>(MerkleTreeId::ARCHIVE, { block_header_hash }, fork_id);
517 } else {
518 throw std::runtime_error("Can't update archive tree: Block state does not match world state");
519 }
520}
521
523{
524 // NOTE: the calling code is expected to ensure no other reads or writes happen during commit
526 std::atomic_bool success = true;
527 std::string message;
528 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
529
530 {
533 status.dbStats.nullifierTreeStats, signal, *wrapper.tree, success, message, status.meta.nullifierTreeMeta);
534 }
535 {
538 signal,
539 *wrapper.tree,
540 success,
541 message,
542 status.meta.publicDataTreeMeta);
543 }
544
545 {
546 auto& wrapper = std::get<TreeWithStore<FrTree>>(fork->_trees.at(MerkleTreeId::NOTE_HASH_TREE));
548 status.dbStats.noteHashTreeStats, signal, *wrapper.tree, success, message, status.meta.noteHashTreeMeta);
549 }
550
551 {
554 status.dbStats.messageTreeStats, signal, *wrapper.tree, success, message, status.meta.messageTreeMeta);
555 }
556
557 {
558 auto& wrapper = std::get<TreeWithStore<FrTree>>(fork->_trees.at(MerkleTreeId::ARCHIVE));
560 status.dbStats.archiveTreeStats, signal, *wrapper.tree, success, message, status.meta.archiveTreeMeta);
561 }
562
563 signal.wait_for_level(0);
564 return std::make_pair(success.load(), message);
565}
566
568{
569 // NOTE: the calling code is expected to ensure no other reads or writes happen during rollback
571 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
572 for (auto& [id, tree] : fork->_trees) {
573 std::visit(
574 [&signal](auto&& wrapper) {
575 wrapper.tree->rollback([&signal](const Response&) { signal.signal_decrement(); });
576 },
577 tree);
578 }
579 signal.wait_for_level();
580}
581
583 const bb::fr& block_header_hash,
584 const std::vector<bb::fr>& notes,
585 const std::vector<bb::fr>& l1_to_l2_messages,
588{
592 is_archive_tip(WorldStateRevision::uncommitted(), block_header_hash)) {
593 std::pair<bool, std::string> result = commit(status);
594 if (!result.first) {
595 throw std::runtime_error(result.second);
596 }
598 return status;
599 }
600 rollback();
601
603 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
604 std::atomic_bool success = true;
605 std::string err_message;
606 auto decr = [&signal, &success, &err_message](const auto& resp) {
607 // take the first error
608 bool expected = true;
609 if (!resp.success && success.compare_exchange_strong(expected, false)) {
610 err_message = resp.message;
611 }
612
613 signal.signal_decrement();
614 };
615
616 {
618 NullifierTree::AddCompletionCallback completion = [&](const auto& resp) -> void {
619 // take the first error
620 bool expected = true;
621 if (!resp.success && success.compare_exchange_strong(expected, false)) {
622 err_message = resp.message;
623 }
624
625 signal.signal_decrement();
626 };
627 wrapper.tree->add_or_update_values(nullifiers, 0, completion);
628 }
629
630 {
631 auto& wrapper = std::get<TreeWithStore<FrTree>>(fork->_trees.at(MerkleTreeId::NOTE_HASH_TREE));
632 wrapper.tree->add_values(notes, decr);
633 }
634
635 {
637 wrapper.tree->add_values(l1_to_l2_messages, decr);
638 }
639
640 {
641 auto& wrapper = std::get<TreeWithStore<FrTree>>(fork->_trees.at(MerkleTreeId::ARCHIVE));
642 wrapper.tree->add_value(block_header_hash, decr);
643 }
644
645 {
647 PublicDataTree::AddCompletionCallback completion = [&](const auto& resp) -> void {
648 // take the first error
649 bool expected = true;
650 if (!resp.success && success.compare_exchange_strong(expected, false)) {
651 err_message = resp.message;
652 }
653
654 signal.signal_decrement();
655 };
656 wrapper.tree->add_or_update_values_sequentially(public_writes, completion);
657 }
658
659 signal.wait_for_level();
660
661 if (!success) {
662 throw std::runtime_error("Failed to sync block: " + err_message);
663 }
664
665 if (!is_archive_tip(WorldStateRevision::uncommitted(), block_header_hash)) {
666 throw std::runtime_error("Can't synch block: block header hash is not the tip of the archive tree");
667 }
668
670 throw std::runtime_error("Can't synch block: block state does not match world state");
671 }
672
673 std::pair<bool, std::string> result = commit(status);
674 if (!result.first) {
675 throw std::runtime_error(result.second);
676 }
678 return status;
679}
680
682 MerkleTreeId tree_id,
683 const bb::fr& leaf_key) const
684{
685 Fork::SharedPtr fork = retrieve_fork(revision.forkId);
686 Signal signal;
688 auto callback = [&signal, &low_leaf_info](TypedResponse<GetLowIndexedLeafResponse>& response) {
689 low_leaf_info = std::move(response);
690 signal.signal_level();
691 };
692
693 if (const auto* wrapper = std::get_if<TreeWithStore<NullifierTree>>(&fork->_trees.at(tree_id))) {
694 if (revision.blockNumber != 0U) {
695 wrapper->tree->find_low_leaf(leaf_key, revision.blockNumber, revision.includeUncommitted, callback);
696 } else {
697 wrapper->tree->find_low_leaf(leaf_key, revision.includeUncommitted, callback);
698 }
699
700 } else if (const auto* wrapper = std::get_if<TreeWithStore<PublicDataTree>>(&fork->_trees.at(tree_id))) {
701 if (revision.blockNumber != 0U) {
702 wrapper->tree->find_low_leaf(leaf_key, revision.blockNumber, revision.includeUncommitted, callback);
703 } else {
704 wrapper->tree->find_low_leaf(leaf_key, revision.includeUncommitted, callback);
705 }
706
707 } else {
708 throw std::runtime_error("Invalid tree type for find_low_leaf");
709 }
710
711 signal.wait_for_level();
712
713 if (!low_leaf_info.success) {
714 throw std::runtime_error(low_leaf_info.message);
715 }
716 return low_leaf_info.inner;
717}
718
720{
721 // This will throw if it fails
722 set_finalized_block(toBlockNumber);
724 get_status_summary(status);
725 return status;
726}
728{
729 WorldStateRevision revision{ .forkId = CANONICAL_FORK_ID, .blockNumber = 0, .includeUncommitted = false };
731 get_all_tree_info(revision, responses);
732
733 // Get the set of unfinalized block numbers and unwind to the target block
734 std::array<block_number_t, NUM_TREES> unfinalizedBlockNumbers{
735 responses[NULLIFIER_TREE].unfinalizedBlockHeight,
736 responses[NOTE_HASH_TREE].unfinalizedBlockHeight,
737 responses[PUBLIC_DATA_TREE].unfinalizedBlockHeight,
738 responses[L1_TO_L2_MESSAGE_TREE].unfinalizedBlockHeight,
739 responses[ARCHIVE].unfinalizedBlockHeight
740 };
741
742 auto* const it = std::max_element(std::begin(unfinalizedBlockNumbers), std::end(unfinalizedBlockNumbers));
743 block_number_t highestUnfinalizedBlock = *it;
744
745 if (toBlockNumber >= highestUnfinalizedBlock) {
746 throw std::runtime_error(format("Unable to unwind blocks to block number ",
747 toBlockNumber,
748 ", current pending block ",
749 highestUnfinalizedBlock));
750 }
751
753 for (block_number_t blockNumber = highestUnfinalizedBlock; blockNumber > toBlockNumber; blockNumber--) {
754 // This will throw if it fails
755 unwind_block(blockNumber, status);
756 }
758 return status;
759}
760
762{
763 WorldStateRevision revision{ .forkId = CANONICAL_FORK_ID, .blockNumber = 0, .includeUncommitted = false };
765 get_all_tree_info(revision, responses);
766
767 // Get the set of historic block numbers and remove to the target block
768 std::array<block_number_t, NUM_TREES> historicalBlockNumbers{ responses[NULLIFIER_TREE].oldestHistoricBlock,
769 responses[NOTE_HASH_TREE].oldestHistoricBlock,
770 responses[PUBLIC_DATA_TREE].oldestHistoricBlock,
771 responses[L1_TO_L2_MESSAGE_TREE].oldestHistoricBlock,
772 responses[ARCHIVE].oldestHistoricBlock };
773 auto* const it = std::min_element(std::begin(historicalBlockNumbers), std::end(historicalBlockNumbers));
774 block_number_t oldestHistoricBlock = *it;
775 if (toBlockNumber <= oldestHistoricBlock) {
776 throw std::runtime_error(format("Unable to remove historical blocks to block number ",
777 toBlockNumber,
778 ", blocks not found. Current oldest block: ",
779 oldestHistoricBlock));
780 }
782 for (block_number_t blockNumber = oldestHistoricBlock; blockNumber < toBlockNumber; blockNumber++) {
783 // This will throw if it fails
784 remove_historical_block(blockNumber, status);
785 }
787 return status;
788}
789
791{
793 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
795 std::mutex mtx;
796 for (auto& [id, tree] : fork->_trees) {
797 std::visit(
798 [&signal, &local, blockNumber, id, &mtx](auto&& wrapper) {
799 wrapper.tree->finalize_block(blockNumber, [&signal, &local, &mtx, id](Response& resp) {
800 {
802 local[id] = std::move(resp);
803 }
804 signal.signal_decrement();
805 });
806 },
807 tree);
808 }
809 signal.wait_for_level();
810 for (auto& m : local) {
811 if (!m.success) {
812 throw std::runtime_error(m.message);
813 }
814 }
815 return true;
816}
818{
819 std::atomic_bool success = true;
820 std::string message;
822 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
823 {
826 signal,
827 *wrapper.tree,
828 success,
829 message,
830 status.meta.nullifierTreeMeta,
831 blockNumber);
832 }
833 {
836 signal,
837 *wrapper.tree,
838 success,
839 message,
841 blockNumber);
842 }
843
844 {
845 auto& wrapper = std::get<TreeWithStore<FrTree>>(fork->_trees.at(MerkleTreeId::NOTE_HASH_TREE));
847 signal,
848 *wrapper.tree,
849 success,
850 message,
851 status.meta.noteHashTreeMeta,
852 blockNumber);
853 }
854
855 {
858 signal,
859 *wrapper.tree,
860 success,
861 message,
862 status.meta.messageTreeMeta,
863 blockNumber);
864 }
865
866 {
867 auto& wrapper = std::get<TreeWithStore<FrTree>>(fork->_trees.at(MerkleTreeId::ARCHIVE));
869 signal,
870 *wrapper.tree,
871 success,
872 message,
873 status.meta.archiveTreeMeta,
874 blockNumber);
875 }
876 signal.wait_for_level();
877 if (!success) {
878 throw std::runtime_error(message);
879 }
880 remove_forks_for_block(blockNumber);
881 return true;
882}
884{
885 std::atomic_bool success = true;
886 std::string message;
888 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
889 {
892 signal,
893 *wrapper.tree,
894 success,
895 message,
896 status.meta.nullifierTreeMeta,
897 blockNumber);
898 }
899 {
902 signal,
903 *wrapper.tree,
904 success,
905 message,
907 blockNumber);
908 }
909
910 {
911 auto& wrapper = std::get<TreeWithStore<FrTree>>(fork->_trees.at(MerkleTreeId::NOTE_HASH_TREE));
913 signal,
914 *wrapper.tree,
915 success,
916 message,
917 status.meta.noteHashTreeMeta,
918 blockNumber);
919 }
920
921 {
924 signal,
925 *wrapper.tree,
926 success,
927 message,
928 status.meta.messageTreeMeta,
929 blockNumber);
930 }
931
932 {
933 auto& wrapper = std::get<TreeWithStore<FrTree>>(fork->_trees.at(MerkleTreeId::ARCHIVE));
935 signal,
936 *wrapper.tree,
937 success,
938 message,
939 status.meta.archiveTreeMeta,
940 blockNumber);
941 }
942 signal.wait_for_level();
943 if (!success) {
944 throw std::runtime_error(message);
945 }
946 remove_forks_for_block(blockNumber);
947 return true;
948}
949
950bb::fr WorldState::compute_initial_block_header_hash(const StateReference& initial_state_ref, uint32_t generator_point)
951{
952 // NOTE: this hash operations needs to match the one in
953 // noir-project/noir-protocol-circuits/crates/types/src/abis/block_header.nr
954 return HashPolicy::hash({ generator_point,
955 // last archive - which, at genesis, is all 0s
956 0, // root
957 0, // next_available_leaf_index
958 // content commitment - all 0s
959 0, // blobs_hash
960 0, // in_hash
961 0, // out_hash
962 // state reference - the initial state for all the trees (accept the archive tree)
963 initial_state_ref.at(MerkleTreeId::L1_TO_L2_MESSAGE_TREE).first,
964 initial_state_ref.at(MerkleTreeId::L1_TO_L2_MESSAGE_TREE).second,
965 initial_state_ref.at(MerkleTreeId::NOTE_HASH_TREE).first,
966 initial_state_ref.at(MerkleTreeId::NOTE_HASH_TREE).second,
967 initial_state_ref.at(MerkleTreeId::NULLIFIER_TREE).first,
968 initial_state_ref.at(MerkleTreeId::NULLIFIER_TREE).second,
969 initial_state_ref.at(MerkleTreeId::PUBLIC_DATA_TREE).first,
970 initial_state_ref.at(MerkleTreeId::PUBLIC_DATA_TREE).second,
971 // global variables
972 0, // chain_id
973 0, // version
974 0, // block_number
975 0, // slot_number
976 0, // timestamp
977 0, // coinbase
978 0, // fee_recipient
979 0, // gas_fee.fee_per_da_gas
980 0, // gas_fee.fee_per_l2_gas
981 // total fees
982 0,
983 // total mana used
984 0 });
985}
986
987bool WorldState::is_archive_tip(const WorldStateRevision& revision, const bb::fr& block_header_hash) const
988{
990
991 try {
992 find_leaf_indices<fr>(revision, MerkleTreeId::ARCHIVE, { block_header_hash }, indices);
993 } catch (std::runtime_error&) {
994 }
995
996 if (indices.empty() || !indices[0].has_value()) {
997 return false;
998 }
999
1000 TreeMetaResponse archive_state = get_tree_info(revision, MerkleTreeId::ARCHIVE);
1001 return archive_state.meta.size == indices[0].value() + 1;
1002}
1003
1005{
1006 WorldStateRevision revision{ .forkId = CANONICAL_FORK_ID, .blockNumber = 0, .includeUncommitted = false };
1008 get_all_tree_info(revision, responses);
1010}
1011
1013 std::array<TreeMeta, NUM_TREES>& metaResponses)
1014{
1015 TreeMeta& archive_state = metaResponses[MerkleTreeId::ARCHIVE];
1016 status.unfinalizedBlockNumber = archive_state.unfinalizedBlockHeight;
1017 status.finalizedBlockNumber = archive_state.finalizedBlockHeight;
1018 status.oldestHistoricalBlock = archive_state.oldestHistoricBlock;
1019 status.treesAreSynched = determine_if_synched(metaResponses);
1020}
1021
1033
1034bool WorldState::is_same_state_reference(const WorldStateRevision& revision, const StateReference& state_ref) const
1035{
1036 return state_ref == get_state_reference(revision);
1037}
1038
1040{
1041 WorldStateRevision revision{ .forkId = CANONICAL_FORK_ID, .blockNumber = 0, .includeUncommitted = false };
1043 get_all_tree_info(revision, responses);
1044
1046 throw std::runtime_error("World state trees are out of sync");
1047 }
1048}
1049
1051{
1052 block_number_t blockNumber = metaResponses[0].unfinalizedBlockHeight;
1053 block_number_t finalizedBlockNumber = metaResponses[0].finalizedBlockHeight;
1054 for (size_t i = 1; i < metaResponses.size(); i++) {
1055 if (blockNumber != metaResponses[i].unfinalizedBlockHeight) {
1056 return false;
1057 }
1058 if (finalizedBlockNumber != metaResponses[i].finalizedBlockHeight) {
1059 return false;
1060 }
1061 }
1062 return true;
1063}
1064
1065void WorldState::checkpoint(const uint64_t& forkId)
1066{
1067 Fork::SharedPtr fork = retrieve_fork(forkId);
1068 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
1070 std::mutex mtx;
1071 for (auto& [id, tree] : fork->_trees) {
1072 std::visit(
1073 [&signal, &local, id, &mtx](auto&& wrapper) {
1074 wrapper.tree->checkpoint([&signal, &local, &mtx, id](Response& resp) {
1075 {
1077 local[id] = std::move(resp);
1078 }
1079 signal.signal_decrement();
1080 });
1081 },
1082 tree);
1083 }
1084 signal.wait_for_level();
1085 for (auto& m : local) {
1086 if (!m.success) {
1087 throw std::runtime_error(m.message);
1088 }
1089 }
1090}
1091
1092void WorldState::commit_checkpoint(const uint64_t& forkId)
1093{
1094 Fork::SharedPtr fork = retrieve_fork(forkId);
1095 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
1097 std::mutex mtx;
1098 for (auto& [id, tree] : fork->_trees) {
1099 std::visit(
1100 [&signal, &local, id, &mtx](auto&& wrapper) {
1101 wrapper.tree->commit_checkpoint([&signal, &local, &mtx, id](Response& resp) {
1102 {
1104 local[id] = std::move(resp);
1105 }
1106 signal.signal_decrement();
1107 });
1108 },
1109 tree);
1110 }
1111 signal.wait_for_level();
1112 for (auto& m : local) {
1113 if (!m.success) {
1114 throw std::runtime_error(m.message);
1115 }
1116 }
1117}
1118
1119void WorldState::revert_checkpoint(const uint64_t& forkId)
1120{
1121 Fork::SharedPtr fork = retrieve_fork(forkId);
1122 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
1124 std::mutex mtx;
1125 for (auto& [id, tree] : fork->_trees) {
1126 std::visit(
1127 [&signal, &local, id, &mtx](auto&& wrapper) {
1128 wrapper.tree->revert_checkpoint([&signal, &local, &mtx, id](Response& resp) {
1129 {
1131 local[id] = std::move(resp);
1132 }
1133 signal.signal_decrement();
1134 });
1135 },
1136 tree);
1137 }
1138 signal.wait_for_level();
1139 for (auto& m : local) {
1140 if (!m.success) {
1141 throw std::runtime_error(m.message);
1142 }
1143 }
1144}
1145
1146void WorldState::commit_all_checkpoints(const uint64_t& forkId)
1147{
1148 Fork::SharedPtr fork = retrieve_fork(forkId);
1149 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
1151 std::mutex mtx;
1152 for (auto& [id, tree] : fork->_trees) {
1153 std::visit(
1154 [&signal, &local, id, &mtx](auto&& wrapper) {
1155 wrapper.tree->commit_all_checkpoints([&signal, &local, &mtx, id](Response& resp) {
1156 {
1158 local[id] = std::move(resp);
1159 }
1160 signal.signal_decrement();
1161 });
1162 },
1163 tree);
1164 }
1165 signal.wait_for_level();
1166 for (auto& m : local) {
1167 if (!m.success) {
1168 throw std::runtime_error(m.message);
1169 }
1170 }
1171}
1172
1173void WorldState::revert_all_checkpoints(const uint64_t& forkId)
1174{
1175 Fork::SharedPtr fork = retrieve_fork(forkId);
1176 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
1178 std::mutex mtx;
1179 for (auto& [id, tree] : fork->_trees) {
1180 std::visit(
1181 [&signal, &local, id, &mtx](auto&& wrapper) {
1182 wrapper.tree->revert_all_checkpoints([&signal, &local, &mtx, id](Response& resp) {
1183 {
1185 local[id] = std::move(resp);
1186 }
1187 signal.signal_decrement();
1188 });
1189 },
1190 tree);
1191 }
1192 signal.wait_for_level();
1193 for (auto& m : local) {
1194 if (!m.success) {
1195 throw std::runtime_error(m.message);
1196 }
1197 }
1198}
1199
1201{
1202 WorldStateRevision revision{ .forkId = CANONICAL_FORK_ID, .blockNumber = 0, .includeUncommitted = false };
1204 get_all_tree_info(revision, responses);
1205
1206 // Get all historic block numbers
1207 std::array<block_number_t, NUM_TREES> historicalBlockNumbers{ responses[NULLIFIER_TREE].oldestHistoricBlock,
1208 responses[NOTE_HASH_TREE].oldestHistoricBlock,
1209 responses[PUBLIC_DATA_TREE].oldestHistoricBlock,
1210 responses[L1_TO_L2_MESSAGE_TREE].oldestHistoricBlock,
1211 responses[ARCHIVE].oldestHistoricBlock };
1212
1213 // Get all unfinalized block numbers
1214 std::array<block_number_t, NUM_TREES> unfinalizedBlockNumbers{
1215 responses[NULLIFIER_TREE].unfinalizedBlockHeight,
1216 responses[NOTE_HASH_TREE].unfinalizedBlockHeight,
1217 responses[PUBLIC_DATA_TREE].unfinalizedBlockHeight,
1218 responses[L1_TO_L2_MESSAGE_TREE].unfinalizedBlockHeight,
1219 responses[ARCHIVE].unfinalizedBlockHeight
1220 };
1221
1222 // Get all finalized block numbers
1223 std::array<block_number_t, NUM_TREES> finalizedBlockNumbers{ responses[NULLIFIER_TREE].finalizedBlockHeight,
1224 responses[NOTE_HASH_TREE].finalizedBlockHeight,
1225 responses[PUBLIC_DATA_TREE].finalizedBlockHeight,
1226 responses[L1_TO_L2_MESSAGE_TREE].finalizedBlockHeight,
1227 responses[ARCHIVE].finalizedBlockHeight };
1228
1229 // Get the min and max of each set of block numbers
1230 auto historicBlockRange = std::minmax_element(std::begin(historicalBlockNumbers), std::end(historicalBlockNumbers));
1231
1232 auto unfinalizedBlockRange =
1233 std::minmax_element(std::begin(unfinalizedBlockNumbers), std::end(unfinalizedBlockNumbers));
1234
1235 auto finalizedBlockRange = std::minmax_element(std::begin(finalizedBlockNumbers), std::end(finalizedBlockNumbers));
1236
1237 // We re-sync by
1238 // 1. Unwinding any blocks that are ahread of the lowest unfinalized block number
1239 // 2. Increasing finalized block numbers to the highest finalized block number
1240 // 3. Removing any historical blocks that are lower then the highest historic block number
1241
1242 WorldStateStatusFull status;
1243 block_number_t blockToUnwind = *unfinalizedBlockRange.second;
1244 while (blockToUnwind > *unfinalizedBlockRange.first) {
1245 unwind_block(blockToUnwind, status);
1246 blockToUnwind--;
1247 }
1248
1249 if (*finalizedBlockRange.first != *finalizedBlockRange.second) {
1250 set_finalized_block(*finalizedBlockRange.second);
1251 }
1252
1253 block_number_t blockToRemove = *historicBlockRange.first;
1254 while (blockToRemove < *historicBlockRange.second) {
1255 remove_historical_block(blockToRemove, status);
1256 blockToRemove++;
1257 }
1258
1260 return status;
1261}
1262
1263} // namespace bb::world_state
bb::bbapi::CommandResponse responses
std::function< void(TypedResponse< AddDataResponse > &)> AddCompletionCallback
std::shared_ptr< LMDBTreeStore > SharedPtr
Used in parallel insertions in the the IndexedTree. Workers signal to other following workes as they ...
Definition signal.hpp:17
void signal_level(uint32_t level=0)
Signals that the given level has been passed.
Definition signal.hpp:54
void signal_decrement(uint32_t delta=1)
Definition signal.hpp:60
void wait_for_level(uint32_t level=0)
Causes the thread to wait until the required level has been signalled.
Definition signal.hpp:40
Holds the Merkle trees responsible for storing the state of the Aztec protocol.
WorldStateStatusFull remove_historical_blocks(const block_number_t &toBlockNumber)
std::shared_ptr< bb::ThreadPool > _workers
void remove_forks_for_block(const block_number_t &blockNumber)
bool unwind_block(const block_number_t &blockNumber, WorldStateStatusFull &status)
static void get_status_summary_from_meta_responses(WorldStateStatusSummary &status, std::array< TreeMeta, NUM_TREES > &metaResponses)
void commit_tree(TreeDBStats &dbStats, Signal &signal, TreeType &tree, std::atomic_bool &success, std::string &message, TreeMeta &meta)
StateReference get_initial_state_reference() const
Gets the initial state reference for all the trees in the world state.
void revert_checkpoint(const uint64_t &forkId)
WorldStateStatusFull attempt_tree_resync()
crypto::merkle_tree::TreeMetaResponse get_tree_info(const WorldStateRevision &revision, MerkleTreeId tree_id) const
Get tree metadata for a particular tree.
static void populate_status_summary(WorldStateStatusFull &status)
WorldState(uint64_t thread_pool_size, const std::string &data_dir, uint64_t map_size, const std::unordered_map< MerkleTreeId, uint32_t > &tree_heights, const std::unordered_map< MerkleTreeId, index_t > &tree_prefill, uint32_t initial_header_generator_point)
void unwind_tree(TreeDBStats &dbStats, Signal &signal, TreeType &tree, std::atomic_bool &success, std::string &message, TreeMeta &meta, const block_number_t &blockNumber)
std::unordered_map< uint64_t, Fork::SharedPtr > _forks
void create_canonical_fork(const std::string &dataDir, const std::unordered_map< MerkleTreeId, uint64_t > &dbSize, const std::vector< PublicDataLeafValue > &prefilled_public_data, uint64_t maxReaders)
std::pair< bool, std::string > commit(WorldStateStatusFull &status)
Commits the current state of the world state.
void remove_historic_block_for_tree(TreeDBStats &dbStats, Signal &signal, TreeType &tree, std::atomic_bool &success, std::string &message, TreeMeta &meta, const block_number_t &blockNumber)
void get_block_numbers_for_leaf_indices(const WorldStateRevision &revision, MerkleTreeId tree_id, const std::vector< index_t > &leafIndices, std::vector< std::optional< block_number_t > > &blockNumbers) const
StateReference get_state_reference(const WorldStateRevision &revision) const
Gets the state reference for all the trees in the world state.
WorldStateStatusFull unwind_blocks(const block_number_t &toBlockNumber)
bool is_archive_tip(const WorldStateRevision &revision, const bb::fr &block_header_hash) const
static bool determine_if_synched(std::array< TreeMeta, NUM_TREES > &metaResponses)
void update_public_data(const crypto::merkle_tree::PublicDataLeafValue &new_value, Fork::Id fork_id=CANONICAL_FORK_ID)
Updates a leaf in an existing Merkle Tree.
void commit_checkpoint(const uint64_t &forkId)
Fork::SharedPtr create_new_fork(const block_number_t &blockNumber)
void get_status_summary(WorldStateStatusSummary &status) const
void revert_all_checkpoints(const uint64_t &forkId)
void rollback()
Rolls back any uncommitted changes made to the world state.
void commit_all_checkpoints(const uint64_t &forkId)
WorldStateStatusFull sync_block(const StateReference &block_state_ref, const bb::fr &block_header_hash, const std::vector< bb::fr > &notes, const std::vector< bb::fr > &l1_to_l2_messages, const std::vector< crypto::merkle_tree::NullifierLeafValue > &nullifiers, const std::vector< crypto::merkle_tree::PublicDataLeafValue > &public_writes)
WorldStateStatusSummary set_finalized_blocks(const block_number_t &toBlockNumber)
static bb::fr compute_initial_block_header_hash(const StateReference &initial_state_ref, uint32_t generator_point)
std::unordered_map< MerkleTreeId, index_t > _initial_tree_size
void delete_fork(const uint64_t &forkId)
bool remove_historical_block(const block_number_t &blockNumber, WorldStateStatusFull &status)
std::unordered_map< MerkleTreeId, uint32_t > _tree_heights
uint64_t create_fork(const std::optional< block_number_t > &blockNumber)
crypto::merkle_tree::fr_sibling_path get_sibling_path(const WorldStateRevision &revision, MerkleTreeId tree_id, index_t leaf_index) const
Get the sibling path object for a leaf in a tree.
void checkpoint(const uint64_t &forkId)
bool is_same_state_reference(const WorldStateRevision &revision, const StateReference &state_ref) const
crypto::merkle_tree::GetLowIndexedLeafResponse find_low_leaf_index(const WorldStateRevision &revision, MerkleTreeId tree_id, const bb::fr &leaf_key) const
Finds the leaf that would have its nextIdx/nextValue fields modified if the target leaf were to be in...
void copy_stores(const std::string &dstPath, bool compact) const
Copies all underlying LMDB stores to the target directory while acquiring a write lock.
void update_archive(const StateReference &block_state_ref, const bb::fr &block_header_hash, Fork::Id fork_id=CANONICAL_FORK_ID)
Updates the archive tree with a new block.
bool set_finalized_block(const block_number_t &blockNumber)
WorldStateStores::Ptr _persistentStores
Fork::SharedPtr retrieve_fork(const uint64_t &forkId) const
void get_all_tree_info(const WorldStateRevision &revision, std::array< TreeMeta, NUM_TREES > &responses) const
std::string format(Args... args)
Definition log.hpp:20
uint32_t block_number_t
Definition types.hpp:19
std::vector< fr > fr_sibling_path
Definition hash_path.hpp:16
@ L1_TO_L2_MESSAGE_TREE
Definition types.hpp:22
const uint64_t DEFAULT_MIN_NUMBER_OF_READERS
const uint64_t CANONICAL_FORK_ID
Definition types.hpp:26
std::string getMerkleTreeName(MerkleTreeId id)
Definition types.cpp:6
std::unordered_map< MerkleTreeId, TreeStateReference > StateReference
Definition types.hpp:32
STL namespace.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static fr hash(const std::vector< fr > &inputs)
Definition hash.hpp:30
std::shared_ptr< Fork > SharedPtr
Definition fork.hpp:29
static WorldStateRevision committed()
Definition types.hpp:41
static WorldStateRevision uncommitted()
Definition types.hpp:42
WorldStateStatusSummary summary
Definition types.hpp:207