6#ifndef ALT_INTEGRATION_INCLUDE_VERIBLOCK_BLOCKCHAIN_BLOCKTREE_HPP_
7#define ALT_INTEGRATION_INCLUDE_VERIBLOCK_BLOCKCHAIN_BLOCKTREE_HPP_
11#include <unordered_map>
13#include "base_block_tree.hpp"
14#include "block_index.hpp"
15#include "blockchain_util.hpp"
17#include "tree_algo.hpp"
18#include "veriblock/pop/fmt.hpp"
19#include "veriblock/pop/stateless_validation.hpp"
20#include "veriblock/pop/storage/block_reader.hpp"
21#include "veriblock/pop/validation_state.hpp"
29template <
typename Block,
typename ChainParams>
32 using block_t = Block;
33 using params_t = ChainParams;
36 using hash_t =
typename Block::hash_t;
37 using prev_block_hash_t =
typename Block::prev_hash_t;
38 using height_t =
typename Block::height_t;
43 :
base(blockProvider), param_(¶m) {}
45 const ChainParams& getParams()
const {
return *param_; }
55 this->bootstrap(0, block, state);
69 const std::vector<block_t>& chain) {
73 VBK_ASSERT_MSG(!chain.empty(),
"bootstrap chain must not be empty");
75 chain.size() >= param_->numBlocksForBootstrap(),
76 "bootstrap chain is too small (%d) expected at least %d blocks",
78 param_->numBlocksForBootstrap());
83 auto genesis = chain[0];
84 this->bootstrap(startHeight, genesis, state);
89 for (
size_t i = 1, size = chain.size(); i < size; i++) {
90 auto& block = chain[i];
91 bool ok = this->acceptBlockHeaderImpl(
92 std::make_shared<block_t>(block), state,
false);
94 "found statelssly invalid bootstrap block: %d, %s",
99 index->setIsBootstrap(
true);
104 return acceptBlockHeaderImpl(std::make_shared<block_t>(block), state,
true);
107 bool acceptBlockHeader(
const std::shared_ptr<block_t>& block,
108 ValidationState& state) {
109 return acceptBlockHeaderImpl(block, state,
true);
112 std::string toPrettyString(
size_t level = 0)
const {
113 std::string pad(level,
' ');
114 return format(
"{}{}BlockTree{{blocks={}\n{}\n{}}}",
117 base::getBlocks().size(),
118 base::toPrettyString(level + 2),
127 return state.Invalid(
"bad-header");
130 const auto hash = index.
header->getHash();
142 if (!fast_load && !current->isRoot() &&
144 !contextuallyCheckBlock(
145 *current->pprev, current->getHeader(), state, *param_)) {
146 return state.Invalid(
"bad-block-contextually");
150 current->chainWork = getBlockProof(current->getHeader());
151 if (!current->isRoot()) {
152 current->chainWork += current->pprev->chainWork;
156 current->clearBlockOfProofEndorsement();
168 return tip->getHeight() - height > getParams().getOldBlocksWindow();
174 if (index ==
nullptr) {
182 void finalizeBlocks() {
184 base::finalizeBlocks(this->getParams().getMaxReorgBlocks(),
185 this->getParams().preserveBlocksBehindFinal());
189 const ChainParams* param_ =
nullptr;
191 bool acceptBlockHeaderImpl(
const std::shared_ptr<block_t>& block,
192 ValidationState& state,
193 bool shouldContextuallyCheck) {
194 index_t* index =
nullptr;
195 if (!validateAndAddBlock(block, state, shouldContextuallyCheck, &index)) {
200 if (!shouldContextuallyCheck) {
205 base::tryAddTip(index);
214 void bootstrap(height_t height,
215 const block_t& block,
216 ValidationState& state) {
217 bootstrap(height, std::make_shared<block_t>(block), state);
220 void bootstrap(height_t height,
221 std::shared_ptr<block_t> block,
222 ValidationState& state) {
225 "found statelessly invalid bootstrap block in %s tree: %s",
229 auto* index = base::insertBlockHeader(block, height);
230 VBK_ASSERT(index !=
nullptr &&
231 "insertBlockHeader should have never returned nullptr");
233 index->setHeight(height);
235 index->setIsBootstrap(
true);
237 VBK_ASSERT(index->finalized);
240 "getBlockIndex must be able to find the block added by "
241 "insertBlockHeader");
243 base::tryAddTip(index);
248 base::appliedBlockCount = 1;
251 bool validateAndAddBlock(
const std::shared_ptr<block_t>& block,
252 ValidationState& state,
253 bool shouldContextuallyCheck,
256 return state.Invalid(block_t::name() +
"-check-block");
261 if (prev ==
nullptr) {
262 return state.Invalid(
263 block_t::name() +
"-bad-prev-block",
264 "can not find previous block: " +
HexStr(block->getPreviousBlock()));
267 if (shouldContextuallyCheck &&
268 !contextuallyCheckBlock(*prev, *block, state, *param_)) {
269 return state.Invalid(block_t::name() +
"-contextually-check-block");
272 auto index = base::insertBlockHeader(block);
273 VBK_ASSERT(index !=
nullptr &&
274 "insertBlockHeader should have never returned nullptr");
283 if (!prev->isValid()) {
285 return state.Invalid(
286 block_t::name() +
"-bad-chain",
287 format(
"Previous block is invalid={}", prev->toPrettyString()));
295 VBK_ASSERT(bestTip !=
nullptr &&
"must be bootstrapped");
297 if (bestTip == &candidate) {
302 if (!candidate.isValid()) {
306 if (bestTip->chainWork < candidate.chainWork) {
308 this->setState(candidate, state);
314 newIndex->chainWork = getBlockProof(newIndex->getHeader());
315 if (!newIndex->isRoot()) {
316 newIndex->chainWork += newIndex->pprev->chainWork;
322template <
typename Block,
typename ChainParams>
323void PrintTo(
const BlockTree<Block, ChainParams>& tree, std::ostream* os) {
324 *os << tree.toPrettyString();
Class that is used for storing validation state.
@ BLOCK_VALID_TREE
acceptBlockHeader succeded.
@ BLOCK_CONNECTED
the block is connected via connectBlock, which means that this block and all its ancestors are at lea...
@ BLOCK_CAN_BE_APPLIED
the chain with the block at its tip is fully valid, so if we do SetState on this block,...
@ BLOCK_FAILED_CHILD
block is state{lessly,fully} valid and the altchain did not report it as invalid, but some of the anc...
@ BLOCK_ACTIVE
the block is currently applied via SetState.
@ BLOCK_BOOTSTRAP
this is a bootstrap block
std::string HexStr(const T itbegin, const T itend)
Convert bytes to hex.
bool checkBlock(const BtcBlock &block, ValidationState &state, const BtcChainParams ¶ms)
Stateless validation for a single block.
void PrintTo(const ArithUint256 &uint, ::std::ostream *os)
custom gtest printer, which prints Blob of any size as hexstring @ private
Base block tree that stores all blocks, maintains tree tips, maintains active chain.
const Chain< index_t > & getBestChain() const
Getter for currently Active Chain.
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.
bool isLoadingBlocks_
if true, we're in "loading blocks" state
bool isLoaded_
if true, we can no longer execute loadBlock/loadTip on this tree.
bool isBootstrapped() const
Check if the blockchain is bootstrapped.
index_t * getBlockIndex(const T &hash)
Get BlockIndex by block hash.
An abstraction over on-disk storage iterator.
BlockTree is a tree of blocks with single "bootstrap" block as root.
void determineBestChain(index_t &candidate, ValidationState &state) override
bool loadBlockForward(const stored_index_t &index, bool fast_load, ValidationState &state) override
void onBlockInserted(index_t *newIndex) final
whenever new block is inserted, BlockTree has to update its ChainWork
virtual void bootstrapWithGenesis(const block_t &block)
Bootstrap blockchain with a single genesis block.
virtual void bootstrapWithChain(height_t startHeight, const std::vector< block_t > &chain)
Bootstrap network with a chain that starts at 'startHeight'.
bool isBlockOld(const hash_t &hash) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool isBlockOld(height_t height) const
block is considered old if it is behind current tip further than 'old blocks window' blocks
std::shared_ptr< block_t > header
block header