6#ifndef ALTINTEGRATION_BASE_BLOCK_TREE_HPP
7#define ALTINTEGRATION_BASE_BLOCK_TREE_HPP
10#include <unordered_map>
11#include <unordered_set>
13#include "block_index.hpp"
14#include "blockchain_util.hpp"
16#include "payloads_index.hpp"
17#include "tree_algo.hpp"
18#include "veriblock/pop/algorithm.hpp"
19#include "veriblock/pop/assert.hpp"
20#include "veriblock/pop/fmt.hpp"
21#include "veriblock/pop/logger.hpp"
22#include "veriblock/pop/signals.hpp"
23#include "veriblock/pop/storage/block_reader.hpp"
24#include "veriblock/pop/storage/stored_block_index.hpp"
25#include "veriblock/pop/trace.hpp"
34template <
typename Block>
36 using block_t = Block;
37 using block_height_t =
typename block_t::height_t;
38 using hash_t =
typename Block::hash_t;
39 using prev_block_hash_t =
typename Block::prev_hash_t;
42 using on_invalidate_t = void(
const index_t&);
44 std::unordered_map<prev_block_hash_t, std::unique_ptr<index_t>>;
46 const std::unordered_set<index_t*>& getTips()
const {
return tips_; }
47 std::vector<index_t*> getBlocks()
const {
48 std::vector<index_t*> blocks;
50 for (
const auto& el :
blocks_) {
51 if (!el.second->isDeleted()) {
52 blocks.push_back(el.second.get());
58 std::vector<index_t*> getAllBlocks()
const {
59 std::vector<index_t*> blocks;
61 for (
const auto& el :
blocks_) {
62 blocks.push_back(el.second.get());
67 const BlockReader& getBlockProvider()
const {
return blockProvider_; }
74 deallocateTree(getRoot());
78 : blockProvider_(blockProvider) {}
98 inline prev_block_hash_t makePrevHash(
const T& h)
const {
109 template <
typename T,
110 typename =
typename std::enable_if<
111 std::is_same<T, hash_t>::value ||
112 std::is_same<T, prev_block_hash_t>::value>::type>
114 const auto& t = as_const(*
this);
115 return const_cast<index_t*
>(t.getBlockIndex(hash));
119 template <
typename T,
120 typename =
typename std::enable_if<
121 std::is_same<T, hash_t>::value ||
122 std::is_same<T, prev_block_hash_t>::value>::type>
125 return blockIndex !=
nullptr && blockIndex->isDeleted() ? nullptr
135 template <
typename T,
136 typename =
typename std::enable_if<
137 std::is_same<T, hash_t>::value ||
138 std::is_same<T, prev_block_hash_t>::value>::type>
140 const auto& t = as_const(*
this);
141 return const_cast<index_t*
>(t.findBlockIndex(hash));
145 template <
typename T,
146 typename =
typename std::enable_if<
147 std::is_same<T, hash_t>::value ||
148 std::is_same<T, prev_block_hash_t>::value>::type>
150 VBK_TRACE_ZONE_SCOPED;
151 auto shortHash = makePrevHash(hash);
152 auto it =
blocks_.find(shortHash);
153 return it ==
blocks_.end() ? nullptr : it->second.get();
157 VBK_TRACE_ZONE_SCOPED;
159 "%s tree must be in LoadingBlocks state!",
162 !this->
isLoaded_,
"%s tree is already loaded!", block_t::name());
165 if (tip ==
nullptr) {
166 return state.Invalid(
167 block_t::name() +
"-no-tip",
168 format(
"tip {} doesn't exist in block tree",
HexStr(hash)));
171 this->overrideTip(*tip);
192 return loadBlockInner(index,
true, fast_load, state);
201 VBK_TRACE_ZONE_SCOPED;
202 VBK_LOG_DEBUG(
"remove subtree %s", toRemove.toPrettyString());
204 VBK_ASSERT_MSG(!toRemove.isRoot(),
"cannot remove the root block");
206 auto* prev = toRemove.
pprev;
211 bool success = this->setState(*prev, dummy);
212 VBK_ASSERT_MSG(success,
"err: %s", dummy.toString());
215 forEachNodePostorder<block_t>(
218 onBeforeLeafRemoved(next);
220 VBK_ASSERT(!next.isDeleted());
221 next.deleteTemporarily();
223 [&](
index_t& next) {
return !next.isDeleted(); });
238 index,
"cannot find the subtree to remove: %s",
HexStr(toRemove));
246 VBK_ASSERT_MSG(nondeletedDescendantCount == 0,
247 "not a leaf block %s, has %d non-deleted descendants",
248 toRemove.toPrettyString(),
249 nondeletedDescendantCount);
267 bool shouldDetermineBestChain =
true) {
268 VBK_TRACE_ZONE_SCOPED;
269 VBK_LOG_DEBUG(
"Invalidating %s subtree: reason=%d block=%s",
272 toBeInvalidated.toShortPrettyString());
274 VBK_ASSERT_MSG(!toBeInvalidated.isRoot(),
275 "cannot invalidate the root block");
278 "invalid invalidation reason");
280 if (toBeInvalidated.hasFlags(reason)) {
286 if (!toBeInvalidated.isValid()) {
287 doInvalidate(toBeInvalidated, reason);
291 bool isOnMainChain =
activeChain_.contains(&toBeInvalidated);
294 bool success = this->setState(*toBeInvalidated.
pprev, dummy);
298 doInvalidate(toBeInvalidated, reason);
301 for (
auto* ptr : toBeInvalidated.
pnext) {
303 forEachNodePreorder<block_t>(*pnext, [&](
index_t& index) {
304 bool failed = index.isFailed();
311 tryAddTip(toBeInvalidated.
pprev);
313 if (shouldDetermineBestChain) {
322 bool shouldDetermineBestChain =
true) {
324 VBK_ASSERT(index &&
"cannot find the subtree to invalidate");
328 void revalidateSubtree(
const hash_t& hash,
330 bool shouldDetermineBestChain =
true) {
332 VBK_ASSERT(index &&
"cannot find the subtree to revalidate");
333 revalidateSubtree(*index, reason, shouldDetermineBestChain);
340 void revalidateSubtree(index_t& toBeValidated,
342 bool shouldDetermineBestChain =
true) {
343 VBK_TRACE_ZONE_SCOPED;
344 VBK_LOG_DEBUG(
"Revalidating %s subtree: reason=%d block=%s",
347 toBeValidated.toShortPrettyString());
349 VBK_ASSERT_MSG(!toBeValidated.isRoot(),
"cannot revalidate the root block");
352 "invalid revalidation reason");
354 if (!toBeValidated.hasFlags(reason)) {
362 doReValidate(toBeValidated, reason);
366 doReValidate(toBeValidated, reason);
368 for (
auto* pnext : toBeValidated.pnext) {
369 forEachNodePreorder<block_t>(*pnext, [&](index_t& index) ->
bool {
371 bool failed = index.isFailed();
376 if (shouldDetermineBestChain) {
397 "state corruption: the blockchain is neither bootstrapped nor empty");
403 if (index ==
nullptr) {
404 return state.Invalid(block_t::name() +
"-setstate-unknown-block",
405 "could not find the block to set the state to");
407 return setState(*index, state);
410 virtual bool setState(index_t& index, ValidationState&) {
411 VBK_TRACE_ZONE_SCOPED;
416 virtual void overrideTip(index_t& to) {
422 index_t& getRoot()
const {
425 VBK_ASSERT_MSG(root,
"must be bootstrapped");
431 size_t appliedBlockCount = 0;
436 int32_t maxReorgBlocks,
437 int32_t preserveBlocksBehindFinal,
440 int32_t maxFinalizeBlockHeight = std::numeric_limits<int32_t>::max()) {
442 VBK_ASSERT_MSG(tip,
"%s tree must be bootstrapped", block_t::name());
445 if (tip->getHeight() < maxReorgBlocks) {
450 int32_t firstBlockHeight = tip->getHeight() - maxReorgBlocks;
451 int32_t bootstrapBlockHeight = this->getRoot().getHeight();
452 firstBlockHeight = std::max(bootstrapBlockHeight, firstBlockHeight);
453 auto* finalizedIndex = this->
getBestChain()[firstBlockHeight];
454 VBK_ASSERT(finalizedIndex !=
nullptr);
455 if (finalizedIndex->getHeight() >= maxFinalizeBlockHeight) {
459 "Skipping finalization of %s because its height >= %d (max "
460 "possible height to finalize)",
461 finalizedIndex->toShortPrettyString(),
462 maxFinalizeBlockHeight);
466 this->finalizeBlockImpl(*finalizedIndex, preserveBlocksBehindFinal);
470 virtual void determineBestChain(index_t& candidate,
471 ValidationState& state) = 0;
474 void tryAddTip(index_t* index) {
475 VBK_TRACE_ZONE_SCOPED;
478 if (!index->isValidTip()) {
482 auto it =
tips_.find(index->pprev);
483 if (it !=
tips_.end()) {
498 VBK_TRACE_ZONE_SCOPED;
499 VBK_ASSERT_MSG(block.isDeleted(),
500 "cannot erase a block that is not deleted %s",
501 block.toPrettyString());
504 "cannot erase a block that has ancestors as that would split "
506 block.toPrettyString());
509 auto shortHash = makePrevHash(block.getHash());
510 auto count =
blocks_.erase(shortHash);
511 VBK_ASSERT_MSG(count == 1,
512 "state corruption: block %s is not in the store",
513 block.toPrettyString());
518 index_t* createBlockIndex(
const hash_t& hash, index_t& prev) {
519 VBK_TRACE_ZONE_SCOPED;
520 auto shortHash = makePrevHash(hash);
521 auto newIndex = make_unique<index_t>(&prev);
522 auto inserted =
blocks_.emplace(shortHash, std::move(newIndex));
523 VBK_ASSERT_MSG(inserted.second,
524 "attempted to create a blockindex with duplicate hash %s",
526 return inserted.first->second.get();
531 index_t* createBootstrapBlockIndex(
const hash_t& hash,
532 block_height_t height) {
533 VBK_TRACE_ZONE_SCOPED;
534 auto shortHash = makePrevHash(hash);
535 auto newIndex = make_unique<index_t>(height);
536 auto inserted =
blocks_.emplace(shortHash, std::move(newIndex));
537 VBK_ASSERT_MSG(inserted.second,
538 "attempted to create a blockindex with duplicate hash %s",
540 auto* index = inserted.first->second.get();
542 index->finalized =
true;
548 index_t* doInsertBlockHeader(
const std::shared_ptr<block_t>& header,
549 block_height_t bootstrapHeight = 0) {
550 VBK_TRACE_ZONE_SCOPED;
551 VBK_ASSERT(header !=
nullptr);
556 "already bootstrapped");
560 ? createBootstrapBlockIndex(header->getHash(), bootstrapHeight)
561 : createBlockIndex(header->getHash(), *prev);
562 current->setHeader(std::move(header));
568 index_t* insertBlockHeader(
const std::shared_ptr<block_t>& block,
569 block_height_t bootstrapHeight = 0) {
570 VBK_TRACE_ZONE_SCOPED;
572 assertBlockSanity(*block);
574 auto hash = block->getHash();
577 if (current !=
nullptr) {
579 if (!current->isDeleted()) {
583 VBK_ASSERT_MSG(*block == current->getHeader(),
584 "hash collision detected between %s and %s",
585 block->toPrettyString(),
586 current->getHeader().toPrettyString());
588 current = doInsertBlockHeader(block, bootstrapHeight);
594 this->onBlockInserted(current);
602 bool loadBlockInner(
const stored_index_t& index,
605 ValidationState& state) {
606 VBK_TRACE_ZONE_SCOPED;
610 !this->
isLoaded_,
"%s tree must not be loaded", block_t::name());
616 auto& root = getRoot();
618 if (connectForward) {
619 if (index.height < root.getHeight()) {
620 auto hash = index.header->getHash();
621 return state.Invalid(
623 format(
"Blocks can be forward connected after root only. "
624 "index.height: {}, root.height: {}, index.hash: {}",
627 HexStr(hash.begin(), hash.end())));
630 if (index.height + 1 != root.getHeight()) {
631 auto hash = index.header->getHash();
632 return state.Invalid(
634 format(
"Blocks can be backwards connected only to the "
635 "root, index.height: {}, root.height: {}, index.hash: {}",
638 HexStr(hash.begin(), hash.end())));
643 if (!fast_load && index.height == root.getHeight() &&
644 index.header->getHash() != root.getHash()) {
646 return state.Invalid(
"bad-root",
647 format(
"Can't overwrite root block with block {}",
648 index.toPrettyString()));
651 auto& header = *index.header;
652 auto currentHash = header.getHash();
657 if (!fast_load && current !=
nullptr && !current->isDeleted() &&
659 return state.Invalid(
661 "Found duplicate block, which is not bootstrap block");
664 if (current !=
nullptr) {
665 if (current->isDeleted()) {
672 if (prev ==
nullptr) {
674 if (connectForward) {
675 return state.Invalid(
"bad-prev",
676 "Block does not connect to current tree");
679 if (root.getHeader().getPreviousBlock() !=
680 makePrevHash(currentHash)) {
681 return state.Invalid(
683 format(
"Can't replace root block with block {} ",
684 index.toPrettyString()));
688 current = createBootstrapBlockIndex(currentHash, index.height);
690 current->pnext.insert(&root);
691 VBK_ASSERT(root.isRoot());
692 root.pprev = current;
695 current = createBlockIndex(currentHash, *prev);
701 VBK_ASSERT(!current->isDeleted());
705 auto next = current->pnext;
706 auto prev = current->pprev;
708 current->mergeFrom(index);
711 current->setNullInmemFields();
713 current->pnext = next;
715 current->pprev = prev;
718 if (!fast_load && current->pprev !=
nullptr) {
720 auto expectedHeight = current->pprev->getHeight() + 1;
721 if (current->getHeight() != expectedHeight) {
722 return state.Invalid(
"bad-height");
728 if ((current->pprev ==
nullptr) && (
activeChain_.first() != current) &&
733 VBK_ASSERT(!current->isDeleted());
735 current->unsetDirty();
742 std::string toPrettyString(
size_t level = 0)
const {
744 std::string pad(level,
' ');
746 std::string blocksStr{};
748 std::vector<std::pair<int, index_t*>> byheight;
749 byheight.reserve(
blocks_.size());
750 for (
const auto& p :
blocks_) {
753 if (!p.second->isDeleted()) {
754 byheight.emplace_back(p.second->getHeight(), p.second.get());
757 std::sort(byheight.rbegin(), byheight.rend());
758 for (
const auto& p : byheight) {
759 blocksStr += (p.second->toPrettyString(level + 2) +
"\n");
762 std::string tipsStr{};
763 for (
const auto* _tip :
tips_) {
764 tipsStr += (_tip->toPrettyString(level + 2) +
"\n");
767 return format(
"{}{{tip={}}}\n{}{{blocks=\n{}{}}}\n{}{{tips=\n{}{}}}",
769 (tip ? tip->toPrettyString() :
"<empty>"),
779 const FinalizedPayloadsIndex<index_t>& getFinalizedPayloadsIndex()
const {
783 const PayloadsIndex<index_t>& getPayloadsIndex()
const {
788 class DeferForkResolutionGuard {
789 BaseBlockTree<Block>& tree_;
792 DeferForkResolutionGuard(BaseBlockTree<Block>& tree) : tree_(tree) {
793 tree_.deferForkResolution();
797 DeferForkResolutionGuard(
const DeferForkResolutionGuard& other) =
delete;
798 DeferForkResolutionGuard& operator=(
const DeferForkResolutionGuard& other) =
801 DeferForkResolutionGuard(DeferForkResolutionGuard&& o)
noexcept =
default;
802 DeferForkResolutionGuard& operator=(DeferForkResolutionGuard&& o)
noexcept =
805 void overrideDeferredForkResolution(index_t* bestChain) {
808 if (bestChain !=
nullptr) {
809 return tree_.overrideDeferredForkResolution(*bestChain);
813 ~DeferForkResolutionGuard() { tree_.continueForkResolution(); }
816 DeferForkResolutionGuard deferForkResolutionGuard() {
817 return DeferForkResolutionGuard(*
this);
829 void deallocateTree(index_t& toDelete) {
830 VBK_TRACE_ZONE_SCOPED;
831 auto* index = &toDelete;
834 while (index !=
nullptr && !index->isRoot()) {
835 index = index->pprev;
840 forEachNodePostorder<block_t>(
846 if (!next.isDeleted()) {
847 onBeforeLeafRemoved(next);
848 next.deleteTemporarily();
851 next.disconnectFromPrev();
854 [&](index_t&) {
return true; });
857 inline void decreaseAppliedBlockCount(
size_t erasedBlocks) {
858 VBK_ASSERT_MSG(appliedBlockCount >= erasedBlocks,
859 "Tree: %s, Applied: %d, erased: %d",
863 appliedBlockCount -= erasedBlocks;
882 virtual void finalizeBlockImpl(index_t& index,
884 int32_t preserveBlocksBehindFinal) {
885 VBK_TRACE_ZONE_SCOPED;
886 VBK_LOG_DEBUG(
"Finalize %s, preserve %d blocks behind",
887 index.toShortPrettyString(),
888 preserveBlocksBehindFinal);
889 VBK_ASSERT_MSG(appliedBlockCount ==
activeChain_.blocksCount(),
890 "Tree=%s Applied=%d active chain=%d",
895 index_t* finalizedBlock = &index;
896 if (finalizedBlock == &this->getRoot()) {
897 if (finalizedBlock->finalized) {
902 finalizedBlock->finalized =
true;
911 for (
auto* walkBlock = finalizedBlock; walkBlock !=
nullptr;
912 walkBlock = walkBlock->pprev) {
913 if (walkBlock->isDirty()) {
914 finalizedBlock = walkBlock;
919 erase_if<decltype(tips_), index_t*>(
920 tips_, [
this, &finalizedBlock](index_t* tip) ->
bool {
928 bool newFinalized =
false;
929 auto* walkBlock = tip;
930 for (; tip !=
nullptr && !
activeChain_.contains(walkBlock);
931 walkBlock = walkBlock->pprev) {
932 if (walkBlock->isDirty()) {
938 finalizedBlock = walkBlock;
947 VBK_ASSERT(finalizedBlock !=
nullptr);
953 int32_t firstBlockHeight =
954 finalizedBlock->getHeight() - preserveBlocksBehindFinal;
955 int32_t rootBlockHeight = getRoot().getHeight();
956 firstBlockHeight = std::max(rootBlockHeight, firstBlockHeight);
963 auto* rootPrev = newRoot->pprev;
964 if (newRoot->pprev !=
nullptr) {
965 newRoot->pprev->pnext.erase(newRoot);
966 newRoot->pprev =
nullptr;
969 deallocateTree(*rootPrev);
973 if (finalizedBlock->pprev !=
nullptr) {
974 auto parallelBlocks = finalizedBlock->pprev->pnext;
975 parallelBlocks.erase(finalizedBlock);
976 for (
auto* par : parallelBlocks) {
978 par->disconnectFromPrev();
979 deallocateTree(*par);
985 VBK_ASSERT(firstBlockHeight >= rootBlockHeight);
986 size_t deallocatedBlocks = firstBlockHeight - rootBlockHeight;
990 if (deallocatedBlocks > 0) {
991 VBK_LOG_WARN(
"Deallocated %d blocks in %s tree. Active chain %d..%d",
999 index_t* ptr = finalizedBlock;
1000 while (ptr !=
nullptr && !ptr->finalized) {
1001 ptr->finalized =
true;
1009 virtual void onBlockInserted(index_t* ) {
1018 void updateAffectedTips(index_t& modifiedBlock) {
1019 VBK_TRACE_ZONE_SCOPED;
1020 if (deferForkResolutionDepth == 0) {
1021 ValidationState dummy;
1022 return doUpdateAffectedTips(modifiedBlock, dummy);
1025 if (!isUpdateTipsDeferred) {
1026 if (lastModifiedBlock ==
nullptr) {
1027 lastModifiedBlock = &modifiedBlock;
1029 isUpdateTipsDeferred =
true;
1030 lastModifiedBlock =
nullptr;
1040 void doUpdateAffectedTips(index_t& modifiedBlock, ValidationState& state) {
1041 VBK_TRACE_ZONE_SCOPED;
1042 auto tips = findValidTips<block_t>(this->getTips(), modifiedBlock);
1044 "Found %d affected valid tips in %s", tips.size(), block_t::name());
1045 for (
auto* tip : tips) {
1046 determineBestChain(*tip, state);
1052 if (deferForkResolutionDepth == 0) {
1053 return doUpdateTips();
1055 isUpdateTipsDeferred =
true;
1056 lastModifiedBlock =
nullptr;
1060 int deferForkResolutionDepth = 0;
1061 bool isUpdateTipsDeferred =
false;
1062 index_t* lastModifiedBlock =
nullptr;
1064 void doUpdateTips() {
1065 VBK_TRACE_ZONE_SCOPED;
1066 for (
auto* tip :
tips_) {
1067 VBK_ASSERT_MSG(tip->isValidTip(),
1068 "found block %s in tips_ which is not a valid tip",
1069 tip->toPrettyString());
1071 ValidationState state;
1072 determineBestChain(*tip, state);
1080 void overrideDeferredForkResolution(index_t& bestChain) {
1081 lastModifiedBlock =
nullptr;
1082 isUpdateTipsDeferred =
false;
1084 ValidationState dummy;
1085 bool success = setState(bestChain, dummy);
1088 "state corruption: could not revert to the saved best chain tip: %s",
1092 void deferForkResolution() { ++deferForkResolutionDepth; }
1094 void continueForkResolution() {
1095 VBK_ASSERT(deferForkResolutionDepth > 0);
1096 --deferForkResolutionDepth;
1098 if (deferForkResolutionDepth > 0) {
1102 if (lastModifiedBlock !=
nullptr) {
1103 VBK_ASSERT(!isUpdateTipsDeferred);
1104 ValidationState dummy;
1105 doUpdateAffectedTips(*lastModifiedBlock, dummy);
1106 lastModifiedBlock =
nullptr;
1109 if (isUpdateTipsDeferred) {
1111 isUpdateTipsDeferred =
false;
1117 virtual void onBeforeLeafRemoved(
const index_t&) {}
1120 VBK_TRACE_ZONE_SCOPED;
1124 "attempted to set mutually exclusive flags BLOCK_CAN_BE_APPLIED "
1125 "and BLOCK_FAILED_POP for block %s",
1126 block.toPrettyString());
1128 block.setFlag(reason);
1129 tips_.erase(&block);
1135 block.unsetFlag(reason);
Class that is used for storing validation state.
@ BLOCK_VALID_TREE
acceptBlockHeader succeded.
@ BLOCK_CAN_BE_APPLIED
the chain with the block at its tip is fully valid, so if we do SetState on this block,...
BlockValidityStatus
Flags that describe block status.
@ BLOCK_FAILED_CHILD
block is state{lessly,fully} valid and the altchain did not report it as invalid, but some of the anc...
@ BLOCK_FAILED_MASK
all invalidity flags
@ BLOCK_FAILED_POP
block failed state{less,ful} validation due to its payloads
@ BLOCK_BOOTSTRAP
this is a bootstrap block
bool isBlockOutdated(const BlockIndex< Block > &finalBlock, const BlockIndex< Block > &candidate)
a candidate is considered outdated if it is behind finalBlock, or on same height and not equal to fin...
std::string HexStr(const T itbegin, const T itend)
Convert bytes to hex.
constexpr bool isValidInvalidationReason(const BlockValidityStatus &reason)
Check if the reason value can be used as the reason for invalidateSubtree and revalidateSubtree.
Base block tree that stores all blocks, maintains tree tips, maintains active chain.
void removeSubtree(const hash_t &toRemove)
This is an overloaded member function, provided for convenience. It differs from the above function o...
index_t * findBlockIndex(const T &hash)
Get BlockIndex by block hash.
void removeSubtree(index_t &toRemove)
Removes block and all its successors.
signals::Signal< void(const index_t &index)> onBeforeOverrideTip
chain reorg signal - the tip is being changed
signals::Signal< void(const index_t &)> onBlockBeforeDeallocated
before we deallocate any block index we emit it to this signal
void deallocateBlock(index_t &block)
Permanently erases block from a block tree.
const Chain< index_t > & getBestChain() const
Getter for currently Active Chain.
std::unordered_set< index_t * > tips_
stores ONLY VALID tips, including currently active tip
signals::Signal< on_invalidate_t > onBlockValidityChanged
signals to the end user that block have been invalidated
Chain< index_t > activeChain_
currently applied chain
virtual bool loadBlockForward(const stored_index_t &index, bool fast_load, ValidationState &state)
Efficiently connects BlockIndex to this tree as a leaf, when it is loaded from disk.
void removeLeaf(index_t &toRemove)
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool isLoadingBlocks_
if true, we're in "loading blocks" state
void invalidateSubtree(const hash_t &toBeInvalidated, enum BlockValidityStatus reason, bool shouldDetermineBestChain=true)
This is an overloaded member function, provided for convenience. It differs from the above function o...
block_index_t blocks_
stores ALL blocks, including valid and invalid
bool isLoaded_
if true, we can no longer execute loadBlock/loadTip on this tree.
void invalidateSubtree(index_t &toBeInvalidated, enum BlockValidityStatus reason, bool shouldDetermineBestChain=true)
Mark given block as invalid.
bool isBootstrapped() const
Check if the blockchain is bootstrapped.
const index_t * getBlockIndex(const T &hash) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
const index_t * findBlockIndex(const T &hash) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
FinalizedPayloadsIndex< index_t > finalizedPayloadsIndex_
stores mapping of payload id -> its containing ALT/VBK block which are already finalized.
index_t * getBlockIndex(const T &hash)
Get BlockIndex by block hash.
PayloadsIndex< index_t > payloadsIndex_
stores mapping of payload id -> its containing ALT/VBK blocks which are not yet finalized.
size_t nondeletedDescendantCount() const
Count the descendants that are not deleted.
BlockIndex * pprev
(memory only) pointer to the previous block
std::set< BlockIndex * > pnext
(memory only) a set of pointers for forward iteration
An abstraction over on-disk storage iterator.
Fully in-memory chain representation.
Stores a mapping "payload id -> containing block" hash of payloads that are stored in finalized block...
Payloads index that stores mapping "payload id -> set of containing blocks" from all NON-FINALIZED bl...