diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 88855fc764f4e48c2c5c7f527eceb20737b608ba..dafffdd07630b039d3f3a004b679fd387fc7936e 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -294,7 +294,9 @@ struct controller_impl { if( add_to_fork_db ) { pending->_pending_block_state->validated = true; - head = fork_db.add( pending->_pending_block_state ); + auto new_bsp = fork_db.add( pending->_pending_block_state ); + head = fork_db.head(); + FC_ASSERT( new_bsp == head, "committed block did not become the new head in fork database" ); } pending->push(); @@ -466,6 +468,7 @@ struct controller_impl { pending = db.start_undo_session(true); pending->_pending_block_state = std::make_shared( *head, when ); + pending->_pending_block_state->in_current_chain = true; try { auto onbtrx = std::make_shared( get_on_block_transaction() ); @@ -522,22 +525,28 @@ struct controller_impl { try { abort_block(); apply_block( b ); + fork_db.mark_in_current_chain( new_head, true ); fork_db.set_validity( new_head, true ); head = new_head; } catch ( const fc::exception& e ) { - fork_db.set_validity( new_head, false ); + fork_db.set_validity( new_head, false ); // Removes new_head from fork_db index, so no need to mark it as not in the current chain. throw; } - } else { + } else if( new_head->id != head->id ) { auto branches = fork_db.fetch_branch_from( new_head->id, head->id ); - while( head_block_id() != branches.second.back()->header.previous ) + for( auto itr = branches.second.begin(); itr != branches.second.end(); ++itr ) { + fork_db.mark_in_current_chain( *itr , false ); pop_block(); + } + FC_ASSERT( head_block_id() == branches.second.back()->header.previous, + "loss of sync between fork_db and chainbase during fork switch" ); // _should_ never fail for( auto ritr = branches.first.rbegin(); ritr != branches.first.rend(); ++ritr) { optional except; try { apply_block( (*ritr)->block ); + fork_db.mark_in_current_chain( *ritr, true ); } catch (const fc::exception& e) { except = e; } if (except) { @@ -549,12 +558,17 @@ struct controller_impl { } // pop all blocks from the bad fork - while( head_block_id() != branches.second.back()->header.previous ) + for( auto itr = (ritr + 1).base(); itr != branches.second.end(); ++itr ) { + fork_db.mark_in_current_chain( *itr , false ); pop_block(); + } + FC_ASSERT( head_block_id() == branches.second.back()->header.previous, + "loss of sync between fork_db and chainbase during fork switch reversal" ); // _should_ never fail // re-apply good blocks for( auto ritr = branches.second.rbegin(); ritr != branches.second.rend(); ++ritr ) { apply_block( (*ritr)->block ); + fork_db.mark_in_current_chain( *ritr, true ); } throw *except; } // end if exception @@ -646,18 +660,6 @@ struct controller_impl { }); } - /** - * This method only works for blocks within the TAPOS range, (last 65K blocks). It - * will return block_id_type() for older blocks. - */ - block_id_type get_block_id_for_num( uint32_t block_num ) { - auto sid = block_num & 0xffff; - auto id = db.get(sid).block_id; - auto num = block_header::num_from_id( id ); - if( num == block_num ) return id; - return block_id_type(); - } - void clear_expired_transactions() { //Look for expired transactions in the deduplication list, and remove them. auto& transaction_idx = db.get_mutable_index(); @@ -851,9 +853,8 @@ void controller::log_irreversible_blocks() { if( lib > 1 ) { while( log_head && log_head->block_num() < lib ) { auto lhead = log_head->block_num(); - auto blk_id = my->get_block_id_for_num( lhead + 1 ); - auto blk = my->fork_db.get_block( blk_id ); - FC_ASSERT( blk, "unable to find block state", ("id",blk_id)); + auto blk = my->fork_db.get_block_in_current_chain_by_num( lhead + 1 ); + FC_ASSERT( blk, "unable to find block state", ("block_num",lhead+1)); irreversible_block( blk ); my->blog.append( *blk->block ); my->fork_db.prune( blk ); @@ -873,8 +874,7 @@ signed_block_ptr controller::fetch_block_by_number( uint32_t block_num )const { optional b = my->blog.read_block_by_num(block_num); if( b ) return std::make_shared( move(*b) ); - auto blk_id = my->get_block_id_for_num( block_num ); - auto blk_state = my->fork_db.get_block( blk_id ); + auto blk_state = my->fork_db.get_block_in_current_chain_by_num( block_num ); if( blk_state ) return blk_state->block; return signed_block_ptr(); } diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index f7fe6b31434220ccaf38e6e9ba8dc1f2568ce868..27375c02e0c6af66056d0f7bdce089407521c983 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -22,11 +22,17 @@ namespace eosio { namespace chain { block_state_ptr, indexed_by< hashed_unique< tag, member, std::hash>, - hashed_non_unique< tag, const_mem_fun, + hashed_non_unique< tag, const_mem_fun, std::hash>, - ordered_non_unique< tag, member>, - ordered_non_unique< tag, + ordered_non_unique< tag, + composite_key< block_state, + member, + member + >, + composite_key_compare< std::less, std::greater > + >, + ordered_non_unique< tag, composite_key< block_header_state, member, member @@ -135,7 +141,7 @@ namespace eosio { namespace chain { auto first_branch = get_block(first); auto second_branch = get_block(second); - while( first_branch->block_num> second_branch->block_num ) + while( first_branch->block_num > second_branch->block_num ) { result.first.push_back(first_branch); first_branch = get_block( first_branch->header.previous ); @@ -187,34 +193,54 @@ namespace eosio { namespace chain { void fork_database::set_validity( const block_state_ptr& h, bool valid ) { if( !valid ) { - remove( h->id ); + remove( h->id ); } else { /// remove older than irreversible and mark block as valid h->validated = true; } } + + void fork_database::mark_in_current_chain( const block_state_ptr& h, bool in_current_chain ) { + if( h->in_current_chain == in_current_chain ) + return; + + auto& by_id_idx = my->index.get(); + auto itr = by_id_idx.find( h->id ); + FC_ASSERT( itr != by_id_idx.end(), "could not find block in fork database" ); + + by_id_idx.modify( itr, [&]( auto& bsp ) { // Need to modify this way rather than directly so that Boost MultiIndex can re-sort + bsp->in_current_chain = in_current_chain; + }); + } + void fork_database::prune( const block_state_ptr& h ) { auto num = h->block_num; - + auto itr = my->index.find( h->id ); if( itr != my->index.end() ) my->index.erase(itr); auto& numidx = my->index.get(); - auto nitr = numidx.find( num ); - if( nitr != numidx.end() ) { + for( auto nitr = numidx.lower_bound( num ); nitr != numidx.end() && (*nitr)->block_num == num; ++nitr ) { remove( (*nitr)->id ); } } - block_state_ptr fork_database::get_block(const block_id_type& id)const { auto itr = my->index.find( id ); - if( itr != my->index.end() ) + if( itr != my->index.end() ) return *itr; return block_state_ptr(); } - + block_state_ptr fork_database::get_block_in_current_chain_by_num( uint32_t n )const { + const auto& numidx = my->index.get(); + auto nitr = numidx.lower_bound( n ); + FC_ASSERT( nitr != numidx.end() && (*nitr)->block_num == n, + "could not find block in fork database with block number ${block_num}", ("block_num", n) ); + FC_ASSERT( (*nitr)->in_current_chain == true, + "block (with block number ${block_num}) found in fork database is not in the current chain", ("block_num", n) ); + return *nitr; + } } } /// eosio::chain diff --git a/libraries/chain/include/eosio/chain/block_state.hpp b/libraries/chain/include/eosio/chain/block_state.hpp index c13e37f50e7abaeadd572b6e89ac5546c30954b5..572ce7e29a99f6da0a4e9e8406090bb75875fba0 100644 --- a/libraries/chain/include/eosio/chain/block_state.hpp +++ b/libraries/chain/include/eosio/chain/block_state.hpp @@ -20,6 +20,7 @@ namespace eosio { namespace chain { /// weak_ptr prev_block_state.... signed_block_ptr block; bool validated = false; + bool in_current_chain = false; /// this data is redundant with the data stored in block, but facilitates /// recapturing transactions when we pop a block diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index f33f8be3fc9abeca735620e675fe57e93b453c74..b8ac8f800d04bdb0fe29209ae5839299b7bf7533 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include #include @@ -17,7 +17,7 @@ namespace eosio { namespace chain { * As new blocks are received, they are pushed into the fork database. The fork * database tracks the longest chain and the last irreversible block number. All * blocks older than the last irreversible block are freed after emitting the - * irreversible signal. + * irreversible signal. */ class fork_database { public: @@ -26,6 +26,7 @@ namespace eosio { namespace chain { ~fork_database(); block_state_ptr get_block(const block_id_type& id)const; + block_state_ptr get_block_in_current_chain_by_num( uint32_t n )const; // vector get_blocks_by_number(uint32_t n)const; /** @@ -34,11 +35,11 @@ namespace eosio { namespace chain { void set( block_state_ptr s ); /** this method will attempt to append the block to an exsting - * block_state and will return a pointer to the head block or + * block_state and will return a pointer to the new block state or * throw on error. */ - block_state_ptr add( signed_block_ptr b ); - block_state_ptr add( block_state_ptr next_block ); + block_state_ptr add( signed_block_ptr b ); + block_state_ptr add( block_state_ptr next_block ); void remove( const block_id_type& id ); const block_state_ptr& head()const; @@ -56,6 +57,7 @@ namespace eosio { namespace chain { * than the LIB are pruned after emitting irreversible signal. */ void set_validity( const block_state_ptr& h, bool valid ); + void mark_in_current_chain( const block_state_ptr& h, bool in_current_chain ); void prune( const block_state_ptr& h ); /**