20#include "msgpack/assert.hpp"
29#include <unordered_map>
79 bool includeUncommitted)
const;
102 bool includeUncommitted)
const;
176 bool includeUncommitted)
const;
254template <
typename LeafValueType>
258 : forkConstantData_{ .name_ = (
std::move(name)), .depth_ = levels }
259 , dataStore_(dataStore)
265template <
typename LeafValueType>
271 : forkConstantData_{ .name_ = (
std::move(name)), .depth_ = levels }
272 , dataStore_(dataStore)
284 std::unique_lock lock(mtx_);
285 return cache_.checkpoint();
290 std::unique_lock lock(mtx_);
296 std::unique_lock lock(mtx_);
302 std::unique_lock lock(mtx_);
308 std::unique_lock lock(mtx_);
314 std::unique_lock lock(mtx_);
315 cache_.commit_to_depth(depth);
320 std::unique_lock lock(mtx_);
321 cache_.revert_to_depth(depth);
326 std::unique_lock lock(mtx_);
327 return cache_.depth();
330template <
typename LeafValueType>
337 if (forkConstantData_.initialized_from_block_.has_value()) {
339 sizeLimit = forkConstantData_.initialized_from_block_.value().size;
344 get_meta(m, tx,
false);
347 if (requestContext.
maxIndex.has_value() && requestContext.
maxIndex.value() < sizeLimit) {
348 sizeLimit = requestContext.
maxIndex.value();
353template <
typename LeafValueType>
359 index_t constrainedSize = constrain_tree_size_to_only_committed(
context, tx);
360 if (
index >= constrainedSize) {
364 bool success = dataStore_->find_block_for_index(
index, blockNumber, tx);
368template <
typename LeafValueType>
373 dataStore_->write_block_index_data(blockNumber,
index, tx);
376template <
typename LeafValueType>
381 dataStore_->delete_block_index(
index, blockNumber, tx);
384template <
typename LeafValueType>
388 auto new_value_as_number =
uint256_t(new_leaf_key);
396 fr found_key = dataStore_->find_low_leaf(new_leaf_key, committed, sizeLimit, tx);
401 bool already_present = retrieved_value == new_value_as_number;
402 if (already_present) {
412 std::unique_lock lock(mtx_);
413 return cache_.find_low_value(new_leaf_key, retrieved_value, db_index);
416template <
typename LeafValueType>
420 bool includeUncommitted)
const
423 if (includeUncommitted) {
425 std::unique_lock lock(mtx_);
426 if (cache_.get_leaf_preimage_by_hash(leaf_hash, leafData)) {
430 if (dataStore_->read_leaf_by_hash(leaf_hash, leafData, tx)) {
436template <
typename LeafValueType>
441 std::unique_lock lock(mtx_);
442 cache_.put_leaf_preimage_by_hash(leaf_hash, leafPreImage);
445template <
typename LeafValueType>
450 std::unique_lock lock(mtx_);
452 if (cache_.get_leaf_by_index(
index, leafPreImage)) {
458template <
typename LeafValueType>
463 std::unique_lock lock(mtx_);
464 cache_.put_leaf_by_index(
index, leafPreImage);
467template <
typename LeafValueType>
475template <
typename LeafValueType>
480 std::unique_lock lock(mtx_);
481 cache_.update_leaf_key_index(
index, leaf);
484template <
typename LeafValueType>
488 return find_leaf_index_from(leaf, 0, requestContext, tx);
491template <
typename LeafValueType>
500 std::unique_lock lock(mtx_);
502 if (cached.has_value()) {
505 if (cached.value() >= start_index) {
515 bool success = dataStore_->read_leaf_index(
key, committed, tx);
520 index_t sizeLimit = constrain_tree_size_to_only_committed(requestContext, tx);
521 if (committed < start_index) {
524 if (committed >= sizeLimit) {
532template <
typename LeafValueType>
536 std::unique_lock lock(mtx_);
537 cache_.put_node(nodeHash, payload);
540template <
typename LeafValueType>
544 bool includeUncommitted)
const
546 if (includeUncommitted) {
548 std::unique_lock lock(mtx_);
549 if (cache_.get_node(nodeHash, payload)) {
553 return dataStore_->read_node(nodeHash, payload, transaction);
556template <
typename LeafValueType>
560 bool overwriteIfPresent)
563 std::unique_lock lock(mtx_);
564 if (!overwriteIfPresent) {
566 if (cached.has_value()) {
570 cache_.put_node_by_index(level,
index,
data);
573template <
typename LeafValueType>
579 std::unique_lock lock(mtx_);
581 if (cached.has_value()) {
582 data = cached.value();
591 std::unique_lock lock(mtx_);
595template <
typename LeafValueType>
598 bool includeUncommitted)
const
600 if (includeUncommitted) {
604 read_persisted_meta(m, tx);
610 std::unique_lock lock(mtx_);
611 m = cache_.get_meta();
614template <
typename LeafValueType>
619 return dataStore_->read_block_data(blockNumber, blockData, tx);
622template <
typename LeafValueType>
625 if (!dataStore_->read_meta_data(m, tx)) {
629 enrich_meta_from_fork_constant_data(m);
633template <
typename LeafValueType>
639 if (forkConstantData_.initialized_from_block_.has_value()) {
640 m.
size = forkConstantData_.initialized_from_block_->size;
641 m.
committedSize = forkConstantData_.initialized_from_block_->size;
642 m.
root = forkConstantData_.initialized_from_block_->root;
647template <
typename LeafValueType>
650 if (includeUncommitted) {
652 if (get_cached_node_by_index(0, 0, root)) {
657 get_meta(meta, tx, includeUncommitted);
665template <
typename LeafValueType>
669 for (
const auto& idx : indices) {
671 dataStore_->write_leaf_index(
key, idx.second, tx);
678 bool dataPresent =
false;
681 if (forkConstantData_.initialized_from_block_.has_value()) {
682 throw std::runtime_error(
"Committing a fork is forbidden");
686 dataPresent = cache_.get_node(meta.
root, rootPayload);
691 persist_leaf_indices(*tx);
696 persist_meta(meta, *tx);
702 dataStore_->write_block_data(0, genesisBlock, *tx);
703 dataStore_->write_block_index_data(0, meta.
initialSize, *tx);
706 }
catch (std::exception& e) {
708 throw std::runtime_error(
709 format(
"Unable to commit genesis data to tree: ", forkConstantData_.name_,
" Error: ", e.what()));
716template <
typename LeafValueType>
719 bool dataPresent =
false;
723 if (forkConstantData_.initialized_from_block_.has_value()) {
724 throw std::runtime_error(
"Committing a fork is forbidden");
728 dataPresent = cache_.get_node(meta.
root, rootPayload);
735 persist_leaf_indices(*tx);
743 if (dataPresent || meta.
size > 0) {
753 dataStore_->write_block_index_data(block.blockNumber, block.size, *tx);
756 persist_meta(meta, *tx);
758 }
catch (std::exception& e) {
760 throw std::runtime_error(
761 format(
"Unable to commit data to tree: ", forkConstantData_.name_,
" Error: ", e.what()));
769 extract_db_stats(dbStats);
772template <
typename LeafValueType>
777 extract_db_stats(stats, *tx);
778 }
catch (std::exception&) {
782template <
typename LeafValueType>
786 dataStore_->get_stats(stats, tx);
787 }
catch (std::exception&) {
791template <
typename LeafValueType>
801 stack.push_back({ .opHash = optional_hash, .lvl = level });
803 while (!stack.empty()) {
804 StackObject so = stack.back();
810 if (!so.opHash.has_value()) {
813 fr hash = so.opHash.value();
815 if (so.lvl == forkConstantData_.depth_) {
818 if (cache_.get_leaf_preimage_by_hash(hash, leafPreImage)) {
819 dataStore_->write_leaf_by_hash(hash, leafPreImage, tx);
825 if (!cache_.get_node(hash, nodePayload)) {
827 dataStore_->increment_node_reference_count(hash, tx);
831 dataStore_->set_or_increment_node_reference_count(hash, nodePayload, tx);
832 if (nodePayload.
ref != 1) {
837 stack.push_back({ .opHash = nodePayload.
left, .lvl = so.lvl + 1 });
838 stack.push_back({ .opHash = nodePayload.
right, .lvl = so.lvl + 1 });
845 cache_.reset(forkConstantData_.depth_);
849 read_persisted_meta(committedMeta, *tx);
850 cache_.put_meta(committedMeta);
854template <
typename LeafValueType>
857 dataStore_->write_meta_data(m, tx);
860template <
typename LeafValueType>
866 if (blockNumber < 1) {
867 throw std::runtime_error(
868 format(
"Unable to advance finalized block: ", blockNumber,
". Tree name: ", forkConstantData_.name_));
870 if (forkConstantData_.initialized_from_block_.has_value()) {
871 throw std::runtime_error(
"Advancing the finalized block on a fork is forbidden");
876 get_meta(uncommittedMeta);
877 get_meta(committedMeta, *readTx,
false);
882 if (!dataStore_->read_block_data(blockNumber, blockPayload, *readTx)) {
883 throw std::runtime_error(
format(
"Unable to advance finalized block: ",
885 ". Failed to read block data. Tree name: ",
886 forkConstantData_.name_));
892 throw std::runtime_error(
format(
"Unable to finalize block ",
894 " currently unfinalized block height ",
904 persist_meta(committedMeta, *writeTx);
906 }
catch (std::exception& e) {
907 writeTx->try_abort();
908 throw std::runtime_error(
format(
"Unable to commit advance of finalized block: ",
911 forkConstantData_.name_,
919 put_meta(uncommittedMeta);
922template <
typename LeafValueType>
931 if (blockNumber < 1) {
932 throw std::runtime_error(
933 format(
"Unable to unwind block: ", blockNumber,
". Tree name: ", forkConstantData_.name_));
935 if (forkConstantData_.initialized_from_block_.has_value()) {
936 throw std::runtime_error(
"Removing a block on a fork is forbidden");
940 get_meta(uncommittedMeta);
941 get_meta(committedMeta, *readTx,
false);
942 if (committedMeta != uncommittedMeta) {
943 throw std::runtime_error(
944 format(
"Unable to unwind block: ",
946 " Can't unwind with uncommitted data, first rollback before unwinding. Tree name: ",
947 forkConstantData_.name_));
951 finalMeta = uncommittedMeta;
952 extract_db_stats(dbStats, *readTx);
956 throw std::runtime_error(
format(
"Unable to unwind block: ",
958 " unfinalizedBlockHeight: ",
961 forkConstantData_.name_));
964 throw std::runtime_error(
format(
"Unable to unwind block: ",
966 " finalizedBlockHeight: ",
969 forkConstantData_.name_));
973 if (blockNumber == 1) {
977 }
else if (!dataStore_->read_block_data(blockNumber - 1, previousBlockData, *readTx)) {
978 throw std::runtime_error(
format(
"Unable to unwind block: ",
980 ". Failed to read previous block data. Tree name: ",
981 forkConstantData_.name_));
985 if (!dataStore_->read_block_data(blockNumber, blockData, *readTx)) {
986 throw std::runtime_error(
format(
"Unable to unwind block: ",
988 ". Failed to read block data. Tree name: ",
989 forkConstantData_.name_));
1000 if (blockData.
size > 0) {
1006 dataStore_->delete_block_data(blockNumber, *writeTx);
1007 dataStore_->delete_block_index(blockData.
size, blockData.
blockNumber, *writeTx);
1009 uncommittedMeta.
size = previousBlockData.
size;
1011 uncommittedMeta.
root = previousBlockData.
root;
1014 persist_meta(uncommittedMeta, *writeTx);
1016 }
catch (std::exception& e) {
1017 writeTx->try_abort();
1018 throw std::runtime_error(
format(
"Unable to commit unwind of block: ",
1021 forkConstantData_.name_,
1028 put_meta(uncommittedMeta);
1029 finalMeta = uncommittedMeta;
1031 extract_db_stats(dbStats);
1034template <
typename LeafValueType>
1042 if (blockNumber < 1) {
1043 throw std::runtime_error(
1044 format(
"Unable to remove historical block: ", blockNumber,
". Tree name: ", forkConstantData_.name_));
1046 if (forkConstantData_.initialized_from_block_.has_value()) {
1047 throw std::runtime_error(
"Removing a block on a fork is forbidden");
1053 get_meta(uncommittedMeta);
1054 get_meta(committedMeta, *readTx,
false);
1057 finalMeta = uncommittedMeta;
1058 extract_db_stats(dbStats, *readTx);
1062 throw std::runtime_error(
format(
"Unable to remove historical block: ",
1064 " oldestHistoricBlock: ",
1067 forkConstantData_.name_));
1070 throw std::runtime_error(
format(
"Unable to remove historical block: ",
1072 " finalizedBlockHeight: ",
1075 forkConstantData_.name_));
1078 if (!dataStore_->read_block_data(blockNumber, blockData, *readTx)) {
1079 throw std::runtime_error(
format(
"Unable to remove historical block: ",
1081 ". Failed to read block data. Tree name: ",
1082 forkConstantData_.name_));
1090 if (blockData.
size > 0) {
1096 dataStore_->delete_block_data(blockNumber, *writeTx);
1099 persist_meta(committedMeta, *writeTx);
1101 }
catch (std::exception& e) {
1102 writeTx->try_abort();
1103 throw std::runtime_error(
format(
"Unable to commit removal of historical block: ",
1106 forkConstantData_.name_,
1114 put_meta(uncommittedMeta);
1115 finalMeta = uncommittedMeta;
1117 extract_db_stats(dbStats);
1120template <
typename LeafValueType>
1128 if (dataStore_->read_leaf_index(
key,
index, tx)) {
1129 if (
index >= maxIndex) {
1131 dataStore_->delete_leaf_index(
key, tx);
1136template <
typename LeafValueType>
1142 if (maxIndex.has_value()) {
1147 if constexpr (requires_preimage_for_key<LeafValueType>()) {
1149 if (!dataStore_->read_leaf_by_hash(hash, leaf_preimage, tx)) {
1150 throw std::runtime_error(
"Failed to find leaf pre-image when attempting to delete indices");
1157 remove_leaf_index(
key, maxIndex.value(), tx);
1160 dataStore_->delete_leaf_by_hash(hash, tx);
1163template <
typename LeafValueType>
1169 struct StackObject {
1174 stack.push_back({ .opHash = optional_hash, .lvl = level });
1176 while (!stack.empty()) {
1177 StackObject so = stack.back();
1180 if (!so.opHash.has_value()) {
1183 fr hash = so.opHash.value();
1187 dataStore_->decrement_node_reference_count(hash, nodeData, tx);
1189 if (nodeData.
ref != 0) {
1194 if (so.lvl == forkConstantData_.depth_) {
1195 remove_leaf(hash, maxIndex, tx);
1210 bool success = read_persisted_meta(meta, *tx);
1212 if (forkConstantData_.name_ == meta.
name && forkConstantData_.depth_ == meta.
depth) {
1213 cache_.put_meta(meta);
1216 throw std::runtime_error(
1217 format(
"Tree found to be uninitialized when attempting to create ", forkConstantData_.name_));
1222 meta.
name = forkConstantData_.name_;
1227 meta.
depth = forkConstantData_.depth_;
1234 persist_meta(meta, *tx);
1236 }
catch (std::exception& e) {
1240 cache_.put_meta(meta);
1243template <
typename LeafValueType>
1251 bool success = read_persisted_meta(meta, *tx);
1253 if (forkConstantData_.name_ != meta.
name || forkConstantData_.depth_ != meta.
depth) {
1254 throw std::runtime_error(
format(
"Inconsistent tree meta data when initializing ",
1255 forkConstantData_.name_,
1257 forkConstantData_.depth_,
1267 throw std::runtime_error(
format(
"Tree found to be uninitialized when attempting to create ",
1268 forkConstantData_.name_,
1274 throw std::runtime_error(
format(
"Unable to initialize from future block: ",
1276 " unfinalizedBlockHeight: ",
1279 forkConstantData_.name_));
1282 throw std::runtime_error(
format(
"Unable to fork from expired historical block: ",
1284 " unfinalizedBlockHeight: ",
1287 forkConstantData_.name_));
1290 if (blockNumber == 0) {
1294 }
else if (get_block_data(blockNumber, blockData, *tx) ==
false) {
1295 throw std::runtime_error(
1296 format(
"Failed to retrieve block data: ", blockNumber,
". Tree name: ", forkConstantData_.name_));
1298 forkConstantData_.initialized_from_block_ = blockData;
1300 enrich_meta_from_fork_constant_data(meta);
1301 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.
ContentAddressedCachedTreeStore(std::string name, uint32_t levels, const block_number_t &referenceBlockNumber, PersistedStoreType::SharedPtr dataStore)
std::optional< IndexedLeafValueType > get_cached_leaf_by_index(const index_t &index) const
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 commit_all_checkpoints_to()
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 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 revert_all_checkpoints_to()
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 get_meta(TreeMeta &m) const
Reads the uncommitted tree meta data.
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
WriteTransactionPtr create_write_transaction() const
std::unique_ptr< ReadTransaction > ReadTransactionPtr
void revert_to_depth(uint32_t depth)
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
ContentAddressedCachedTreeStore(std::string name, uint32_t levels, PersistedStoreType::SharedPtr dataStore)
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.
void commit_to_depth(uint32_t depth)
void extract_db_stats(TreeDBStats &stats, ReadTransaction &tx)
index_t constrain_tree_size_to_only_committed(const RequestContext &requestContext, ReadTransaction &tx) const
uint32_t checkpoint_depth() 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
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()