Skip to content

Commit

Permalink
GH-2045 Add block_token as a wrapper of block_state and block_state_l…
Browse files Browse the repository at this point in the history
…egacy. Also add block_exists as a performance improvement.
  • Loading branch information
heifner committed Jan 15, 2024
1 parent c3e8423 commit 07f1036
Show file tree
Hide file tree
Showing 18 changed files with 200 additions and 150 deletions.
101 changes: 61 additions & 40 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2835,7 +2835,7 @@ struct controller_impl {
auto producer_block_id = bsp->id();
start_block( b->timestamp, b->confirmed, new_protocol_feature_activations, s, producer_block_id, fc::time_point::maximum() );

// validated in create_block_state_future()
// validated in create_block_token()
std::get<building_block>(pending->_block_stage).trx_mroot_or_receipt_digests() = b->transaction_mroot;

const bool existing_trxs_metas = !bsp->trxs_metas().empty();
Expand Down Expand Up @@ -2956,14 +2956,8 @@ struct controller_impl {


// thread safe, expected to be called from thread other than the main thread
block_state_legacy_ptr create_block_state_i( const block_id_type& id, const signed_block_ptr& b, const block_header_state_legacy& prev ) {
// There is a small race condition at time of activation where create_block_state_i
// is called right before hs_irreversible_block_num is set. If that happens,
// the block is considered invalid, and the node will attempt to sync the block
// in the future and succeed
uint32_t instant_finality_lib = hs_irreversible_block_num.load();
const bool instant_finality_active = instant_finality_lib > 0;
auto trx_mroot = calculate_trx_merkle( b->transactions, instant_finality_active );
block_token create_block_state_i( const block_id_type& id, const signed_block_ptr& b, const block_header_state_legacy& prev ) {
auto trx_mroot = calculate_trx_merkle( b->transactions, false );
EOS_ASSERT( b->transaction_mroot == trx_mroot, block_validate_exception,
"invalid block transaction merkle root ${b} != ${c}", ("b", b->transaction_mroot)("c", trx_mroot) );

Expand All @@ -2981,13 +2975,36 @@ struct controller_impl {

EOS_ASSERT( id == bsp->id(), block_validate_exception,
"provided id ${id} does not match block id ${bid}", ("id", id)("bid", bsp->id()) );
return bsp;
return block_token{bsp};
}

std::future<block_state_legacy_ptr> create_block_state_future( const block_id_type& id, const signed_block_ptr& b ) {
// thread safe, expected to be called from thread other than the main thread
block_token create_block_state_i( const block_id_type& id, const signed_block_ptr& b, const block_header_state& prev ) {
auto trx_mroot = calculate_trx_merkle( b->transactions, true );
EOS_ASSERT( b->transaction_mroot == trx_mroot, block_validate_exception,
"invalid block transaction merkle root ${b} != ${c}", ("b", b->transaction_mroot)("c", trx_mroot) );

const bool skip_validate_signee = false;
auto bsp = std::make_shared<block_state>(
prev,
b,
protocol_features.get_protocol_feature_set(),
[this]( block_timestamp_type timestamp,
const flat_set<digest_type>& cur_features,
const vector<digest_type>& new_features )
{ check_protocol_features( timestamp, cur_features, new_features ); },
skip_validate_signee
);

EOS_ASSERT( id == bsp->id(), block_validate_exception,
"provided id ${id} does not match block id ${bid}", ("id", id)("bid", bsp->id()) );
return block_token{bsp};
}

std::future<block_token> create_block_token_future( const block_id_type& id, const signed_block_ptr& b ) {
EOS_ASSERT( b, block_validate_exception, "null block" );

auto f = [&](auto& fork_db, auto& head) -> std::future<block_state_legacy_ptr> {
auto f = [&](auto& fork_db, auto& head) -> std::future<block_token> {
return post_async_task( thread_pool.get_executor(), [b, id, &fork_db, control=this]() {
// no reason for a block_state if fork_db already knows about block
auto existing = fork_db.get_block( id );
Expand All @@ -3001,14 +3018,14 @@ struct controller_impl {
} );
};

return block_data.apply_dpos<std::future<block_state_legacy_ptr>>(f); // [greg todo] make it work with apply()
return block_data.apply<std::future<block_token>>(f); // [greg todo] make it work with apply()
}

// thread safe, expected to be called from thread other than the main thread
block_state_legacy_ptr create_block_state( const block_id_type& id, const signed_block_ptr& b ) {
std::optional<block_token> create_block_token( const block_id_type& id, const signed_block_ptr& b ) {
EOS_ASSERT( b, block_validate_exception, "null block" );

auto f = [&](auto& fork_db, auto& head) -> block_state_legacy_ptr {
auto f = [&](auto& fork_db, auto& head) -> std::optional<block_token> {
// no reason for a block_state if fork_db already knows about block
auto existing = fork_db.get_block( id );
EOS_ASSERT( !existing, fork_database_exception, "we already know about this block: ${id}", ("id", id) );
Expand All @@ -3017,16 +3034,16 @@ struct controller_impl {
auto prev = fork_db.get_block_header( b->previous );
if( !prev ) return {};

return create_block_state_i( id, b, *prev ); // [greg todo] make it work with apply() - if `create_block_state` needed
return create_block_state_i( id, b, *prev );
};

return block_data.apply_dpos<block_state_legacy_ptr>(f);
return block_data.apply<std::optional<block_token>>(f);
}

template <class BSP>
void push_block( controller::block_report& br,
const BSP& bsp,
const forked_branch_callback_t<BSP>& forked_branch_cb,
const forked_callback_t& forked_branch_cb,
const trx_meta_cache_lookup& trx_lookup )
{
controller::block_status s = controller::block_status::complete;
Expand Down Expand Up @@ -3128,7 +3145,7 @@ struct controller_impl {

template<class BSP>
void maybe_switch_forks( controller::block_report& br, const BSP& new_head, controller::block_status s,
const forked_branch_callback_t<BSP>& forked_branch_cb, const trx_meta_cache_lookup& trx_lookup )
const forked_callback_t& forked_cb, const trx_meta_cache_lookup& trx_lookup )
{
auto do_maybe_switch_forks = [&](auto& fork_db, auto& head) {
bool head_changed = true;
Expand Down Expand Up @@ -3157,9 +3174,15 @@ struct controller_impl {
EOS_ASSERT( self.head_block_id() == branches.second.back()->header.previous, fork_database_exception,
"loss of sync between fork_db and chainbase during fork switch" ); // _should_ never fail

if( forked_branch_cb )
if constexpr (std::is_same_v<BSP, typename std::decay_t<decltype(head)>>)
forked_branch_cb(branches.second);
if( forked_cb ) {
// forked_branch is in reverse order, maintain execution order
for( auto ritr = branches.second.rbegin(), rend = branches.second.rend(); ritr != rend; ++ritr ) {
const auto& bsptr = *ritr;
for( auto itr = bsptr->trxs_metas().begin(), end = bsptr->trxs_metas().end(); itr != end; ++itr ) {
forked_cb(*itr);
}
}
}
}

for( auto ritr = branches.first.rbegin(); ritr != branches.first.rend(); ++ritr ) {
Expand Down Expand Up @@ -3799,33 +3822,23 @@ boost::asio::io_context& controller::get_thread_pool() {
return my->thread_pool.get_executor();
}

std::future<block_state_legacy_ptr> controller::create_block_state_future( const block_id_type& id, const signed_block_ptr& b ) {
return my->create_block_state_future( id, b );
std::future<block_token> controller::create_block_token_future( const block_id_type& id, const signed_block_ptr& b ) {
return my->create_block_token_future( id, b );
}

block_state_legacy_ptr controller::create_block_state( const block_id_type& id, const signed_block_ptr& b ) const {
return my->create_block_state( id, b );
std::optional<block_token> controller::create_block_token( const block_id_type& id, const signed_block_ptr& b ) const {
return my->create_block_token( id, b );
}

void controller::push_block( controller::block_report& br,
const block_state_legacy_ptr& bsp,
const forked_branch_callback_legacy& forked_branch_cb,
void controller::push_block( block_report& br,
const block_token& bt,
const forked_callback_t& forked_cb,
const trx_meta_cache_lookup& trx_lookup )
{
validate_db_available_size();
my->push_block( br, bsp, forked_branch_cb, trx_lookup );
std::visit([&](const auto& bsp) { my->push_block( br, bsp, forked_cb, trx_lookup); }, bt.bsp);
}

void controller::push_block( controller::block_report& br,
const block_state_ptr& bsp,
const forked_branch_callback& forked_branch_cb,
const trx_meta_cache_lookup& trx_lookup )
{
validate_db_available_size();
my->push_block( br, bsp, forked_branch_cb, trx_lookup );
}


transaction_trace_ptr controller::push_transaction( const transaction_metadata_ptr& trx,
fc::time_point block_deadline, fc::microseconds max_transaction_time,
uint32_t billed_cpu_time_us, bool explicit_billed_cpu_time,
Expand Down Expand Up @@ -3994,6 +4007,14 @@ signed_block_ptr controller::fetch_block_by_id( const block_id_type& id )const {
return signed_block_ptr();
}

bool controller::block_exists(const block_id_type&id) const {
auto sb_ptr = my->block_data.fork_db_fetch_block_by_id(id);
if( sb_ptr ) return true;
auto bptr = my->blog.read_block_header_by_num( block_header::num_from_id(id) );
if( bptr && bptr->calculate_id() == id ) return true;
return false;
}

std::optional<signed_block_header> controller::fetch_block_header_by_id( const block_id_type& id )const {
#if 0
// [greg todo] is the below code equivalent??
Expand Down
43 changes: 21 additions & 22 deletions libraries/chain/include/eosio/chain/controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,23 +51,24 @@ namespace eosio::chain {
using resource_limits::resource_limits_manager;
using apply_handler = std::function<void(apply_context&)>;

template<class bsp>
using branch_type_t = fork_database<bsp>::branch_type;

using branch_type_legacy = branch_type_t<block_state_legacy_ptr>;
using branch_type = branch_type_t<block_state_ptr>;

template<class bsp>
using forked_branch_callback_t = std::function<void(const branch_type_t<bsp>&)>;

using forked_branch_callback_legacy = forked_branch_callback_t<block_state_legacy_ptr>;
using forked_branch_callback = forked_branch_callback_t<block_state_ptr>;
using forked_callback_t = std::function<void(const transaction_metadata_ptr&)>;

// lookup transaction_metadata via supplied function to avoid re-creation
using trx_meta_cache_lookup = std::function<transaction_metadata_ptr( const transaction_id_type&)>;

using block_signal_params = std::tuple<const signed_block_ptr&, const block_id_type&>;

// Created via create_block_token(const block_id_type& id, const signed_block_ptr& b)
// Valid to request id and signed_block_ptr it was created from.
// Avoid using internal block_state/block_state_legacy as those types are internal to controller.
struct block_token {
std::variant<block_state_legacy_ptr, block_state_ptr> bsp;

uint32_t block_num() const { return std::visit([](const auto& bsp) { return bsp->block_num(); }, bsp); }
block_id_type id() const { return std::visit([](const auto& bsp) { return bsp->id(); }, bsp); }
signed_block_ptr block() const { return std::visit([](const auto& bsp) { return bsp->block; }, bsp); }
};

enum class db_read_mode {
HEAD,
IRREVERSIBLE,
Expand Down Expand Up @@ -186,26 +187,22 @@ namespace eosio::chain {
void finish_block( block_report& br, const signer_callback_type& signer_callback );
void sign_block( const signer_callback_type& signer_callback );
void commit_block();

// thread-safe
std::future<block_state_legacy_ptr> create_block_state_future( const block_id_type& id, const signed_block_ptr& b );
std::future<block_token> create_block_token_future( const block_id_type& id, const signed_block_ptr& b );
// thread-safe
block_state_legacy_ptr create_block_state( const block_id_type& id, const signed_block_ptr& b ) const;
// returns empty optional if block b is not immediately ready to be processed
std::optional<block_token> create_block_token( const block_id_type& id, const signed_block_ptr& b ) const;

/**
* @param br returns statistics for block
* @param bsp block to push
* @param bt block to push, created by create_block_token
* @param cb calls cb with forked applied transactions for each forked block
* @param trx_lookup user provided lookup function for externally cached transaction_metadata
*/
void push_block( block_report& br,
const block_state_legacy_ptr& bsp,
const forked_branch_callback_legacy& cb,
const trx_meta_cache_lookup& trx_lookup );

void push_block( block_report& br,
const block_state_ptr& bsp,
const forked_branch_callback& cb,
const block_token& bt,
const forked_callback_t& cb,
const trx_meta_cache_lookup& trx_lookup );

boost::asio::io_context& get_thread_pool();
Expand Down Expand Up @@ -284,6 +281,8 @@ namespace eosio::chain {
// thread-safe
signed_block_ptr fetch_block_by_id( const block_id_type& id )const;
// thread-safe
bool block_exists( const block_id_type& id)const;
// thread-safe
std::optional<signed_block_header> fetch_block_header_by_number( uint32_t block_num )const;
// thread-safe
std::optional<signed_block_header> fetch_block_header_by_id( const block_id_type& id )const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,17 +133,9 @@ class unapplied_transaction_queue {
}
}

template<class BRANCH_TYPE>
void add_forked( const BRANCH_TYPE& forked_branch ) {
// forked_branch is in reverse order
for( auto ritr = forked_branch.rbegin(), rend = forked_branch.rend(); ritr != rend; ++ritr ) {
const auto& bsptr = *ritr;
for( auto itr = bsptr->trxs_metas().begin(), end = bsptr->trxs_metas().end(); itr != end; ++itr ) {
const auto& trx = *itr;
auto insert_itr = queue.insert( { trx, trx_enum_type::forked } );
if( insert_itr.second ) added( insert_itr.first );
}
}
void add_forked( const transaction_metadata_ptr& trx ) {
auto insert_itr = queue.insert( { trx, trx_enum_type::forked } );
if( insert_itr.second ) added( insert_itr.first );
}

void add_aborted( deque<transaction_metadata_ptr> aborted_trxs ) {
Expand Down
12 changes: 6 additions & 6 deletions libraries/testing/include/eosio/testing/tester.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -606,9 +606,9 @@ namespace eosio { namespace testing {

signed_block_ptr produce_block( fc::microseconds skip_time = fc::milliseconds(config::block_interval_ms) )override {
auto sb = _produce_block(skip_time, false);
auto bsf = validating_node->create_block_state_future( sb->calculate_id(), sb );
auto btf = validating_node->create_block_token_future( sb->calculate_id(), sb );
controller::block_report br;
validating_node->push_block( br, bsf.get(), {}, trx_meta_cache_lookup{} );
validating_node->push_block( br, btf.get(), {}, trx_meta_cache_lookup{} );

return sb;
}
Expand All @@ -618,17 +618,17 @@ namespace eosio { namespace testing {
}

void validate_push_block(const signed_block_ptr& sb) {
auto bsf = validating_node->create_block_state_future( sb->calculate_id(), sb );
auto btf = validating_node->create_block_token_future( sb->calculate_id(), sb );
controller::block_report br;
validating_node->push_block( br, bsf.get(), {}, trx_meta_cache_lookup{} );
validating_node->push_block( br, btf.get(), {}, trx_meta_cache_lookup{} );
}

signed_block_ptr produce_empty_block( fc::microseconds skip_time = fc::milliseconds(config::block_interval_ms) )override {
unapplied_transactions.add_aborted( control->abort_block() );
auto sb = _produce_block(skip_time, true);
auto bsf = validating_node->create_block_state_future( sb->calculate_id(), sb );
auto btf = validating_node->create_block_token_future( sb->calculate_id(), sb );
controller::block_report br;
validating_node->push_block( br, bsf.get(), {}, trx_meta_cache_lookup{} );
validating_node->push_block( br, btf.get(), {}, trx_meta_cache_lookup{} );

return sb;
}
Expand Down
10 changes: 5 additions & 5 deletions libraries/testing/tester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -376,11 +376,11 @@ namespace eosio { namespace testing {
}

void base_tester::push_block(signed_block_ptr b) {
auto bsf = control->create_block_state_future(b->calculate_id(), b);
auto btf = control->create_block_token_future(b->calculate_id(), b);
unapplied_transactions.add_aborted( control->abort_block() );
controller::block_report br;
control->push_block( br, bsf.get(), [this]( const branch_type_legacy& forked_branch ) {
unapplied_transactions.add_forked( forked_branch );
control->push_block( br, btf.get(), [this]( const transaction_metadata_ptr& trx ) {
unapplied_transactions.add_forked( trx );
}, [this]( const transaction_id_type& id ) {
return unapplied_transactions.get_trx( id );
} );
Expand Down Expand Up @@ -1115,10 +1115,10 @@ namespace eosio { namespace testing {

auto block = a.control->fetch_block_by_number(i);
if( block ) { //&& !b.control->is_known_block(block->id()) ) {
auto bsf = b.control->create_block_state_future( block->calculate_id(), block );
auto btf = b.control->create_block_token_future( block->calculate_id(), block );
b.control->abort_block();
controller::block_report br;
b.control->push_block(br, bsf.get(), {}, trx_meta_cache_lookup{}); //, eosio::chain::validation_steps::created_block);
b.control->push_block(br, btf.get(), {}, trx_meta_cache_lookup{}); //, eosio::chain::validation_steps::created_block);
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ namespace eosio::chain::plugin_interface {
namespace incoming {
namespace methods {
// synchronously push a block/trx to a single provider, block_state_legacy_ptr may be null
using block_sync = method_decl<chain_plugin_interface, bool(const signed_block_ptr&, const std::optional<block_id_type>&, const block_state_legacy_ptr&), first_provider_policy>;
using block_sync = method_decl<chain_plugin_interface, bool(const signed_block_ptr&, const std::optional<block_id_type>&, const std::optional<block_token>&), first_provider_policy>;
using transaction_async = method_decl<chain_plugin_interface, void(const packed_transaction_ptr&, bool, transaction_metadata::trx_type, bool, next_function<transaction_trace_ptr>), first_provider_policy>;
}
}
Expand Down
Loading

0 comments on commit 07f1036

Please sign in to comment.