From 3299adae9e95b53357cafda852588eb9d87c35ed Mon Sep 17 00:00:00 2001 From: arhag Date: Tue, 8 Jan 2019 19:19:43 -0500 Subject: [PATCH 1/4] refactor block_header_state --- libraries/chain/apply_context.cpp | 2 +- libraries/chain/block_header_state.cpp | 392 +++++++++------ libraries/chain/block_state.cpp | 34 +- libraries/chain/controller.cpp | 475 +++++++++++------- libraries/chain/fork_database.cpp | 12 +- .../include/eosio/chain/block_header.hpp | 10 +- .../eosio/chain/block_header_state.hpp | 51 +- .../chain/include/eosio/chain/block_state.hpp | 20 +- .../chain/include/eosio/chain/controller.hpp | 8 +- .../include/eosio/chain/fork_database.hpp | 4 +- libraries/chain/transaction_context.cpp | 2 +- libraries/testing/tester.cpp | 13 +- plugins/history_plugin/history_plugin.cpp | 19 +- plugins/producer_plugin/producer_plugin.cpp | 68 ++- .../test_control_plugin.cpp | 20 +- unittests/forked_tests.cpp | 14 +- unittests/producer_schedule_tests.cpp | 8 +- 17 files changed, 700 insertions(+), 452 deletions(-) diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index 1beb647eed6..550c26b8f34 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -38,7 +38,7 @@ void apply_context::exec_one( action_trace& trace ) r.act_digest = digest_type::hash(act); trace.trx_id = trx_context.id; - trace.block_num = control.pending_block_state()->block_num; + trace.block_num = control.head_block_num() + 1; trace.block_time = control.pending_block_time(); trace.producer_block_id = control.pending_producer_block_id(); trace.act = act; diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index 7189073f975..9fc95885dae 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -15,113 +15,245 @@ namespace eosio { namespace chain { return active_schedule.producers[index]; } - uint32_t block_header_state::calc_dpos_last_irreversible()const { + uint32_t block_header_state::calc_dpos_last_irreversible( account_name producer_of_next_block )const { vector blocknums; blocknums.reserve( producer_to_last_implied_irb.size() ); for( auto& i : producer_to_last_implied_irb ) { - blocknums.push_back(i.second); + blocknums.push_back( (i.first == producer_of_next_block) ? dpos_proposed_irreversible_blocknum : i.second); } /// 2/3 must be greater, so if I go 1/3 into the list sorted from low to high, then 2/3 are greater if( blocknums.size() == 0 ) return 0; - /// TODO: update to nth_element + /// TODO: update to nth_element std::sort( blocknums.begin(), blocknums.end() ); return blocknums[ (blocknums.size()-1) / 3 ]; } - /** - * Generate a template block header state for a given block time, it will not - * contain a transaction mroot, action mroot, or new_producers as those components - * are derived from chain state. - */ - block_header_state block_header_state::generate_next( block_timestamp_type when )const { - block_header_state result; - - if( when != block_timestamp_type() ) { - EOS_ASSERT( when > header.timestamp, block_validate_exception, "next block must be in the future" ); - } else { - (when = header.timestamp).slot++; - } - result.header.timestamp = when; - result.header.previous = id; - result.header.schedule_version = active_schedule.version; - - auto prokey = get_scheduled_producer(when); - result.block_signing_key = prokey.block_signing_key; - result.header.producer = prokey.producer_name; - - result.pending_schedule_lib_num = pending_schedule_lib_num; - result.pending_schedule_hash = pending_schedule_hash; - result.block_num = block_num + 1; - result.producer_to_last_produced = producer_to_last_produced; - result.producer_to_last_implied_irb = producer_to_last_implied_irb; - result.producer_to_last_produced[prokey.producer_name] = result.block_num; - result.blockroot_merkle = blockroot_merkle; - result.blockroot_merkle.append( id ); - - result.active_schedule = active_schedule; - result.pending_schedule = pending_schedule; - result.dpos_proposed_irreversible_blocknum = dpos_proposed_irreversible_blocknum; - result.bft_irreversible_blocknum = bft_irreversible_blocknum; - - result.producer_to_last_implied_irb[prokey.producer_name] = result.dpos_proposed_irreversible_blocknum; - result.dpos_irreversible_blocknum = result.calc_dpos_last_irreversible(); - - /// grow the confirmed count - static_assert(std::numeric_limits::max() >= (config::max_producers * 2 / 3) + 1, "8bit confirmations may not be able to hold all of the needed confirmations"); - - // This uses the previous block active_schedule because thats the "schedule" that signs and therefore confirms _this_ block - auto num_active_producers = active_schedule.producers.size(); - uint32_t required_confs = (uint32_t)(num_active_producers * 2 / 3) + 1; - - if( confirm_count.size() < config::maximum_tracked_dpos_confirmations ) { - result.confirm_count.reserve( confirm_count.size() + 1 ); - result.confirm_count = confirm_count; - result.confirm_count.resize( confirm_count.size() + 1 ); - result.confirm_count.back() = (uint8_t)required_confs; - } else { - result.confirm_count.resize( confirm_count.size() ); - memcpy( &result.confirm_count[0], &confirm_count[1], confirm_count.size() - 1 ); - result.confirm_count.back() = (uint8_t)required_confs; - } - - return result; - } /// generate_next - - bool block_header_state::maybe_promote_pending() { + pending_block_header_state block_header_state::next( block_timestamp_type when, + uint16_t num_prev_blocks_to_confirm )const + { + pending_block_header_state result; + + if( when != block_timestamp_type() ) { + EOS_ASSERT( when > header.timestamp, block_validate_exception, "next block must be in the future" ); + } else { + (when = header.timestamp).slot++; + } + + auto prokey = get_scheduled_producer(when); + + auto itr = producer_to_last_produced.find( prokey.producer_name ); + if( itr != producer_to_last_produced.end() ) { + EOS_ASSERT( itr->second < (block_num+1) - num_prev_blocks_to_confirm, producer_double_confirm, + "producer ${prod} double-confirming known range", + ("prod", prokey.producer_name)("num", block_num+1) + ("confirmed", num_prev_blocks_to_confirm)("last_produced", itr->second) ); + } + + result.block_num = block_num + 1; + result.previous = id; + result.timestamp = when; + result.confirmed = num_prev_blocks_to_confirm; + result.active_schedule_version = active_schedule.version; + + result.block_signing_key = prokey.block_signing_key; + result.producer = prokey.producer_name; + + result.blockroot_merkle = blockroot_merkle; + result.blockroot_merkle.append( id ); + + /// grow the confirmed count + static_assert(std::numeric_limits::max() >= (config::max_producers * 2 / 3) + 1, "8bit confirmations may not be able to hold all of the needed confirmations"); + + // This uses the previous block active_schedule because thats the "schedule" that signs and therefore confirms _this_ block + auto num_active_producers = active_schedule.producers.size(); + uint32_t required_confs = (uint32_t)(num_active_producers * 2 / 3) + 1; + + if( confirm_count.size() < config::maximum_tracked_dpos_confirmations ) { + result.confirm_count.reserve( confirm_count.size() + 1 ); + result.confirm_count = confirm_count; + result.confirm_count.resize( confirm_count.size() + 1 ); + result.confirm_count.back() = (uint8_t)required_confs; + } else { + result.confirm_count.resize( confirm_count.size() ); + memcpy( &result.confirm_count[0], &confirm_count[1], confirm_count.size() - 1 ); + result.confirm_count.back() = (uint8_t)required_confs; + } + + auto new_dpos_proposed_irreversible_blocknum = dpos_proposed_irreversible_blocknum; + + int32_t i = (int32_t)(result.confirm_count.size() - 1); + uint32_t blocks_to_confirm = num_prev_blocks_to_confirm + 1; /// confirm the head block too + while( i >= 0 && blocks_to_confirm ) { + --result.confirm_count[i]; + //idump((confirm_count[i])); + if( result.confirm_count[i] == 0 ) + { + uint32_t block_num_for_i = result.block_num - (uint32_t)(result.confirm_count.size() - 1 - i); + new_dpos_proposed_irreversible_blocknum = block_num_for_i; + //idump((dpos2_lib)(block_num)(dpos_irreversible_blocknum)); + + if (i == result.confirm_count.size() - 1) { + result.confirm_count.resize(0); + } else { + memmove( &result.confirm_count[0], &result.confirm_count[i + 1], result.confirm_count.size() - i - 1); + result.confirm_count.resize( result.confirm_count.size() - i - 1 ); + } + + break; + } + --i; + --blocks_to_confirm; + } + + result.dpos_proposed_irreversible_blocknum = new_dpos_proposed_irreversible_blocknum; + result.dpos_irreversible_blocknum = calc_dpos_last_irreversible( prokey.producer_name ); + + result.prev_pending_schedule = pending_schedule; + result.prev_pending_schedule_lib_num = pending_schedule_lib_num; + result.prev_pending_schedule_hash = pending_schedule_hash; + if( pending_schedule.producers.size() && - dpos_irreversible_blocknum >= pending_schedule_lib_num ) + result.dpos_irreversible_blocknum >= pending_schedule_lib_num ) { - active_schedule = move( pending_schedule ); + result.active_schedule = pending_schedule; flat_map new_producer_to_last_produced; - for( const auto& pro : active_schedule.producers ) { - auto existing = producer_to_last_produced.find( pro.producer_name ); - if( existing != producer_to_last_produced.end() ) { - new_producer_to_last_produced[pro.producer_name] = existing->second; + + for( const auto& pro : result.active_schedule.producers ) { + if( pro.producer_name == prokey.producer_name ) { + new_producer_to_last_produced[pro.producer_name] = result.block_num; } else { - new_producer_to_last_produced[pro.producer_name] = dpos_irreversible_blocknum; + auto existing = producer_to_last_produced.find( pro.producer_name ); + if( existing != producer_to_last_produced.end() ) { + new_producer_to_last_produced[pro.producer_name] = existing->second; + } else { + new_producer_to_last_produced[pro.producer_name] = result.dpos_irreversible_blocknum; + } } } + result.producer_to_last_produced = std::move( new_producer_to_last_produced ); + flat_map new_producer_to_last_implied_irb; - for( const auto& pro : active_schedule.producers ) { - auto existing = producer_to_last_implied_irb.find( pro.producer_name ); - if( existing != producer_to_last_implied_irb.end() ) { - new_producer_to_last_implied_irb[pro.producer_name] = existing->second; + + for( const auto& pro : result.active_schedule.producers ) { + if( pro.producer_name == prokey.producer_name ) { + new_producer_to_last_implied_irb[pro.producer_name] = dpos_proposed_irreversible_blocknum; } else { - new_producer_to_last_implied_irb[pro.producer_name] = dpos_irreversible_blocknum; + auto existing = producer_to_last_implied_irb.find( pro.producer_name ); + if( existing != producer_to_last_implied_irb.end() ) { + new_producer_to_last_implied_irb[pro.producer_name] = existing->second; + } else { + new_producer_to_last_implied_irb[pro.producer_name] = result.dpos_irreversible_blocknum; + } } } - producer_to_last_produced = move( new_producer_to_last_produced ); - producer_to_last_implied_irb = move( new_producer_to_last_implied_irb); - producer_to_last_produced[header.producer] = block_num; + result.producer_to_last_implied_irb = std::move( new_producer_to_last_implied_irb ); + + result.was_pending_promoted = true; + } else { + result.active_schedule = active_schedule; + result.producer_to_last_produced = producer_to_last_produced; + result.producer_to_last_produced[prokey.producer_name] = block_num; + result.producer_to_last_implied_irb = producer_to_last_implied_irb; + result.producer_to_last_implied_irb[prokey.producer_name] = dpos_proposed_irreversible_blocknum; + } + + return result; + } + + signed_block_header pending_block_header_state::make_block_header( const checksum256_type& transaction_mroot, + const checksum256_type& action_mroot, + optional&& new_producers )const + { + signed_block_header h; + + h.timestamp = timestamp; + h.producer = producer; + h.confirmed = confirmed; + h.previous = previous; + h.transaction_mroot = transaction_mroot; + h.action_mroot = action_mroot; + h.schedule_version = active_schedule_version; + h.new_producers = std::move(new_producers); + + return h; + } + + block_header_state pending_block_header_state::_finish_next( const signed_block_header& h )&& + { + EOS_ASSERT( h.timestamp == timestamp, block_validate_exception, "timestamp mismatch" ); + EOS_ASSERT( h.previous == previous, unlinkable_block_exception, "previous mismatch" ); + EOS_ASSERT( h.confirmed == confirmed, block_validate_exception, "confirmed mismatch" ); + EOS_ASSERT( h.producer == producer, wrong_producer, "wrong producer specified" ); + EOS_ASSERT( h.schedule_version == active_schedule_version, producer_schedule_exception, "schedule_version in signed block is corrupted" ); + + EOS_ASSERT( h.header_extensions.size() == 0, block_validate_exception, "no supported extensions" ); + + if( h.new_producers ) { + EOS_ASSERT( !was_pending_promoted, producer_schedule_exception, "cannot set pending producer schedule in the same block in which pending was promoted to active" ); + EOS_ASSERT( h.new_producers->version == active_schedule.version + 1, producer_schedule_exception, "wrong producer schedule version specified" ); + EOS_ASSERT( prev_pending_schedule.producers.size() == 0, producer_schedule_exception, + "cannot set new pending producers until last pending is confirmed" ); + } + + block_header_state result; + + result.id = h.id(); + result.block_num = block_num; + result.header = h; + + result.dpos_proposed_irreversible_blocknum = dpos_proposed_irreversible_blocknum; + result.dpos_irreversible_blocknum = dpos_irreversible_blocknum; + + if( h.new_producers ) { + result.pending_schedule = *h.new_producers; + result.pending_schedule_hash = digest_type::hash( result.pending_schedule ); + result.pending_schedule_lib_num = block_num; + } else { + if( was_pending_promoted ) { + result.pending_schedule.version = prev_pending_schedule.version; + } else { + result.pending_schedule = prev_pending_schedule; + } + result.pending_schedule_hash = std::move(prev_pending_schedule_hash); + result.pending_schedule_lib_num = prev_pending_schedule_lib_num; + } + + result.active_schedule = std::move(active_schedule); + result.blockroot_merkle = std::move(blockroot_merkle); + result.producer_to_last_produced = std::move(producer_to_last_produced); + result.producer_to_last_implied_irb = std::move(producer_to_last_implied_irb); + result.block_signing_key = std::move(block_signing_key); + result.confirm_count = std::move(confirm_count); + + return result; + } + + block_header_state pending_block_header_state::finish_next( const signed_block_header& h, + bool skip_validate_signee )&& + { + auto result = std::move(*this)._finish_next( h ); - return true; + // ASSUMPTION FROM controller_impl::apply_block = all untrusted blocks will have their signatures pre-validated here + if( !skip_validate_signee ) { + result.verify_signee( result.signee() ); } - return false; + + return result; + } + + block_header_state pending_block_header_state::finish_next( signed_block_header& h, + const std::function& signer )&& + { + auto result = std::move(*this)._finish_next( h ); + result.sign( signer ); + h.producer_signature = result.header.producer_signature; + return result; } + /* void block_header_state::set_new_producers( producer_schedule_type pending ) { EOS_ASSERT( pending.version == active_schedule.version + 1, producer_schedule_exception, "wrong producer schedule version specified" ); EOS_ASSERT( pending_schedule.producers.size() == 0, producer_schedule_exception, @@ -131,92 +263,19 @@ namespace eosio { namespace chain { pending_schedule = *header.new_producers; pending_schedule_lib_num = block_num; } - - - /** - * Transitions the current header state into the next header state given the supplied signed block header. - * - * Given a signed block header, generate the expected template based upon the header time, - * then validate that the provided header matches the template. - * - * If the header specifies new_producers then apply them accordingly. */ - block_header_state block_header_state::next( const signed_block_header& h, bool skip_validate_signee )const { - EOS_ASSERT( h.timestamp != block_timestamp_type(), block_validate_exception, "", ("h",h) ); - EOS_ASSERT( h.header_extensions.size() == 0, block_validate_exception, "no supported extensions" ); - - EOS_ASSERT( h.timestamp > header.timestamp, block_validate_exception, "block must be later in time" ); - EOS_ASSERT( h.previous == id, unlinkable_block_exception, "block must link to current state" ); - auto result = generate_next( h.timestamp ); - EOS_ASSERT( result.header.producer == h.producer, wrong_producer, "wrong producer specified" ); - EOS_ASSERT( result.header.schedule_version == h.schedule_version, producer_schedule_exception, "schedule_version in signed block is corrupted" ); - - auto itr = producer_to_last_produced.find(h.producer); - if( itr != producer_to_last_produced.end() ) { - EOS_ASSERT( itr->second < result.block_num - h.confirmed, producer_double_confirm, "producer ${prod} double-confirming known range", ("prod", h.producer) ); - } - - // FC_ASSERT( result.header.block_mroot == h.block_mroot, "mismatch block merkle root" ); - - /// below this point is state changes that cannot be validated with headers alone, but never-the-less, - /// must result in header state changes - - result.set_confirmed( h.confirmed ); - - auto was_pending_promoted = result.maybe_promote_pending(); - - if( h.new_producers ) { - EOS_ASSERT( !was_pending_promoted, producer_schedule_exception, "cannot set pending producer schedule in the same block in which pending was promoted to active" ); - result.set_new_producers( *h.new_producers ); - } - - result.header.action_mroot = h.action_mroot; - result.header.transaction_mroot = h.transaction_mroot; - result.header.producer_signature = h.producer_signature; - result.id = result.header.id(); - - // ASSUMPTION FROM controller_impl::apply_block = all untrusted blocks will have their signatures pre-validated here - if( !skip_validate_signee ) { - result.verify_signee( result.signee() ); - } - - return result; - } /// next - - void block_header_state::set_confirmed( uint16_t num_prev_blocks ) { - /* - idump((num_prev_blocks)(confirm_count.size())); - - for( uint32_t i = 0; i < confirm_count.size(); ++i ) { - std::cerr << "confirm_count["<= 0 && blocks_to_confirm ) { - --confirm_count[i]; - //idump((confirm_count[i])); - if( confirm_count[i] == 0 ) - { - uint32_t block_num_for_i = block_num - (uint32_t)(confirm_count.size() - 1 - i); - dpos_proposed_irreversible_blocknum = block_num_for_i; - //idump((dpos2_lib)(block_num)(dpos_irreversible_blocknum)); - - if (i == confirm_count.size() - 1) { - confirm_count.resize(0); - } else { - memmove( &confirm_count[0], &confirm_count[i + 1], confirm_count.size() - i - 1); - confirm_count.resize( confirm_count.size() - i - 1 ); - } - - return; - } - --i; - --blocks_to_confirm; - } - } + + /** + * Transitions the current header state into the next header state given the supplied signed block header. + * + * Given a signed block header, generate the expected template based upon the header time, + * then validate that the provided header matches the template. + * + * If the header specifies new_producers then apply them accordingly. + */ + block_header_state block_header_state::next( const signed_block_header& h, bool skip_validate_signee )const { + return next( h.timestamp, h.confirmed ).finish_next( h, skip_validate_signee ); + } digest_type block_header_state::sig_digest()const { auto header_bmroot = digest_type::hash( std::make_pair( header.digest(), blockroot_merkle.get_root() ) ); @@ -238,6 +297,7 @@ namespace eosio { namespace chain { ("block_signing_key", block_signing_key)( "signee", signee ) ); } + /* void block_header_state::add_confirmation( const header_confirmation& conf ) { for( const auto& c : confirmations ) EOS_ASSERT( c.producer != conf.producer, producer_double_confirm, "block already confirmed by this producer" ); @@ -249,6 +309,6 @@ namespace eosio { namespace chain { confirmations.emplace_back( conf ); } - + */ } } /// namespace eosio::chain diff --git a/libraries/chain/block_state.cpp b/libraries/chain/block_state.cpp index b4834775951..ecd7fceefc8 100644 --- a/libraries/chain/block_state.cpp +++ b/libraries/chain/block_state.cpp @@ -3,17 +3,33 @@ namespace eosio { namespace chain { - block_state::block_state( const block_header_state& prev, block_timestamp_type when ) - :block_header_state( prev.generate_next( when ) ), - block( std::make_shared() ) - { - static_cast(*block) = header; - } + block_state::block_state( const block_header_state& prev, + signed_block_ptr b, + bool skip_validate_signee + ) + :block_header_state( prev.next( *b, skip_validate_signee ) ) + ,block( std::move(b) ) + {} - block_state::block_state( const block_header_state& prev, signed_block_ptr b, bool skip_validate_signee ) - :block_header_state( prev.next( *b, skip_validate_signee )), block( move(b) ) - { } + block_state::block_state( pending_block_header_state&& cur, + signed_block_ptr&& b, + vector&& trx_metas, + const std::function& signer + ) + :block_header_state( std::move(cur).finish_next( *b, signer ) ) + ,block( std::move(b) ) + ,trxs( std::move(trx_metas) ) + {} + block_state::block_state( pending_block_header_state&& cur, + const signed_block_ptr& b, + vector&& trx_metas, + bool skip_validate_signee + ) + :block_header_state( std::move(cur).finish_next( *b, skip_validate_signee ) ) + ,block( b ) + ,trxs( std::move(trx_metas) ) + {} } } /// eosio::chain diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 98b2065d1e1..2a3071b5752 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -94,19 +94,69 @@ class maybe_session { optional _session; }; +struct building_block { + building_block( const block_header_state& prev, block_timestamp_type when, uint16_t num_prev_blocks_to_confirm ) + :_pending_block_header_state( prev.next( when, num_prev_blocks_to_confirm ) ) + {} + + pending_block_header_state _pending_block_header_state; + optional _new_pending_producer_schedule; + vector _pending_trx_metas; + vector _pending_trx_receipts; + vector _actions; +}; + +struct assembled_block { + pending_block_header_state _pending_block_header_state; + vector _trx_metas; + signed_block_ptr _unsigned_block; +}; + +struct completed_block { + block_state_ptr _block_state; +}; + +using block_stage_type = fc::static_variant; + struct pending_state { - pending_state( maybe_session&& s ) - :_db_session( move(s) ){} + pending_state( maybe_session&& s, const block_header_state& prev, + block_timestamp_type when, uint16_t num_prev_blocks_to_confirm ) + :_db_session( move(s) ) + ,_block_stage( building_block( prev, when, num_prev_blocks_to_confirm ) ) + {} maybe_session _db_session; + block_stage_type _block_stage; + controller::block_status _block_status = controller::block_status::incomplete; + optional _producer_block_id; - block_state_ptr _pending_block_state; + /** @pre _block_stage cannot hold completed_block alternative */ + const pending_block_header_state& get_pending_block_header_state()const { + if( _block_stage.contains() ) + return _block_stage.get()._pending_block_header_state; - vector _actions; + return _block_stage.get()._pending_block_header_state; + } - controller::block_status _block_status = controller::block_status::incomplete; + const vector& get_trx_receipts()const { + if( _block_stage.contains() ) + return _block_stage.get()._pending_trx_receipts; - optional _producer_block_id; + if( _block_stage.contains() ) + return _block_stage.get()._unsigned_block->transactions; + + return _block_stage.get()._block_state->block->transactions; + } + + const vector& get_trx_metas()const { + if( _block_stage.contains() ) + return _block_stage.get()._pending_trx_metas; + + if( _block_stage.contains() ) + return _block_stage.get()._trx_metas; + + return _block_stage.get()._block_state->trxs; + } void push() { _db_session.push(); @@ -534,7 +584,8 @@ struct controller_impl { block_header_state head_header_state; section.read_row(head_header_state, db); - auto head_state = std::make_shared(head_header_state); + auto head_state = std::make_shared(); + static_cast(*head_state) = head_header_state; fork_db.set(head_state); fork_db.set_validity(head_state, true); fork_db.mark_in_current_chain(head_state, true); @@ -594,7 +645,8 @@ struct controller_impl { genheader.id = genheader.header.id(); genheader.block_num = genheader.header.block_num(); - head = std::make_shared( genheader ); + head = std::make_shared(); + static_cast(*head) = genheader; head->block = std::make_shared(genheader.header); fork_db.set( head ); db.set_revision( head->block_num ); @@ -673,58 +725,22 @@ struct controller_impl { conf.genesis.initial_timestamp ); } - - - /** - * @post regardless of the success of commit block there is no active pending block - */ - void commit_block( bool add_to_fork_db ) { - auto reset_pending_on_exit = fc::make_scoped_exit([this]{ - pending.reset(); - }); - - try { - if (add_to_fork_db) { - pending->_pending_block_state->validated = true; - auto new_bsp = fork_db.add(pending->_pending_block_state, true); - emit(self.accepted_block_header, pending->_pending_block_state); - head = fork_db.head(); - EOS_ASSERT(new_bsp == head, fork_database_exception, "committed block did not become the new head in fork database"); - } - - if( !replaying ) { - reversible_blocks.create( [&]( auto& ubo ) { - ubo.blocknum = pending->_pending_block_state->block_num; - ubo.set_block( pending->_pending_block_state->block ); - }); - } - - emit( self.accepted_block, pending->_pending_block_state ); - } catch (...) { - // dont bother resetting pending, instead abort the block - reset_pending_on_exit.cancel(); - abort_block(); - throw; - } - - // push the state for pending. - pending->push(); - } - // The returned scoped_exit should not exceed the lifetime of the pending which existed when make_block_restore_point was called. fc::scoped_exit> make_block_restore_point() { - auto orig_block_transactions_size = pending->_pending_block_state->block->transactions.size(); - auto orig_state_transactions_size = pending->_pending_block_state->trxs.size(); - auto orig_state_actions_size = pending->_actions.size(); + auto& bb = pending->_block_stage.get(); + auto orig_block_transactions_size = bb._pending_trx_receipts.size(); + auto orig_state_transactions_size = bb._pending_trx_metas.size(); + auto orig_state_actions_size = bb._actions.size(); std::function callback = [this, orig_block_transactions_size, orig_state_transactions_size, orig_state_actions_size]() { - pending->_pending_block_state->block->transactions.resize(orig_block_transactions_size); - pending->_pending_block_state->trxs.resize(orig_state_transactions_size); - pending->_actions.resize(orig_state_actions_size); + auto& bb = pending->_block_stage.get(); + bb._pending_trx_receipts.resize(orig_block_transactions_size); + bb._pending_trx_metas.resize(orig_state_transactions_size); + bb._actions.resize(orig_state_actions_size); }; return fc::make_scoped_exit( std::move(callback) ); @@ -762,7 +778,7 @@ struct controller_impl { auto restore = make_block_restore_point(); trace->receipt = push_receipt( gtrx.trx_id, transaction_receipt::soft_fail, trx_context.billed_cpu_time_us, trace->net_usage ); - fc::move_append( pending->_actions, move(trx_context.executed) ); + fc::move_append( pending->_block_stage.get()._actions, move(trx_context.executed) ); trx_context.squash(); restore.cancel(); @@ -846,7 +862,7 @@ struct controller_impl { if( gtrx.expiration < self.pending_block_time() ) { trace = std::make_shared(); trace->id = gtrx.trx_id; - trace->block_num = self.pending_block_state()->block_num; + trace->block_num = self.head_block_num() + 1; trace->block_time = self.pending_block_time(); trace->producer_block_id = self.pending_producer_block_id(); trace->scheduled = true; @@ -888,7 +904,7 @@ struct controller_impl { trx_context.billed_cpu_time_us, trace->net_usage ); - fc::move_append( pending->_actions, move(trx_context.executed) ); + fc::move_append( pending->_block_stage.get()._actions, move(trx_context.executed) ); emit( self.accepted_transaction, trx ); emit( self.applied_transaction, trace ); @@ -976,8 +992,9 @@ struct controller_impl { uint64_t cpu_usage_us, uint64_t net_usage ) { uint64_t net_usage_words = net_usage / 8; EOS_ASSERT( net_usage_words*8 == net_usage, transaction_exception, "net_usage is not divisible by 8" ); - pending->_pending_block_state->block->transactions.emplace_back( trx ); - transaction_receipt& r = pending->_pending_block_state->block->transactions.back(); + auto& receipts = pending->_block_stage.get()._pending_trx_receipts; + receipts.emplace_back( trx ); + transaction_receipt& r = receipts.back(); r.cpu_usage_us = cpu_usage_us; r.net_usage_words = net_usage_words; r.status = status; @@ -1053,7 +1070,7 @@ struct controller_impl { ? transaction_receipt::executed : transaction_receipt::delayed; trace->receipt = push_receipt(*trx->packed_trx, s, trx_context.billed_cpu_time_us, trace->net_usage); - pending->_pending_block_state->trxs.emplace_back(trx); + pending->_block_stage.get()._pending_trx_metas.emplace_back(trx); } else { transaction_receipt_header r; r.status = transaction_receipt::executed; @@ -1062,7 +1079,7 @@ struct controller_impl { trace->receipt = r; } - fc::move_append(pending->_actions, move(trx_context.executed)); + fc::move_append(pending->_block_stage.get()._actions, move(trx_context.executed)); // call the accept signal but only once for this transaction if (!trx->accepted) { @@ -1115,44 +1132,43 @@ struct controller_impl { EOS_ASSERT( db.revision() == head->block_num, database_exception, "db revision is not on par with head block", ("db.revision()", db.revision())("controller_head_block", head->block_num)("fork_db_head_block", fork_db.head()->block_num) ); - pending.emplace(maybe_session(db)); + pending.emplace( maybe_session(db), *head, when, confirm_block_count ); } else { - pending.emplace(maybe_session()); + pending.emplace( maybe_session(), *head, when, confirm_block_count ); } pending->_block_status = s; pending->_producer_block_id = producer_block_id; - pending->_pending_block_state = std::make_shared( *head, when ); // promotes pending schedule (if any) to active - pending->_pending_block_state->in_current_chain = true; - pending->_pending_block_state->set_confirmed(confirm_block_count); - - auto was_pending_promoted = pending->_pending_block_state->maybe_promote_pending(); + const auto& pbhs = pending->get_pending_block_header_state(); //modify state in speculative block only if we are speculative reads mode (other wise we need clean state for head or irreversible reads) - if ( read_mode == db_read_mode::SPECULATIVE || pending->_block_status != controller::block_status::incomplete ) { - + if ( read_mode == db_read_mode::SPECULATIVE || pending->_block_status != controller::block_status::incomplete ) + { const auto& gpo = db.get(); if( gpo.proposed_schedule_block_num.valid() && // if there is a proposed schedule that was proposed in a block ... - ( *gpo.proposed_schedule_block_num <= pending->_pending_block_state->dpos_irreversible_blocknum ) && // ... that has now become irreversible ... - pending->_pending_block_state->pending_schedule.producers.size() == 0 && // ... and there is room for a new pending schedule ... - !was_pending_promoted // ... and not just because it was promoted to active at the start of this block, then: + ( *gpo.proposed_schedule_block_num <= pbhs.dpos_irreversible_blocknum ) && // ... that has now become irreversible ... + pbhs.prev_pending_schedule.producers.size() == 0 // ... and there was room for a new pending schedule prior to any possible promotion ) - { - // Promote proposed schedule to pending schedule. - if( !replaying ) { - ilog( "promoting proposed schedule (set in block ${proposed_num}) to pending; current block: ${n} lib: ${lib} schedule: ${schedule} ", - ("proposed_num", *gpo.proposed_schedule_block_num)("n", pending->_pending_block_state->block_num) - ("lib", pending->_pending_block_state->dpos_irreversible_blocknum) - ("schedule", static_cast(gpo.proposed_schedule) ) ); - } - pending->_pending_block_state->set_new_producers( gpo.proposed_schedule ); - db.modify( gpo, [&]( auto& gp ) { - gp.proposed_schedule_block_num = optional(); - gp.proposed_schedule.clear(); - }); + { + // Promote proposed schedule to pending schedule. + if( !replaying ) { + ilog( "promoting proposed schedule (set in block ${proposed_num}) to pending; current block: ${n} lib: ${lib} schedule: ${schedule} ", + ("proposed_num", *gpo.proposed_schedule_block_num)("n", pbhs.block_num) + ("lib", pbhs.dpos_irreversible_blocknum) + ("schedule", static_cast(gpo.proposed_schedule) ) ); } + EOS_ASSERT( gpo.proposed_schedule.version == pbhs.active_schedule_version + 1, + producer_schedule_exception, "wrong producer schedule version specified" ); + + pending->_block_stage.get()._new_pending_producer_schedule = gpo.proposed_schedule; + db.modify( gpo, [&]( auto& gp ) { + gp.proposed_schedule_block_num = optional(); + gp.proposed_schedule.clear(); + }); + } + try { auto onbtrx = std::make_shared( get_on_block_transaction() ); onbtrx->implicit = true; @@ -1175,17 +1191,106 @@ struct controller_impl { } guard_pending.cancel(); - } // start_block + } /// start_block + signed_block_ptr finalize_block() + { + EOS_ASSERT( pending, block_validate_exception, "it is not valid to finalize when there is no pending block"); + EOS_ASSERT( pending->_block_stage.contains(), block_validate_exception, "already called finalize_block"); + try { - void sign_block( const std::function& signer_callback ) { - auto p = pending->_pending_block_state; + auto& pbhs = pending->get_pending_block_header_state(); - p->sign( signer_callback ); + // Update resource limits: + resource_limits.process_account_limit_updates(); + const auto& chain_config = self.get_global_properties().configuration; + uint32_t max_virtual_mult = 1000; + uint64_t CPU_TARGET = EOS_PERCENT(chain_config.max_block_cpu_usage, chain_config.target_block_cpu_usage_pct); + resource_limits.set_block_parameters( + { CPU_TARGET, chain_config.max_block_cpu_usage, config::block_cpu_usage_average_window_ms / config::block_interval_ms, max_virtual_mult, {99, 100}, {1000, 999}}, + {EOS_PERCENT(chain_config.max_block_net_usage, chain_config.target_block_net_usage_pct), chain_config.max_block_net_usage, config::block_size_average_window_ms / config::block_interval_ms, max_virtual_mult, {99, 100}, {1000, 999}} + ); + resource_limits.process_block_usage(pbhs.block_num); + + auto& bb = pending->_block_stage.get(); + + // Create (unsigned) block: + auto block_ptr = std::make_shared( pbhs.make_block_header( + calculate_trx_merkle(), + calculate_action_merkle(), + std::move( bb._new_pending_producer_schedule ) + ) ); - static_cast(*p->block) = p->header; - } /// sign_block + block_ptr->transactions = std::move( bb._pending_trx_receipts ); + + // Update TaPoS table: + create_block_summary( block_ptr->id() ); + + /* + ilog( "finalized block ${n} (${id}) at ${t} by ${p} (${signing_key}); schedule_version: ${v} lib: ${lib} #dtrxs: ${ndtrxs} ${np}", + ("n",pbhs.block_num) + ("id",block_ptr->id()) + ("t",pbhs.timestamp) + ("p",pbhs.producer) + ("signing_key", pbhs.block_signing_key) + ("v",pbhs.active_schedule_version) + ("lib",pbhs.dpos_irreversible_blocknum) + ("ndtrxs",db.get_index().size()) + ("np",block_ptr->new_producers) + ); + */ + + pending->_block_stage = assembled_block{ + std::move( bb._pending_block_header_state ), + std::move( bb._pending_trx_metas ), + block_ptr + }; + + return block_ptr; + } FC_CAPTURE_AND_RETHROW() } /// finalize_block + + /** + * @post regardless of the success of commit block there is no active pending block + */ + void commit_block( bool add_to_fork_db ) { + auto reset_pending_on_exit = fc::make_scoped_exit([this]{ + pending.reset(); + }); + + try { + EOS_ASSERT( pending->_block_stage.contains(), block_validate_exception, + "cannot call commit_block until pending block is completed" ); + + auto bsp = pending->_block_stage.get()._block_state; + + if (add_to_fork_db) { + bsp->in_current_chain = true; + bsp->validated = true; + auto new_bsp = fork_db.add(bsp, true); + emit(self.accepted_block_header, bsp); + head = fork_db.head(); + EOS_ASSERT(new_bsp == head, fork_database_exception, "committed block did not become the new head in fork database"); + } + + if( !replaying ) { + reversible_blocks.create( [&]( auto& ubo ) { + ubo.blocknum = bsp->block_num; + ubo.set_block( bsp->block ); + }); + } + + emit( self.accepted_block, bsp ); + } catch (...) { + // dont bother resetting pending, instead abort the block + reset_pending_on_exit.cancel(); + abort_block(); + throw; + } + + // push the state for pending. + pending->push(); + } void apply_block( const signed_block_ptr& b, controller::block_status s ) { try { try { @@ -1210,7 +1315,8 @@ struct controller_impl { size_t packed_idx = 0; for( const auto& receipt : b->transactions ) { - auto num_pending_receipts = pending->_pending_block_state->block->transactions.size(); + const auto& trx_receipts = pending->_block_stage.get()._pending_trx_receipts; + auto num_pending_receipts = trx_receipts.size(); if( receipt.trx.contains() ) { trace = push_transaction( packed_transactions.at(packed_idx++), fc::time_point::maximum(), receipt.cpu_usage_us, true ); } else if( receipt.trx.contains() ) { @@ -1226,36 +1332,37 @@ struct controller_impl { throw *trace->except; } - EOS_ASSERT( pending->_pending_block_state->block->transactions.size() > 0, + EOS_ASSERT( trx_receipts.size() > 0, block_validate_exception, "expected a receipt", ("block", *b)("expected_receipt", receipt) ); - EOS_ASSERT( pending->_pending_block_state->block->transactions.size() == num_pending_receipts + 1, + EOS_ASSERT( trx_receipts.size() == num_pending_receipts + 1, block_validate_exception, "expected receipt was not added", ("block", *b)("expected_receipt", receipt) ); - const transaction_receipt_header& r = pending->_pending_block_state->block->transactions.back(); + const transaction_receipt_header& r = trx_receipts.back(); EOS_ASSERT( r == static_cast(receipt), block_validate_exception, "receipt does not match", - ("producer_receipt", receipt)("validator_receipt", pending->_pending_block_state->block->transactions.back()) ); + ("producer_receipt", receipt)("validator_receipt", trx_receipts.back()) ); } - finalize_block(); + auto block_ptr = finalize_block(); // this implicitly asserts that all header fields (less the signature) are identical - EOS_ASSERT(producer_block_id == pending->_pending_block_state->header.id(), - block_validate_exception, "Block ID does not match", - ("producer_block_id",producer_block_id)("validator_block_id",pending->_pending_block_state->header.id())); + auto id = block_ptr->id(); + EOS_ASSERT( producer_block_id == id, block_validate_exception, "Block ID does not match", + ("producer_block_id",producer_block_id)("validator_block_id",id) ); - // We need to fill out the pending block state's block because that gets serialized in the reversible block log - // in the future we can optimize this by serializing the original and not the copy + auto&& ab = pending->_block_stage.get(); - // we can always trust this signature because, - // - prior to apply_block, we call fork_db.add which does a signature check IFF the block is untrusted - // - OTHERWISE the block is trusted and therefore we trust that the signature is valid - // Also, as ::sign_block does not lazily calculate the digest of the block, we can just short-circuit to save cycles - pending->_pending_block_state->header.producer_signature = b->producer_signature; - static_cast(*pending->_pending_block_state->block) = pending->_pending_block_state->header; + auto bsp = std::make_shared( + std::move( ab._pending_block_header_state ), + b, + std::move( ab._trx_metas ), + true // signature should have already been verified (assuming untrusted) prior to apply_block + ); + + pending->_block_stage = completed_block{ bsp }; commit_block(false); return; @@ -1404,7 +1511,7 @@ struct controller_impl { void abort_block() { if( pending ) { if ( read_mode == db_read_mode::SPECULATIVE ) { - for( const auto& t : pending->_pending_block_state->trxs ) + for( const auto& t : pending->get_trx_metas() ) unapplied_transactions[t->signed_id] = t; } pending.reset(); @@ -1416,69 +1523,28 @@ struct controller_impl { return false; } - void set_action_merkle() { + checksum256_type calculate_action_merkle() { vector action_digests; - action_digests.reserve( pending->_actions.size() ); - for( const auto& a : pending->_actions ) + const auto& actions = pending->_block_stage.get()._actions; + action_digests.reserve( actions.size() ); + for( const auto& a : actions ) action_digests.emplace_back( a.digest() ); - pending->_pending_block_state->header.action_mroot = merkle( move(action_digests) ); + return merkle( move(action_digests) ); } - void set_trx_merkle() { + checksum256_type calculate_trx_merkle() { vector trx_digests; - const auto& trxs = pending->_pending_block_state->block->transactions; + const auto& trxs = pending->_block_stage.get()._pending_trx_receipts; trx_digests.reserve( trxs.size() ); for( const auto& a : trxs ) trx_digests.emplace_back( a.digest() ); - pending->_pending_block_state->header.transaction_mroot = merkle( move(trx_digests) ); + return merkle( move(trx_digests) ); } - - void finalize_block() - { - EOS_ASSERT(pending, block_validate_exception, "it is not valid to finalize when there is no pending block"); - try { - - - /* - ilog( "finalize block ${n} (${id}) at ${t} by ${p} (${signing_key}); schedule_version: ${v} lib: ${lib} #dtrxs: ${ndtrxs} ${np}", - ("n",pending->_pending_block_state->block_num) - ("id",pending->_pending_block_state->header.id()) - ("t",pending->_pending_block_state->header.timestamp) - ("p",pending->_pending_block_state->header.producer) - ("signing_key", pending->_pending_block_state->block_signing_key) - ("v",pending->_pending_block_state->header.schedule_version) - ("lib",pending->_pending_block_state->dpos_irreversible_blocknum) - ("ndtrxs",db.get_index().size()) - ("np",pending->_pending_block_state->header.new_producers) - ); - */ - - // Update resource limits: - resource_limits.process_account_limit_updates(); - const auto& chain_config = self.get_global_properties().configuration; - uint32_t max_virtual_mult = 1000; - uint64_t CPU_TARGET = EOS_PERCENT(chain_config.max_block_cpu_usage, chain_config.target_block_cpu_usage_pct); - resource_limits.set_block_parameters( - { CPU_TARGET, chain_config.max_block_cpu_usage, config::block_cpu_usage_average_window_ms / config::block_interval_ms, max_virtual_mult, {99, 100}, {1000, 999}}, - {EOS_PERCENT(chain_config.max_block_net_usage, chain_config.target_block_net_usage_pct), chain_config.max_block_net_usage, config::block_size_average_window_ms / config::block_interval_ms, max_virtual_mult, {99, 100}, {1000, 999}} - ); - resource_limits.process_block_usage(pending->_pending_block_state->block_num); - - set_action_merkle(); - set_trx_merkle(); - - auto p = pending->_pending_block_state; - p->id = p->header.id(); - - create_block_summary(p->id); - - } FC_CAPTURE_AND_RETHROW() } - void update_producers_authority() { - const auto& producers = pending->_pending_block_state->active_schedule.producers; + const auto& producers = pending->get_pending_block_header_state().active_schedule.producers; auto update_permission = [&]( auto& permission, auto threshold ) { auto auth = authority( threshold, {}, {}); @@ -1753,13 +1819,23 @@ void controller::start_block( block_timestamp_type when, uint16_t confirm_block_ my->start_block(when, confirm_block_count, block_status::incomplete, optional() ); } -void controller::finalize_block() { +block_state_ptr controller::finalize_block( const std::function& signer_callback ) { validate_db_available_size(); - my->finalize_block(); -} -void controller::sign_block( const std::function& signer_callback ) { - my->sign_block( signer_callback ); + auto block_ptr = my->finalize_block(); + + auto& ab = my->pending->_block_stage.get(); + + auto bsp = std::make_shared( + std::move( ab._pending_block_header_state ), + std::move( block_ptr ), + std::move( ab._trx_metas ), + signer_callback + ); + + my->pending->_block_stage = completed_block{ bsp }; + + return bsp; } void controller::commit_block() { @@ -1876,13 +1952,31 @@ account_name controller::fork_db_head_block_producer()const { return my->fork_db.head()->header.producer; } -block_state_ptr controller::pending_block_state()const { - if( my->pending ) return my->pending->_pending_block_state; - return block_state_ptr(); -} time_point controller::pending_block_time()const { EOS_ASSERT( my->pending, block_validate_exception, "no pending block" ); - return my->pending->_pending_block_state->header.timestamp; + + if( my->pending->_block_stage.contains() ) + return my->pending->_block_stage.get()._block_state->header.timestamp; + + return my->pending->get_pending_block_header_state().timestamp; +} + +account_name controller::pending_block_producer()const { + EOS_ASSERT( my->pending, block_validate_exception, "no pending block" ); + + if( my->pending->_block_stage.contains() ) + return my->pending->_block_stage.get()._block_state->header.producer; + + return my->pending->get_pending_block_header_state().producer; +} + +public_key_type controller::pending_block_signing_key()const { + EOS_ASSERT( my->pending, block_validate_exception, "no pending block" ); + + if( my->pending->_block_stage.contains() ) + return my->pending->_block_stage.get()._block_state->block_signing_key; + + return my->pending->get_pending_block_header_state().block_signing_key; } optional controller::pending_producer_block_id()const { @@ -1890,6 +1984,11 @@ optional controller::pending_producer_block_id()const { return my->pending->_producer_block_id; } +const vector& controller::get_pending_trx_receipts()const { + EOS_ASSERT( my->pending, block_validate_exception, "no pending block" ); + return my->pending->get_trx_receipts(); +} + uint32_t controller::last_irreversible_block_num() const { return std::max(std::max(my->head->bft_irreversible_blocknum, my->head->dpos_irreversible_blocknum), my->snapshot_head_block); } @@ -1984,13 +2083,14 @@ int64_t controller::set_proposed_producers( vector producers ) { decltype(sch.producers.cend()) end; decltype(end) begin; - if( my->pending->_pending_block_state->pending_schedule.producers.size() == 0 ) { - const auto& active_sch = my->pending->_pending_block_state->active_schedule; + const auto& pending_sch = pending_producers(); + + if( pending_sch.producers.size() == 0 ) { + const auto& active_sch = active_producers(); begin = active_sch.producers.begin(); end = active_sch.producers.end(); sch.version = active_sch.version + 1; } else { - const auto& pending_sch = my->pending->_pending_block_state->pending_schedule; begin = pending_sch.producers.begin(); end = pending_sch.producers.end(); sch.version = pending_sch.version + 1; @@ -2003,6 +2103,8 @@ int64_t controller::set_proposed_producers( vector producers ) { int64_t version = sch.version; + wlog( "proposed producer schedule with version ${v}", ("v", version) ); + my->db.modify( gpo, [&]( auto& gp ) { gp.proposed_schedule_block_num = cur_block_num; gp.proposed_schedule = std::move(sch); @@ -2011,15 +2113,34 @@ int64_t controller::set_proposed_producers( vector producers ) { } const producer_schedule_type& controller::active_producers()const { - if ( !(my->pending) ) + if( !(my->pending) ) return my->head->active_schedule; - return my->pending->_pending_block_state->active_schedule; + + if( my->pending->_block_stage.contains() ) + return my->pending->_block_stage.get()._block_state->active_schedule; + + return my->pending->get_pending_block_header_state().active_schedule; } const producer_schedule_type& controller::pending_producers()const { - if ( !(my->pending) ) + if( !(my->pending) ) return my->head->pending_schedule; - return my->pending->_pending_block_state->pending_schedule; + + if( my->pending->_block_stage.contains() ) + return my->pending->_block_stage.get()._block_state->pending_schedule; + + if( my->pending->_block_stage.contains() ) { + const auto& np = my->pending->_block_stage.get()._unsigned_block->new_producers; + if( np ) + return *np; + } + + const auto& bb = my->pending->_block_stage.get(); + + if( bb._new_pending_producer_schedule ) + return *bb._new_pending_producer_schedule; + + return bb._pending_block_header_state.prev_pending_schedule; } optional controller::proposed_producers()const { @@ -2163,6 +2284,10 @@ void controller::check_key_list( const public_key_type& key )const { my->check_key_list( key ); } +bool controller::is_building_block()const { + return my->pending.valid(); +} + bool controller::is_producing_block()const { if( !my->pending ) return false; diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 52b49fff449..1a6518cfa1b 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -110,7 +110,7 @@ namespace eosio { namespace chain { void fork_database::set( block_state_ptr s ) { auto result = my->index.insert( s ); - EOS_ASSERT( s->id == s->header.id(), fork_database_exception, + EOS_ASSERT( s->id == s->header.id(), fork_database_exception, "block state id (${id}) is different from block state header id (${hid})", ("id", string(s->id))("hid", string(s->header.id())) ); //FC_ASSERT( s->block_num == s->header.block_num() ); @@ -196,8 +196,8 @@ namespace eosio { namespace chain { result.second.push_back(second_branch); first_branch = get_block( first_branch->header.previous ); second_branch = get_block( second_branch->header.previous ); - EOS_ASSERT( first_branch && second_branch, fork_db_block_not_found, - "either block ${fid} or ${sid} does not exist", + EOS_ASSERT( first_branch && second_branch, fork_db_block_not_found, + "either block ${fid} or ${sid} does not exist", ("fid", string(first_branch->header.previous))("sid", string(second_branch->header.previous)) ); } @@ -297,6 +297,7 @@ namespace eosio { namespace chain { return *nitr; } + /* void fork_database::add( const header_confirmation& c ) { auto b = get_block( c.block_id ); EOS_ASSERT( b, fork_db_block_not_found, "unable to find block id ${id}", ("id",c.block_id)); @@ -307,7 +308,8 @@ namespace eosio { namespace chain { set_bft_irreversible( c.block_id ); } } - + */ + /** * This method will set this block as being BFT irreversible and will update * all blocks which build off of it to have the same bft_irb if their existing @@ -315,6 +317,7 @@ namespace eosio { namespace chain { * * This will require a search over all forks */ +#if 0 void fork_database::set_bft_irreversible( block_id_type id ) { auto& idx = my->index.get(); auto itr = idx.find(id); @@ -354,5 +357,6 @@ namespace eosio { namespace chain { queue = update( queue ); } } +#endif } } /// eosio::chain diff --git a/libraries/chain/include/eosio/chain/block_header.hpp b/libraries/chain/include/eosio/chain/block_header.hpp index bf9cf0bedb8..53de64eba67 100644 --- a/libraries/chain/include/eosio/chain/block_header.hpp +++ b/libraries/chain/include/eosio/chain/block_header.hpp @@ -10,15 +10,15 @@ namespace eosio { namespace chain { account_name producer; /** - * By signing this block this producer is confirming blocks [block_num() - confirmed, blocknum()) + * By signing this block this producer is confirming blocks [block_num() - confirmed, blocknum()) * as being the best blocks for that range and that he has not signed any other - * statements that would contradict. + * statements that would contradict. * * No producer should sign a block with overlapping ranges or it is proof of byzantine * behavior. When producing a block a producer is always confirming at least the block he * is building off of. A producer cannot confirm "this" block, only prior blocks. */ - uint16_t confirmed = 1; + uint16_t confirmed = 1; block_id_type previous; @@ -35,6 +35,8 @@ namespace eosio { namespace chain { extensions_type header_extensions; + block_header() = default; + digest_type digest()const; block_id_type id() const; uint32_t block_num() const { return num_from_id(previous) + 1; } @@ -55,7 +57,7 @@ namespace eosio { namespace chain { } } /// namespace eosio::chain -FC_REFLECT(eosio::chain::block_header, +FC_REFLECT(eosio::chain::block_header, (timestamp)(producer)(confirmed)(previous) (transaction_mroot)(action_mroot) (schedule_version)(new_producers)(header_extensions)) diff --git a/libraries/chain/include/eosio/chain/block_header_state.hpp b/libraries/chain/include/eosio/chain/block_header_state.hpp index c318843d5df..6319355a936 100644 --- a/libraries/chain/include/eosio/chain/block_header_state.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state.hpp @@ -5,6 +5,42 @@ namespace eosio { namespace chain { +struct block_header_state; + +struct pending_block_header_state { + uint32_t block_num = 0; + block_id_type previous; + block_timestamp_type timestamp; + account_name producer; + uint16_t confirmed = 1; + uint32_t dpos_proposed_irreversible_blocknum = 0; + uint32_t dpos_irreversible_blocknum = 0; + uint32_t active_schedule_version = 0; + uint32_t prev_pending_schedule_lib_num = 0; /// last irr block num + digest_type prev_pending_schedule_hash; + producer_schedule_type prev_pending_schedule; + producer_schedule_type active_schedule; + incremental_merkle blockroot_merkle; + flat_map producer_to_last_produced; + flat_map producer_to_last_implied_irb; + public_key_type block_signing_key; + vector confirm_count; + bool was_pending_promoted = false; + + signed_block_header make_block_header( const checksum256_type& transaction_mroot, + const checksum256_type& action_mroot, + optional&& new_producers )const; + + block_header_state finish_next( const signed_block_header& h, bool skip_validate_signee = false )&&; + + block_header_state finish_next( signed_block_header& h, + const std::function& signer )&&; + +private: + block_header_state _finish_next( const signed_block_header& h )&&; +}; + + /** * @struct block_header_state * @brief defines the minimum state necessary to validate transaction headers @@ -27,17 +63,18 @@ struct block_header_state { vector confirm_count; vector confirmations; - block_header_state next( const signed_block_header& h, bool trust = false )const; - block_header_state generate_next( block_timestamp_type when )const; + pending_block_header_state next( block_timestamp_type when, uint16_t num_prev_blocks_to_confirm )const; + + block_header_state next( const signed_block_header& h, bool skip_validate_signee = false )const; - void set_new_producers( producer_schedule_type next_pending ); - void set_confirmed( uint16_t num_prev_blocks ); - void add_confirmation( const header_confirmation& c ); - bool maybe_promote_pending(); + //void set_new_producers( producer_schedule_type next_pending ); + //void set_confirmed( uint16_t num_prev_blocks ); + //void add_confirmation( const header_confirmation& c ); + //bool maybe_promote_pending(); bool has_pending_producers()const { return pending_schedule.producers.size(); } - uint32_t calc_dpos_last_irreversible()const; + uint32_t calc_dpos_last_irreversible( account_name producer_of_next_block )const; bool is_active_producer( account_name n )const; /* diff --git a/libraries/chain/include/eosio/chain/block_state.hpp b/libraries/chain/include/eosio/chain/block_state.hpp index 2292392ade4..98fb1594299 100644 --- a/libraries/chain/include/eosio/chain/block_state.hpp +++ b/libraries/chain/include/eosio/chain/block_state.hpp @@ -12,9 +12,23 @@ namespace eosio { namespace chain { struct block_state : public block_header_state { - explicit block_state( const block_header_state& cur ):block_header_state(cur){} - block_state( const block_header_state& prev, signed_block_ptr b, bool skip_validate_signee ); - block_state( const block_header_state& prev, block_timestamp_type when ); + block_state( const block_header_state& prev, + signed_block_ptr b, + bool skip_validate_signee + ); + + block_state( pending_block_header_state&& cur, + signed_block_ptr&& b, // unsigned block + vector&& trx_metas, + const std::function& signer + ); + + block_state( pending_block_header_state&& cur, + const signed_block_ptr& b, // signed block + vector&& trx_metas, + bool skip_validate_signee + ); + block_state() = default; /// weak_ptr prev_block_state.... diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index f0d5a53f52d..00e9f54d8db 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -139,7 +139,7 @@ namespace eosio { namespace chain { */ transaction_trace_ptr push_scheduled_transaction( const transaction_id_type& scheduled, fc::time_point deadline, uint32_t billed_cpu_time_us = 0 ); - void finalize_block(); + block_state_ptr finalize_block( const std::function& signer_callback ); void sign_block( const std::function& signer_callback ); void commit_block(); void pop_block(); @@ -188,9 +188,12 @@ namespace eosio { namespace chain { account_name fork_db_head_block_producer()const; time_point pending_block_time()const; - block_state_ptr pending_block_state()const; + account_name pending_block_producer()const; + public_key_type pending_block_signing_key()const; optional pending_producer_block_id()const; + const vector& get_pending_trx_receipts()const; + const producer_schedule_type& active_producers()const; const producer_schedule_type& pending_producers()const; optional proposed_producers()const; @@ -214,6 +217,7 @@ namespace eosio { namespace chain { void check_contract_list( account_name code )const; void check_action_list( account_name code, action_name action )const; void check_key_list( const public_key_type& key )const; + bool is_building_block()const; bool is_producing_block()const; bool is_ram_billing_in_notify_allowed()const; diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index 998157ab41a..7473de2d582 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -44,7 +44,7 @@ namespace eosio { namespace chain { block_state_ptr add( const block_state_ptr& next_block, bool skip_validate_previous ); void remove( const block_id_type& id ); - void add( const header_confirmation& c ); + //void add( const header_confirmation& c ); const block_state_ptr& head()const; @@ -71,7 +71,7 @@ namespace eosio { namespace chain { signal irreversible; private: - void set_bft_irreversible( block_id_type id ); + //void set_bft_irreversible( block_id_type id ); unique_ptr my; }; diff --git a/libraries/chain/transaction_context.cpp b/libraries/chain/transaction_context.cpp index d5da8ca279b..226e6863a16 100644 --- a/libraries/chain/transaction_context.cpp +++ b/libraries/chain/transaction_context.cpp @@ -162,7 +162,7 @@ namespace bacc = boost::accumulators; undo_session = c.mutable_db().start_undo_session(true); } trace->id = id; - trace->block_num = c.pending_block_state()->block_num; + trace->block_num = c.head_block_num() + 1; trace->block_time = c.pending_block_time(); trace->producer_block_id = c.pending_producer_block_id(); executed.reserve( trx.total_actions() ); diff --git a/libraries/testing/tester.cpp b/libraries/testing/tester.cpp index bcf811434d5..311a8dc3685 100644 --- a/libraries/testing/tester.cpp +++ b/libraries/testing/tester.cpp @@ -158,7 +158,7 @@ namespace eosio { namespace testing { auto head_time = control->head_block_time(); auto next_time = head_time + skip_time; - if( !control->pending_block_state() || control->pending_block_state()->header.timestamp != next_time ) { + if( !control->is_building_block() || control->pending_block_time() != next_time ) { _start_block( next_time ); } @@ -203,7 +203,7 @@ namespace eosio { namespace testing { } signed_block_ptr base_tester::_finish_block() { - FC_ASSERT( control->pending_block_state(), "must first start a block before it can be finished" ); + FC_ASSERT( control->is_building_block(), "must first start a block before it can be finished" ); auto producer = control->head_block_state()->get_scheduled_producer( control->pending_block_time() ); private_key_type priv_key; @@ -216,10 +216,9 @@ namespace eosio { namespace testing { priv_key = private_key_itr->second; } - control->finalize_block(); - control->sign_block( [&]( digest_type d ) { + control->finalize_block( [&]( digest_type d ) { return priv_key.sign(d); - }); + } ); control->commit_block(); last_produced_block[control->head_block_state()->header.producer] = control->head_block_state()->id; @@ -331,7 +330,7 @@ namespace eosio { namespace testing { uint32_t billed_cpu_time_us ) { try { - if( !control->pending_block_state() ) + if( !control->is_building_block() ) _start_block(control->head_block_time() + fc::microseconds(config::block_interval_us)); auto r = control->push_transaction( std::make_shared(std::make_shared(trx)), deadline, billed_cpu_time_us ); if( r->except_ptr ) std::rethrow_exception( r->except_ptr ); @@ -344,7 +343,7 @@ namespace eosio { namespace testing { uint32_t billed_cpu_time_us ) { try { - if( !control->pending_block_state() ) + if( !control->is_building_block() ) _start_block(control->head_block_time() + fc::microseconds(config::block_interval_us)); auto c = packed_transaction::none; diff --git a/plugins/history_plugin/history_plugin.cpp b/plugins/history_plugin/history_plugin.cpp index 0be3d9f11ca..4371a910388 100644 --- a/plugins/history_plugin/history_plugin.cpp +++ b/plugins/history_plugin/history_plugin.cpp @@ -264,7 +264,7 @@ namespace eosio { datastream ds( aho.packed_action_trace.data(), ps ); fc::raw::pack( ds, at ); aho.action_sequence_num = at.receipt.global_sequence; - aho.block_num = chain.pending_block_state()->block_num; + aho.block_num = chain.head_block_num() + 1; aho.block_time = chain.pending_block_time(); aho.trx_id = at.trx_id; }); @@ -348,7 +348,7 @@ namespace eosio { auto& chain = my->chain_plug->chain(); chainbase::database& db = const_cast( chain.db() ); // Override read-only access to state DB (highly unrecommended practice!) - // TODO: Use separate chainbase database for managing the state of the history_plugin (or remove deprecated history_plugin entirely) + // TODO: Use separate chainbase database for managing the state of the history_plugin (or remove deprecated history_plugin entirely) db.add_index(); db.add_index(); db.add_index(); @@ -493,15 +493,16 @@ namespace eosio { ++itr; } + + const vector* receipts = nullptr; auto blk = chain.fetch_block_by_number( result.block_num ); - if( blk == nullptr ) { // still in pending - auto blk_state = chain.pending_block_state(); - if( blk_state != nullptr ) { - blk = blk_state->block; - } + if( blk ) { + receipts = &blk->transactions; + } else if( chain.is_building_block() ) { // still in pending + receipts = &chain.get_pending_trx_receipts(); } - if( blk != nullptr ) { - for (const auto &receipt: blk->transactions) { + if( receipts ) { + for (const auto &receipt: *receipts) { if (receipt.trx.contains()) { auto &pt = receipt.trx.get(); if (pt.id() == result.id) { diff --git a/plugins/producer_plugin/producer_plugin.cpp b/plugins/producer_plugin/producer_plugin.cpp index 18d7f2b795a..8ead8107b11 100644 --- a/plugins/producer_plugin/producer_plugin.cpp +++ b/plugins/producer_plugin/producer_plugin.cpp @@ -219,16 +219,13 @@ class producer_plugin_impl : public std::enable_shared_from_thischain(); const auto hbn = bsp->block_num; - auto new_block_header = bsp->header; - new_block_header.timestamp = new_block_header.timestamp.next(); - new_block_header.previous = bsp->id; - auto new_bs = bsp->generate_next(new_block_header.timestamp); + auto new_pbhs = bsp->next(bsp->header.timestamp.next(), 0); // for newly installed producers we can set their watermarks to the block they became active - if (new_bs.maybe_promote_pending() && bsp->active_schedule.version != new_bs.active_schedule.version) { + if( bsp->active_schedule.version != new_pbhs.active_schedule.version ) { flat_set new_producers; - new_producers.reserve(new_bs.active_schedule.producers.size()); - for( const auto& p: new_bs.active_schedule.producers) { + new_producers.reserve(new_pbhs.active_schedule.producers.size()); + for( const auto& p: new_pbhs.active_schedule.producers) { if (_producers.count(p.producer_name) > 0) new_producers.insert(p.producer_name); } @@ -361,12 +358,12 @@ class producer_plugin_impl : public std::enable_shared_from_this next) { chain::controller& chain = chain_plug->chain(); - if (!chain.pending_block_state()) { + if (!chain.is_building_block()) { _pending_incoming_transactions.emplace_back(trx, persist_until_expired, next); return; } - auto block_time = chain.pending_block_state()->header.timestamp.to_time_point(); + auto block_time = chain.pending_block_time(); auto send_response = [this, &trx, &chain, &next](const fc::static_variant& response) { next(response); @@ -375,7 +372,7 @@ class producer_plugin_impl : public std::enable_shared_from_thisheader.producer) + ("prod", chain.pending_block_producer()) ("txid", trx->id) ("why",response.get()->what())); } else { @@ -388,7 +385,7 @@ class producer_plugin_impl : public std::enable_shared_from_thisheader.producer) + ("prod", chain.pending_block_producer()) ("txid", trx->id)); } else { fc_dlog(_trx_trace_log, "[TRX_TRACE] Speculative execution is ACCEPTING tx: ${txid}", @@ -423,7 +420,7 @@ class producer_plugin_impl : public std::enable_shared_from_thisheader.producer) + ("prod", chain.pending_block_producer()) ("txid", trx->id)); } else { fc_dlog(_trx_trace_log, "[TRX_TRACE] Speculative execution COULD NOT FIT tx: ${txid} RETRYING", @@ -910,7 +907,7 @@ producer_plugin::integrity_hash_information producer_plugin::get_integrity_hash( my->schedule_production_loop(); }); - if (chain.pending_block_state()) { + if (chain.is_building_block()) { // abort the pending block chain.abort_block(); } else { @@ -927,7 +924,7 @@ producer_plugin::snapshot_information producer_plugin::create_snapshot() const { my->schedule_production_loop(); }); - if (chain.pending_block_state()) { + if (chain.is_building_block()) { // abort the pending block chain.abort_block(); } else { @@ -975,7 +972,7 @@ optional producer_plugin_impl::calculate_next_block_time(const a if (current_watermark_itr != _producer_watermarks.end()) { auto watermark = current_watermark_itr->second; auto block_num = chain.head_block_state()->block_num; - if (chain.pending_block_state()) { + if (chain.is_building_block()) { ++block_num; } if (watermark > block_num) { @@ -1107,11 +1104,13 @@ producer_plugin_impl::start_block_result producer_plugin_impl::start_block(bool chain.start_block(block_time, blocks_to_confirm); } FC_LOG_AND_DROP(); - const auto& pbs = chain.pending_block_state(); - if (pbs) { - if (_pending_block_mode == pending_block_mode::producing && pbs->block_signing_key != scheduled_producer.block_signing_key) { - elog("Block Signing Key is not expected value, reverting to speculative mode! [expected: \"${expected}\", actual: \"${actual\"", ("expected", scheduled_producer.block_signing_key)("actual", pbs->block_signing_key)); + if( chain.is_building_block() ) { + auto pending_block_time = chain.pending_block_time(); + auto pending_block_signing_key = chain.pending_block_signing_key(); + + if (_pending_block_mode == pending_block_mode::producing && pending_block_signing_key != scheduled_producer.block_signing_key) { + elog("Block Signing Key is not expected value, reverting to speculative mode! [expected: \"${expected}\", actual: \"${actual\"", ("expected", scheduled_producer.block_signing_key)("actual", pending_block_signing_key)); _pending_block_mode = pending_block_mode::speculating; } @@ -1125,12 +1124,12 @@ producer_plugin_impl::start_block_result producer_plugin_impl::start_block(bool int num_expired_persistent = 0; int orig_count = _persistent_transactions.size(); - while(!persisted_by_expiry.empty() && persisted_by_expiry.begin()->expiry <= pbs->header.timestamp.to_time_point()) { + while(!persisted_by_expiry.empty() && persisted_by_expiry.begin()->expiry <= pending_block_time) { auto const& txid = persisted_by_expiry.begin()->trx_id; if (_pending_block_mode == pending_block_mode::producing) { fc_dlog(_trx_trace_log, "[TRX_TRACE] Block ${block_num} for producer ${prod} is EXPIRING PERSISTED tx: ${txid}", ("block_num", chain.head_block_num() + 1) - ("prod", chain.pending_block_state()->header.producer) + ("prod", chain.pending_block_producer()) ("txid", txid)); } else { fc_dlog(_trx_trace_log, "[TRX_TRACE] Speculative execution is EXPIRING PERSISTED tx: ${txid}", @@ -1162,7 +1161,7 @@ producer_plugin_impl::start_block_result producer_plugin_impl::start_block(bool apply_trxs.reserve(unapplied_trxs.size()); auto calculate_transaction_category = [&](const transaction_metadata_ptr& trx) { - if (trx->packed_trx->expiration() < pbs->header.timestamp.to_time_point()) { + if (trx->packed_trx->expiration() < pending_block_time) { return tx_category::EXPIRED; } else if (persisted_by_id.find(trx->id) != persisted_by_id.end()) { return tx_category::PERSISTED; @@ -1384,20 +1383,20 @@ void producer_plugin_impl::schedule_production_loop() { static const boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1)); if (result == start_block_result::succeeded) { // ship this block off no later than its deadline - EOS_ASSERT( chain.pending_block_state(), missing_pending_block_state, "producing without pending_block_state, start_block succeeded" ); + EOS_ASSERT( chain.is_building_block(), missing_pending_block_state, "producing without pending_block_state, start_block succeeded" ); auto deadline = chain.pending_block_time().time_since_epoch().count() + (last_block ? _last_block_time_offset_us : _produce_time_offset_us); _timer.expires_at( epoch + boost::posix_time::microseconds( deadline )); - fc_dlog(_log, "Scheduling Block Production on Normal Block #${num} for ${time}", ("num", chain.pending_block_state()->block_num)("time",deadline)); + fc_dlog(_log, "Scheduling Block Production on Normal Block #${num} for ${time}", ("num", chain.head_block_num()+1)("time",deadline)); } else { - EOS_ASSERT( chain.pending_block_state(), missing_pending_block_state, "producing without pending_block_state" ); + EOS_ASSERT( chain.is_building_block(), missing_pending_block_state, "producing without pending_block_state" ); auto expect_time = chain.pending_block_time() - fc::microseconds(config::block_interval_us); // ship this block off up to 1 block time earlier or immediately if (fc::time_point::now() >= expect_time) { _timer.expires_from_now( boost::posix_time::microseconds( 0 )); - fc_dlog(_log, "Scheduling Block Production on Exhausted Block #${num} immediately", ("num", chain.pending_block_state()->block_num)); + fc_dlog(_log, "Scheduling Block Production on Exhausted Block #${num} immediately", ("num", chain.head_block_num()+1)); } else { _timer.expires_at(epoch + boost::posix_time::microseconds(expect_time.time_since_epoch().count())); - fc_dlog(_log, "Scheduling Block Production on Exhausted Block #${num} at ${time}", ("num", chain.pending_block_state()->block_num)("time",expect_time)); + fc_dlog(_log, "Scheduling Block Production on Exhausted Block #${num} at ${time}", ("num", chain.head_block_num()+1)("time",expect_time)); } } @@ -1405,16 +1404,15 @@ void producer_plugin_impl::schedule_production_loop() { auto self = weak_this.lock(); if (self && ec != boost::asio::error::operation_aborted && cid == self->_timer_corelation_id) { // pending_block_state expected, but can't assert inside async_wait - auto block_num = chain.pending_block_state() ? chain.pending_block_state()->block_num : 0; + auto block_num = chain.is_building_block() ? chain.head_block_num() + 1 : 0; auto res = self->maybe_produce_block(); fc_dlog(_log, "Producing Block #${num} returned: ${res}", ("num", block_num)("res", res)); } }); } else if (_pending_block_mode == pending_block_mode::speculating && !_producers.empty() && !production_disabled_by_policy()){ fc_dlog(_log, "Specualtive Block Created; Scheduling Speculative/Production Change"); - EOS_ASSERT( chain.pending_block_state(), missing_pending_block_state, "speculating without pending_block_state" ); - const auto& pbs = chain.pending_block_state(); - schedule_delayed_production_loop(weak_this, pbs->header.timestamp); + EOS_ASSERT( chain.is_building_block(), missing_pending_block_state, "speculating without pending_block_state" ); + schedule_delayed_production_loop(weak_this, chain.pending_block_time()); } else { fc_dlog(_log, "Speculative Block Created"); } @@ -1495,16 +1493,14 @@ void producer_plugin_impl::produce_block() { //ilog("produce_block ${t}", ("t", fc::time_point::now())); // for testing _produce_time_offset_us EOS_ASSERT(_pending_block_mode == pending_block_mode::producing, producer_exception, "called produce_block while not actually producing"); chain::controller& chain = chain_plug->chain(); - const auto& pbs = chain.pending_block_state(); const auto& hbs = chain.head_block_state(); - EOS_ASSERT(pbs, missing_pending_block_state, "pending_block_state does not exist but it should, another plugin may have corrupted it"); - auto signature_provider_itr = _signature_providers.find( pbs->block_signing_key ); + EOS_ASSERT(chain.is_building_block(), missing_pending_block_state, "pending_block_state does not exist but it should, another plugin may have corrupted it"); + auto signature_provider_itr = _signature_providers.find( chain.pending_block_signing_key() ); EOS_ASSERT(signature_provider_itr != _signature_providers.end(), producer_priv_key_not_found, "Attempting to produce a block for which we don't have the private key"); //idump( (fc::time_point::now() - chain.pending_block_time()) ); - chain.finalize_block(); - chain.sign_block( [&]( const digest_type& d ) { + chain.finalize_block( [&]( const digest_type& d ) { auto debug_logger = maybe_make_debug_time_logger(); return signature_provider_itr->second(d); } ); diff --git a/plugins/test_control_plugin/test_control_plugin.cpp b/plugins/test_control_plugin/test_control_plugin.cpp index 483e859c30f..170079a8016 100644 --- a/plugins/test_control_plugin/test_control_plugin.cpp +++ b/plugins/test_control_plugin/test_control_plugin.cpp @@ -23,8 +23,7 @@ class test_control_plugin_impl { private: void accepted_block(const chain::block_state_ptr& bsp); void applied_irreversible_block(const chain::block_state_ptr& bsp); - void retrieve_next_block_state(const chain::block_state_ptr& bsp); - void process_next_block_state(const chain::block_header_state& bhs); + void process_next_block_state(const chain::block_state_ptr& bsp); fc::optional _accepted_block_connection; fc::optional _irreversible_block_connection; @@ -55,26 +54,17 @@ void test_control_plugin_impl::disconnect() { void test_control_plugin_impl::applied_irreversible_block(const chain::block_state_ptr& bsp) { if (_track_lib) - retrieve_next_block_state(bsp); + process_next_block_state(bsp); } void test_control_plugin_impl::accepted_block(const chain::block_state_ptr& bsp) { if (_track_head) - retrieve_next_block_state(bsp); + process_next_block_state(bsp); } -void test_control_plugin_impl::retrieve_next_block_state(const chain::block_state_ptr& bsp) { - const auto hbn = bsp->block_num; - auto new_block_header = bsp->header; - new_block_header.timestamp = new_block_header.timestamp.next(); - new_block_header.previous = bsp->id; - auto new_bs = bsp->generate_next(new_block_header.timestamp); - process_next_block_state(new_bs); -} - -void test_control_plugin_impl::process_next_block_state(const chain::block_header_state& bhs) { +void test_control_plugin_impl::process_next_block_state(const chain::block_state_ptr& bsp) { const auto block_time = _chain.head_block_time() + fc::microseconds(chain::config::block_interval_us); - const auto& producer_name = bhs.get_scheduled_producer(block_time).producer_name; + auto producer_name = bsp->get_scheduled_producer(block_time).producer_name; // start counting sequences for this producer (once we if (producer_name == _producer && _clean_producer_sequence) { _producer_sequence += 1; diff --git a/unittests/forked_tests.cpp b/unittests/forked_tests.cpp index 9543a791174..9ebab8e4c3b 100644 --- a/unittests/forked_tests.cpp +++ b/unittests/forked_tests.cpp @@ -43,7 +43,7 @@ BOOST_AUTO_TEST_CASE( irrblock ) try { wlog("set producer schedule to [dan,sam,pam]"); c.produce_blocks(50); -} FC_LOG_AND_RETHROW() +} FC_LOG_AND_RETHROW() struct fork_tracker { vector blocks; @@ -60,7 +60,7 @@ BOOST_AUTO_TEST_CASE( fork_with_bad_block ) try { auto res = bios.set_producers( {N(a),N(b),N(c),N(d),N(e)} ); // run until the producers are installed and its the start of "a's" round - while( bios.control->pending_block_state()->header.producer.to_string() != "a" || bios.control->head_block_state()->header.producer.to_string() != "e") { + while( bios.control->pending_block_producer().to_string() != "a" || bios.control->head_block_state()->header.producer.to_string() != "e") { bios.produce_block(); } @@ -313,7 +313,7 @@ BOOST_AUTO_TEST_CASE( prune_remove_branch ) try { auto nextproducer = [](tester &c, int skip_interval) ->account_name { auto head_time = c.control->head_block_time(); auto next_time = head_time + fc::milliseconds(config::block_interval_ms * skip_interval); - return c.control->head_block_state()->get_scheduled_producer(next_time).producer_name; + return c.control->head_block_state()->get_scheduled_producer(next_time).producer_name; }; // fork c: 2 producers: dan, sam @@ -323,18 +323,18 @@ BOOST_AUTO_TEST_CASE( prune_remove_branch ) try { account_name next1 = nextproducer(c, skip1); if (next1 == N(dan) || next1 == N(sam)) { c.produce_block(fc::milliseconds(config::block_interval_ms * skip1)); skip1 = 1; - } + } else ++skip1; account_name next2 = nextproducer(c2, skip2); if (next2 == N(scott)) { c2.produce_block(fc::milliseconds(config::block_interval_ms * skip2)); skip2 = 1; - } + } else ++skip2; } BOOST_REQUIRE_EQUAL(87, c.control->head_block_num()); BOOST_REQUIRE_EQUAL(73, c2.control->head_block_num()); - + // push fork from c2 => c int p = fork_num; while ( p < c2.control->head_block_num()) { @@ -344,7 +344,7 @@ BOOST_AUTO_TEST_CASE( prune_remove_branch ) try { BOOST_REQUIRE_EQUAL(73, c.control->head_block_num()); -} FC_LOG_AND_RETHROW() +} FC_LOG_AND_RETHROW() BOOST_AUTO_TEST_CASE( read_modes ) try { diff --git a/unittests/producer_schedule_tests.cpp b/unittests/producer_schedule_tests.cpp index a7279499656..07435f97d96 100644 --- a/unittests/producer_schedule_tests.cpp +++ b/unittests/producer_schedule_tests.cpp @@ -239,7 +239,7 @@ BOOST_FIXTURE_TEST_CASE( producer_schedule_promotion_test, TESTER ) try { produce_blocks(23); // Alice produces the last block of her first round. // Bob's first block (which advances LIB to Alice's last block) is started but not finalized. BOOST_REQUIRE_EQUAL( control->head_block_producer(), N(alice) ); - BOOST_REQUIRE_EQUAL( control->pending_block_state()->header.producer, N(bob) ); + BOOST_REQUIRE_EQUAL( control->pending_block_producer(), N(bob) ); BOOST_CHECK_EQUAL( control->pending_producers().version, 2 ); produce_blocks(12); // Bob produces his first 11 blocks @@ -247,7 +247,7 @@ BOOST_FIXTURE_TEST_CASE( producer_schedule_promotion_test, TESTER ) try { produce_blocks(12); // Bob produces his 12th block. // Alice's first block of the second round is started but not finalized (which advances LIB to Bob's last block). BOOST_REQUIRE_EQUAL( control->head_block_producer(), N(alice) ); - BOOST_REQUIRE_EQUAL( control->pending_block_state()->header.producer, N(bob) ); + BOOST_REQUIRE_EQUAL( control->pending_block_producer(), N(bob) ); BOOST_CHECK_EQUAL( control->active_producers().version, 2 ); BOOST_CHECK_EQUAL( true, compare_schedules( sch2, control->active_producers() ) ); @@ -299,7 +299,7 @@ BOOST_FIXTURE_TEST_CASE( producer_schedule_reduction, tester ) try { produce_blocks(48); BOOST_REQUIRE_EQUAL( control->head_block_producer(), N(bob) ); - BOOST_REQUIRE_EQUAL( control->pending_block_state()->header.producer, N(carol) ); + BOOST_REQUIRE_EQUAL( control->pending_block_producer(), N(carol) ); BOOST_CHECK_EQUAL( control->pending_producers().version, 2 ); produce_blocks(47); @@ -307,7 +307,7 @@ BOOST_FIXTURE_TEST_CASE( producer_schedule_reduction, tester ) try { produce_blocks(1); BOOST_REQUIRE_EQUAL( control->head_block_producer(), N(carol) ); - BOOST_REQUIRE_EQUAL( control->pending_block_state()->header.producer, N(alice) ); + BOOST_REQUIRE_EQUAL( control->pending_block_producer(), N(alice) ); BOOST_CHECK_EQUAL( control->active_producers().version, 2 ); BOOST_CHECK_EQUAL( true, compare_schedules( sch2, control->active_producers() ) ); From 5961a38c7abb418eae4342c1f8a6d5e92eb61170 Mon Sep 17 00:00:00 2001 From: arhag Date: Wed, 16 Jan 2019 21:24:16 -0500 Subject: [PATCH 2/4] improve handling of pending trx receipts in history_plugin --- plugins/history_plugin/history_plugin.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/plugins/history_plugin/history_plugin.cpp b/plugins/history_plugin/history_plugin.cpp index 4371a910388..a44321ca0bc 100644 --- a/plugins/history_plugin/history_plugin.cpp +++ b/plugins/history_plugin/history_plugin.cpp @@ -493,16 +493,10 @@ namespace eosio { ++itr; } - - const vector* receipts = nullptr; auto blk = chain.fetch_block_by_number( result.block_num ); - if( blk ) { - receipts = &blk->transactions; - } else if( chain.is_building_block() ) { // still in pending - receipts = &chain.get_pending_trx_receipts(); - } - if( receipts ) { - for (const auto &receipt: *receipts) { + if( blk || chain.is_building_block() ) { + const vector& receipts = blk ? blk->transactions : chain.get_pending_trx_receipts(); + for (const auto &receipt: receipts) { if (receipt.trx.contains()) { auto &pt = receipt.trx.get(); if (pt.id() == result.id) { @@ -519,7 +513,7 @@ namespace eosio { break; } } - } + } } } else { auto blk = chain.fetch_block_by_number(*p.block_num_hint); From 2cb4dbc8825af74f45c18f5b35d030d2888bfd3c Mon Sep 17 00:00:00 2001 From: arhag Date: Thu, 17 Jan 2019 12:12:24 -0500 Subject: [PATCH 3/4] cleanup unused code --- libraries/chain/block_header_state.cpp | 26 -------- libraries/chain/fork_database.cpp | 62 ------------------- .../include/eosio/chain/block_header.hpp | 7 --- .../eosio/chain/block_header_state.hpp | 16 +---- .../chain/include/eosio/chain/controller.hpp | 1 - .../include/eosio/chain/fork_database.hpp | 2 - .../include/eosio/chain/plugin_interface.hpp | 2 - plugins/chain_plugin/chain_plugin.cpp | 14 +---- 8 files changed, 3 insertions(+), 127 deletions(-) diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index 9fc95885dae..470be0c2ff5 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -253,18 +253,6 @@ namespace eosio { namespace chain { return result; } - /* - void block_header_state::set_new_producers( producer_schedule_type pending ) { - EOS_ASSERT( pending.version == active_schedule.version + 1, producer_schedule_exception, "wrong producer schedule version specified" ); - EOS_ASSERT( pending_schedule.producers.size() == 0, producer_schedule_exception, - "cannot set new pending producers until last pending is confirmed" ); - header.new_producers = move(pending); - pending_schedule_hash = digest_type::hash( *header.new_producers ); - pending_schedule = *header.new_producers; - pending_schedule_lib_num = block_num; - } - */ - /** * Transitions the current header state into the next header state given the supplied signed block header. * @@ -297,18 +285,4 @@ namespace eosio { namespace chain { ("block_signing_key", block_signing_key)( "signee", signee ) ); } - /* - void block_header_state::add_confirmation( const header_confirmation& conf ) { - for( const auto& c : confirmations ) - EOS_ASSERT( c.producer != conf.producer, producer_double_confirm, "block already confirmed by this producer" ); - - auto key = active_schedule.get_producer_key( conf.producer ); - EOS_ASSERT( key != public_key_type(), producer_not_in_schedule, "producer not in current schedule" ); - auto signer = fc::crypto::public_key( conf.producer_signature, sig_digest(), true ); - EOS_ASSERT( signer == key, wrong_signing_key, "confirmation not signed by expected key" ); - - confirmations.emplace_back( conf ); - } - */ - } } /// namespace eosio::chain diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 1a6518cfa1b..6adcea6f65e 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -297,66 +297,4 @@ namespace eosio { namespace chain { return *nitr; } - /* - void fork_database::add( const header_confirmation& c ) { - auto b = get_block( c.block_id ); - EOS_ASSERT( b, fork_db_block_not_found, "unable to find block id ${id}", ("id",c.block_id)); - b->add_confirmation( c ); - - if( b->bft_irreversible_blocknum < b->block_num && - b->confirmations.size() >= ((b->active_schedule.producers.size() * 2) / 3 + 1) ) { - set_bft_irreversible( c.block_id ); - } - } - */ - - /** - * This method will set this block as being BFT irreversible and will update - * all blocks which build off of it to have the same bft_irb if their existing - * bft irb is less than this block num. - * - * This will require a search over all forks - */ -#if 0 - void fork_database::set_bft_irreversible( block_id_type id ) { - auto& idx = my->index.get(); - auto itr = idx.find(id); - uint32_t block_num = (*itr)->block_num; - idx.modify( itr, [&]( auto& bsp ) { - bsp->bft_irreversible_blocknum = bsp->block_num; - }); - - /** to prevent stack-overflow, we perform a bredth-first traversal of the - * fork database. At each stage we iterate over the leafs from the prior stage - * and find all nodes that link their previous. If we update the bft lib then we - * add it to a queue for the next layer. This lambda takes one layer and returns - * all block ids that need to be iterated over for next layer. - */ - auto update = [&]( const vector& in ) { - vector updated; - - for( const auto& i : in ) { - auto& pidx = my->index.get(); - auto pitr = pidx.lower_bound( i ); - auto epitr = pidx.upper_bound( i ); - while( pitr != epitr ) { - pidx.modify( pitr, [&]( auto& bsp ) { - if( bsp->bft_irreversible_blocknum < block_num ) { - bsp->bft_irreversible_blocknum = block_num; - updated.push_back( bsp->id ); - } - }); - ++pitr; - } - } - return updated; - }; - - vector queue{id}; - while( queue.size() ) { - queue = update( queue ); - } - } -#endif - } } /// eosio::chain diff --git a/libraries/chain/include/eosio/chain/block_header.hpp b/libraries/chain/include/eosio/chain/block_header.hpp index 53de64eba67..2849ee00f31 100644 --- a/libraries/chain/include/eosio/chain/block_header.hpp +++ b/libraries/chain/include/eosio/chain/block_header.hpp @@ -49,12 +49,6 @@ namespace eosio { namespace chain { signature_type producer_signature; }; - struct header_confirmation { - block_id_type block_id; - account_name producer; - signature_type producer_signature; - }; - } } /// namespace eosio::chain FC_REFLECT(eosio::chain::block_header, @@ -63,4 +57,3 @@ FC_REFLECT(eosio::chain::block_header, (schedule_version)(new_producers)(header_extensions)) FC_REFLECT_DERIVED(eosio::chain::signed_block_header, (eosio::chain::block_header), (producer_signature)) -FC_REFLECT(eosio::chain::header_confirmation, (block_id)(producer)(producer_signature) ) diff --git a/libraries/chain/include/eosio/chain/block_header_state.hpp b/libraries/chain/include/eosio/chain/block_header_state.hpp index 6319355a936..81c925dfec5 100644 --- a/libraries/chain/include/eosio/chain/block_header_state.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state.hpp @@ -61,29 +61,15 @@ struct block_header_state { flat_map producer_to_last_implied_irb; public_key_type block_signing_key; vector confirm_count; - vector confirmations; pending_block_header_state next( block_timestamp_type when, uint16_t num_prev_blocks_to_confirm )const; block_header_state next( const signed_block_header& h, bool skip_validate_signee = false )const; - //void set_new_producers( producer_schedule_type next_pending ); - //void set_confirmed( uint16_t num_prev_blocks ); - //void add_confirmation( const header_confirmation& c ); - //bool maybe_promote_pending(); - - bool has_pending_producers()const { return pending_schedule.producers.size(); } uint32_t calc_dpos_last_irreversible( account_name producer_of_next_block )const; bool is_active_producer( account_name n )const; - /* - block_timestamp_type get_slot_time( uint32_t slot_num )const; - uint32_t get_slot_at_time( block_timestamp_type t )const; - producer_key get_scheduled_producer( uint32_t slot_num )const; - uint32_t producer_participation_rate()const; - */ - producer_key get_scheduled_producer( block_timestamp_type t )const; const block_id_type& prev()const { return header.previous; } digest_type sig_digest()const; @@ -101,4 +87,4 @@ FC_REFLECT( eosio::chain::block_header_state, (pending_schedule_lib_num)(pending_schedule_hash) (pending_schedule)(active_schedule)(blockroot_merkle) (producer_to_last_produced)(producer_to_last_implied_irb)(block_signing_key) - (confirm_count)(confirmations) ) + (confirm_count) ) diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index 00e9f54d8db..f1e28bba78d 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -257,7 +257,6 @@ namespace eosio { namespace chain { signal irreversible_block; signal accepted_transaction; signal applied_transaction; - signal accepted_confirmation; signal bad_alloc; /* diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index 7473de2d582..2ac957f4a65 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -44,8 +44,6 @@ namespace eosio { namespace chain { block_state_ptr add( const block_state_ptr& next_block, bool skip_validate_previous ); void remove( const block_id_type& id ); - //void add( const header_confirmation& c ); - const block_state_ptr& head()const; /** diff --git a/plugins/chain_interface/include/eosio/chain/plugin_interface.hpp b/plugins/chain_interface/include/eosio/chain/plugin_interface.hpp index b62915b5220..cdec12008ef 100644 --- a/plugins/chain_interface/include/eosio/chain/plugin_interface.hpp +++ b/plugins/chain_interface/include/eosio/chain/plugin_interface.hpp @@ -29,8 +29,6 @@ namespace eosio { namespace chain { namespace plugin_interface { using irreversible_block = channel_decl; using accepted_transaction = channel_decl; using applied_transaction = channel_decl; - using accepted_confirmation = channel_decl; - } namespace methods { diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 91d130d3ec5..8b26838e44d 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -146,7 +146,6 @@ class chain_plugin_impl { ,irreversible_block_channel(app().get_channel()) ,accepted_transaction_channel(app().get_channel()) ,applied_transaction_channel(app().get_channel()) - ,accepted_confirmation_channel(app().get_channel()) ,incoming_block_channel(app().get_channel()) ,incoming_block_sync_method(app().get_method()) ,incoming_transaction_async_method(app().get_method()) @@ -174,7 +173,6 @@ class chain_plugin_impl { channels::irreversible_block::channel_type& irreversible_block_channel; channels::accepted_transaction::channel_type& accepted_transaction_channel; channels::applied_transaction::channel_type& applied_transaction_channel; - channels::accepted_confirmation::channel_type& accepted_confirmation_channel; incoming::channels::block::channel_type& incoming_block_channel; // retained references to methods for easy calling @@ -194,8 +192,6 @@ class chain_plugin_impl { fc::optional irreversible_block_connection; fc::optional accepted_transaction_connection; fc::optional applied_transaction_connection; - fc::optional accepted_confirmation_connection; - }; @@ -698,11 +694,6 @@ void chain_plugin::plugin_initialize(const variables_map& options) { my->applied_transaction_channel.publish( trace ); } ); - my->accepted_confirmation_connection = my->chain->accepted_confirmation.connect( - [this]( const header_confirmation& conf ) { - my->accepted_confirmation_channel.publish( conf ); - } ); - my->chain->add_indices(); } FC_LOG_AND_RETHROW() @@ -744,7 +735,6 @@ void chain_plugin::plugin_shutdown() { my->irreversible_block_connection.reset(); my->accepted_transaction_connection.reset(); my->applied_transaction_connection.reset(); - my->accepted_confirmation_connection.reset(); my->chain->get_thread_pool().stop(); my->chain->get_thread_pool().join(); my->chain.reset(); @@ -1099,7 +1089,7 @@ uint64_t convert_to_type(const string& str, const string& desc) { try { return boost::lexical_cast(str.c_str(), str.size()); } catch( ... ) { } - + try { auto trimmed_str = str; boost::trim(trimmed_str); @@ -1113,7 +1103,7 @@ uint64_t convert_to_type(const string& str, const string& desc) { return symb.value(); } catch( ... ) { } } - + try { return ( eosio::chain::string_to_symbol( 0, str.c_str() ) >> 8 ); } catch( ... ) { From be795a8fad651857a9b467eff2695fd1cac3195f Mon Sep 17 00:00:00 2001 From: arhag Date: Thu, 17 Jan 2019 16:04:56 -0500 Subject: [PATCH 4/4] reduce duplication in block header state structs (addresses review comments) --- libraries/chain/block_header_state.cpp | 43 +++---- libraries/chain/controller.cpp | 24 ++-- libraries/chain/fork_database.cpp | 9 +- .../eosio/chain/block_header_state.hpp | 116 ++++++++++-------- unittests/block_tests.cpp | 6 +- unittests/forked_tests.cpp | 2 +- 6 files changed, 104 insertions(+), 96 deletions(-) diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index 470be0c2ff5..3f3fefd15dd 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -109,13 +109,11 @@ namespace eosio { namespace chain { result.dpos_irreversible_blocknum = calc_dpos_last_irreversible( prokey.producer_name ); result.prev_pending_schedule = pending_schedule; - result.prev_pending_schedule_lib_num = pending_schedule_lib_num; - result.prev_pending_schedule_hash = pending_schedule_hash; - if( pending_schedule.producers.size() && - result.dpos_irreversible_blocknum >= pending_schedule_lib_num ) + if( pending_schedule.schedule.producers.size() && + result.dpos_irreversible_blocknum >= pending_schedule.schedule_lib_num ) { - result.active_schedule = pending_schedule; + result.active_schedule = pending_schedule.schedule; flat_map new_producer_to_last_produced; @@ -194,40 +192,31 @@ namespace eosio { namespace chain { if( h.new_producers ) { EOS_ASSERT( !was_pending_promoted, producer_schedule_exception, "cannot set pending producer schedule in the same block in which pending was promoted to active" ); EOS_ASSERT( h.new_producers->version == active_schedule.version + 1, producer_schedule_exception, "wrong producer schedule version specified" ); - EOS_ASSERT( prev_pending_schedule.producers.size() == 0, producer_schedule_exception, + EOS_ASSERT( prev_pending_schedule.schedule.producers.size() == 0, producer_schedule_exception, "cannot set new pending producers until last pending is confirmed" ); } - block_header_state result; + auto block_number = block_num; - result.id = h.id(); - result.block_num = block_num; - result.header = h; + block_header_state result( std::move( *static_cast(this) ) ); - result.dpos_proposed_irreversible_blocknum = dpos_proposed_irreversible_blocknum; - result.dpos_irreversible_blocknum = dpos_irreversible_blocknum; + result.id = h.id(); + result.header = h; if( h.new_producers ) { - result.pending_schedule = *h.new_producers; - result.pending_schedule_hash = digest_type::hash( result.pending_schedule ); - result.pending_schedule_lib_num = block_num; + result.pending_schedule.schedule = *h.new_producers; + result.pending_schedule.schedule_hash = digest_type::hash( result.pending_schedule ); + result.pending_schedule.schedule_lib_num = block_number; } else { if( was_pending_promoted ) { - result.pending_schedule.version = prev_pending_schedule.version; + result.pending_schedule.schedule.version = prev_pending_schedule.schedule.version; } else { - result.pending_schedule = prev_pending_schedule; + result.pending_schedule.schedule = std::move( prev_pending_schedule.schedule ); } - result.pending_schedule_hash = std::move(prev_pending_schedule_hash); - result.pending_schedule_lib_num = prev_pending_schedule_lib_num; + result.pending_schedule.schedule_hash = std::move( prev_pending_schedule.schedule_hash ); + result.pending_schedule.schedule_lib_num = prev_pending_schedule.schedule_lib_num; } - result.active_schedule = std::move(active_schedule); - result.blockroot_merkle = std::move(blockroot_merkle); - result.producer_to_last_produced = std::move(producer_to_last_produced); - result.producer_to_last_implied_irb = std::move(producer_to_last_implied_irb); - result.block_signing_key = std::move(block_signing_key); - result.confirm_count = std::move(confirm_count); - return result; } @@ -267,7 +256,7 @@ namespace eosio { namespace chain { digest_type block_header_state::sig_digest()const { auto header_bmroot = digest_type::hash( std::make_pair( header.digest(), blockroot_merkle.get_root() ) ); - return digest_type::hash( std::make_pair(header_bmroot, pending_schedule_hash) ); + return digest_type::hash( std::make_pair(header_bmroot, pending_schedule.schedule_hash) ); } void block_header_state::sign( const std::function& signer ) { diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 2a3071b5752..4d76e0da363 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -637,13 +637,13 @@ struct controller_impl { producer_schedule_type initial_schedule{ 0, {{config::system_account_name, conf.genesis.initial_key}} }; block_header_state genheader; - genheader.active_schedule = initial_schedule; - genheader.pending_schedule = initial_schedule; - genheader.pending_schedule_hash = fc::sha256::hash(initial_schedule); - genheader.header.timestamp = conf.genesis.initial_timestamp; - genheader.header.action_mroot = conf.genesis.compute_chain_id(); - genheader.id = genheader.header.id(); - genheader.block_num = genheader.header.block_num(); + genheader.active_schedule = initial_schedule; + genheader.pending_schedule.schedule = initial_schedule; + genheader.pending_schedule.schedule_hash = fc::sha256::hash(initial_schedule); + genheader.header.timestamp = conf.genesis.initial_timestamp; + genheader.header.action_mroot = conf.genesis.compute_chain_id(); + genheader.id = genheader.header.id(); + genheader.block_num = genheader.header.block_num(); head = std::make_shared(); static_cast(*head) = genheader; @@ -1148,7 +1148,7 @@ struct controller_impl { const auto& gpo = db.get(); if( gpo.proposed_schedule_block_num.valid() && // if there is a proposed schedule that was proposed in a block ... ( *gpo.proposed_schedule_block_num <= pbhs.dpos_irreversible_blocknum ) && // ... that has now become irreversible ... - pbhs.prev_pending_schedule.producers.size() == 0 // ... and there was room for a new pending schedule prior to any possible promotion + pbhs.prev_pending_schedule.schedule.producers.size() == 0 // ... and there was room for a new pending schedule prior to any possible promotion ) { // Promote proposed schedule to pending schedule. @@ -1990,7 +1990,7 @@ const vector& controller::get_pending_trx_receipts()const { } uint32_t controller::last_irreversible_block_num() const { - return std::max(std::max(my->head->bft_irreversible_blocknum, my->head->dpos_irreversible_blocknum), my->snapshot_head_block); + return std::max( my->head->dpos_irreversible_blocknum, my->snapshot_head_block); } block_id_type controller::last_irreversible_block_id() const { @@ -2124,10 +2124,10 @@ const producer_schedule_type& controller::active_producers()const { const producer_schedule_type& controller::pending_producers()const { if( !(my->pending) ) - return my->head->pending_schedule; + return my->head->pending_schedule.schedule; if( my->pending->_block_stage.contains() ) - return my->pending->_block_stage.get()._block_state->pending_schedule; + return my->pending->_block_stage.get()._block_state->pending_schedule.schedule; if( my->pending->_block_stage.contains() ) { const auto& np = my->pending->_block_stage.get()._unsigned_block->new_producers; @@ -2140,7 +2140,7 @@ const producer_schedule_type& controller::pending_producers()const { if( bb._new_pending_producer_schedule ) return *bb._new_pending_producer_schedule; - return bb._pending_block_header_state.prev_pending_schedule; + return bb._pending_block_header_state.prev_pending_schedule.schedule; } optional controller::proposed_producers()const { diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 6adcea6f65e..f73b3aec07d 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -25,18 +25,17 @@ namespace eosio { namespace chain { ordered_non_unique< tag, const_mem_fun >, ordered_non_unique< tag, composite_key< block_state, - member, + member, member >, composite_key_compare< std::less, std::greater > >, ordered_non_unique< tag, composite_key< block_header_state, - member, - member, - member + member, + member >, - composite_key_compare< std::greater, std::greater, std::greater > + composite_key_compare< std::greater, std::greater > > > > fork_multi_index_type; diff --git a/libraries/chain/include/eosio/chain/block_header_state.hpp b/libraries/chain/include/eosio/chain/block_header_state.hpp index 81c925dfec5..85a09047fdd 100644 --- a/libraries/chain/include/eosio/chain/block_header_state.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state.hpp @@ -7,25 +7,34 @@ namespace eosio { namespace chain { struct block_header_state; -struct pending_block_header_state { - uint32_t block_num = 0; +namespace detail { + struct block_header_state_common { + uint32_t block_num = 0; + uint32_t dpos_proposed_irreversible_blocknum = 0; + uint32_t dpos_irreversible_blocknum = 0; + producer_schedule_type active_schedule; + incremental_merkle blockroot_merkle; + flat_map producer_to_last_produced; + flat_map producer_to_last_implied_irb; + public_key_type block_signing_key; + vector confirm_count; + }; + + struct schedule_info { + uint32_t schedule_lib_num = 0; /// last irr block num + digest_type schedule_hash; + producer_schedule_type schedule; + }; +} + +struct pending_block_header_state : public detail::block_header_state_common { + detail::schedule_info prev_pending_schedule; + bool was_pending_promoted = false; block_id_type previous; - block_timestamp_type timestamp; account_name producer; - uint16_t confirmed = 1; - uint32_t dpos_proposed_irreversible_blocknum = 0; - uint32_t dpos_irreversible_blocknum = 0; + block_timestamp_type timestamp; uint32_t active_schedule_version = 0; - uint32_t prev_pending_schedule_lib_num = 0; /// last irr block num - digest_type prev_pending_schedule_hash; - producer_schedule_type prev_pending_schedule; - producer_schedule_type active_schedule; - incremental_merkle blockroot_merkle; - flat_map producer_to_last_produced; - flat_map producer_to_last_implied_irb; - public_key_type block_signing_key; - vector confirm_count; - bool was_pending_promoted = false; + uint16_t confirmed = 1; signed_block_header make_block_header( const checksum256_type& transaction_mroot, const checksum256_type& action_mroot, @@ -36,7 +45,7 @@ struct pending_block_header_state { block_header_state finish_next( signed_block_header& h, const std::function& signer )&&; -private: +protected: block_header_state _finish_next( const signed_block_header& h )&&; }; @@ -45,46 +54,57 @@ struct pending_block_header_state { * @struct block_header_state * @brief defines the minimum state necessary to validate transaction headers */ -struct block_header_state { - block_id_type id; - uint32_t block_num = 0; - signed_block_header header; - uint32_t dpos_proposed_irreversible_blocknum = 0; - uint32_t dpos_irreversible_blocknum = 0; - uint32_t bft_irreversible_blocknum = 0; - uint32_t pending_schedule_lib_num = 0; /// last irr block num - digest_type pending_schedule_hash; - producer_schedule_type pending_schedule; - producer_schedule_type active_schedule; - incremental_merkle blockroot_merkle; - flat_map producer_to_last_produced; - flat_map producer_to_last_implied_irb; - public_key_type block_signing_key; - vector confirm_count; +struct block_header_state : public detail::block_header_state_common { + block_id_type id; + signed_block_header header; + detail::schedule_info pending_schedule; + + block_header_state() = default; + + block_header_state( detail::block_header_state_common&& base ) + :detail::block_header_state_common( std::move(base) ) + {} pending_block_header_state next( block_timestamp_type when, uint16_t num_prev_blocks_to_confirm )const; block_header_state next( const signed_block_header& h, bool skip_validate_signee = false )const; - bool has_pending_producers()const { return pending_schedule.producers.size(); } - uint32_t calc_dpos_last_irreversible( account_name producer_of_next_block )const; - bool is_active_producer( account_name n )const; + bool has_pending_producers()const { return pending_schedule.schedule.producers.size(); } + uint32_t calc_dpos_last_irreversible( account_name producer_of_next_block )const; + bool is_active_producer( account_name n )const; - producer_key get_scheduled_producer( block_timestamp_type t )const; - const block_id_type& prev()const { return header.previous; } - digest_type sig_digest()const; - void sign( const std::function& signer ); - public_key_type signee()const; - void verify_signee(const public_key_type& signee)const; + producer_key get_scheduled_producer( block_timestamp_type t )const; + const block_id_type& prev()const { return header.previous; } + digest_type sig_digest()const; + void sign( const std::function& signer ); + public_key_type signee()const; + void verify_signee(const public_key_type& signee)const; }; } } /// namespace eosio::chain -FC_REFLECT( eosio::chain::block_header_state, - (id)(block_num)(header)(dpos_proposed_irreversible_blocknum)(dpos_irreversible_blocknum)(bft_irreversible_blocknum) - (pending_schedule_lib_num)(pending_schedule_hash) - (pending_schedule)(active_schedule)(blockroot_merkle) - (producer_to_last_produced)(producer_to_last_implied_irb)(block_signing_key) - (confirm_count) ) +FC_REFLECT( eosio::chain::detail::block_header_state_common, + (block_num) + (dpos_proposed_irreversible_blocknum) + (dpos_irreversible_blocknum) + (active_schedule) + (blockroot_merkle) + (producer_to_last_produced) + (producer_to_last_implied_irb) + (block_signing_key) + (confirm_count) +) + +FC_REFLECT( eosio::chain::detail::schedule_info, + (schedule_lib_num) + (schedule_hash) + (schedule) +) + +FC_REFLECT_DERIVED( eosio::chain::block_header_state, (eosio::chain::detail::block_header_state_common), + (id) + (header) + (pending_schedule) +) diff --git a/unittests/block_tests.cpp b/unittests/block_tests.cpp index 045255da6d9..76e5746e1fa 100644 --- a/unittests/block_tests.cpp +++ b/unittests/block_tests.cpp @@ -31,13 +31,13 @@ BOOST_AUTO_TEST_CASE(block_with_invalid_tx_test) // Re-sign the transaction signed_tx.signatures.clear(); signed_tx.sign(main.get_private_key(config::system_account_name, "active"), main.control->get_chain_id()); - // Replace the valid transaction with the invalid transaction + // Replace the valid transaction with the invalid transaction auto invalid_packed_tx = packed_transaction(signed_tx); copy_b->transactions.back().trx = invalid_packed_tx; // Re-sign the block auto header_bmroot = digest_type::hash( std::make_pair( copy_b->digest(), main.control->head_block_state()->blockroot_merkle.get_root() ) ); - auto sig_digest = digest_type::hash( std::make_pair(header_bmroot, main.control->head_block_state()->pending_schedule_hash) ); + auto sig_digest = digest_type::hash( std::make_pair(header_bmroot, main.control->head_block_state()->pending_schedule.schedule_hash) ); copy_b->producer_signature = main.get_private_key(config::system_account_name, "active").sign(sig_digest); // Push block with invalid transaction to other chain @@ -78,7 +78,7 @@ std::pair corrupt_trx_in_block(validating_te // Re-sign the block auto header_bmroot = digest_type::hash( std::make_pair( copy_b->digest(), main.control->head_block_state()->blockroot_merkle.get_root() ) ); - auto sig_digest = digest_type::hash( std::make_pair(header_bmroot, main.control->head_block_state()->pending_schedule_hash) ); + auto sig_digest = digest_type::hash( std::make_pair(header_bmroot, main.control->head_block_state()->pending_schedule.schedule_hash) ); copy_b->producer_signature = main.get_private_key(b->producer, "active").sign(sig_digest); return std::pair(b, copy_b); } diff --git a/unittests/forked_tests.cpp b/unittests/forked_tests.cpp index 9ebab8e4c3b..9a07999f7a8 100644 --- a/unittests/forked_tests.cpp +++ b/unittests/forked_tests.cpp @@ -100,7 +100,7 @@ BOOST_AUTO_TEST_CASE( fork_with_bad_block ) try { // re-sign the block auto header_bmroot = digest_type::hash( std::make_pair( copy_b->digest(), fork.block_merkle.get_root() ) ); - auto sig_digest = digest_type::hash( std::make_pair(header_bmroot, remote.control->head_block_state()->pending_schedule_hash) ); + auto sig_digest = digest_type::hash( std::make_pair(header_bmroot, remote.control->head_block_state()->pending_schedule.schedule_hash) ); copy_b->producer_signature = remote.get_private_key(N(b), "active").sign(sig_digest); // add this new block to our corrupted block merkle