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)
281 std::unique_lock lock(mtx_);
287 std::unique_lock lock(mtx_);
293 std::unique_lock lock(mtx_);
299 std::unique_lock lock(mtx_);
305 std::unique_lock lock(mtx_);
309template <
typename LeafValueType>
316 if (forkConstantData_.initialized_from_block_.has_value()) {
318 sizeLimit = forkConstantData_.initialized_from_block_.value().size;
323 get_meta(m, tx,
false);
326 if (requestContext.
maxIndex.has_value() && requestContext.
maxIndex.value() < sizeLimit) {
327 sizeLimit = requestContext.
maxIndex.value();
332template <
typename LeafValueType>
338 index_t constrainedSize = constrain_tree_size_to_only_committed(
context, tx);
339 if (
index >= constrainedSize) {
343 bool success = dataStore_->find_block_for_index(
index, blockNumber, tx);
347template <
typename LeafValueType>
352 dataStore_->write_block_index_data(blockNumber,
index, tx);
355template <
typename LeafValueType>
360 dataStore_->delete_block_index(
index, blockNumber, tx);
363template <
typename LeafValueType>
367 auto new_value_as_number =
uint256_t(new_leaf_key);
375 fr found_key = dataStore_->find_low_leaf(new_leaf_key, committed, sizeLimit, tx);
380 bool already_present = retrieved_value == new_value_as_number;
381 if (already_present) {
391 std::unique_lock lock(mtx_);
392 return cache_.find_low_value(new_leaf_key, retrieved_value, db_index);
395template <
typename LeafValueType>
399 bool includeUncommitted)
const
402 if (includeUncommitted) {
404 std::unique_lock lock(mtx_);
405 if (cache_.get_leaf_preimage_by_hash(leaf_hash, leafData)) {
409 if (dataStore_->read_leaf_by_hash(leaf_hash, leafData, tx)) {
415template <
typename LeafValueType>
420 std::unique_lock lock(mtx_);
421 cache_.put_leaf_preimage_by_hash(leaf_hash, leafPreImage);
424template <
typename LeafValueType>
429 std::unique_lock lock(mtx_);
431 if (cache_.get_leaf_by_index(
index, leafPreImage)) {
437template <
typename LeafValueType>
442 std::unique_lock lock(mtx_);
443 cache_.put_leaf_by_index(
index, leafPreImage);
446template <
typename LeafValueType>
454template <
typename LeafValueType>
459 std::unique_lock lock(mtx_);
460 cache_.update_leaf_key_index(
index, leaf);
463template <
typename LeafValueType>
467 return find_leaf_index_from(leaf, 0, requestContext, tx);
470template <
typename LeafValueType>
479 std::unique_lock lock(mtx_);
481 if (cached.has_value()) {
484 if (cached.value() >= start_index) {
494 bool success = dataStore_->read_leaf_index(
key, committed, tx);
499 index_t sizeLimit = constrain_tree_size_to_only_committed(requestContext, tx);
500 if (committed < start_index) {
503 if (committed >= sizeLimit) {
511template <
typename LeafValueType>
515 std::unique_lock lock(mtx_);
516 cache_.put_node(nodeHash, payload);
519template <
typename LeafValueType>
523 bool includeUncommitted)
const
525 if (includeUncommitted) {
527 std::unique_lock lock(mtx_);
528 if (cache_.get_node(nodeHash, payload)) {
532 return dataStore_->read_node(nodeHash, payload, transaction);
535template <
typename LeafValueType>
539 bool overwriteIfPresent)
542 std::unique_lock lock(mtx_);
543 if (!overwriteIfPresent) {
545 if (cached.has_value()) {
549 cache_.put_node_by_index(level,
index,
data);
552template <
typename LeafValueType>
558 std::unique_lock lock(mtx_);
560 if (cached.has_value()) {
561 data = cached.value();
570 std::unique_lock lock(mtx_);
574template <
typename LeafValueType>
577 bool includeUncommitted)
const
579 if (includeUncommitted) {
583 read_persisted_meta(m, tx);
589 std::unique_lock lock(mtx_);
590 m = cache_.get_meta();
593template <
typename LeafValueType>
598 return dataStore_->read_block_data(blockNumber, blockData, tx);
601template <
typename LeafValueType>
604 if (!dataStore_->read_meta_data(m, tx)) {
608 enrich_meta_from_fork_constant_data(m);
612template <
typename LeafValueType>
618 if (forkConstantData_.initialized_from_block_.has_value()) {
619 m.
size = forkConstantData_.initialized_from_block_->size;
620 m.
committedSize = forkConstantData_.initialized_from_block_->size;
621 m.
root = forkConstantData_.initialized_from_block_->root;
626template <
typename LeafValueType>
629 if (includeUncommitted) {
631 if (get_cached_node_by_index(0, 0, root)) {
636 get_meta(meta, tx, includeUncommitted);
644template <
typename LeafValueType>
648 for (
const auto& idx : indices) {
650 dataStore_->write_leaf_index(
key, idx.second, tx);
657 bool dataPresent =
false;
660 if (forkConstantData_.initialized_from_block_.has_value()) {
661 throw std::runtime_error(
"Committing a fork is forbidden");
665 dataPresent = cache_.get_node(meta.
root, rootPayload);
670 persist_leaf_indices(*tx);
675 persist_meta(meta, *tx);
677 }
catch (std::exception& e) {
679 throw std::runtime_error(
680 format(
"Unable to commit genesis data to tree: ", forkConstantData_.name_,
" Error: ", e.what()));
687template <
typename LeafValueType>
690 bool dataPresent =
false;
694 if (forkConstantData_.initialized_from_block_.has_value()) {
695 throw std::runtime_error(
"Committing a fork is forbidden");
699 dataPresent = cache_.get_node(meta.
root, rootPayload);
706 persist_leaf_indices(*tx);
714 if (dataPresent || meta.
size > 0) {
724 dataStore_->write_block_index_data(block.blockNumber, block.size, *tx);
727 persist_meta(meta, *tx);
729 }
catch (std::exception& e) {
731 throw std::runtime_error(
732 format(
"Unable to commit data to tree: ", forkConstantData_.name_,
" Error: ", e.what()));
740 extract_db_stats(dbStats);
743template <
typename LeafValueType>
748 extract_db_stats(stats, *tx);
749 }
catch (std::exception&) {
753template <
typename LeafValueType>
757 dataStore_->get_stats(stats, tx);
758 }
catch (std::exception&) {
762template <
typename LeafValueType>
772 stack.push_back({ .opHash = optional_hash, .lvl = level });
774 while (!stack.empty()) {
775 StackObject so = stack.back();
781 if (!so.opHash.has_value()) {
784 fr hash = so.opHash.value();
786 if (so.lvl == forkConstantData_.depth_) {
789 if (cache_.get_leaf_preimage_by_hash(
hash, leafPreImage)) {
790 dataStore_->write_leaf_by_hash(
hash, leafPreImage, tx);
796 if (!cache_.get_node(
hash, nodePayload)) {
798 dataStore_->increment_node_reference_count(
hash, tx);
802 dataStore_->set_or_increment_node_reference_count(
hash, nodePayload, tx);
803 if (nodePayload.
ref != 1) {
808 stack.push_back({ .opHash = nodePayload.
left, .lvl = so.lvl + 1 });
809 stack.push_back({ .opHash = nodePayload.
right, .lvl = so.lvl + 1 });
816 cache_.reset(forkConstantData_.depth_);
820 read_persisted_meta(committedMeta, *tx);
821 cache_.put_meta(committedMeta);
825template <
typename LeafValueType>
828 dataStore_->write_meta_data(m, tx);
831template <
typename LeafValueType>
837 if (blockNumber < 1) {
838 throw std::runtime_error(
839 format(
"Unable to advance finalized block: ", blockNumber,
". Tree name: ", forkConstantData_.name_));
841 if (forkConstantData_.initialized_from_block_.has_value()) {
842 throw std::runtime_error(
"Advancing the finalized block on a fork is forbidden");
847 get_meta(uncommittedMeta);
848 get_meta(committedMeta, *readTx,
false);
849 if (!dataStore_->read_block_data(blockNumber, blockPayload, *readTx)) {
850 throw std::runtime_error(
format(
"Unable to advance finalized block: ",
852 ". Failed to read block data. Tree name: ",
853 forkConstantData_.name_));
863 throw std::runtime_error(
format(
"Unable to finalize block ",
865 " currently unfinalized block height ",
875 persist_meta(committedMeta, *writeTx);
877 }
catch (std::exception& e) {
878 writeTx->try_abort();
879 throw std::runtime_error(
format(
"Unable to commit advance of finalized block: ",
882 forkConstantData_.name_,
890 put_meta(uncommittedMeta);
893template <
typename LeafValueType>
902 if (blockNumber < 1) {
903 throw std::runtime_error(
904 format(
"Unable to unwind block: ", blockNumber,
". Tree name: ", forkConstantData_.name_));
906 if (forkConstantData_.initialized_from_block_.has_value()) {
907 throw std::runtime_error(
"Removing a block on a fork is forbidden");
911 get_meta(uncommittedMeta);
912 get_meta(committedMeta, *readTx,
false);
913 if (committedMeta != uncommittedMeta) {
914 throw std::runtime_error(
915 format(
"Unable to unwind block: ",
917 " Can't unwind with uncommitted data, first rollback before unwinding. Tree name: ",
918 forkConstantData_.name_));
922 finalMeta = uncommittedMeta;
923 extract_db_stats(dbStats, *readTx);
927 throw std::runtime_error(
format(
"Unable to unwind block: ",
929 " unfinalizedBlockHeight: ",
932 forkConstantData_.name_));
935 throw std::runtime_error(
format(
"Unable to unwind block: ",
937 " finalizedBlockHeight: ",
940 forkConstantData_.name_));
944 if (blockNumber == 1) {
948 }
else if (!dataStore_->read_block_data(blockNumber - 1, previousBlockData, *readTx)) {
949 throw std::runtime_error(
format(
"Unable to unwind block: ",
951 ". Failed to read previous block data. Tree name: ",
952 forkConstantData_.name_));
956 if (!dataStore_->read_block_data(blockNumber, blockData, *readTx)) {
957 throw std::runtime_error(
format(
"Unable to unwind block: ",
959 ". Failed to read block data. Tree name: ",
960 forkConstantData_.name_));
971 if (blockData.
size > 0) {
977 dataStore_->delete_block_data(blockNumber, *writeTx);
978 dataStore_->delete_block_index(blockData.
size, blockData.
blockNumber, *writeTx);
980 uncommittedMeta.
size = previousBlockData.
size;
982 uncommittedMeta.
root = previousBlockData.
root;
985 persist_meta(uncommittedMeta, *writeTx);
987 }
catch (std::exception& e) {
988 writeTx->try_abort();
989 throw std::runtime_error(
format(
"Unable to commit unwind of block: ",
992 forkConstantData_.name_,
999 put_meta(uncommittedMeta);
1000 finalMeta = uncommittedMeta;
1002 extract_db_stats(dbStats);
1005template <
typename LeafValueType>
1013 if (blockNumber < 1) {
1014 throw std::runtime_error(
1015 format(
"Unable to remove historical block: ", blockNumber,
". Tree name: ", forkConstantData_.name_));
1017 if (forkConstantData_.initialized_from_block_.has_value()) {
1018 throw std::runtime_error(
"Removing a block on a fork is forbidden");
1024 get_meta(uncommittedMeta);
1025 get_meta(committedMeta, *readTx,
false);
1028 finalMeta = uncommittedMeta;
1029 extract_db_stats(dbStats, *readTx);
1033 throw std::runtime_error(
format(
"Unable to remove historical block: ",
1035 " oldestHistoricBlock: ",
1038 forkConstantData_.name_));
1041 throw std::runtime_error(
format(
"Unable to remove historical block: ",
1043 " finalizedBlockHeight: ",
1046 forkConstantData_.name_));
1049 if (!dataStore_->read_block_data(blockNumber, blockData, *readTx)) {
1050 throw std::runtime_error(
format(
"Unable to remove historical block: ",
1052 ". Failed to read block data. Tree name: ",
1053 forkConstantData_.name_));
1061 if (blockData.
size > 0) {
1067 dataStore_->delete_block_data(blockNumber, *writeTx);
1070 persist_meta(committedMeta, *writeTx);
1072 }
catch (std::exception& e) {
1073 writeTx->try_abort();
1074 throw std::runtime_error(
format(
"Unable to commit removal of historical block: ",
1077 forkConstantData_.name_,
1085 put_meta(uncommittedMeta);
1086 finalMeta = uncommittedMeta;
1088 extract_db_stats(dbStats);
1091template <
typename LeafValueType>
1099 if (dataStore_->read_leaf_index(
key,
index, tx)) {
1100 if (
index >= maxIndex) {
1102 dataStore_->delete_leaf_index(
key, tx);
1107template <
typename LeafValueType>
1113 if (maxIndex.has_value()) {
1118 if constexpr (requires_preimage_for_key<LeafValueType>()) {
1120 if (!dataStore_->read_leaf_by_hash(
hash, leaf_preimage, tx)) {
1121 throw std::runtime_error(
"Failed to find leaf pre-image when attempting to delete indices");
1128 remove_leaf_index(
key, maxIndex.value(), tx);
1131 dataStore_->delete_leaf_by_hash(
hash, tx);
1134template <
typename LeafValueType>
1140 struct StackObject {
1145 stack.push_back({ .opHash = optional_hash, .lvl = level });
1147 while (!stack.empty()) {
1148 StackObject so = stack.back();
1151 if (!so.opHash.has_value()) {
1154 fr hash = so.opHash.value();
1158 dataStore_->decrement_node_reference_count(
hash, nodeData, tx);
1160 if (nodeData.
ref != 0) {
1165 if (so.lvl == forkConstantData_.depth_) {
1166 remove_leaf(
hash, maxIndex, tx);
1181 bool success = read_persisted_meta(meta, *tx);
1183 if (forkConstantData_.name_ == meta.
name && forkConstantData_.depth_ == meta.
depth) {
1184 cache_.put_meta(meta);
1187 throw std::runtime_error(
1188 format(
"Tree found to be uninitialized when attempting to create ", forkConstantData_.name_));
1193 meta.
name = forkConstantData_.name_;
1198 meta.
depth = forkConstantData_.depth_;
1205 persist_meta(meta, *tx);
1207 }
catch (std::exception& e) {
1211 cache_.put_meta(meta);
1214template <
typename LeafValueType>
1222 bool success = read_persisted_meta(meta, *tx);
1224 if (forkConstantData_.name_ != meta.
name || forkConstantData_.depth_ != meta.
depth) {
1225 throw std::runtime_error(
format(
"Inconsistent tree meta data when initializing ",
1226 forkConstantData_.name_,
1228 forkConstantData_.depth_,
1238 throw std::runtime_error(
format(
"Tree found to be uninitialized when attempting to create ",
1239 forkConstantData_.name_,
1245 throw std::runtime_error(
format(
"Unable to initialize from future block: ",
1247 " unfinalizedBlockHeight: ",
1250 forkConstantData_.name_));
1253 throw std::runtime_error(
format(
"Unable to fork from expired historical block: ",
1255 " unfinalizedBlockHeight: ",
1258 forkConstantData_.name_));
1261 if (blockNumber == 0) {
1265 }
else if (get_block_data(blockNumber, blockData, *tx) ==
false) {
1266 throw std::runtime_error(
1267 format(
"Failed to retrieve block data: ", blockNumber,
". Tree name: ", forkConstantData_.name_));
1269 forkConstantData_.initialized_from_block_ = blockData;
1271 enrich_meta_from_fork_constant_data(meta);
1272 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< MemoryValue > 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()