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 bool ephemeral)
45 : _workers(std::make_shared<ThreadPool>(thread_pool_size))
46 , _tree_heights(tree_heights)
47 , _initial_tree_size(tree_prefill)
48 , _forkId(CANONICAL_FORK_ID)
49 , _initial_header_generator_point(initial_header_generator_point)
50 , _genesis_timestamp(genesis_timestamp)
51{
52 // We set the max readers to be high, at least the number of given threads or the default if higher
53 uint64_t maxReaders = std::max(thread_pool_size, DEFAULT_MIN_NUMBER_OF_READERS);
54 create_canonical_fork(data_dir, map_size, prefilled_public_data, maxReaders, ephemeral);
55 try {
57 } catch (std::exception& e) {
58 // We don't do anything with this. If any attept to re-sync failed it will be picked up later in TS land
59 }
60}
61
62WorldState::WorldState(uint64_t thread_pool_size,
63 const std::string& data_dir,
65 const std::unordered_map<MerkleTreeId, uint32_t>& tree_heights,
66 const std::unordered_map<MerkleTreeId, index_t>& tree_prefill,
67 uint32_t initial_header_generator_point,
68 uint64_t genesis_timestamp,
69 bool ephemeral)
70 : WorldState::WorldState(thread_pool_size,
71 data_dir,
72 map_size,
73 tree_heights,
74 tree_prefill,
75 std::vector<PublicDataLeafValue>(),
76 initial_header_generator_point,
77 genesis_timestamp,
78 ephemeral)
79{}
80
81WorldState::WorldState(uint64_t thread_pool_size,
82 const std::string& data_dir,
83 uint64_t map_size,
84 const std::unordered_map<MerkleTreeId, uint32_t>& tree_heights,
85 const std::unordered_map<MerkleTreeId, index_t>& tree_prefill,
86 const std::vector<PublicDataLeafValue>& prefilled_public_data,
87 uint32_t initial_header_generator_point,
88 uint64_t genesis_timestamp,
89 bool ephemeral)
90 : WorldState(thread_pool_size,
91 data_dir,
92 {
93 { MerkleTreeId::NULLIFIER_TREE, map_size },
95 { MerkleTreeId::ARCHIVE, map_size },
96 { MerkleTreeId::NOTE_HASH_TREE, map_size },
98 },
99 tree_heights,
100 tree_prefill,
101 prefilled_public_data,
102 initial_header_generator_point,
103 genesis_timestamp,
104 ephemeral)
105{}
106
107WorldState::WorldState(uint64_t thread_pool_size,
108 const std::string& data_dir,
109 uint64_t map_size,
110 const std::unordered_map<MerkleTreeId, uint32_t>& tree_heights,
111 const std::unordered_map<MerkleTreeId, index_t>& tree_prefill,
112 uint32_t initial_header_generator_point,
113 uint64_t genesis_timestamp,
114 bool ephemeral)
115 : WorldState(thread_pool_size,
116 data_dir,
117 map_size,
118 tree_heights,
119 tree_prefill,
120 std::vector<PublicDataLeafValue>(),
121 initial_header_generator_point,
122 genesis_timestamp,
123 ephemeral)
124{}
125
126void WorldState::create_canonical_fork(const std::string& dataDir,
128 const std::vector<PublicDataLeafValue>& prefilled_public_data,
129 uint64_t maxReaders,
130 bool ephemeral)
131{
132 // create the underlying stores
133 auto createStore = [&](MerkleTreeId id) {
134 auto name = getMerkleTreeName(id);
135 std::filesystem::path directory = dataDir;
136 directory /= name;
137 std::filesystem::create_directories(directory);
138 return std::make_shared<LMDBTreeStore>(directory, name, dbSize.at(id), maxReaders, ephemeral);
139 };
142 createStore(MerkleTreeId::ARCHIVE),
143 createStore(MerkleTreeId::NOTE_HASH_TREE),
145
147 fork->_forkId = _forkId++;
148 {
149 uint32_t levels = _tree_heights.at(MerkleTreeId::NULLIFIER_TREE);
153 auto tree = std::make_unique<NullifierTree>(std::move(store), _workers, initial_size);
154 fork->_trees.insert({ MerkleTreeId::NULLIFIER_TREE, TreeWithStore(std::move(tree)) });
155 }
156 {
157 uint32_t levels = _tree_heights.at(MerkleTreeId::NOTE_HASH_TREE);
158 auto store = std::make_unique<FrStore>(
160 auto tree = std::make_unique<FrTree>(std::move(store), _workers);
161 fork->_trees.insert({ MerkleTreeId::NOTE_HASH_TREE, TreeWithStore(std::move(tree)) });
162 }
163 {
164 uint32_t levels = _tree_heights.at(MerkleTreeId::PUBLIC_DATA_TREE);
168 auto tree = std::make_unique<PublicDataTree>(std::move(store), _workers, initial_size, prefilled_public_data);
169 fork->_trees.insert({ MerkleTreeId::PUBLIC_DATA_TREE, TreeWithStore(std::move(tree)) });
170 }
171 {
173 auto store = std::make_unique<FrStore>(
175 auto tree = std::make_unique<FrTree>(std::move(store), _workers);
176 fork->_trees.insert({ MerkleTreeId::L1_TO_L2_MESSAGE_TREE, TreeWithStore(std::move(tree)) });
177 }
178 {
179 uint32_t levels = _tree_heights.at(MerkleTreeId::ARCHIVE);
180 std::vector<bb::fr> initial_values{ compute_initial_block_header_hash(
184 auto store = std::make_unique<FrStore>(
186 auto tree = std::make_unique<FrTree>(std::move(store), _workers, initial_values);
187 fork->_trees.insert({ MerkleTreeId::ARCHIVE, TreeWithStore(std::move(tree)) });
188 }
189 _forks[fork->_forkId] = fork;
190}
191
192void WorldState::copy_stores(const std::string& dstPath, bool compact) const
193{
194 auto copyStore = [&](const LMDBTreeStore::SharedPtr& store) {
195 std::filesystem::path directory = dstPath;
196 directory /= store->get_name();
197 std::filesystem::create_directories(directory);
198 store->copy_store(directory, compact);
199 };
200
201 std::for_each(_persistentStores->begin(), _persistentStores->end(), copyStore);
202}
203
204Fork::SharedPtr WorldState::retrieve_fork(const uint64_t& forkId) const
205{
206 std::unique_lock lock(mtx);
207 auto it = _forks.find(forkId);
208 if (it == _forks.end()) {
209 throw std::runtime_error("Fork not found");
210 }
211 return it->second;
212}
214{
215 block_number_t blockNumberForFork = 0;
216 if (!blockNumber.has_value()) {
217 // we are forking at latest
218 WorldStateRevision revision{ .forkId = CANONICAL_FORK_ID, .includeUncommitted = false };
220 blockNumberForFork = archiveMeta.meta.unfinalizedBlockHeight;
221 } else {
222 blockNumberForFork = blockNumber.value();
223 }
224 Fork::SharedPtr fork = create_new_fork(blockNumberForFork);
225 std::unique_lock lock(mtx);
226 uint64_t forkId = _forkId++;
227 fork->_forkId = forkId;
228 _forks[forkId] = fork;
229 return forkId;
230}
231
233{
234 // capture the shared pointers outside of the lock scope so we are not under the lock when the objects are destroyed
236 {
237 std::unique_lock lock(mtx);
238 for (auto it = _forks.begin(); it != _forks.end();) {
239 if (it->second->_blockNumber == blockNumber) {
240 forks.push_back(it->second);
241 it = _forks.erase(it);
242
243 } else {
244 it++;
245 }
246 }
247 }
248}
249
250void WorldState::delete_fork(const uint64_t& forkId)
251{
252 if (forkId == 0) {
253 throw std::runtime_error("Unable to delete canonical fork");
254 }
255 // Retrieving the shared pointer here means we throw if the fork is not available, it also means we are not under a
256 // lock when we destroy the object
257 Fork::SharedPtr fork = retrieve_fork(forkId);
258 {
259 std::unique_lock lock(mtx);
260 _forks.erase(forkId);
261 }
262}
263
265{
267 fork->_blockNumber = blockNumber;
268 {
269 uint32_t levels = _tree_heights.at(MerkleTreeId::NULLIFIER_TREE);
272 getMerkleTreeName(MerkleTreeId::NULLIFIER_TREE), levels, blockNumber, _persistentStores->nullifierStore);
273 auto tree = std::make_unique<NullifierTree>(std::move(store), _workers, initial_size);
274 fork->_trees.insert({ MerkleTreeId::NULLIFIER_TREE, TreeWithStore(std::move(tree)) });
275 }
276 {
277 uint32_t levels = _tree_heights.at(MerkleTreeId::NOTE_HASH_TREE);
278 auto store = std::make_unique<FrStore>(
279 getMerkleTreeName(MerkleTreeId::NOTE_HASH_TREE), levels, blockNumber, _persistentStores->noteHashStore);
280 auto tree = std::make_unique<FrTree>(std::move(store), _workers);
281 fork->_trees.insert({ MerkleTreeId::NOTE_HASH_TREE, TreeWithStore(std::move(tree)) });
282 }
283 {
284 uint32_t levels = _tree_heights.at(MerkleTreeId::PUBLIC_DATA_TREE);
287 getMerkleTreeName(MerkleTreeId::PUBLIC_DATA_TREE), levels, blockNumber, _persistentStores->publicDataStore);
288 auto tree = std::make_unique<PublicDataTree>(std::move(store), _workers, initial_size);
289 fork->_trees.insert({ MerkleTreeId::PUBLIC_DATA_TREE, TreeWithStore(std::move(tree)) });
290 }
291 {
294 levels,
295 blockNumber,
296 _persistentStores->messageStore);
297 auto tree = std::make_unique<FrTree>(std::move(store), _workers);
298 fork->_trees.insert({ MerkleTreeId::L1_TO_L2_MESSAGE_TREE, TreeWithStore(std::move(tree)) });
299 }
300 {
301 uint32_t levels = _tree_heights.at(MerkleTreeId::ARCHIVE);
302 auto store = std::make_unique<FrStore>(
303 getMerkleTreeName(MerkleTreeId::ARCHIVE), levels, blockNumber, _persistentStores->archiveStore);
304 auto tree = std::make_unique<FrTree>(std::move(store), _workers);
305 fork->_trees.insert({ MerkleTreeId::ARCHIVE, TreeWithStore(std::move(tree)) });
306 }
307 return fork;
308}
309
311{
312 Fork::SharedPtr fork = retrieve_fork(revision.forkId);
313 return std::visit(
314 [=](auto&& wrapper) {
315 Signal signal(1);
317
318 auto callback = [&](TypedResponse<TreeMetaResponse>& meta) {
319 local = std::move(meta);
320 signal.signal_level(0);
321 };
322
323 if (revision.is_historical()) {
324 wrapper.tree->get_meta_data(revision.blockNumber, revision.includeUncommitted, callback);
325 } else {
326 wrapper.tree->get_meta_data(revision.includeUncommitted, callback);
327 }
328 signal.wait_for_level(0);
329
330 if (!local.success) {
331 throw std::runtime_error(local.message);
332 }
333 return local.inner;
334 },
335 fork->_trees.at(tree_id));
336}
337
339{
340 Fork::SharedPtr fork = retrieve_fork(revision.forkId);
341
345 };
346
347 Signal signal(static_cast<uint32_t>(tree_ids.size()));
348 std::mutex mutex;
350
351 for (auto id : tree_ids) {
352 const auto& tree = fork->_trees.at(id);
353 auto callback = [&signal, &local, &mutex, id](TypedResponse<TreeMetaResponse>& meta) {
354 {
355 std::lock_guard<std::mutex> lock(mutex);
356 local[id] = std::move(meta);
357 }
358 signal.signal_decrement();
359 };
360 std::visit(
361 [&callback, &revision](auto&& wrapper) {
362 if (revision.is_historical()) {
363 wrapper.tree->get_meta_data(revision.blockNumber, revision.includeUncommitted, callback);
364 } else {
365 wrapper.tree->get_meta_data(revision.includeUncommitted, callback);
366 }
367 },
368 tree);
369 }
370
371 signal.wait_for_level(0);
372
373 for (auto tree_id : tree_ids) {
374 auto& m = local[tree_id];
375 if (!m.success) {
376 throw std::runtime_error(m.message);
377 }
378 responses[tree_id] = std::move(m.inner.meta);
379 }
380}
381
383{
384 return get_state_reference(revision, retrieve_fork(revision.forkId));
385}
386
393
395 Fork::SharedPtr fork,
396 bool initial_state)
397{
398 if (fork->_forkId != revision.forkId) {
399 throw std::runtime_error("Fork does not match revision");
400 }
401
407 };
408
409 Signal signal(static_cast<uint32_t>(tree_ids.size()));
410 StateReference state_reference;
412 std::mutex state_ref_mutex;
413
414 for (auto id : tree_ids) {
415 const auto& tree = fork->_trees.at(id);
416 auto callback = [&signal, &local, &state_ref_mutex, id](TypedResponse<TreeMetaResponse>& meta) {
417 {
418 std::lock_guard<std::mutex> lock(state_ref_mutex);
419 local[id] = std::move(meta);
420 }
421 signal.signal_decrement();
422 };
423 std::visit(
424 [&callback, &revision](auto&& wrapper) {
425 if (revision.is_historical()) {
426 wrapper.tree->get_meta_data(revision.blockNumber, revision.includeUncommitted, callback);
427 } else {
428 wrapper.tree->get_meta_data(revision.includeUncommitted, callback);
429 }
430 },
431 tree);
432 }
433
434 signal.wait_for_level(0);
435
436 for (auto tree_id : tree_ids) {
437 auto& m = local[tree_id];
438 if (!m.success) {
439 throw std::runtime_error(m.message);
440 }
441 if (initial_state) {
442 state_reference[tree_id] = std::make_pair(m.inner.meta.initialRoot, m.inner.meta.initialSize);
443 continue;
444 }
445 state_reference[tree_id] = std::make_pair(m.inner.meta.root, m.inner.meta.size);
446 }
447
448 return state_reference;
449}
450
452 MerkleTreeId tree_id,
453 index_t leaf_index) const
454{
455 Fork::SharedPtr fork = retrieve_fork(revision.forkId);
456
457 return std::visit(
458 [leaf_index, revision](auto&& wrapper) {
459 Signal signal(1);
461
462 auto callback = [&signal, &local](TypedResponse<GetSiblingPathResponse>& response) {
463 local = std::move(response);
464 signal.signal_level(0);
465 };
466
467 if (revision.is_historical()) {
468 wrapper.tree->get_sibling_path(leaf_index, revision.blockNumber, callback, revision.includeUncommitted);
469 } else {
470 wrapper.tree->get_sibling_path(leaf_index, callback, revision.includeUncommitted);
471 }
472 signal.wait_for_level(0);
473
474 if (!local.success) {
475 throw std::runtime_error(local.message);
476 }
477 return local.inner.path;
478 },
479 fork->_trees.at(tree_id));
480}
481
483 MerkleTreeId tree_id,
484 const std::vector<index_t>& leafIndices,
485 std::vector<std::optional<block_number_t>>& blockNumbers) const
486{
487 Fork::SharedPtr fork = retrieve_fork(revision.forkId);
488
489 std::visit(
490 [&leafIndices, revision, &blockNumbers](auto&& wrapper) {
491 Signal signal(1);
493
494 auto callback = [&signal, &local](TypedResponse<BlockForIndexResponse>& response) {
495 local = std::move(response);
496 signal.signal_level();
497 };
498
499 if (revision.is_historical()) {
500 wrapper.tree->find_block_numbers(leafIndices, revision.blockNumber, callback);
501 } else {
502 wrapper.tree->find_block_numbers(leafIndices, callback);
503 }
504 signal.wait_for_level(0);
505
506 if (!local.success) {
507 throw std::runtime_error(local.message);
508 }
509 blockNumbers = std::move(local.inner.blockNumbers);
510 },
511 fork->_trees.at(tree_id));
512}
513
515{
516 Fork::SharedPtr fork = retrieve_fork(fork_id);
517 if (const auto* wrapper =
519 Signal signal;
520 wrapper->tree->add_or_update_value(
521 new_value,
523 signal.wait_for_level();
524 } else {
525 throw std::runtime_error("Invalid tree type for PublicDataTree");
526 }
527}
528
529void WorldState::update_archive(const StateReference& block_state_ref,
530 const bb::fr& block_header_hash,
531 Fork::Id fork_id)
532{
533 if (is_same_state_reference(WorldStateRevision{ .forkId = fork_id, .includeUncommitted = true }, block_state_ref)) {
534 append_leaves<fr>(MerkleTreeId::ARCHIVE, { block_header_hash }, fork_id);
535 } else {
536 throw std::runtime_error("Can't update archive tree: Block state does not match world state");
537 }
538}
539
541{
542 // NOTE: the calling code is expected to ensure no other reads or writes happen during commit
544 std::atomic_bool success = true;
545 std::string message;
546 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
547
548 {
551 status.dbStats.nullifierTreeStats, signal, *wrapper.tree, success, message, status.meta.nullifierTreeMeta);
552 }
553 {
556 signal,
557 *wrapper.tree,
558 success,
559 message,
560 status.meta.publicDataTreeMeta);
561 }
562
563 {
564 auto& wrapper = std::get<TreeWithStore<FrTree>>(fork->_trees.at(MerkleTreeId::NOTE_HASH_TREE));
566 status.dbStats.noteHashTreeStats, signal, *wrapper.tree, success, message, status.meta.noteHashTreeMeta);
567 }
568
569 {
572 status.dbStats.messageTreeStats, signal, *wrapper.tree, success, message, status.meta.messageTreeMeta);
573 }
574
575 {
576 auto& wrapper = std::get<TreeWithStore<FrTree>>(fork->_trees.at(MerkleTreeId::ARCHIVE));
578 status.dbStats.archiveTreeStats, signal, *wrapper.tree, success, message, status.meta.archiveTreeMeta);
579 }
580
581 signal.wait_for_level(0);
582 return std::make_pair(success.load(), message);
583}
584
586{
587 // NOTE: the calling code is expected to ensure no other reads or writes happen during rollback
589 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
590 for (auto& [id, tree] : fork->_trees) {
591 std::visit(
592 [&signal](auto&& wrapper) {
593 wrapper.tree->rollback([&signal](const Response&) { signal.signal_decrement(); });
594 },
595 tree);
596 }
597 signal.wait_for_level();
598}
599
601 const bb::fr& block_header_hash,
602 const std::vector<bb::fr>& notes,
603 const std::vector<bb::fr>& l1_to_l2_messages,
606{
608 rollback();
609
611 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
612 std::atomic_bool success = true;
613 std::string err_message;
614 auto decr = [&signal, &success, &err_message](const auto& resp) {
615 // take the first error
616 bool expected = true;
617 if (!resp.success && success.compare_exchange_strong(expected, false)) {
618 err_message = resp.message;
619 }
620
621 signal.signal_decrement();
622 };
623
624 {
626 NullifierTree::AddCompletionCallback completion = [&](const auto& resp) -> void {
627 // take the first error
628 bool expected = true;
629 if (!resp.success && success.compare_exchange_strong(expected, false)) {
630 err_message = resp.message;
631 }
632
633 signal.signal_decrement();
634 };
635 wrapper.tree->add_or_update_values(nullifiers, 0, completion);
636 }
637
638 {
639 auto& wrapper = std::get<TreeWithStore<FrTree>>(fork->_trees.at(MerkleTreeId::NOTE_HASH_TREE));
640 wrapper.tree->add_values(notes, decr);
641 }
642
643 {
645 wrapper.tree->add_values(l1_to_l2_messages, decr);
646 }
647
648 {
649 auto& wrapper = std::get<TreeWithStore<FrTree>>(fork->_trees.at(MerkleTreeId::ARCHIVE));
650 wrapper.tree->add_value(block_header_hash, decr);
651 }
652
653 {
655 PublicDataTree::AddCompletionCallback completion = [&](const auto& resp) -> void {
656 // take the first error
657 bool expected = true;
658 if (!resp.success && success.compare_exchange_strong(expected, false)) {
659 err_message = resp.message;
660 }
661
662 signal.signal_decrement();
663 };
664 wrapper.tree->add_or_update_values_sequentially(public_writes, completion);
665 }
666
667 signal.wait_for_level();
668
669 // Check resulting state and commit if successful
671 try {
672 if (!success) {
673 throw std::runtime_error("Failed to sync block: " + err_message);
674 }
675
676 if (!is_archive_tip(WorldStateRevision::uncommitted(), block_header_hash)) {
677 throw std::runtime_error("Can't synch block: block header hash is not the tip of the archive tree");
678 }
679
681 throw std::runtime_error("Can't synch block: block state does not match world state");
682 }
683
684 std::pair<bool, std::string> result = commit(status);
685 if (!result.first) {
686 throw std::runtime_error(result.second);
687 }
688 } catch (const std::exception& e) {
689 // We failed, rollback any uncommitted state before leaving
690 rollback();
691 throw;
692 }
693
694 // Success return the status
696 return status;
697}
698
700 MerkleTreeId tree_id,
701 const bb::fr& leaf_key) const
702{
703 Fork::SharedPtr fork = retrieve_fork(revision.forkId);
704 Signal signal;
706 auto callback = [&signal, &low_leaf_info](TypedResponse<GetLowIndexedLeafResponse>& response) {
707 low_leaf_info = std::move(response);
708 signal.signal_level();
709 };
710
711 if (const auto* wrapper = std::get_if<TreeWithStore<NullifierTree>>(&fork->_trees.at(tree_id))) {
712 if (revision.is_historical()) {
713 wrapper->tree->find_low_leaf(leaf_key, revision.blockNumber, revision.includeUncommitted, callback);
714 } else {
715 wrapper->tree->find_low_leaf(leaf_key, revision.includeUncommitted, callback);
716 }
717
718 } else if (const auto* wrapper = std::get_if<TreeWithStore<PublicDataTree>>(&fork->_trees.at(tree_id))) {
719 if (revision.is_historical()) {
720 wrapper->tree->find_low_leaf(leaf_key, revision.blockNumber, revision.includeUncommitted, callback);
721 } else {
722 wrapper->tree->find_low_leaf(leaf_key, revision.includeUncommitted, callback);
723 }
724
725 } else {
726 throw std::runtime_error("Invalid tree type for find_low_leaf");
727 }
728
729 signal.wait_for_level();
730
731 if (!low_leaf_info.success) {
732 throw std::runtime_error(low_leaf_info.message);
733 }
734 return low_leaf_info.inner;
735}
736
738{
739 // This will throw if it fails
740 set_finalized_block(toBlockNumber);
742 get_status_summary(status);
743 return status;
744}
746{
747 // Ensure no uncommitted state
748 rollback();
749
750 WorldStateRevision revision{ .forkId = CANONICAL_FORK_ID, .includeUncommitted = false };
752 get_all_tree_info(revision, responses);
753
754 // Get the set of unfinalized block numbers and unwind to the target block
755 std::array<block_number_t, NUM_TREES> unfinalizedBlockNumbers{
756 responses[NULLIFIER_TREE].unfinalizedBlockHeight,
757 responses[NOTE_HASH_TREE].unfinalizedBlockHeight,
758 responses[PUBLIC_DATA_TREE].unfinalizedBlockHeight,
759 responses[L1_TO_L2_MESSAGE_TREE].unfinalizedBlockHeight,
760 responses[ARCHIVE].unfinalizedBlockHeight
761 };
762
763 auto* const it = std::max_element(std::begin(unfinalizedBlockNumbers), std::end(unfinalizedBlockNumbers));
764 block_number_t highestUnfinalizedBlock = *it;
765
766 if (toBlockNumber >= highestUnfinalizedBlock) {
767 throw std::runtime_error(format("Unable to unwind blocks to block number ",
768 toBlockNumber,
769 ", current pending block ",
770 highestUnfinalizedBlock));
771 }
772
774 for (block_number_t blockNumber = highestUnfinalizedBlock; blockNumber > toBlockNumber; blockNumber--) {
775 // This will throw if it fails
776 unwind_block(blockNumber, status);
777 }
779 return status;
780}
781
783{
784 WorldStateRevision revision{ .forkId = CANONICAL_FORK_ID, .includeUncommitted = false };
786 get_all_tree_info(revision, responses);
787
788 // Get the set of historic block numbers and remove to the target block
789 std::array<block_number_t, NUM_TREES> historicalBlockNumbers{ responses[NULLIFIER_TREE].oldestHistoricBlock,
790 responses[NOTE_HASH_TREE].oldestHistoricBlock,
791 responses[PUBLIC_DATA_TREE].oldestHistoricBlock,
792 responses[L1_TO_L2_MESSAGE_TREE].oldestHistoricBlock,
793 responses[ARCHIVE].oldestHistoricBlock };
794 auto* const it = std::min_element(std::begin(historicalBlockNumbers), std::end(historicalBlockNumbers));
795 block_number_t oldestHistoricBlock = *it;
796 if (toBlockNumber <= oldestHistoricBlock) {
797 throw std::runtime_error(format("Unable to remove historical blocks to block number ",
798 toBlockNumber,
799 ", blocks not found. Current oldest block: ",
800 oldestHistoricBlock));
801 }
803 for (block_number_t blockNumber = oldestHistoricBlock; blockNumber < toBlockNumber; blockNumber++) {
804 // This will throw if it fails
805 remove_historical_block(blockNumber, status);
806 }
808 return status;
809}
810
812{
814 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
816 std::mutex mtx;
817 for (auto& [id, tree] : fork->_trees) {
818 std::visit(
819 [&signal, &local, blockNumber, id, &mtx](auto&& wrapper) {
820 wrapper.tree->finalize_block(blockNumber, [&signal, &local, &mtx, id](Response& resp) {
821 {
823 local[id] = std::move(resp);
824 }
825 signal.signal_decrement();
826 });
827 },
828 tree);
829 }
830 signal.wait_for_level();
831 for (auto& m : local) {
832 if (!m.success) {
833 throw std::runtime_error(m.message);
834 }
835 }
836 return true;
837}
839{
840 std::atomic_bool success = true;
841 std::string message;
843 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
844 {
847 signal,
848 *wrapper.tree,
849 success,
850 message,
851 status.meta.nullifierTreeMeta,
852 blockNumber);
853 }
854 {
857 signal,
858 *wrapper.tree,
859 success,
860 message,
862 blockNumber);
863 }
864
865 {
866 auto& wrapper = std::get<TreeWithStore<FrTree>>(fork->_trees.at(MerkleTreeId::NOTE_HASH_TREE));
868 signal,
869 *wrapper.tree,
870 success,
871 message,
872 status.meta.noteHashTreeMeta,
873 blockNumber);
874 }
875
876 {
879 signal,
880 *wrapper.tree,
881 success,
882 message,
883 status.meta.messageTreeMeta,
884 blockNumber);
885 }
886
887 {
888 auto& wrapper = std::get<TreeWithStore<FrTree>>(fork->_trees.at(MerkleTreeId::ARCHIVE));
890 signal,
891 *wrapper.tree,
892 success,
893 message,
894 status.meta.archiveTreeMeta,
895 blockNumber);
896 }
897 signal.wait_for_level();
898 if (!success) {
899 throw std::runtime_error(message);
900 }
901 remove_forks_for_block(blockNumber);
902 return true;
903}
905{
906 std::atomic_bool success = true;
907 std::string message;
909 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
910 {
913 signal,
914 *wrapper.tree,
915 success,
916 message,
917 status.meta.nullifierTreeMeta,
918 blockNumber);
919 }
920 {
923 signal,
924 *wrapper.tree,
925 success,
926 message,
928 blockNumber);
929 }
930
931 {
932 auto& wrapper = std::get<TreeWithStore<FrTree>>(fork->_trees.at(MerkleTreeId::NOTE_HASH_TREE));
934 signal,
935 *wrapper.tree,
936 success,
937 message,
938 status.meta.noteHashTreeMeta,
939 blockNumber);
940 }
941
942 {
945 signal,
946 *wrapper.tree,
947 success,
948 message,
949 status.meta.messageTreeMeta,
950 blockNumber);
951 }
952
953 {
954 auto& wrapper = std::get<TreeWithStore<FrTree>>(fork->_trees.at(MerkleTreeId::ARCHIVE));
956 signal,
957 *wrapper.tree,
958 success,
959 message,
960 status.meta.archiveTreeMeta,
961 blockNumber);
962 }
963 signal.wait_for_level();
964 if (!success) {
965 throw std::runtime_error(message);
966 }
967 remove_forks_for_block(blockNumber);
968 return true;
969}
970
972 uint32_t generator_point,
973 uint64_t genesis_timestamp)
974{
975 // NOTE: this hash operations needs to match the one in
976 // noir-project/noir-protocol-circuits/crates/types/src/abis/block_header.nr
978 { generator_point,
979 // last archive - which, at genesis, is all 0s
980 0, // root
981 0, // next_available_leaf_index
982 // state reference - the initial state for all the trees (accept the archive tree)
983 initial_state_ref.at(MerkleTreeId::L1_TO_L2_MESSAGE_TREE).first,
984 initial_state_ref.at(MerkleTreeId::L1_TO_L2_MESSAGE_TREE).second,
985 initial_state_ref.at(MerkleTreeId::NOTE_HASH_TREE).first,
986 initial_state_ref.at(MerkleTreeId::NOTE_HASH_TREE).second,
987 initial_state_ref.at(MerkleTreeId::NULLIFIER_TREE).first,
988 initial_state_ref.at(MerkleTreeId::NULLIFIER_TREE).second,
989 initial_state_ref.at(MerkleTreeId::PUBLIC_DATA_TREE).first,
990 initial_state_ref.at(MerkleTreeId::PUBLIC_DATA_TREE).second,
991 0, // sponge_blob_hash
992 // global variables
993 0, // chain_id
994 0, // version
995 0, // block_number
996 0, // slot_number
997 bb::fr(genesis_timestamp), // timestamp
998 0, // coinbase
999 0, // fee_recipient
1000 0, // gas_fee.fee_per_da_gas
1001 0, // gas_fee.fee_per_l2_gas
1002 // total fees
1003 0,
1004 // total mana used
1005 0 });
1006}
1007
1008bool WorldState::is_archive_tip(const WorldStateRevision& revision, const bb::fr& block_header_hash) const
1009{
1011
1012 try {
1013 find_leaf_indices<fr>(revision, MerkleTreeId::ARCHIVE, { block_header_hash }, indices);
1014 } catch (std::runtime_error&) {
1015 }
1016
1017 if (indices.empty() || !indices[0].has_value()) {
1018 return false;
1019 }
1020
1021 TreeMetaResponse archive_state = get_tree_info(revision, MerkleTreeId::ARCHIVE);
1022 return archive_state.meta.size == indices[0].value() + 1;
1023}
1024
1032
1034 std::array<TreeMeta, NUM_TREES>& metaResponses)
1035{
1036 TreeMeta& archive_state = metaResponses[MerkleTreeId::ARCHIVE];
1037 status.unfinalizedBlockNumber = archive_state.unfinalizedBlockHeight;
1038 status.finalizedBlockNumber = archive_state.finalizedBlockHeight;
1039 status.oldestHistoricalBlock = archive_state.oldestHistoricBlock;
1040 status.treesAreSynched = determine_if_synched(metaResponses);
1041}
1042
1054
1055bool WorldState::is_same_state_reference(const WorldStateRevision& revision, const StateReference& state_ref) const
1056{
1057 return state_ref == get_state_reference(revision);
1058}
1059
1061{
1062 WorldStateRevision revision{ .forkId = CANONICAL_FORK_ID, .includeUncommitted = false };
1064 get_all_tree_info(revision, responses);
1065
1067 throw std::runtime_error("World state trees are out of sync");
1068 }
1069}
1070
1072{
1073 block_number_t blockNumber = metaResponses[0].unfinalizedBlockHeight;
1074 block_number_t finalizedBlockNumber = metaResponses[0].finalizedBlockHeight;
1075 for (size_t i = 1; i < metaResponses.size(); i++) {
1076 if (blockNumber != metaResponses[i].unfinalizedBlockHeight) {
1077 return false;
1078 }
1079 if (finalizedBlockNumber != metaResponses[i].finalizedBlockHeight) {
1080 return false;
1081 }
1082 }
1083 return true;
1084}
1085
1086uint32_t WorldState::checkpoint(const uint64_t& forkId)
1087{
1088 Fork::SharedPtr fork = retrieve_fork(forkId);
1089 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
1091 std::mutex mtx;
1092 for (auto& [id, tree] : fork->_trees) {
1093 std::visit(
1094 [&signal, &local, id, &mtx](auto&& wrapper) {
1095 wrapper.tree->checkpoint([&signal, &local, &mtx, id](TypedResponse<CheckpointResponse>& resp) {
1096 {
1098 local[id] = std::move(resp);
1099 }
1100 signal.signal_decrement();
1101 });
1102 },
1103 tree);
1104 }
1105 signal.wait_for_level();
1106 for (auto& m : local) {
1107 if (!m.success) {
1108 throw std::runtime_error(m.message);
1109 }
1110 }
1111 // All trees have the same checkpoint depth; return it from the first tree's response
1112 return local[0].inner.depth;
1113}
1114
1115void WorldState::commit_checkpoint(const uint64_t& forkId)
1116{
1117 Fork::SharedPtr fork = retrieve_fork(forkId);
1118 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
1120 std::mutex mtx;
1121 for (auto& [id, tree] : fork->_trees) {
1122 std::visit(
1123 [&signal, &local, id, &mtx](auto&& wrapper) {
1124 wrapper.tree->commit_checkpoint([&signal, &local, &mtx, id](Response& resp) {
1125 {
1127 local[id] = std::move(resp);
1128 }
1129 signal.signal_decrement();
1130 });
1131 },
1132 tree);
1133 }
1134 signal.wait_for_level();
1135 for (auto& m : local) {
1136 if (!m.success) {
1137 throw std::runtime_error(m.message);
1138 }
1139 }
1140}
1141
1142void WorldState::revert_checkpoint(const uint64_t& forkId)
1143{
1144 Fork::SharedPtr fork = retrieve_fork(forkId);
1145 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
1147 std::mutex mtx;
1148 for (auto& [id, tree] : fork->_trees) {
1149 std::visit(
1150 [&signal, &local, id, &mtx](auto&& wrapper) {
1151 wrapper.tree->revert_checkpoint([&signal, &local, &mtx, id](Response& resp) {
1152 {
1154 local[id] = std::move(resp);
1155 }
1156 signal.signal_decrement();
1157 });
1158 },
1159 tree);
1160 }
1161 signal.wait_for_level();
1162 for (auto& m : local) {
1163 if (!m.success) {
1164 throw std::runtime_error(m.message);
1165 }
1166 }
1167}
1168
1169void WorldState::commit_all_checkpoints_to(const uint64_t& forkId, uint32_t depth)
1170{
1171 Fork::SharedPtr fork = retrieve_fork(forkId);
1172 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
1174 std::mutex mtx;
1175 for (auto& [id, tree] : fork->_trees) {
1176 std::visit(
1177 [&signal, &local, id, &mtx, depth](auto&& wrapper) {
1178 auto callback = [&signal, &local, &mtx, id](Response& resp) {
1179 {
1181 local[id] = std::move(resp);
1182 }
1183 signal.signal_decrement();
1184 };
1185 wrapper.tree->commit_to_depth(depth, callback);
1186 },
1187 tree);
1188 }
1189 signal.wait_for_level();
1190 for (auto& m : local) {
1191 if (!m.success) {
1192 throw std::runtime_error(m.message);
1193 }
1194 }
1195}
1196
1197void WorldState::revert_all_checkpoints_to(const uint64_t& forkId, uint32_t depth)
1198{
1199 Fork::SharedPtr fork = retrieve_fork(forkId);
1200 Signal signal(static_cast<uint32_t>(fork->_trees.size()));
1202 std::mutex mtx;
1203 for (auto& [id, tree] : fork->_trees) {
1204 std::visit(
1205 [&signal, &local, id, &mtx, depth](auto&& wrapper) {
1206 auto callback = [&signal, &local, &mtx, id](Response& resp) {
1207 {
1209 local[id] = std::move(resp);
1210 }
1211 signal.signal_decrement();
1212 };
1213 wrapper.tree->revert_to_depth(depth, callback);
1214 },
1215 tree);
1216 }
1217 signal.wait_for_level();
1218 for (auto& m : local) {
1219 if (!m.success) {
1220 throw std::runtime_error(m.message);
1221 }
1222 }
1223}
1224
1226{
1227 WorldStateRevision revision{ .forkId = CANONICAL_FORK_ID, .includeUncommitted = false };
1229 get_all_tree_info(revision, responses);
1230
1231 // Get all historic block numbers
1232 std::array<block_number_t, NUM_TREES> historicalBlockNumbers{ responses[NULLIFIER_TREE].oldestHistoricBlock,
1233 responses[NOTE_HASH_TREE].oldestHistoricBlock,
1234 responses[PUBLIC_DATA_TREE].oldestHistoricBlock,
1235 responses[L1_TO_L2_MESSAGE_TREE].oldestHistoricBlock,
1236 responses[ARCHIVE].oldestHistoricBlock };
1237
1238 // Get all unfinalized block numbers
1239 std::array<block_number_t, NUM_TREES> unfinalizedBlockNumbers{
1240 responses[NULLIFIER_TREE].unfinalizedBlockHeight,
1241 responses[NOTE_HASH_TREE].unfinalizedBlockHeight,
1242 responses[PUBLIC_DATA_TREE].unfinalizedBlockHeight,
1243 responses[L1_TO_L2_MESSAGE_TREE].unfinalizedBlockHeight,
1244 responses[ARCHIVE].unfinalizedBlockHeight
1245 };
1246
1247 // Get all finalized block numbers
1248 std::array<block_number_t, NUM_TREES> finalizedBlockNumbers{ responses[NULLIFIER_TREE].finalizedBlockHeight,
1249 responses[NOTE_HASH_TREE].finalizedBlockHeight,
1250 responses[PUBLIC_DATA_TREE].finalizedBlockHeight,
1251 responses[L1_TO_L2_MESSAGE_TREE].finalizedBlockHeight,
1252 responses[ARCHIVE].finalizedBlockHeight };
1253
1254 // Get the min and max of each set of block numbers
1255 auto historicBlockRange = std::minmax_element(std::begin(historicalBlockNumbers), std::end(historicalBlockNumbers));
1256
1257 auto unfinalizedBlockRange =
1258 std::minmax_element(std::begin(unfinalizedBlockNumbers), std::end(unfinalizedBlockNumbers));
1259
1260 auto finalizedBlockRange = std::minmax_element(std::begin(finalizedBlockNumbers), std::end(finalizedBlockNumbers));
1261
1262 // We re-sync by
1263 // 1. Unwinding any blocks that are ahread of the lowest unfinalized block number
1264 // 2. Increasing finalized block numbers to the highest finalized block number
1265 // 3. Removing any historical blocks that are lower then the highest historic block number
1266
1267 WorldStateStatusFull status;
1268 block_number_t blockToUnwind = *unfinalizedBlockRange.second;
1269 while (blockToUnwind > *unfinalizedBlockRange.first) {
1270 unwind_block(blockToUnwind, status);
1271 blockToUnwind--;
1272 }
1273
1274 if (*finalizedBlockRange.first != *finalizedBlockRange.second) {
1275 set_finalized_block(*finalizedBlockRange.second);
1276 }
1277
1278 block_number_t blockToRemove = *historicBlockRange.first;
1279 while (blockToRemove < *historicBlockRange.second) {
1280 remove_historical_block(blockToRemove, status);
1281 blockToRemove++;
1282 }
1283
1285 return status;
1286}
1287
1288} // 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, bool ephemeral)
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)
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, bool ephemeral=false)
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