20#include "msgpack/assert.hpp"
29#include <unordered_map>
79 bool includeUncommitted)
const;
102 bool includeUncommitted)
const;
176 bool includeUncommitted)
const;
251template <
typename LeafValueType>
255 : forkConstantData_{ .name_ = (
std::move(name)), .depth_ = levels }
256 , dataStore_(dataStore)
262template <
typename LeafValueType>
268 : forkConstantData_{ .name_ = (
std::move(name)), .depth_ = levels }
269 , dataStore_(dataStore)
304template <
typename LeafValueType>
311 if (forkConstantData_.initialized_from_block_.has_value()) {
313 sizeLimit = forkConstantData_.initialized_from_block_.value().size;
318 get_meta(m, tx,
false);
321 if (requestContext.
maxIndex.has_value() && requestContext.
maxIndex.value() < sizeLimit) {
322 sizeLimit = requestContext.
maxIndex.value();
327template <
typename LeafValueType>
333 index_t constrainedSize = constrain_tree_size_to_only_committed(
context, tx);
334 if (index >= constrainedSize) {
338 bool success = dataStore_->find_block_for_index(index, blockNumber, tx);
342template <
typename LeafValueType>
347 dataStore_->write_block_index_data(blockNumber, index, tx);
350template <
typename LeafValueType>
355 dataStore_->delete_block_index(index, blockNumber, tx);
358template <
typename LeafValueType>
362 auto new_value_as_number =
uint256_t(new_leaf_key);
370 fr found_key = dataStore_->find_low_leaf(new_leaf_key, committed, sizeLimit, tx);
375 bool already_present = retrieved_value == new_value_as_number;
376 if (already_present) {
386 std::unique_lock lock(mtx_);
387 return cache_.find_low_value(new_leaf_key, retrieved_value, db_index);
390template <
typename LeafValueType>
394 bool includeUncommitted)
const
397 if (includeUncommitted) {
399 std::unique_lock lock(mtx_);
400 if (cache_.get_leaf_preimage_by_hash(leaf_hash, leafData)) {
404 if (dataStore_->read_leaf_by_hash(leaf_hash, leafData, tx)) {
410template <
typename LeafValueType>
415 std::unique_lock lock(mtx_);
416 cache_.put_leaf_preimage_by_hash(leaf_hash, leafPreImage);
419template <
typename LeafValueType>
424 std::unique_lock lock(mtx_);
426 if (cache_.get_leaf_by_index(index, leafPreImage)) {
432template <
typename LeafValueType>
437 std::unique_lock lock(mtx_);
438 cache_.put_leaf_by_index(index, leafPreImage);
441template <
typename LeafValueType>
449template <
typename LeafValueType>
454 std::unique_lock lock(mtx_);
455 cache_.update_leaf_key_index(index, leaf);
458template <
typename LeafValueType>
462 return find_leaf_index_from(leaf, 0, requestContext, tx);
465template <
typename LeafValueType>
474 std::unique_lock lock(mtx_);
476 if (cached.has_value()) {
479 if (cached.value() >= start_index) {
489 bool success = dataStore_->read_leaf_index(
key, committed, tx);
494 index_t sizeLimit = constrain_tree_size_to_only_committed(requestContext, tx);
495 if (committed < start_index) {
498 if (committed >= sizeLimit) {
506template <
typename LeafValueType>
510 std::unique_lock lock(mtx_);
511 cache_.put_node(nodeHash, payload);
514template <
typename LeafValueType>
518 bool includeUncommitted)
const
520 if (includeUncommitted) {
522 std::unique_lock lock(mtx_);
523 if (cache_.get_node(nodeHash, payload)) {
527 return dataStore_->read_node(nodeHash, payload, transaction);
530template <
typename LeafValueType>
534 bool overwriteIfPresent)
537 std::unique_lock lock(mtx_);
538 if (!overwriteIfPresent) {
540 if (cached.has_value()) {
544 cache_.put_node_by_index(level, index,
data);
547template <
typename LeafValueType>
553 std::unique_lock lock(mtx_);
555 if (cached.has_value()) {
556 data = cached.value();
565 std::unique_lock lock(mtx_);
569template <
typename LeafValueType>
572 bool includeUncommitted)
const
574 if (includeUncommitted) {
578 read_persisted_meta(m, tx);
584 std::unique_lock lock(mtx_);
585 m = cache_.get_meta();
588template <
typename LeafValueType>
593 return dataStore_->read_block_data(blockNumber, blockData, tx);
596template <
typename LeafValueType>
599 if (!dataStore_->read_meta_data(m, tx)) {
603 enrich_meta_from_fork_constant_data(m);
607template <
typename LeafValueType>
613 if (forkConstantData_.initialized_from_block_.has_value()) {
614 m.
size = forkConstantData_.initialized_from_block_->size;
615 m.
committedSize = forkConstantData_.initialized_from_block_->size;
616 m.
root = forkConstantData_.initialized_from_block_->root;
621template <
typename LeafValueType>
624 if (includeUncommitted) {
626 if (get_cached_node_by_index(0, 0, root)) {
631 get_meta(meta, tx, includeUncommitted);
639template <
typename LeafValueType>
643 for (
const auto& idx : indices) {
645 dataStore_->write_leaf_index(
key, idx.second, tx);
652 bool dataPresent =
false;
655 if (forkConstantData_.initialized_from_block_.has_value()) {
656 throw std::runtime_error(
"Committing a fork is forbidden");
660 dataPresent = cache_.get_node(meta.
root, rootPayload);
665 persist_leaf_indices(*tx);
670 persist_meta(meta, *tx);
672 }
catch (std::exception& e) {
674 throw std::runtime_error(
675 format(
"Unable to commit genesis data to tree: ", forkConstantData_.name_,
" Error: ", e.what()));
682template <
typename LeafValueType>
685 bool dataPresent =
false;
689 if (forkConstantData_.initialized_from_block_.has_value()) {
690 throw std::runtime_error(
"Committing a fork is forbidden");
694 dataPresent = cache_.get_node(meta.
root, rootPayload);
701 persist_leaf_indices(*tx);
709 if (dataPresent || meta.
size > 0) {
719 dataStore_->write_block_index_data(block.blockNumber, block.size, *tx);
722 persist_meta(meta, *tx);
724 }
catch (std::exception& e) {
726 throw std::runtime_error(
727 format(
"Unable to commit data to tree: ", forkConstantData_.name_,
" Error: ", e.what()));
735 extract_db_stats(dbStats);
738template <
typename LeafValueType>
743 extract_db_stats(stats, *tx);
744 }
catch (std::exception&) {
748template <
typename LeafValueType>
752 dataStore_->get_stats(stats, tx);
753 }
catch (std::exception&) {
757template <
typename LeafValueType>
767 stack.push_back({ .opHash = optional_hash, .lvl = level });
769 while (!stack.empty()) {
770 StackObject so = stack.back();
776 if (!so.opHash.has_value()) {
779 fr hash = so.opHash.value();
781 if (so.lvl == forkConstantData_.depth_) {
784 if (cache_.get_leaf_preimage_by_hash(
hash, leafPreImage)) {
785 dataStore_->write_leaf_by_hash(
hash, leafPreImage, tx);
791 if (!cache_.get_node(
hash, nodePayload)) {
793 dataStore_->increment_node_reference_count(
hash, tx);
797 dataStore_->set_or_increment_node_reference_count(
hash, nodePayload, tx);
798 if (nodePayload.
ref != 1) {
803 stack.push_back({ .opHash = nodePayload.
left, .lvl = so.lvl + 1 });
804 stack.push_back({ .opHash = nodePayload.
right, .lvl = so.lvl + 1 });
811 cache_.reset(forkConstantData_.depth_);
815 read_persisted_meta(committedMeta, *tx);
816 cache_.put_meta(committedMeta);
820template <
typename LeafValueType>
823 dataStore_->write_meta_data(m, tx);
826template <
typename LeafValueType>
832 if (blockNumber < 1) {
833 throw std::runtime_error(
834 format(
"Unable to advance finalized block: ", blockNumber,
". Tree name: ", forkConstantData_.name_));
836 if (forkConstantData_.initialized_from_block_.has_value()) {
837 throw std::runtime_error(
"Advancing the finalized block on a fork is forbidden");
842 get_meta(uncommittedMeta);
843 get_meta(committedMeta, *readTx,
false);
844 if (!dataStore_->read_block_data(blockNumber, blockPayload, *readTx)) {
845 throw std::runtime_error(
format(
"Unable to advance finalized block: ",
847 ". Failed to read block data. Tree name: ",
848 forkConstantData_.name_));
858 throw std::runtime_error(
format(
"Unable to finalize block ",
860 " currently unfinalized block height ",
870 persist_meta(committedMeta, *writeTx);
872 }
catch (std::exception& e) {
873 writeTx->try_abort();
874 throw std::runtime_error(
format(
"Unable to commit advance of finalized block: ",
877 forkConstantData_.name_,
885 put_meta(uncommittedMeta);
888template <
typename LeafValueType>
897 if (blockNumber < 1) {
898 throw std::runtime_error(
899 format(
"Unable to unwind block: ", blockNumber,
". Tree name: ", forkConstantData_.name_));
901 if (forkConstantData_.initialized_from_block_.has_value()) {
902 throw std::runtime_error(
"Removing a block on a fork is forbidden");
906 get_meta(uncommittedMeta);
907 get_meta(committedMeta, *readTx,
false);
908 if (committedMeta != uncommittedMeta) {
909 throw std::runtime_error(
910 format(
"Unable to unwind block: ",
912 " Can't unwind with uncommitted data, first rollback before unwinding. Tree name: ",
913 forkConstantData_.name_));
917 finalMeta = uncommittedMeta;
918 extract_db_stats(dbStats, *readTx);
922 throw std::runtime_error(
format(
"Unable to unwind block: ",
924 " unfinalizedBlockHeight: ",
927 forkConstantData_.name_));
930 throw std::runtime_error(
format(
"Unable to unwind block: ",
932 " finalizedBlockHeight: ",
935 forkConstantData_.name_));
939 if (blockNumber == 1) {
943 }
else if (!dataStore_->read_block_data(blockNumber - 1, previousBlockData, *readTx)) {
944 throw std::runtime_error(
format(
"Unable to unwind block: ",
946 ". Failed to read previous block data. Tree name: ",
947 forkConstantData_.name_));
951 if (!dataStore_->read_block_data(blockNumber, blockData, *readTx)) {
952 throw std::runtime_error(
format(
"Unable to unwind block: ",
954 ". Failed to read block data. Tree name: ",
955 forkConstantData_.name_));
966 if (blockData.
size > 0) {
972 dataStore_->delete_block_data(blockNumber, *writeTx);
973 dataStore_->delete_block_index(blockData.
size, blockData.
blockNumber, *writeTx);
975 uncommittedMeta.
size = previousBlockData.
size;
977 uncommittedMeta.
root = previousBlockData.
root;
980 persist_meta(uncommittedMeta, *writeTx);
982 }
catch (std::exception& e) {
983 writeTx->try_abort();
984 throw std::runtime_error(
format(
"Unable to commit unwind of block: ",
987 forkConstantData_.name_,
994 put_meta(uncommittedMeta);
995 finalMeta = uncommittedMeta;
997 extract_db_stats(dbStats);
1000template <
typename LeafValueType>
1008 if (blockNumber < 1) {
1009 throw std::runtime_error(
1010 format(
"Unable to remove historical block: ", blockNumber,
". Tree name: ", forkConstantData_.name_));
1012 if (forkConstantData_.initialized_from_block_.has_value()) {
1013 throw std::runtime_error(
"Removing a block on a fork is forbidden");
1019 get_meta(uncommittedMeta);
1020 get_meta(committedMeta, *readTx,
false);
1023 finalMeta = uncommittedMeta;
1024 extract_db_stats(dbStats, *readTx);
1028 throw std::runtime_error(
format(
"Unable to remove historical block: ",
1030 " oldestHistoricBlock: ",
1033 forkConstantData_.name_));
1036 throw std::runtime_error(
format(
"Unable to remove historical block: ",
1038 " finalizedBlockHeight: ",
1041 forkConstantData_.name_));
1044 if (!dataStore_->read_block_data(blockNumber, blockData, *readTx)) {
1045 throw std::runtime_error(
format(
"Unable to remove historical block: ",
1047 ". Failed to read block data. Tree name: ",
1048 forkConstantData_.name_));
1056 if (blockData.
size > 0) {
1062 dataStore_->delete_block_data(blockNumber, *writeTx);
1065 persist_meta(committedMeta, *writeTx);
1067 }
catch (std::exception& e) {
1068 writeTx->try_abort();
1069 throw std::runtime_error(
format(
"Unable to commit removal of historical block: ",
1072 forkConstantData_.name_,
1080 put_meta(uncommittedMeta);
1081 finalMeta = uncommittedMeta;
1083 extract_db_stats(dbStats);
1086template <
typename LeafValueType>
1094 if (dataStore_->read_leaf_index(
key, index, tx)) {
1095 if (index >= maxIndex) {
1097 dataStore_->delete_leaf_index(
key, tx);
1102template <
typename LeafValueType>
1108 if (maxIndex.has_value()) {
1113 if constexpr (requires_preimage_for_key<LeafValueType>()) {
1115 if (!dataStore_->read_leaf_by_hash(
hash, leaf_preimage, tx)) {
1116 throw std::runtime_error(
"Failed to find leaf pre-image when attempting to delete indices");
1123 remove_leaf_index(
key, maxIndex.value(), tx);
1126 dataStore_->delete_leaf_by_hash(
hash, tx);
1129template <
typename LeafValueType>
1135 struct StackObject {
1140 stack.push_back({ .opHash = optional_hash, .lvl = level });
1142 while (!stack.empty()) {
1143 StackObject so = stack.back();
1146 if (!so.opHash.has_value()) {
1149 fr hash = so.opHash.value();
1153 dataStore_->decrement_node_reference_count(
hash, nodeData, tx);
1155 if (nodeData.
ref != 0) {
1160 if (so.lvl == forkConstantData_.depth_) {
1161 remove_leaf(
hash, maxIndex, tx);
1176 bool success = read_persisted_meta(meta, *tx);
1178 if (forkConstantData_.name_ == meta.
name && forkConstantData_.depth_ == meta.
depth) {
1179 cache_.put_meta(meta);
1182 throw std::runtime_error(
1183 format(
"Tree found to be uninitialized when attempting to create ", forkConstantData_.name_));
1188 meta.
name = forkConstantData_.name_;
1193 meta.
depth = forkConstantData_.depth_;
1200 persist_meta(meta, *tx);
1202 }
catch (std::exception& e) {
1206 cache_.put_meta(meta);
1209template <
typename LeafValueType>
1217 bool success = read_persisted_meta(meta, *tx);
1219 if (forkConstantData_.name_ != meta.
name || forkConstantData_.depth_ != meta.
depth) {
1220 throw std::runtime_error(
format(
"Inconsistent tree meta data when initializing ",
1221 forkConstantData_.name_,
1223 forkConstantData_.depth_,
1233 throw std::runtime_error(
format(
"Tree found to be uninitialized when attempting to create ",
1234 forkConstantData_.name_,
1240 throw std::runtime_error(
format(
"Unable to initialize from future block: ",
1242 " unfinalizedBlockHeight: ",
1245 forkConstantData_.name_));
1248 throw std::runtime_error(
format(
"Unable to fork from expired historical block: ",
1250 " unfinalizedBlockHeight: ",
1253 forkConstantData_.name_));
1256 if (blockNumber == 0) {
1260 }
else if (get_block_data(blockNumber, blockData, *tx) ==
false) {
1261 throw std::runtime_error(
1262 format(
"Failed to retrieve block data: ", blockNumber,
". Tree name: ", forkConstantData_.name_));
1264 forkConstantData_.initialized_from_block_ = blockData;
1266 enrich_meta_from_fork_constant_data(meta);
1267 cache_.put_meta(meta);
Serves as a key-value node store for merkle trees. Caches all changes in memory before persisting the...
std::string get_name() const
Returns the name of the tree.
std::optional< IndexedLeafValueType > get_cached_leaf_by_index(const index_t &index) const
IndexedLeaf< LeafValueType > IndexedLeafValueType
void rollback()
Rolls back the uncommitted state.
void persist_node(const std::optional< fr > &optional_hash, uint32_t level, WriteTransaction &tx)
void commit_block(TreeMeta &finalMeta, TreeDBStats &dbStats)
Commits the uncommitted data to the underlying store.
bool get_cached_node_by_index(uint32_t level, const index_t &index, fr &data) const
Returns the data at the given node coordinates if available.
void put_cached_node_by_index(uint32_t level, const index_t &index, const fr &data, bool overwriteIfPresent=true)
Writes the provided data at the given node coordinates. Only writes to uncommitted data.
typename PersistedStoreType::WriteTransaction WriteTransaction
void persist_meta(TreeMeta &m, WriteTransaction &tx)
std::optional< IndexedLeafValueType > get_leaf(const index_t &index, ReadTransaction &tx, bool includeUncommitted) const
Returns the leaf at the provided index, if one exists.
ContentAddressedCachedTreeStore & operator=(ContentAddressedCachedTreeStore const &&other)=delete
std::optional< index_t > find_leaf_index(const LeafValueType &leaf, const RequestContext &requestContext, ReadTransaction &tx) const
Finds the index of the given leaf value in the tree if available. Includes uncommitted data if reques...
std::optional< IndexedLeafValueType > get_leaf_by_hash(const fr &leaf_hash, ReadTransaction &tx, bool includeUncommitted) const
void put_node_by_hash(const fr &nodeHash, const NodePayload &payload)
Writes the provided data at the given node coordinates. Only writes to uncommitted data.
ContentAddressedCachedTreeStore(ContentAddressedCachedTreeStore const &other)=delete
void remove_leaf(const fr &hash, const std::optional< index_t > &maxIndex, WriteTransaction &tx)
void commit_all_checkpoints()
void remove_historical_block(const block_number_t &blockNumber, TreeMeta &finalMeta, TreeDBStats &dbStats)
void commit_genesis_state()
Commits the initial state of uncommitted data to the underlying store.
bool get_node_by_hash(const fr &nodeHash, NodePayload &payload, ReadTransaction &transaction, bool includeUncommitted) const
Returns the data at the given node coordinates if available. Reads from uncommitted state if requeste...
PersistedStoreType::SharedPtr dataStore_
void remove_node(const std::optional< fr > &optional_hash, uint32_t level, const std::optional< index_t > &maxIndex, WriteTransaction &tx)
void put_leaf_by_hash(const fr &leaf_hash, const IndexedLeafValueType &leafPreImage)
void enrich_meta_from_fork_constant_data(TreeMeta &m) const
void set_leaf_key_at_index(const index_t &index, const IndexedLeafValueType &leaf)
Adds the leaf at the given index, updates the leaf index if requested.
void initialize_from_block(const block_number_t &blockNumber)
ForkConstantData forkConstantData_
ReadTransactionPtr create_read_transaction() const
Returns a read transaction against the underlying store.
void delete_block_for_index(const block_number_t &blockNumber, const index_t &index, WriteTransaction &tx)
ContentAddressedCachedTreeStore(ContentAddressedCachedTreeStore const &&other)=delete
void put_cached_leaf_by_index(const index_t &index, const IndexedLeafValueType &leafPreImage)
void remove_leaf_index(const fr &key, const index_t &maxIndex, WriteTransaction &tx)
void advance_finalized_block(const block_number_t &blockNumber)
void persist_block_for_index(const block_number_t &blockNumber, const index_t &index, WriteTransaction &tx)
ContentAddressedCachedTreeStore()=delete
void extract_db_stats(TreeDBStats &stats)
bool read_persisted_meta(TreeMeta &m, ReadTransaction &tx) const
void revert_all_checkpoints()
WriteTransactionPtr create_write_transaction() const
std::unique_ptr< ReadTransaction > ReadTransactionPtr
bool get_block_data(const block_number_t &blockNumber, BlockPayload &blockData, ReadTransaction &tx) const
Reads the tree meta data, including uncommitted data if requested.
void unwind_block(const block_number_t &blockNumber, TreeMeta &finalMeta, TreeDBStats &dbStats)
ContentAddressedCachedTreeStore & operator=(ContentAddressedCachedTreeStore const &other)=delete
~ContentAddressedCachedTreeStore()=default
typename PersistedStoreType::ReadTransaction ReadTransaction
void update_index(const index_t &index, const fr &leaf)
Updates the leaf index.
std::unique_ptr< WriteTransaction > WriteTransactionPtr
void persist_leaf_indices(WriteTransaction &tx)
std::optional< block_number_t > find_block_for_index(const index_t &index, ReadTransaction &tx) const
void get_meta(TreeMeta &m, ReadTransaction &tx, bool includeUncommitted) const
Reads the tree meta data, including uncommitted data if requested.
fr get_current_root(ReadTransaction &tx, bool includeUncommitted) const
std::pair< bool, index_t > find_low_value(const fr &new_leaf_key, const RequestContext &requestContext, ReadTransaction &tx) const
Returns the index of the leaf with a value immediately lower than the value provided.
std::optional< index_t > find_leaf_index_from(const LeafValueType &leaf, const index_t &start_index, const RequestContext &requestContext, ReadTransaction &tx) const
Finds the index of the given leaf value in the tree if available. Includes uncommitted data if reques...
void put_meta(const TreeMeta &m)
Writes the provided meta data to uncommitted state.
index_t constrain_tree_size_to_only_committed(const RequestContext &requestContext, ReadTransaction &tx) const
std::shared_ptr< LMDBTreeStore > SharedPtr
LMDBWriteTransaction WriteTransaction
LMDBReadTransaction ReadTransaction
std::string format(Args... args)
const std::vector< FF > data
StrictMock< MockContext > context
PublicDataLeafValue LeafValueType
void hash(State &state) noexcept
fr preimage_to_key(const LeafType &leaf)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
block_number_t blockNumber
std::optional< BlockPayload > initialized_from_block_
std::optional< fr > right
std::optional< index_t > maxIndex
static constexpr field zero()