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 uint64_t genesis_timestamp)
44 : _workers(std::make_shared<ThreadPool>(thread_pool_size))
45 , _tree_heights(tree_heights)
46 , _initial_tree_size(tree_prefill)
47 , _forkId(CANONICAL_FORK_ID)
48 , _initial_header_generator_point(initial_header_generator_point)
49 , _genesis_timestamp(genesis_timestamp)
50{
51 // We set the max readers to be high, at least the number of given threads or the default if higher
52 uint64_t maxReaders = std::max(thread_pool_size, DEFAULT_MIN_NUMBER_OF_READERS);
53 create_canonical_fork(data_dir, map_size, prefilled_public_data, maxReaders);
54 try {
56 } catch (std::exception& e) {
57 // We don't do anything with this. If any attept to re-sync failed it will be picked up later in TS land
58 }
59}
60
61WorldState::WorldState(uint64_t thread_pool_size,
62 const std::string& data_dir,
64 const std::unordered_map<MerkleTreeId, uint32_t>& tree_heights,
65 const std::unordered_map<MerkleTreeId, index_t>& tree_prefill,
66 uint32_t initial_header_generator_point,
67 uint64_t genesis_timestamp)
68 : WorldState::WorldState(thread_pool_size,
69 data_dir,
70 map_size,
71 tree_heights,
72 tree_prefill,
73 std::vector<PublicDataLeafValue>(),
74 initial_header_generator_point,
75 genesis_timestamp)
76{}
77
78WorldState::WorldState(uint64_t thread_pool_size,
79 const std::string& data_dir,
80 uint64_t map_size,
81 const std::unordered_map<MerkleTreeId, uint32_t>& tree_heights,
82 const std::unordered_map<MerkleTreeId, index_t>& tree_prefill,
83 const std::vector<PublicDataLeafValue>& prefilled_public_data,
84 uint32_t initial_header_generator_point,
85 uint64_t genesis_timestamp)
86 : WorldState(thread_pool_size,
87 data_dir,
88 {
89 { MerkleTreeId::NULLIFIER_TREE, map_size },
91 { MerkleTreeId::ARCHIVE, map_size },
92 { MerkleTreeId::NOTE_HASH_TREE, map_size },
94 },
95 tree_heights,
96 tree_prefill,
97 prefilled_public_data,
98 initial_header_generator_point,
99 genesis_timestamp)
100{}
101
102WorldState::WorldState(uint64_t thread_pool_size,
103 const std::string& data_dir,
104 uint64_t map_size,
105 const std::unordered_map<MerkleTreeId, uint32_t>& tree_heights,
106 const std::unordered_map<MerkleTreeId, index_t>& tree_prefill,
107 uint32_t initial_header_generator_point,
108 uint64_t genesis_timestamp)
109 : WorldState(thread_pool_size,
110 data_dir,
111 map_size,
112 tree_heights,
113 tree_prefill,
114 std::vector<PublicDataLeafValue>(),
115 initial_header_generator_point,
116 genesis_timestamp)
117{}
118
119void WorldState::create_canonical_fork(const std::string& dataDir,
121 const std::vector<PublicDataLeafValue>& prefilled_public_data,
122 uint64_t maxReaders)
123{
124 // create the underlying stores
125 auto createStore = [&](MerkleTreeId id) {
126 auto name = getMerkleTreeName(id);
127 std::filesystem::path directory = dataDir;
128 directory /= name;
129 std::filesystem::create_directories(directory);
130 return std::make_shared<LMDBTreeStore>(directory, name, dbSize.at(id), maxReaders);
131 };
134 createStore(MerkleTreeId::ARCHIVE),
135 createStore(MerkleTreeId::NOTE_HASH_TREE),
137
139 fork->_forkId = _forkId++;
140 {
141 uint32_t levels = _tree_heights.at(MerkleTreeId::NULLIFIER_TREE);
145 auto tree = std::make_unique<NullifierTree>(std::move(store), _workers, initial_size);
146 fork->_trees.insert({ MerkleTreeId::NULLIFIER_TREE, TreeWithStore(std::move(tree)) });
147 }
148 {
149 uint32_t levels = _tree_heights.at(MerkleTreeId::NOTE_HASH_TREE);
150 auto store = std::make_unique<FrStore>(
152 auto tree = std::make_unique<FrTree>(std::move(store), _workers);
153 fork->_trees.insert({ MerkleTreeId::NOTE_HASH_TREE, TreeWithStore(std::move(tree)) });
154 }
155 {
156 uint32_t levels = _tree_heights.at(MerkleTreeId::PUBLIC_DATA_TREE);
160 auto tree = std::make_unique<PublicDataTree>(std::move(store), _workers, initial_size, prefilled_public_data);
161 fork->_trees.insert({ MerkleTreeId::PUBLIC_DATA_TREE, TreeWithStore(std::move(tree)) });
162 }
163 {
165 auto store = std::make_unique<FrStore>(
167 auto tree = std::make_unique<FrTree>(std::move(store), _workers);
168 fork->_trees.insert({ MerkleTreeId::L1_TO_L2_MESSAGE_TREE, TreeWithStore(std::move(tree)) });
169 }
170 {
171 uint32_t levels = _tree_heights.at(MerkleTreeId::ARCHIVE);
172 std::vector<bb::fr> initial_values{ compute_initial_block_header_hash(
176 auto store = std::make_unique<FrStore>(
178 auto tree = std::make_unique<FrTree>(std::move(store), _workers, initial_values);
179 fork->_trees.insert({ MerkleTreeId::ARCHIVE, TreeWithStore(std::move(tree)) });
180 }
181 _forks[fork->_forkId] = fork;
182}
183
184void WorldState::copy_stores(const std::string& dstPath, bool compact) const
185{
186 auto copyStore = [&](const LMDBTreeStore::SharedPtr& store) {
187 std::filesystem::path directory = dstPath;
188 directory /= store->get_name();
189 std::filesystem::create_directories(directory);
190 store->copy_store(directory, compact);
191 };
192
193 std::for_each(_persistentStores->begin(), _persistentStores->end(), copyStore);
194}
195
196Fork::SharedPtr WorldState::retrieve_fork(const uint64_t& forkId) const
197{
198 std::unique_lock lock(mtx);
199 auto it = _forks.find(forkId);
200 if (it == _forks.end()) {
201 throw std::runtime_error("Fork not found");
202 }
203 return it->second;
204}
206{
207 block_number_t blockNumberForFork = 0;
208 if (!blockNumber.has_value()) {
209 // we are forking at latest
210 WorldStateRevision revision{ .forkId = CANONICAL_FORK_ID, .includeUncommitted = false };
212 blockNumberForFork = archiveMeta.meta.unfinalizedBlockHeight;
213 } else {
214 blockNumberForFork = blockNumber.value();
215 }
216 Fork::SharedPtr fork = create_new_fork(blockNumberForFork);
217 std::unique_lock lock(mtx);
218 uint64_t forkId = _forkId++;
219 fork->_forkId = forkId;
220 _forks[forkId] = fork;
221 return forkId;
222}
223
225{
226 // capture the shared pointers outside of the lock scope so we are not under the lock when the objects are destroyed
228 {
229 std::unique_lock lock(mtx);
230 for (auto it = _forks.begin(); it != _forks.end();) {
231 if (it->second->_blockNumber == blockNumber) {
232 forks.push_back(it->second);
233 it = _forks.erase(it);
234
235 } else {
236 it++;
237 }
238 }
239 }
240}
241
242void WorldState::delete_fork(const uint64_t& forkId)
243{
244 if (forkId == 0) {
245 throw std::runtime_error("Unable to delete canonical fork");
246 }
247 // Retrieving the shared pointer here means we throw if the fork is not available, it also means we are not under a
248 // lock when we destroy the object
249 Fork::SharedPtr fork = retrieve_fork(forkId);
250 {
251 std::unique_lock lock(mtx);
252 _forks.erase(forkId);
253 }
254}
255
257{
259 fork->_blockNumber = blockNumber;
260 {
261 uint32_t levels = _tree_heights.at(MerkleTreeId::NULLIFIER_TREE);
264 getMerkleTreeName(MerkleTreeId::NULLIFIER_TREE), levels, blockNumber, _persistentStores->nullifierStore);
265 auto tree = std::make_unique<NullifierTree>(std::move(store), _workers, initial_size);
266 fork->_trees.insert({ MerkleTreeId::NULLIFIER_TREE, TreeWithStore(std::move(tree)) });
267 }
268 {
269 uint32_t levels = _tree_heights.at(MerkleTreeId::NOTE_HASH_TREE);
270 auto store = std::make_unique<FrStore>(
271 getMerkleTreeName(MerkleTreeId::NOTE_HASH_TREE), levels, blockNumber, _persistentStores->noteHashStore);
272 auto tree = std::make_unique<FrTree>(std::move(store), _workers);
273 fork->_trees.insert({ MerkleTreeId::NOTE_HASH_TREE, TreeWithStore(std::move(tree)) });
274 }
275 {
276 uint32_t levels = _tree_heights.at(MerkleTreeId::PUBLIC_DATA_TREE);
279 getMerkleTreeName(MerkleTreeId::PUBLIC_DATA_TREE), levels, blockNumber, _persistentStores->publicDataStore);
280 auto tree = std::make_unique<PublicDataTree>(std::move(store), _workers, initial_size);
281 fork->_trees.insert({ MerkleTreeId::PUBLIC_DATA_TREE, TreeWithStore(std::move(tree)) });
282 }
283 {
286 levels,
287 blockNumber,
288 _persistentStores->messageStore);
289 auto tree = std::make_unique<FrTree>(std::move(store), _workers);
290 fork->_trees.insert({ MerkleTreeId::L1_TO_L2_MESSAGE_TREE, TreeWithStore(std::move(tree)) });
291 }
292 {
293 uint32_t levels = _tree_heights.at(MerkleTreeId::ARCHIVE);
294 auto store = std::make_unique<FrStore>(
295 getMerkleTreeName(MerkleTreeId::ARCHIVE), levels, blockNumber, _persistentStores->archiveStore);
296 auto tree = std::make_unique<FrTree>(std::move(store), _workers);
297 fork->_trees.insert({ MerkleTreeId::ARCHIVE, TreeWithStore(std::move(tree)) });
298 }
299 return fork;
300}
301
303{
304 Fork::SharedPtr fork = retrieve_fork(revision.forkId);
305 return std::visit(
306 [=](auto&& wrapper) {
307 Signal signal(1);
309
310 auto callback = [&](TypedResponse<TreeMetaResponse>& meta) {
311 local = std::move(meta);
312 signal.signal_level(0);
313 };
314
315 if (revision.is_historical()) {
316 wrapper.tree->get_meta_data(revision.blockNumber, revision.includeUncommitted, callback);
317 } else {
318 wrapper.tree->get_meta_data(revision.includeUncommitted, callback);
319 }
320 signal.wait_for_level(0);
321
322 if (!local.success) {
323 throw std::runtime_error(local.message);
324 }
325 return local.inner;
326 },
327 fork->_trees.at(tree_id));
328}
329
331{
332 Fork::SharedPtr fork = retrieve_fork(revision.forkId);
333
337 };
338
339 Signal signal(static_cast<uint32_t>(tree_ids.size()));
340 std::mutex mutex;
342
343 for (auto id : tree_ids) {
344 const auto& tree = fork->_trees.at(id);
345 auto callback = [&signal, &local, &mutex, id](TypedResponse<TreeMetaResponse>& meta) {
346 {
347 std::lock_guard<std::mutex> lock(mutex);
348 local[id] = std::move(meta);
349 }
350 signal.signal_decrement();
351 };
352 std::visit(
353 [&callback, &revision](auto&& wrapper) {
354 if (revision.is_historical()) {
355 wrapper.tree->get_meta_data(revision.blockNumber, revision.includeUncommitted, callback);
356 } else {
357 wrapper.tree->get_meta_data(revision.includeUncommitted, callback);
358 }
359 },
360 tree);
361 }
362
363 signal.wait_for_level(0);
364
365 for (auto tree_id : tree_ids) {
366 auto& m = local[tree_id];
367 if (!m.success) {
368 throw std::runtime_error(m.message);
369 }
370 responses[tree_id] = std::move(m.inner.meta);
371 }
372}
373
375{
376 return get_state_reference(revision, retrieve_fork(revision.forkId));
377}
378
385
387 Fork::SharedPtr fork,
388 bool initial_state)
389{
390 if (fork->_forkId != revision.forkId) {
391 throw std::runtime_error("Fork does not match revision");
392 }
393
399 };
400
401 Signal signal(static_cast<uint32_t>(tree_ids.size()));
402 StateReference state_reference;
404 std::mutex state_ref_mutex;
405
406 for (auto id : tree_ids) {
407 const auto& tree = fork->_trees.at(id);
408 auto callback = [&signal, &local, &state_ref_mutex, id](TypedResponse<TreeMetaResponse>& meta) {
409 {
410 std::lock_guard<std::mutex> lock(state_ref_mutex);
411 local[id] = std::move(meta);
412 }
413 signal.signal_decrement();
414 };
415 std::visit(
416 [&callback, &revision](auto&& wrapper) {
417 if (revision.is_historical()) {
418 wrapper.tree->get_meta_data(revision.blockNumber, revision.includeUncommitted, callback);
419 } else {
420 wrapper.tree->get_meta_data(revision.includeUncommitted, callback);
421 }
422 },
423 tree);
424 }
425
426 signal.wait_for_level(0);
427
428 for (auto tree_id : tree_ids) {
429 auto& m = local[tree_id];
430 if (!m.success) {
431 throw std::runtime_error(m.message);
432 }
433 if (initial_state) {
434 state_reference[tree_id] = std::make_pair(m.inner.meta.initialRoot, m.inner.meta.initialSize);
435 continue;
436 }
437 state_reference[tree_id] = std::make_pair(m.inner.meta.root, m.inner.meta.size);
438 }
439
440 return state_reference;
441}
442
444 MerkleTreeId tree_id,
445 index_t leaf_index) const
446{
447 Fork::SharedPtr fork = retrieve_fork(revision.forkId);
448
449 return std::visit(
450 [leaf_index, revision](auto&& wrapper) {
451 Signal signal(1);
453
454 auto callback = [&signal, &local](TypedResponse<GetSiblingPathResponse>& response) {
455 local = std::move(response);
456 signal.signal_level(0);
457 };
458
459 if (revision.is_historical()) {
460 wrapper.tree->get_sibling_path(leaf_index, revision.blockNumber, callback, revision.includeUncommitted);
461 } else {
462 wrapper.tree->get_sibling_path(leaf_index, callback, revision.includeUncommitted);
463 }
464 signal.wait_for_level(0);
465
466 if (!local.success) {
467 throw std::runtime_error(local.message);
468 }
469 return local.inner.path;
470 },
471 fork->_trees.at(tree_id));
472}
473
475 MerkleTreeId tree_id,
476 const std::vector<index_t>& leafIndices,
477 std::vector<std::optional<block_number_t>>& blockNumbers) const
478{
479 Fork::SharedPtr fork = retrieve_fork(revision.forkId);
480
481 std::visit(
482 [&leafIndices, revision, &blockNumbers](auto&& wrapper) {
483 Signal signal(1);
485
486 auto callback = [&signal, &local](TypedResponse<BlockForIndexResponse>& response) {
487 local = std::move(response);
488 signal.signal_level();
489 };
490
491 if (revision.is_historical()) {
492 wrapper.tree->find_block_numbers(leafIndices, revision.blockNumber, callback);
493 } else {
494 wrapper.tree->find_block_numbers(leafIndices, callback);
495 }
496 signal.wait_for_level(0);
497
498 if (!local.success) {
499 throw std::runtime_error(local.message);
500 }
501 blockNumbers = std::move(local.inner.blockNumbers);
502 },
503 fork->_trees.at(tree_id));
504}
505
507{
508 Fork::SharedPtr fork = retrieve_fork(fork_id);
509 if (const auto* wrapper =
511 Signal signal;
512 wrapper->tree->add_or_update_value(
513 new_value,
515 signal.wait_for_level();
516 } else {
517 throw std::runtime_error("Invalid tree type for PublicDataTree");
518 }
519}
520
521void WorldState::update_archive(const StateReference& block_state_ref,
522 const bb::fr& block_header_hash,
523 Fork::Id fork_id)
524{
525 if (is_same_state_reference(WorldStateRevision{ .forkId = fork_id, .includeUncommitted = true }, block_state_ref)) {
526 append_leaves<fr>(MerkleTreeId::ARCHIVE, { block_header_hash }, fork_id);
527 } else {
528 throw std::runtime_error("Can't update archive tree: Block state does not match world state");
529 }
530}
531
533{
534 // NOTE: the calling code is expected to ensure no other reads or writes happen during commit
536 std::atomic_bool success = true;
537 std::string message;
538 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
539
540 {
543 status.dbStats.nullifierTreeStats, signal, *wrapper.tree, success, message, status.meta.nullifierTreeMeta);
544 }
545 {
548 signal,
549 *wrapper.tree,
550 success,
551 message,
552 status.meta.publicDataTreeMeta);
553 }
554
555 {
556 auto& wrapper = std::get<TreeWithStore<FrTree>>(fork->_trees.at(MerkleTreeId::NOTE_HASH_TREE));
558 status.dbStats.noteHashTreeStats, signal, *wrapper.tree, success, message, status.meta.noteHashTreeMeta);
559 }
560
561 {
564 status.dbStats.messageTreeStats, signal, *wrapper.tree, success, message, status.meta.messageTreeMeta);
565 }
566
567 {
568 auto& wrapper = std::get<TreeWithStore<FrTree>>(fork->_trees.at(MerkleTreeId::ARCHIVE));
570 status.dbStats.archiveTreeStats, signal, *wrapper.tree, success, message, status.meta.archiveTreeMeta);
571 }
572
573 signal.wait_for_level(0);
574 return std::make_pair(success.load(), message);
575}
576
578{
579 // NOTE: the calling code is expected to ensure no other reads or writes happen during rollback
581 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
582 for (auto& [id, tree] : fork->_trees) {
583 std::visit(
584 [&signal](auto&& wrapper) {
585 wrapper.tree->rollback([&signal](const Response&) { signal.signal_decrement(); });
586 },
587 tree);
588 }
589 signal.wait_for_level();
590}
591
593 const bb::fr& block_header_hash,
594 const std::vector<bb::fr>& notes,
595 const std::vector<bb::fr>& l1_to_l2_messages,
598{
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 // Check resulting state and commit if successful
663 try {
664 if (!success) {
665 throw std::runtime_error("Failed to sync block: " + err_message);
666 }
667
668 if (!is_archive_tip(WorldStateRevision::uncommitted(), block_header_hash)) {
669 throw std::runtime_error("Can't synch block: block header hash is not the tip of the archive tree");
670 }
671
673 throw std::runtime_error("Can't synch block: block state does not match world state");
674 }
675
676 std::pair<bool, std::string> result = commit(status);
677 if (!result.first) {
678 throw std::runtime_error(result.second);
679 }
680 } catch (const std::exception& e) {
681 // We failed, rollback any uncommitted state before leaving
682 rollback();
683 throw;
684 }
685
686 // Success return the status
688 return status;
689}
690
692 MerkleTreeId tree_id,
693 const bb::fr& leaf_key) const
694{
695 Fork::SharedPtr fork = retrieve_fork(revision.forkId);
696 Signal signal;
698 auto callback = [&signal, &low_leaf_info](TypedResponse<GetLowIndexedLeafResponse>& response) {
699 low_leaf_info = std::move(response);
700 signal.signal_level();
701 };
702
703 if (const auto* wrapper = std::get_if<TreeWithStore<NullifierTree>>(&fork->_trees.at(tree_id))) {
704 if (revision.is_historical()) {
705 wrapper->tree->find_low_leaf(leaf_key, revision.blockNumber, revision.includeUncommitted, callback);
706 } else {
707 wrapper->tree->find_low_leaf(leaf_key, revision.includeUncommitted, callback);
708 }
709
710 } else if (const auto* wrapper = std::get_if<TreeWithStore<PublicDataTree>>(&fork->_trees.at(tree_id))) {
711 if (revision.is_historical()) {
712 wrapper->tree->find_low_leaf(leaf_key, revision.blockNumber, revision.includeUncommitted, callback);
713 } else {
714 wrapper->tree->find_low_leaf(leaf_key, revision.includeUncommitted, callback);
715 }
716
717 } else {
718 throw std::runtime_error("Invalid tree type for find_low_leaf");
719 }
720
721 signal.wait_for_level();
722
723 if (!low_leaf_info.success) {
724 throw std::runtime_error(low_leaf_info.message);
725 }
726 return low_leaf_info.inner;
727}
728
730{
731 // This will throw if it fails
732 set_finalized_block(toBlockNumber);
734 get_status_summary(status);
735 return status;
736}
738{
739 // Ensure no uncommitted state
740 rollback();
741
742 WorldStateRevision revision{ .forkId = CANONICAL_FORK_ID, .includeUncommitted = false };
744 get_all_tree_info(revision, responses);
745
746 // Get the set of unfinalized block numbers and unwind to the target block
747 std::array<block_number_t, NUM_TREES> unfinalizedBlockNumbers{
748 responses[NULLIFIER_TREE].unfinalizedBlockHeight,
749 responses[NOTE_HASH_TREE].unfinalizedBlockHeight,
750 responses[PUBLIC_DATA_TREE].unfinalizedBlockHeight,
751 responses[L1_TO_L2_MESSAGE_TREE].unfinalizedBlockHeight,
752 responses[ARCHIVE].unfinalizedBlockHeight
753 };
754
755 auto* const it = std::max_element(std::begin(unfinalizedBlockNumbers), std::end(unfinalizedBlockNumbers));
756 block_number_t highestUnfinalizedBlock = *it;
757
758 if (toBlockNumber >= highestUnfinalizedBlock) {
759 throw std::runtime_error(format("Unable to unwind blocks to block number ",
760 toBlockNumber,
761 ", current pending block ",
762 highestUnfinalizedBlock));
763 }
764
766 for (block_number_t blockNumber = highestUnfinalizedBlock; blockNumber > toBlockNumber; blockNumber--) {
767 // This will throw if it fails
768 unwind_block(blockNumber, status);
769 }
771 return status;
772}
773
775{
776 WorldStateRevision revision{ .forkId = CANONICAL_FORK_ID, .includeUncommitted = false };
778 get_all_tree_info(revision, responses);
779
780 // Get the set of historic block numbers and remove to the target block
781 std::array<block_number_t, NUM_TREES> historicalBlockNumbers{ responses[NULLIFIER_TREE].oldestHistoricBlock,
782 responses[NOTE_HASH_TREE].oldestHistoricBlock,
783 responses[PUBLIC_DATA_TREE].oldestHistoricBlock,
784 responses[L1_TO_L2_MESSAGE_TREE].oldestHistoricBlock,
785 responses[ARCHIVE].oldestHistoricBlock };
786 auto* const it = std::min_element(std::begin(historicalBlockNumbers), std::end(historicalBlockNumbers));
787 block_number_t oldestHistoricBlock = *it;
788 if (toBlockNumber <= oldestHistoricBlock) {
789 throw std::runtime_error(format("Unable to remove historical blocks to block number ",
790 toBlockNumber,
791 ", blocks not found. Current oldest block: ",
792 oldestHistoricBlock));
793 }
795 for (block_number_t blockNumber = oldestHistoricBlock; blockNumber < toBlockNumber; blockNumber++) {
796 // This will throw if it fails
797 remove_historical_block(blockNumber, status);
798 }
800 return status;
801}
802
804{
806 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
808 std::mutex mtx;
809 for (auto& [id, tree] : fork->_trees) {
810 std::visit(
811 [&signal, &local, blockNumber, id, &mtx](auto&& wrapper) {
812 wrapper.tree->finalize_block(blockNumber, [&signal, &local, &mtx, id](Response& resp) {
813 {
815 local[id] = std::move(resp);
816 }
817 signal.signal_decrement();
818 });
819 },
820 tree);
821 }
822 signal.wait_for_level();
823 for (auto& m : local) {
824 if (!m.success) {
825 throw std::runtime_error(m.message);
826 }
827 }
828 return true;
829}
831{
832 std::atomic_bool success = true;
833 std::string message;
835 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
836 {
839 signal,
840 *wrapper.tree,
841 success,
842 message,
843 status.meta.nullifierTreeMeta,
844 blockNumber);
845 }
846 {
849 signal,
850 *wrapper.tree,
851 success,
852 message,
854 blockNumber);
855 }
856
857 {
858 auto& wrapper = std::get<TreeWithStore<FrTree>>(fork->_trees.at(MerkleTreeId::NOTE_HASH_TREE));
860 signal,
861 *wrapper.tree,
862 success,
863 message,
864 status.meta.noteHashTreeMeta,
865 blockNumber);
866 }
867
868 {
871 signal,
872 *wrapper.tree,
873 success,
874 message,
875 status.meta.messageTreeMeta,
876 blockNumber);
877 }
878
879 {
880 auto& wrapper = std::get<TreeWithStore<FrTree>>(fork->_trees.at(MerkleTreeId::ARCHIVE));
882 signal,
883 *wrapper.tree,
884 success,
885 message,
886 status.meta.archiveTreeMeta,
887 blockNumber);
888 }
889 signal.wait_for_level();
890 if (!success) {
891 throw std::runtime_error(message);
892 }
893 remove_forks_for_block(blockNumber);
894 return true;
895}
897{
898 std::atomic_bool success = true;
899 std::string message;
901 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
902 {
905 signal,
906 *wrapper.tree,
907 success,
908 message,
909 status.meta.nullifierTreeMeta,
910 blockNumber);
911 }
912 {
915 signal,
916 *wrapper.tree,
917 success,
918 message,
920 blockNumber);
921 }
922
923 {
924 auto& wrapper = std::get<TreeWithStore<FrTree>>(fork->_trees.at(MerkleTreeId::NOTE_HASH_TREE));
926 signal,
927 *wrapper.tree,
928 success,
929 message,
930 status.meta.noteHashTreeMeta,
931 blockNumber);
932 }
933
934 {
937 signal,
938 *wrapper.tree,
939 success,
940 message,
941 status.meta.messageTreeMeta,
942 blockNumber);
943 }
944
945 {
946 auto& wrapper = std::get<TreeWithStore<FrTree>>(fork->_trees.at(MerkleTreeId::ARCHIVE));
948 signal,
949 *wrapper.tree,
950 success,
951 message,
952 status.meta.archiveTreeMeta,
953 blockNumber);
954 }
955 signal.wait_for_level();
956 if (!success) {
957 throw std::runtime_error(message);
958 }
959 remove_forks_for_block(blockNumber);
960 return true;
961}
962
964 uint32_t generator_point,
965 uint64_t genesis_timestamp)
966{
967 // NOTE: this hash operations needs to match the one in
968 // noir-project/noir-protocol-circuits/crates/types/src/abis/block_header.nr
970 { generator_point,
971 // last archive - which, at genesis, is all 0s
972 0, // root
973 0, // next_available_leaf_index
974 // state reference - the initial state for all the trees (accept the archive tree)
975 initial_state_ref.at(MerkleTreeId::L1_TO_L2_MESSAGE_TREE).first,
976 initial_state_ref.at(MerkleTreeId::L1_TO_L2_MESSAGE_TREE).second,
977 initial_state_ref.at(MerkleTreeId::NOTE_HASH_TREE).first,
978 initial_state_ref.at(MerkleTreeId::NOTE_HASH_TREE).second,
979 initial_state_ref.at(MerkleTreeId::NULLIFIER_TREE).first,
980 initial_state_ref.at(MerkleTreeId::NULLIFIER_TREE).second,
981 initial_state_ref.at(MerkleTreeId::PUBLIC_DATA_TREE).first,
982 initial_state_ref.at(MerkleTreeId::PUBLIC_DATA_TREE).second,
983 0, // sponge_blob_hash
984 // global variables
985 0, // chain_id
986 0, // version
987 0, // block_number
988 0, // slot_number
989 bb::fr(genesis_timestamp), // timestamp
990 0, // coinbase
991 0, // fee_recipient
992 0, // gas_fee.fee_per_da_gas
993 0, // gas_fee.fee_per_l2_gas
994 // total fees
995 0,
996 // total mana used
997 0 });
998}
999
1000bool WorldState::is_archive_tip(const WorldStateRevision& revision, const bb::fr& block_header_hash) const
1001{
1003
1004 try {
1005 find_leaf_indices<fr>(revision, MerkleTreeId::ARCHIVE, { block_header_hash }, indices);
1006 } catch (std::runtime_error&) {
1007 }
1008
1009 if (indices.empty() || !indices[0].has_value()) {
1010 return false;
1011 }
1012
1013 TreeMetaResponse archive_state = get_tree_info(revision, MerkleTreeId::ARCHIVE);
1014 return archive_state.meta.size == indices[0].value() + 1;
1015}
1016
1024
1026 std::array<TreeMeta, NUM_TREES>& metaResponses)
1027{
1028 TreeMeta& archive_state = metaResponses[MerkleTreeId::ARCHIVE];
1029 status.unfinalizedBlockNumber = archive_state.unfinalizedBlockHeight;
1030 status.finalizedBlockNumber = archive_state.finalizedBlockHeight;
1031 status.oldestHistoricalBlock = archive_state.oldestHistoricBlock;
1032 status.treesAreSynched = determine_if_synched(metaResponses);
1033}
1034
1046
1047bool WorldState::is_same_state_reference(const WorldStateRevision& revision, const StateReference& state_ref) const
1048{
1049 return state_ref == get_state_reference(revision);
1050}
1051
1053{
1054 WorldStateRevision revision{ .forkId = CANONICAL_FORK_ID, .includeUncommitted = false };
1056 get_all_tree_info(revision, responses);
1057
1059 throw std::runtime_error("World state trees are out of sync");
1060 }
1061}
1062
1064{
1065 block_number_t blockNumber = metaResponses[0].unfinalizedBlockHeight;
1066 block_number_t finalizedBlockNumber = metaResponses[0].finalizedBlockHeight;
1067 for (size_t i = 1; i < metaResponses.size(); i++) {
1068 if (blockNumber != metaResponses[i].unfinalizedBlockHeight) {
1069 return false;
1070 }
1071 if (finalizedBlockNumber != metaResponses[i].finalizedBlockHeight) {
1072 return false;
1073 }
1074 }
1075 return true;
1076}
1077
1078uint32_t WorldState::checkpoint(const uint64_t& forkId)
1079{
1080 Fork::SharedPtr fork = retrieve_fork(forkId);
1081 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
1083 std::mutex mtx;
1084 for (auto& [id, tree] : fork->_trees) {
1085 std::visit(
1086 [&signal, &local, id, &mtx](auto&& wrapper) {
1087 wrapper.tree->checkpoint([&signal, &local, &mtx, id](TypedResponse<CheckpointResponse>& resp) {
1088 {
1090 local[id] = std::move(resp);
1091 }
1092 signal.signal_decrement();
1093 });
1094 },
1095 tree);
1096 }
1097 signal.wait_for_level();
1098 for (auto& m : local) {
1099 if (!m.success) {
1100 throw std::runtime_error(m.message);
1101 }
1102 }
1103 // All trees have the same checkpoint depth; return it from the first tree's response
1104 return local[0].inner.depth;
1105}
1106
1107void WorldState::commit_checkpoint(const uint64_t& forkId)
1108{
1109 Fork::SharedPtr fork = retrieve_fork(forkId);
1110 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
1112 std::mutex mtx;
1113 for (auto& [id, tree] : fork->_trees) {
1114 std::visit(
1115 [&signal, &local, id, &mtx](auto&& wrapper) {
1116 wrapper.tree->commit_checkpoint([&signal, &local, &mtx, id](Response& resp) {
1117 {
1119 local[id] = std::move(resp);
1120 }
1121 signal.signal_decrement();
1122 });
1123 },
1124 tree);
1125 }
1126 signal.wait_for_level();
1127 for (auto& m : local) {
1128 if (!m.success) {
1129 throw std::runtime_error(m.message);
1130 }
1131 }
1132}
1133
1134void WorldState::revert_checkpoint(const uint64_t& forkId)
1135{
1136 Fork::SharedPtr fork = retrieve_fork(forkId);
1137 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
1139 std::mutex mtx;
1140 for (auto& [id, tree] : fork->_trees) {
1141 std::visit(
1142 [&signal, &local, id, &mtx](auto&& wrapper) {
1143 wrapper.tree->revert_checkpoint([&signal, &local, &mtx, id](Response& resp) {
1144 {
1146 local[id] = std::move(resp);
1147 }
1148 signal.signal_decrement();
1149 });
1150 },
1151 tree);
1152 }
1153 signal.wait_for_level();
1154 for (auto& m : local) {
1155 if (!m.success) {
1156 throw std::runtime_error(m.message);
1157 }
1158 }
1159}
1160
1161void WorldState::commit_all_checkpoints_to(const uint64_t& forkId, uint32_t depth)
1162{
1163 Fork::SharedPtr fork = retrieve_fork(forkId);
1164 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
1166 std::mutex mtx;
1167 for (auto& [id, tree] : fork->_trees) {
1168 std::visit(
1169 [&signal, &local, id, &mtx, depth](auto&& wrapper) {
1170 auto callback = [&signal, &local, &mtx, id](Response& resp) {
1171 {
1173 local[id] = std::move(resp);
1174 }
1175 signal.signal_decrement();
1176 };
1177 wrapper.tree->commit_to_depth(depth, callback);
1178 },
1179 tree);
1180 }
1181 signal.wait_for_level();
1182 for (auto& m : local) {
1183 if (!m.success) {
1184 throw std::runtime_error(m.message);
1185 }
1186 }
1187}
1188
1189void WorldState::revert_all_checkpoints_to(const uint64_t& forkId, uint32_t depth)
1190{
1191 Fork::SharedPtr fork = retrieve_fork(forkId);
1192 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
1194 std::mutex mtx;
1195 for (auto& [id, tree] : fork->_trees) {
1196 std::visit(
1197 [&signal, &local, id, &mtx, depth](auto&& wrapper) {
1198 auto callback = [&signal, &local, &mtx, id](Response& resp) {
1199 {
1201 local[id] = std::move(resp);
1202 }
1203 signal.signal_decrement();
1204 };
1205 wrapper.tree->revert_to_depth(depth, callback);
1206 },
1207 tree);
1208 }
1209 signal.wait_for_level();
1210 for (auto& m : local) {
1211 if (!m.success) {
1212 throw std::runtime_error(m.message);
1213 }
1214 }
1215}
1216
1218{
1219 WorldStateRevision revision{ .forkId = CANONICAL_FORK_ID, .includeUncommitted = false };
1221 get_all_tree_info(revision, responses);
1222
1223 // Get all historic block numbers
1224 std::array<block_number_t, NUM_TREES> historicalBlockNumbers{ responses[NULLIFIER_TREE].oldestHistoricBlock,
1225 responses[NOTE_HASH_TREE].oldestHistoricBlock,
1226 responses[PUBLIC_DATA_TREE].oldestHistoricBlock,
1227 responses[L1_TO_L2_MESSAGE_TREE].oldestHistoricBlock,
1228 responses[ARCHIVE].oldestHistoricBlock };
1229
1230 // Get all unfinalized block numbers
1231 std::array<block_number_t, NUM_TREES> unfinalizedBlockNumbers{
1232 responses[NULLIFIER_TREE].unfinalizedBlockHeight,
1233 responses[NOTE_HASH_TREE].unfinalizedBlockHeight,
1234 responses[PUBLIC_DATA_TREE].unfinalizedBlockHeight,
1235 responses[L1_TO_L2_MESSAGE_TREE].unfinalizedBlockHeight,
1236 responses[ARCHIVE].unfinalizedBlockHeight
1237 };
1238
1239 // Get all finalized block numbers
1240 std::array<block_number_t, NUM_TREES> finalizedBlockNumbers{ responses[NULLIFIER_TREE].finalizedBlockHeight,
1241 responses[NOTE_HASH_TREE].finalizedBlockHeight,
1242 responses[PUBLIC_DATA_TREE].finalizedBlockHeight,
1243 responses[L1_TO_L2_MESSAGE_TREE].finalizedBlockHeight,
1244 responses[ARCHIVE].finalizedBlockHeight };
1245
1246 // Get the min and max of each set of block numbers
1247 auto historicBlockRange = std::minmax_element(std::begin(historicalBlockNumbers), std::end(historicalBlockNumbers));
1248
1249 auto unfinalizedBlockRange =
1250 std::minmax_element(std::begin(unfinalizedBlockNumbers), std::end(unfinalizedBlockNumbers));
1251
1252 auto finalizedBlockRange = std::minmax_element(std::begin(finalizedBlockNumbers), std::end(finalizedBlockNumbers));
1253
1254 // We re-sync by
1255 // 1. Unwinding any blocks that are ahread of the lowest unfinalized block number
1256 // 2. Increasing finalized block numbers to the highest finalized block number
1257 // 3. Removing any historical blocks that are lower then the highest historic block number
1258
1259 WorldStateStatusFull status;
1260 block_number_t blockToUnwind = *unfinalizedBlockRange.second;
1261 while (blockToUnwind > *unfinalizedBlockRange.first) {
1262 unwind_block(blockToUnwind, status);
1263 blockToUnwind--;
1264 }
1265
1266 if (*finalizedBlockRange.first != *finalizedBlockRange.second) {
1267 set_finalized_block(*finalizedBlockRange.second);
1268 }
1269
1270 block_number_t blockToRemove = *historicBlockRange.first;
1271 while (blockToRemove < *historicBlockRange.second) {
1272 remove_historical_block(blockToRemove, status);
1273 blockToRemove++;
1274 }
1275
1277 return status;
1278}
1279
1280} // 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.
uint32_t checkpoint(const uint64_t &forkId)
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)
void commit_all_checkpoints_to(const uint64_t &forkId, uint32_t depth)
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)
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, uint64_t genesis_timestamp=0)
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 rollback()
Rolls back any uncommitted changes made to the world state.
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)
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
static bb::fr compute_initial_block_header_hash(const StateReference &initial_state_ref, uint32_t generator_point, uint64_t genesis_timestamp=0)
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 revert_all_checkpoints_to(const uint64_t &forkId, uint32_t depth)
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:23
uint32_t block_number_t
Definition types.hpp:19
std::vector< fr > fr_sibling_path
Definition hash_path.hpp:14
@ L1_TO_L2_MESSAGE_TREE
Definition types.hpp:23
const uint64_t DEFAULT_MIN_NUMBER_OF_READERS
const uint64_t CANONICAL_FORK_ID
Definition types.hpp:27
std::string getMerkleTreeName(MerkleTreeId id)
Definition types.cpp:6
std::unordered_map< MerkleTreeId, TreeStateReference > StateReference
Definition types.hpp:33
const uint64_t NUM_TREES
Definition types.hpp:28
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:14
std::shared_ptr< Fork > SharedPtr
Definition fork.hpp:32
static WorldStateRevision committed()
Definition types.hpp:50
static WorldStateRevision uncommitted()
Definition types.hpp:51
WorldStateStatusSummary summary
Definition types.hpp:222