Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
fork_database.cpp
Go to the documentation of this file.
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>
10#include <fc/io/fstream.hpp>
11#include <fstream>
12#include <shared_mutex>
13
14namespace sysio { namespace chain {
15 using boost::multi_index_container;
16 using namespace boost::multi_index;
17
18 const uint32_t fork_database::magic_number = 0x30510FDB;
19
22
23 // work around block_state::is_valid being private
24 inline bool block_state_is_valid( const block_state& bs ) {
25 return bs.is_valid();
26 }
27
33 struct by_block_id;
34 struct by_lib_block_num;
35 struct by_prev;
36 typedef multi_index_container<
38 indexed_by<
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>,
42 composite_key< block_state,
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>
47 >,
48 composite_key_compare<
49 std::greater<bool>,
50 std::greater<uint32_t>,
51 std::greater<uint32_t>,
53 >
54 >
55 >
57
58 bool first_preferred( const block_header_state& lhs, const block_header_state& rhs ) {
59 return std::tie( lhs.dpos_irreversible_blocknum, lhs.block_num )
60 > std::tie( rhs.dpos_irreversible_blocknum, rhs.block_num );
61 }
62
64 explicit fork_database_impl( const fc::path& data_dir )
65 :datadir(data_dir)
66 {}
67
68 std::shared_mutex mtx;
70 block_state_ptr root; // Only uses the block_header_state portion
73
74 void open_impl( const std::function<void( block_timestamp_type,
75 const flat_set<digest_type>&,
76 const vector<digest_type>& )>& validator );
77 void close_impl();
78
79
82 void reset_impl( const block_header_state& root_bhs );
84 void advance_root_impl( const block_id_type& id );
85 void remove_impl( const block_id_type& id );
86 branch_type fetch_branch_impl( const block_id_type& h, uint32_t trim_after_block_num )const;
88 pair<branch_type, branch_type> fetch_branch_from_impl( const block_id_type& first,
89 const block_id_type& second )const;
90 void mark_valid_impl( const block_state_ptr& h );
91
92 void add_impl( const block_state_ptr& n,
93 bool ignore_duplicate, bool validate,
94 const std::function<void( block_timestamp_type,
95 const flat_set<digest_type>&,
96 const vector<digest_type>& )>& validator );
97 };
98
99
101 :my( new fork_database_impl( data_dir ) )
102 {}
103
104
105 void fork_database::open( const std::function<void( block_timestamp_type,
106 const flat_set<digest_type>&,
107 const vector<digest_type>& )>& validator )
108 {
109 std::lock_guard g( my->mtx );
110 my->open_impl( validator );
111 }
112
114 const flat_set<digest_type>&,
115 const vector<digest_type>& )>& validator )
116 {
119
120 auto fork_db_dat = datadir / config::forkdb_filename;
121 if( fc::exists( fork_db_dat ) ) {
122 try {
123 string content;
124 fc::read_file_contents( fork_db_dat, content );
125
126 fc::datastream<const char*> ds( content.data(), content.size() );
127
128 // validate totem
129 uint32_t totem = 0;
130 fc::raw::unpack( ds, totem );
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)
135 ("expected_totem", fork_database::magic_number)
136 );
137
138 // validate version
139 uint32_t version = 0;
140 fc::raw::unpack( ds, version );
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())
146 ("version", version)
149 );
150
152 fc::raw::unpack( ds, bhs );
153 reset_impl( bhs );
154
155 unsigned_int size; fc::raw::unpack( ds, size );
156 for( uint32_t i = 0, n = size.value; i < n; ++i ) {
158 fc::raw::unpack( ds, s );
159 // do not populate transaction_metadatas, they will be created as needed in apply_block with appropriate key recovery
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 );
162 }
163 block_id_type head_id;
164 fc::raw::unpack( ds, head_id );
165
166 if( root->id == head_id ) {
167 head = root;
168 } else {
169 head = get_block_impl( head_id );
171 "could not find head while reconstructing fork database from file; '${filename}' is likely corrupted",
172 ("filename", fork_db_dat.generic_string()) );
173 }
174
175 auto candidate = index.get<by_lib_block_num>().begin();
176 if( candidate == index.get<by_lib_block_num>().end() || !(*candidate)->is_valid() ) {
177 SYS_ASSERT( head->id == root->id, fork_database_exception,
178 "head not set to root despite no better option available; '${filename}' is likely corrupted",
179 ("filename", fork_db_dat.generic_string()) );
180 } else {
182 "head not set to best available option available; '${filename}' is likely corrupted",
183 ("filename", fork_db_dat.generic_string()) );
184 }
185 } FC_CAPTURE_AND_RETHROW( (fork_db_dat) )
186
187 fc::remove( fork_db_dat );
188 }
189 }
190
192 std::lock_guard g( my->mtx );
193 my->close_impl();
194 }
195
197 auto fork_db_dat = datadir / config::forkdb_filename;
198
199 if( !root ) {
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()) );
203 }
204 return;
205 }
206
207 std::ofstream out( fork_db_dat.generic_string().c_str(), std::ios::out | std::ios::binary | std::ofstream::trunc );
209 fc::raw::pack( out, fork_database::max_supported_version ); // write out current version which is always max_supported_version
210 fc::raw::pack( out, *static_cast<block_header_state*>(&*root) );
211 uint32_t num_blocks_in_fork_db = index.size();
212 fc::raw::pack( out, unsigned_int{num_blocks_in_fork_db} );
213
214 const auto& indx = index.get<by_lib_block_num>();
215
216 auto unvalidated_itr = indx.rbegin();
217 auto unvalidated_end = boost::make_reverse_iterator( indx.lower_bound( false ) );
218
219 auto validated_itr = unvalidated_end;
220 auto validated_end = indx.rend();
221
222 for( bool unvalidated_remaining = (unvalidated_itr != unvalidated_end),
223 validated_remaining = (validated_itr != validated_end);
224
225 unvalidated_remaining || validated_remaining;
226
227 unvalidated_remaining = (unvalidated_itr != unvalidated_end),
228 validated_remaining = (validated_itr != validated_end)
229 )
230 {
231 auto itr = (validated_remaining ? validated_itr : unvalidated_itr);
232
233 if( unvalidated_remaining && validated_remaining ) {
234 if( first_preferred( **validated_itr, **unvalidated_itr ) ) {
235 itr = unvalidated_itr;
236 ++unvalidated_itr;
237 } else {
238 ++validated_itr;
239 }
240 } else if( unvalidated_remaining ) {
241 ++unvalidated_itr;
242 } else {
243 ++validated_itr;
244 }
245
246 fc::raw::pack( out, *(*itr) );
247 }
248
249 if( head ) {
250 fc::raw::pack( out, head->id );
251 } else {
252 elog( "head not set in fork database; '${filename}' will be corrupted",
253 ("filename", fork_db_dat.generic_string()) );
254 }
255
256 index.clear();
257 }
258
260 my->close_impl();
261 }
262
263 void fork_database::reset( const block_header_state& root_bhs ) {
264 std::lock_guard g( my->mtx );
265 my->reset_impl(root_bhs);
266 }
267
269 index.clear();
270 root = std::make_shared<block_state>();
271 static_cast<block_header_state&>(*root) = root_bhs;
272 root->validated = true;
273 head = root;
274 }
275
277 std::lock_guard g( my->mtx );
278 my->rollback_head_to_root_impl();
279 }
280
282 auto& by_id_idx = index.get<by_block_id>();
283 auto itr = by_id_idx.begin();
284 while (itr != by_id_idx.end()) {
285 by_id_idx.modify( itr, [&]( block_state_ptr& bsp ) {
286 bsp->validated = false;
287 } );
288 ++itr;
289 }
290 head = root;
291 }
292
294 std::lock_guard g( my->mtx );
295 my->advance_root_impl( id );
296 }
297
299 SYS_ASSERT( root, fork_database_exception, "root not yet set" );
300
301 auto new_root = get_block_impl( id );
303 "cannot advance root to a block that does not exist in the fork database" );
304 SYS_ASSERT( new_root->is_valid(), fork_database_exception,
305 "cannot advance root to a block that has not yet been validated" );
306
307
308 deque<block_id_type> blocks_to_remove;
309 for( auto b = new_root; b; ) {
310 blocks_to_remove.emplace_back( b->header.previous );
311 b = get_block_impl( blocks_to_remove.back() );
312 SYS_ASSERT( b || blocks_to_remove.back() == root->id, fork_database_exception, "invariant violation: orphaned branch was present in forked database" );
313 }
314
315 // The new root block should be erased from the fork database index individually rather than with the remove method,
316 // because we do not want the blocks branching off of it to be removed from the fork database.
317 index.erase( index.find( id ) );
318
319 // The other blocks to be removed are removed using the remove method so that orphaned branches do not remain in the fork database.
320 for( const auto& block_id : blocks_to_remove ) {
321 remove_impl( block_id );
322 }
323
324 // Even though fork database no longer needs block or trxs when a block state becomes a root of the tree,
325 // avoid mutating the block state at all, for example clearing the block shared pointer, because other
326 // parts of the code which run asynchronously may later expect it remain unmodified.
327
328 root = new_root;
329 }
330
332 std::shared_lock g( my->mtx );
333 return my->get_block_header_impl( id );
334 }
335
337 if( root->id == id ) {
338 return root;
339 }
340
341 auto itr = index.find( id );
342 if( itr != index.end() )
343 return *itr;
344
345 return block_header_state_ptr();
346 }
347
349 bool ignore_duplicate, bool validate,
350 const std::function<void( block_timestamp_type,
351 const flat_set<digest_type>&,
352 const vector<digest_type>& )>& validator )
353 {
354 SYS_ASSERT( root, fork_database_exception, "root not yet set" );
355 SYS_ASSERT( n, fork_database_exception, "attempt to add null block state" );
356
357 auto prev_bh = get_block_header_impl( n->header.previous );
358
359 SYS_ASSERT( prev_bh, unlinkable_block_exception,
360 "unlinkable block", ("id", n->id)("previous", n->header.previous) );
361
362 if( validate ) {
363 try {
364 const auto& exts = n->header_exts;
365
366 if( exts.count(protocol_feature_activation::extension_id()) > 0 ) {
367 const auto& new_protocol_features = std::get<protocol_feature_activation>(exts.lower_bound(protocol_feature_activation::extension_id())->second).protocol_features;
368 validator( n->header.timestamp, prev_bh->activated_protocol_features->protocol_features, new_protocol_features );
369 }
370 } SYS_RETHROW_EXCEPTIONS( fork_database_exception, "serialized fork database is incompatible with configured protocol features" )
371 }
372
373 auto inserted = index.insert(n);
374 if( !inserted.second ) {
375 if( ignore_duplicate ) return;
376 SYS_THROW( fork_database_exception, "duplicate block added", ("id", n->id) );
377 }
378
379 auto candidate = index.get<by_lib_block_num>().begin();
380 if( (*candidate)->is_valid() ) {
381 head = *candidate;
382 }
383 }
384
385 void fork_database::add( const block_state_ptr& n, bool ignore_duplicate ) {
386 std::lock_guard g( my->mtx );
387 my->add_impl( n, ignore_duplicate, false,
388 []( block_timestamp_type timestamp,
389 const flat_set<digest_type>& cur_features,
390 const vector<digest_type>& new_features )
391 {}
392 );
393 }
394
396 std::shared_lock g( my->mtx );
397 return my->root;
398 }
399
401 std::shared_lock g( my->mtx );
402 return my->head;
403 }
404
406 std::shared_lock g( my->mtx );
407 const auto& indx = my->index.get<by_lib_block_num>();
408
409 auto itr = indx.lower_bound( false );
410 if( itr != indx.end() && !(*itr)->is_valid() ) {
411 if( first_preferred( **itr, *my->head ) )
412 return *itr;
413 }
414
415 return my->head;
416 }
417
418 branch_type fork_database::fetch_branch( const block_id_type& h, uint32_t trim_after_block_num )const {
419 std::shared_lock g( my->mtx );
420 return my->fetch_branch_impl( h, trim_after_block_num );
421 }
422
424 branch_type result;
425 for( auto s = get_block_impl(h); s; s = get_block_impl( s->header.previous ) ) {
426 if( s->block_num <= trim_after_block_num )
427 result.push_back( s );
428 }
429
430 return result;
431 }
432
434 std::shared_lock g( my->mtx );
435 return my->search_on_branch_impl( h, block_num );
436 }
437
439 for( auto s = get_block_impl(h); s; s = get_block_impl( s->header.previous ) ) {
440 if( s->block_num == block_num )
441 return s;
442 }
443
444 return {};
445 }
446
451 pair< branch_type, branch_type > fork_database::fetch_branch_from( const block_id_type& first,
452 const block_id_type& second )const {
453 std::shared_lock g( my->mtx );
454 return my->fetch_branch_from_impl( first, second );
455 }
456
457 pair< branch_type, branch_type > fork_database_impl::fetch_branch_from_impl( const block_id_type& first,
458 const block_id_type& second )const {
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);
462
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));
465
466 while( first_branch->block_num > second_branch->block_num )
467 {
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",
473 ("id", prev)
474 );
475 }
476
477 while( second_branch->block_num > first_branch->block_num )
478 {
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",
484 ("id", prev)
485 );
486 }
487
488 if (first_branch->id == second_branch->id) return result;
489
490 while( first_branch->header.previous != second_branch->header.previous )
491 {
492 result.first.push_back(first_branch);
493 result.second.push_back(second_branch);
494 const auto &first_prev = first_branch->header.previous;
495 first_branch = get_block_impl( first_prev );
496 const auto &second_prev = second_branch->header.previous;
497 second_branch = get_block_impl( second_prev );
498 SYS_ASSERT( first_branch, fork_db_block_not_found,
499 "block ${id} does not exist",
500 ("id", first_prev)
501 );
502 SYS_ASSERT( second_branch, fork_db_block_not_found,
503 "block ${id} does not exist",
504 ("id", second_prev)
505 );
506 }
507
508 if( first_branch && second_branch )
509 {
510 result.first.push_back(first_branch);
511 result.second.push_back(second_branch);
512 }
513 return result;
514 }
515
518 std::lock_guard g( my->mtx );
519 return my->remove_impl( id );
520 }
521
523 deque<block_id_type> remove_queue{id};
524 const auto& previdx = index.get<by_prev>();
525 const auto& head_id = head->id;
526
527 for( uint32_t i = 0; i < remove_queue.size(); ++i ) {
528 SYS_ASSERT( remove_queue[i] != head_id, fork_database_exception,
529 "removing the block and its descendants would remove the current head block" );
530
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 );
534 ++previtr;
535 }
536 }
537
538 for( const auto& block_id : remove_queue ) {
539 index.erase( block_id );
540 }
541 }
542
544 std::lock_guard g( my->mtx );
545 my->mark_valid_impl( h );
546 }
547
549 if( h->validated ) return;
550
551 auto& by_id_idx = index.get<by_block_id>();
552
553 auto itr = by_id_idx.find( h->id );
554 SYS_ASSERT( itr != by_id_idx.end(), fork_database_exception,
555 "block state not in fork database; cannot mark as valid",
556 ("id", h->id) );
557
558 by_id_idx.modify( itr, []( block_state_ptr& bsp ) {
559 bsp->validated = true;
560 } );
561
562 auto candidate = index.get<by_lib_block_num>().begin();
563 if( first_preferred( **candidate, *head ) ) {
564 head = *candidate;
565 }
566 }
567
569 std::shared_lock g( my->mtx );
570 return my->get_block_impl(id);
571 }
572
574 auto itr = index.find( id );
575 if( itr != index.end() )
576 return *itr;
577 return block_state_ptr();
578 }
579
580} }
#define SYS_THROW(exc_type, FORMAT,...)
#define SYS_ASSERT(expr, exc_type, FORMAT,...)
Definition exceptions.hpp:7
#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
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(...)
#define elog(FORMAT,...)
Definition logger.hpp:130
void unpack(Stream &s, std::deque< T > &value)
Definition raw.hpp:540
void pack(Stream &s, const std::deque< T > &value)
Definition raw.hpp:531
bool remove(const path &p)
void read_file_contents(const fc::path &filename, std::string &result)
Definition fstream.cpp:14
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
unsigned int uint32_t
Definition stdint.h:126
uint32_t value
Definition varint.hpp:17
defines the minimum state necessary to validate transaction headers
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 remove_impl(const block_id_type &id)
char * s