3#include <boost/multi_index_container.hpp>
4#include <boost/multi_index/member.hpp>
5#include <boost/multi_index/ordered_index.hpp>
6#include <boost/multi_index/hashed_index.hpp>
7#include <boost/multi_index/mem_fun.hpp>
8#include <boost/multi_index/global_fun.hpp>
9#include <boost/multi_index/composite_key.hpp>
12#include <shared_mutex>
14namespace sysio {
namespace chain {
15 using boost::multi_index_container;
16 using namespace boost::multi_index;
34 struct by_lib_block_num;
36 typedef multi_index_container<
39 hashed_unique< tag<by_block_id>, member<block_header_state, block_id_type, &block_header_state::id>, std::hash<block_id_type>>,
40 ordered_non_unique< tag<by_prev>, const_mem_fun<block_header_state, const block_id_type&, &block_header_state::prev> >,
41 ordered_unique< tag<by_lib_block_num>,
43 global_fun<const block_state&, bool, &block_state_is_valid>,
44 member<detail::block_header_state_common, uint32_t, &detail::block_header_state_common::dpos_irreversible_blocknum>,
45 member<detail::block_header_state_common, uint32_t, &detail::block_header_state_common::block_num>,
46 member<block_header_state, block_id_type, &block_header_state::id>
48 composite_key_compare<
50 std::greater<uint32_t>,
51 std::greater<uint32_t>,
68 std::shared_mutex
mtx;
75 const flat_set<digest_type>&,
93 bool ignore_duplicate,
bool validate,
95 const flat_set<digest_type>&,
106 const flat_set<digest_type>&,
109 std::lock_guard g( my->mtx );
110 my->open_impl( validator );
114 const flat_set<digest_type>&,
120 auto fork_db_dat =
datadir / config::forkdb_filename;
132 "Fork database file '${filename}' has unexpected magic number: ${actual_totem}. Expected ${expected_totem}",
133 (
"filename", fork_db_dat.generic_string())
134 (
"actual_totem", totem)
143 "Unsupported version of fork database file '${filename}'. "
144 "Fork database version is ${version} while code supports version(s) [${min},${max}]",
145 (
"filename", fork_db_dat.generic_string())
160 s.header_exts =
s.block->validate_and_extract_header_extensions();
161 add_impl( std::make_shared<block_state>( std::move(
s ) ),
false,
true, validator );
166 if( root->id == head_id ) {
171 "could not find head while reconstructing fork database from file; '${filename}' is likely corrupted",
172 (
"filename", fork_db_dat.generic_string()) );
175 auto candidate = index.get<by_lib_block_num>().begin();
176 if( candidate == index.get<by_lib_block_num>().end() || !(*candidate)->is_valid() ) {
178 "head not set to root despite no better option available; '${filename}' is likely corrupted",
179 (
"filename", fork_db_dat.generic_string()) );
182 "head not set to best available option available; '${filename}' is likely corrupted",
183 (
"filename", fork_db_dat.generic_string()) );
192 std::lock_guard g( my->mtx );
197 auto fork_db_dat =
datadir / config::forkdb_filename;
200 if( index.size() > 0 ) {
201 elog(
"fork_database is in a bad state when closing; not writing out '${filename}'",
202 (
"filename", fork_db_dat.generic_string()) );
207 std::ofstream out( fork_db_dat.generic_string().c_str(), std::ios::out | std::ios::binary | std::ofstream::trunc );
211 uint32_t num_blocks_in_fork_db = index.size();
214 const auto& indx = index.get<by_lib_block_num>();
216 auto unvalidated_itr = indx.rbegin();
217 auto unvalidated_end = boost::make_reverse_iterator( indx.lower_bound(
false ) );
219 auto validated_itr = unvalidated_end;
220 auto validated_end = indx.rend();
222 for(
bool unvalidated_remaining = (unvalidated_itr != unvalidated_end),
223 validated_remaining = (validated_itr != validated_end);
225 unvalidated_remaining || validated_remaining;
227 unvalidated_remaining = (unvalidated_itr != unvalidated_end),
228 validated_remaining = (validated_itr != validated_end)
231 auto itr = (validated_remaining ? validated_itr : unvalidated_itr);
233 if( unvalidated_remaining && validated_remaining ) {
235 itr = unvalidated_itr;
240 }
else if( unvalidated_remaining ) {
252 elog(
"head not set in fork database; '${filename}' will be corrupted",
253 (
"filename", fork_db_dat.generic_string()) );
264 std::lock_guard g( my->mtx );
265 my->reset_impl(root_bhs);
270 root = std::make_shared<block_state>();
272 root->validated =
true;
277 std::lock_guard g( my->mtx );
278 my->rollback_head_to_root_impl();
282 auto& by_id_idx = index.get<by_block_id>();
283 auto itr = by_id_idx.begin();
284 while (itr != by_id_idx.end()) {
286 bsp->validated =
false;
294 std::lock_guard g( my->mtx );
295 my->advance_root_impl(
id );
303 "cannot advance root to a block that does not exist in the fork database" );
305 "cannot advance root to a block that has not yet been validated" );
308 deque<block_id_type> blocks_to_remove;
309 for(
auto b = new_root; b; ) {
310 blocks_to_remove.emplace_back( b->header.previous );
317 index.erase( index.find(
id ) );
320 for(
const auto& block_id : blocks_to_remove ) {
332 std::shared_lock g( my->mtx );
333 return my->get_block_header_impl(
id );
337 if( root->id ==
id ) {
341 auto itr = index.find(
id );
342 if( itr != index.end() )
349 bool ignore_duplicate,
bool validate,
351 const flat_set<digest_type>&,
359 SYS_ASSERT( prev_bh, unlinkable_block_exception,
360 "unlinkable block", (
"id", n->id)(
"previous", n->header.previous) );
364 const auto& exts = n->header_exts;
368 validator( n->header.timestamp, prev_bh->activated_protocol_features->protocol_features, new_protocol_features );
373 auto inserted = index.insert(n);
374 if( !inserted.second ) {
375 if( ignore_duplicate )
return;
379 auto candidate = index.get<by_lib_block_num>().begin();
380 if( (*candidate)->is_valid() ) {
386 std::lock_guard g( my->mtx );
387 my->add_impl( n, ignore_duplicate,
false,
389 const flat_set<digest_type>& cur_features,
396 std::shared_lock g( my->mtx );
401 std::shared_lock g( my->mtx );
406 std::shared_lock g( my->mtx );
407 const auto& indx = my->index.get<by_lib_block_num>();
409 auto itr = indx.lower_bound(
false );
410 if( itr != indx.end() && !(*itr)->is_valid() ) {
419 std::shared_lock g( my->mtx );
420 return my->fetch_branch_impl( h, trim_after_block_num );
426 if(
s->block_num <= trim_after_block_num )
427 result.push_back(
s );
434 std::shared_lock g( my->mtx );
435 return my->search_on_branch_impl( h, block_num );
440 if(
s->block_num == block_num )
453 std::shared_lock g( my->mtx );
454 return my->fetch_branch_from_impl( first, second );
459 pair<branch_type,branch_type> result;
460 auto first_branch = (first == root->id) ? root :
get_block_impl(first);
461 auto second_branch = (second == root->id) ? root :
get_block_impl(second);
463 SYS_ASSERT(first_branch, fork_db_block_not_found,
"block ${id} does not exist", (
"id", first));
464 SYS_ASSERT(second_branch, fork_db_block_not_found,
"block ${id} does not exist", (
"id", second));
466 while( first_branch->block_num > second_branch->block_num )
468 result.first.push_back(first_branch);
469 const auto& prev = first_branch->header.previous;
470 first_branch = (prev == root->id) ? root :
get_block_impl( prev );
471 SYS_ASSERT( first_branch, fork_db_block_not_found,
472 "block ${id} does not exist",
477 while( second_branch->block_num > first_branch->block_num )
479 result.second.push_back( second_branch );
480 const auto& prev = second_branch->header.previous;
481 second_branch = (prev == root->id) ? root :
get_block_impl( prev );
482 SYS_ASSERT( second_branch, fork_db_block_not_found,
483 "block ${id} does not exist",
488 if (first_branch->id == second_branch->id)
return result;
490 while( first_branch->header.previous != second_branch->header.previous )
492 result.first.push_back(first_branch);
493 result.second.push_back(second_branch);
494 const auto &first_prev = first_branch->header.previous;
496 const auto &second_prev = second_branch->header.previous;
498 SYS_ASSERT( first_branch, fork_db_block_not_found,
499 "block ${id} does not exist",
502 SYS_ASSERT( second_branch, fork_db_block_not_found,
503 "block ${id} does not exist",
508 if( first_branch && second_branch )
510 result.first.push_back(first_branch);
511 result.second.push_back(second_branch);
518 std::lock_guard g( my->mtx );
519 return my->remove_impl(
id );
523 deque<block_id_type> remove_queue{
id};
524 const auto& previdx = index.get<by_prev>();
525 const auto& head_id =
head->id;
527 for(
uint32_t i = 0; i < remove_queue.size(); ++i ) {
529 "removing the block and its descendants would remove the current head block" );
531 auto previtr = previdx.lower_bound( remove_queue[i] );
532 while( previtr != previdx.end() && (*previtr)->header.previous == remove_queue[i] ) {
533 remove_queue.emplace_back( (*previtr)->id );
538 for(
const auto& block_id : remove_queue ) {
539 index.erase( block_id );
544 std::lock_guard g( my->mtx );
545 my->mark_valid_impl( h );
549 if( h->validated )
return;
551 auto& by_id_idx = index.get<by_block_id>();
553 auto itr = by_id_idx.find( h->id );
555 "block state not in fork database; cannot mark as valid",
559 bsp->validated =
true;
562 auto candidate = index.get<by_lib_block_num>().begin();
569 std::shared_lock g( my->mtx );
570 return my->get_block_impl(
id);
574 auto itr = index.find(
id );
575 if( itr != index.end() )
#define SYS_THROW(exc_type, FORMAT,...)
#define SYS_ASSERT(expr, exc_type, FORMAT,...)
#define SYS_RETHROW_EXCEPTIONS(exception_type, FORMAT,...)
wraps boost::filesystem::path to provide platform independent path manipulation.
static const uint32_t max_supported_version
block_state_ptr pending_head() const
block_state_ptr search_on_branch(const block_id_type &h, uint32_t block_num) const
branch_type fetch_branch(const block_id_type &h, uint32_t trim_after_block_num=std::numeric_limits< uint32_t >::max()) const
void remove(const block_id_type &id)
fetch_branch_from_impl
block_state_ptr root() const
void open(const std::function< void(block_timestamp_type, const flat_set< digest_type > &, const vector< digest_type > &)> &validator)
block_state_ptr head() const
void rollback_head_to_root()
static const uint32_t magic_number
block_header_state_ptr get_block_header(const block_id_type &id) const
void mark_valid(const block_state_ptr &h)
pair< branch_type, branch_type > fetch_branch_from(const block_id_type &first, const block_id_type &second) const
void reset(const block_header_state &root_bhs)
static const uint32_t min_supported_version
void add(const block_state_ptr &next_block, bool ignore_duplicate=false)
fork_database(const fc::path &data_dir)
void advance_root(const block_id_type &id)
block_state_ptr get_block(const block_id_type &id) const
#define FC_CAPTURE_AND_RETHROW(...)
void unpack(Stream &s, std::deque< T > &value)
void pack(Stream &s, const std::deque< T > &value)
bool remove(const path &p)
void read_file_contents(const fc::path &filename, std::string &result)
bool exists(const path &p)
void create_directories(const path &p)
bool is_directory(const path &p)
bool block_state_is_valid(const block_state &bs)
deque< block_state_ptr > branch_type
multi_index_container< block_state_ptr, indexed_by< hashed_unique< tag< by_block_id >, member< block_header_state, block_id_type, &block_header_state::id >, std::hash< block_id_type > >, ordered_non_unique< tag< by_prev >, const_mem_fun< block_header_state, const block_id_type &, &block_header_state::prev > >, ordered_unique< tag< by_lib_block_num >, composite_key< block_state, global_fun< const block_state &, bool, &block_state_is_valid >, member< detail::block_header_state_common, uint32_t, &detail::block_header_state_common::dpos_irreversible_blocknum >, member< detail::block_header_state_common, uint32_t, &detail::block_header_state_common::block_num >, member< block_header_state, block_id_type, &block_header_state::id > >, composite_key_compare< std::greater< bool >, std::greater< uint32_t >, std::greater< uint32_t >, sha256_less > > > > fork_multi_index_type
bool first_preferred(const block_header_state &lhs, const block_header_state &rhs)
std::shared_ptr< block_header_state > block_header_state_ptr
bool validate(const Authority &auth)
std::shared_ptr< block_state > block_state_ptr
key Invalid authority Invalid transaction Invalid block ID Invalid packed transaction Invalid chain ID Invalid symbol Signature type is not a currently activated type fork_database_exception
void add_impl(const block_state_ptr &n, bool ignore_duplicate, bool validate, const std::function< void(block_timestamp_type, const flat_set< digest_type > &, const vector< digest_type > &)> &validator)
void reset_impl(const block_header_state &root_bhs)
pair< branch_type, branch_type > fetch_branch_from_impl(const block_id_type &first, const block_id_type &second) const
block_state_ptr search_on_branch_impl(const block_id_type &h, uint32_t block_num) const
block_header_state_ptr get_block_header_impl(const block_id_type &id) const
void advance_root_impl(const block_id_type &id)
fork_database_impl(const fc::path &data_dir)
block_state_ptr get_block_impl(const block_id_type &id) const
void open_impl(const std::function< void(block_timestamp_type, const flat_set< digest_type > &, const vector< digest_type > &)> &validator)
branch_type fetch_branch_impl(const block_id_type &h, uint32_t trim_after_block_num) const
void mark_valid_impl(const block_state_ptr &h)
void rollback_head_to_root_impl()
void remove_impl(const block_id_type &id)
fork_multi_index_type index
static constexpr uint16_t extension_id()