Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Commit

Permalink
Merge pull request #6167 from EOSIO/light-block-verification
Browse files Browse the repository at this point in the history
Start block state creation early
  • Loading branch information
b1bart authored Nov 13, 2018
2 parents 9db3432 + 7cea107 commit b828413
Show file tree
Hide file tree
Showing 13 changed files with 124 additions and 52 deletions.
14 changes: 8 additions & 6 deletions libraries/chain/block_header_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,6 @@ namespace eosio { namespace chain {
result.blockroot_merkle = blockroot_merkle;
result.blockroot_merkle.append( id );

auto block_mroot = result.blockroot_merkle.get_root();

result.active_schedule = active_schedule;
result.pending_schedule = pending_schedule;
result.dpos_proposed_irreversible_blocknum = dpos_proposed_irreversible_blocknum;
Expand Down Expand Up @@ -143,7 +141,7 @@ namespace eosio { namespace chain {
*
* If the header specifies new_producers then apply them accordingly.
*/
block_header_state block_header_state::next( const signed_block_header& h, bool trust )const {
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" );

Expand Down Expand Up @@ -178,9 +176,8 @@ namespace eosio { namespace chain {
result.id = result.header.id();

// ASSUMPTION FROM controller_impl::apply_block = all untrusted blocks will have their signatures pre-validated here
if( !trust ) {
EOS_ASSERT( result.block_signing_key == result.signee(), wrong_signing_key, "block not signed by expected key",
("result.block_signing_key", result.block_signing_key)("signee", result.signee() ) );
if( !skip_validate_signee ) {
result.verify_signee( result.signee() );
}

return result;
Expand Down Expand Up @@ -236,6 +233,11 @@ namespace eosio { namespace chain {
return fc::crypto::public_key( header.producer_signature, sig_digest(), true );
}

void block_header_state::verify_signee( const public_key_type& signee )const {
EOS_ASSERT( block_signing_key == signee, wrong_signing_key, "block not signed by expected key",
("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" );
Expand Down
6 changes: 3 additions & 3 deletions libraries/chain/block_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ namespace eosio { namespace chain {
static_cast<block_header&>(*block) = header;
}

block_state::block_state( const block_header_state& prev, signed_block_ptr b, bool trust )
:block_header_state( prev.next( *b, trust )), block( 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) )
{ }



Expand Down
89 changes: 70 additions & 19 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ struct controller_impl {

auto start = fc::time_point::now();
while( auto next = blog.read_block_by_num( head->block_num + 1 ) ) {
self.push_block( next, controller::block_status::irreversible );
replay_push_block( next, controller::block_status::irreversible );
if( next->block_num() % 100 == 0 ) {
std::cerr << std::setw(10) << next->block_num() << " of " << blog_head->block_num() <<"\r";
}
Expand All @@ -332,7 +332,7 @@ struct controller_impl {
int rev = 0;
while( auto obj = reversible_blocks.find<reversible_block_object,by_num>(head->block_num+1) ) {
++rev;
self.push_block( obj->get_block(), controller::block_status::validated );
replay_push_block( obj->get_block(), controller::block_status::validated );
}

ilog( "${n} reversible blocks replayed", ("n",rev) );
Expand Down Expand Up @@ -693,7 +693,7 @@ struct controller_impl {
try {
if (add_to_fork_db) {
pending->_pending_block_state->validated = true;
auto new_bsp = fork_db.add(pending->_pending_block_state);
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");
Expand Down Expand Up @@ -1182,13 +1182,18 @@ struct controller_impl {
start_block( b->timestamp, b->confirmed, s , producer_block_id);

std::vector<transaction_metadata_ptr> packed_transactions;
packed_transactions.reserve( b->transactions.size() );
for( const auto& receipt : b->transactions ) {
if( receipt.trx.contains<packed_transaction>()) {
auto& pt = receipt.trx.get<packed_transaction>();
auto mtrx = std::make_shared<transaction_metadata>( pt );
if( !self.skip_auth_check() ) {
mtrx->signing_keys_future = async_thread_pool( [this, mtrx]() {
return std::make_pair( this->chain_id, mtrx->trx.get_signature_keys( this->chain_id ) );
std::weak_ptr<transaction_metadata> mtrx_wp = mtrx;
mtrx->signing_keys_future = async_thread_pool( [chain_id = this->chain_id, mtrx_wp]() {
auto mtrx = mtrx_wp.lock();
return mtrx ?
std::make_pair( chain_id, mtrx->trx.get_signature_keys( chain_id ) ) :
std::make_pair( chain_id, decltype( mtrx->trx.get_signature_keys( chain_id ) ){} );
} );
}
packed_transactions.emplace_back( std::move( mtrx ) );
Expand Down Expand Up @@ -1255,19 +1260,38 @@ struct controller_impl {
}
} FC_CAPTURE_AND_RETHROW() } /// apply_block

std::future<block_state_ptr> create_block_state_future( const signed_block_ptr& b ) {
EOS_ASSERT( b, block_validate_exception, "null block" );

void push_block( const signed_block_ptr& b, controller::block_status s ) {
auto id = b->id();

// 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) );

auto prev = fork_db.get_block( b->previous );
EOS_ASSERT( prev, unlinkable_block_exception, "unlinkable block ${id}", ("id", id)("previous", b->previous) );

return async_thread_pool( [b, prev]() {
const bool skip_validate_signee = false;
return std::make_shared<block_state>( *prev, move( b ), skip_validate_signee );
} );
}

void push_block( std::future<block_state_ptr>& block_state_future ) {
controller::block_status s = controller::block_status::complete;
EOS_ASSERT(!pending, block_validate_exception, "it is not valid to push a block when there is a pending block");

auto reset_prod_light_validation = fc::make_scoped_exit([old_value=trusted_producer_light_validation, this]() {
trusted_producer_light_validation = old_value;
});
try {
EOS_ASSERT( b, block_validate_exception, "trying to push empty block" );
EOS_ASSERT( s != controller::block_status::incomplete, block_validate_exception, "invalid block status for a completed block" );
block_state_ptr new_header_state = block_state_future.get();
auto& b = new_header_state->block;
emit( self.pre_accepted_block, b );
bool trust = !conf.force_all_checks && (s == controller::block_status::irreversible || s == controller::block_status::validated);
auto new_header_state = fork_db.add( b, trust );

fork_db.add( new_header_state, false );

if (conf.trusted_producers.count(b->producer)) {
trusted_producer_light_validation = true;
};
Expand All @@ -1277,6 +1301,29 @@ struct controller_impl {
maybe_switch_forks( s );
}

} FC_LOG_AND_RETHROW( )
}

void replay_push_block( const signed_block_ptr& b, controller::block_status s ) {
self.validate_db_available_size();
self.validate_reversible_available_size();

EOS_ASSERT(!pending, block_validate_exception, "it is not valid to push a block when there is a pending block");

try {
EOS_ASSERT( b, block_validate_exception, "trying to push empty block" );
EOS_ASSERT( (s == controller::block_status::irreversible || s == controller::block_status::validated),
block_validate_exception, "invalid block status for replay" );
emit( self.pre_accepted_block, b );
const bool skip_validate_signee = !conf.force_all_checks;
auto new_header_state = fork_db.add( b, skip_validate_signee );

emit( self.accepted_block_header, new_header_state );

if ( read_mode != db_read_mode::IRREVERSIBLE ) {
maybe_switch_forks( s );
}

// on replay irreversible is not emitted by fork database, so emit it explicitly here
if( s == controller::block_status::irreversible )
emit( self.irreversible_block, new_header_state );
Expand All @@ -1303,13 +1350,13 @@ struct controller_impl {
auto branches = fork_db.fetch_branch_from( new_head->id, head->id );

for( auto itr = branches.second.begin(); itr != branches.second.end(); ++itr ) {
fork_db.mark_in_current_chain( *itr , false );
fork_db.mark_in_current_chain( *itr, false );
pop_block();
}
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
"loss of sync between fork_db and chainbase during fork switch" ); // _should_ never fail

for( auto ritr = branches.first.rbegin(); ritr != branches.first.rend(); ++ritr) {
for( auto ritr = branches.first.rbegin(); ritr != branches.first.rend(); ++ritr ) {
optional<fc::exception> except;
try {
apply_block( (*ritr)->block, (*ritr)->validated ? controller::block_status::validated : controller::block_status::complete );
Expand All @@ -1319,7 +1366,7 @@ struct controller_impl {
}
catch (const fc::exception& e) { except = e; }
if (except) {
elog("exception thrown while switching forks ${e}", ("e",except->to_detail_string()));
elog("exception thrown while switching forks ${e}", ("e", except->to_detail_string()));

// ritr currently points to the block that threw
// if we mark it invalid it will automatically remove all forks built off it.
Expand All @@ -1329,11 +1376,11 @@ struct controller_impl {
// ritr base is a forward itr to the last block successfully applied
auto applied_itr = ritr.base();
for( auto itr = applied_itr; itr != branches.first.end(); ++itr ) {
fork_db.mark_in_current_chain( *itr , false );
fork_db.mark_in_current_chain( *itr, false );
pop_block();
}
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 reversal" ); // _should_ never fail
"loss of sync between fork_db and chainbase during fork switch reversal" ); // _should_ never fail

// re-apply good blocks
for( auto ritr = branches.second.rbegin(); ritr != branches.second.rend(); ++ritr ) {
Expand All @@ -1344,7 +1391,7 @@ struct controller_impl {
throw *except;
} // end if exception
} /// end for each block in branch
ilog("successfully switched fork to new head ${new_head_id}", ("new_head_id", new_head->id));
ilog("successfully switched fork to new head ${new_head_id}", ("new_head_id", new_head->id) );
}
} /// push_block

Expand Down Expand Up @@ -1650,10 +1697,14 @@ void controller::abort_block() {
my->abort_block();
}

void controller::push_block( const signed_block_ptr& b, block_status s ) {
std::future<block_state_ptr> controller::create_block_state_future( const signed_block_ptr& b ) {
return my->create_block_state_future( b );
}

void controller::push_block( std::future<block_state_ptr>& block_state_future ) {
validate_db_available_size();
validate_reversible_available_size();
my->push_block( b, s );
my->push_block( block_state_future );
}

transaction_trace_ptr controller::push_transaction( const transaction_metadata_ptr& trx, fc::time_point deadline, uint32_t billed_cpu_time_us ) {
Expand Down
17 changes: 13 additions & 4 deletions libraries/chain/fork_database.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,16 @@ namespace eosio { namespace chain {
}
}

block_state_ptr fork_database::add( block_state_ptr n ) {
block_state_ptr fork_database::add( const block_state_ptr& n, bool skip_validate_previous ) {
EOS_ASSERT( n, fork_database_exception, "attempt to add null block state" );
EOS_ASSERT( my->head, fork_db_block_not_found, "no head block set" );

if( !skip_validate_previous ) {
auto prior = my->index.find( n->block->previous );
EOS_ASSERT( prior != my->index.end(), unlinkable_block_exception,
"unlinkable block", ("id", n->block->id())("previous", n->block->previous) );
}

auto inserted = my->index.insert(n);
EOS_ASSERT( inserted.second, fork_database_exception, "duplicate block added?" );

Expand All @@ -139,7 +148,7 @@ namespace eosio { namespace chain {
return n;
}

block_state_ptr fork_database::add( signed_block_ptr b, bool trust ) {
block_state_ptr fork_database::add( signed_block_ptr b, bool skip_validate_signee ) {
EOS_ASSERT( b, fork_database_exception, "attempt to add null block" );
EOS_ASSERT( my->head, fork_db_block_not_found, "no head block set" );

Expand All @@ -150,9 +159,9 @@ namespace eosio { namespace chain {
auto prior = by_id_idx.find( b->previous );
EOS_ASSERT( prior != by_id_idx.end(), unlinkable_block_exception, "unlinkable block", ("id", string(b->id()))("previous", string(b->previous)) );

auto result = std::make_shared<block_state>( **prior, move(b), trust );
auto result = std::make_shared<block_state>( **prior, move(b), skip_validate_signee );
EOS_ASSERT( result, fork_database_exception , "fail to add new block state" );
return add(result);
return add(result, true);
}

const block_state_ptr& fork_database::head()const { return my->head; }
Expand Down
2 changes: 1 addition & 1 deletion libraries/chain/include/eosio/chain/block_header_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ struct block_header_state {
public_key_type block_signing_key;
vector<uint8_t> confirm_count;
vector<header_confirmation> confirmations;
std::shared_future<public_key_type> block_signing_key_future;

block_header_state next( const signed_block_header& h, bool trust = false )const;
block_header_state generate_next( block_timestamp_type when )const;
Expand All @@ -53,6 +52,7 @@ struct block_header_state {
digest_type sig_digest()const;
void sign( const std::function<signature_type(const digest_type&)>& signer );
public_key_type signee()const;
void verify_signee(const public_key_type& signee)const;
};


Expand Down
4 changes: 2 additions & 2 deletions libraries/chain/include/eosio/chain/block_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
namespace eosio { namespace chain {

struct block_state : public block_header_state {
block_state( const block_header_state& cur ):block_header_state(cur){}
block_state( const block_header_state& prev, signed_block_ptr b, bool trust = false );
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() = default;

Expand Down
3 changes: 2 additions & 1 deletion libraries/chain/include/eosio/chain/controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ namespace eosio { namespace chain {
void commit_block();
void pop_block();

void push_block( const signed_block_ptr& b, block_status s = block_status::complete );
std::future<block_state_ptr> create_block_state_future( const signed_block_ptr& b );
void push_block( std::future<block_state_ptr>& block_state_future );

const chainbase::database& db()const;

Expand Down
6 changes: 3 additions & 3 deletions libraries/chain/include/eosio/chain/fork_database.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ namespace eosio { namespace chain {
*/
void set( block_state_ptr s );

/** this method will attempt to append the block to an exsting
/** this method will attempt to append the block to an existing
* block_state and will return a pointer to the new block state or
* throw on error.
*/
block_state_ptr add( signed_block_ptr b, bool trust = false );
block_state_ptr add( block_state_ptr next_block );
block_state_ptr add( signed_block_ptr b, bool skip_validate_signee );
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 );
Expand Down
11 changes: 6 additions & 5 deletions libraries/testing/include/eosio/testing/tester.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,8 @@ namespace eosio { namespace testing {

signed_block_ptr produce_block( fc::microseconds skip_time = fc::milliseconds(config::block_interval_ms), uint32_t skip_flag = 0 /*skip_missed_block_penalty*/ )override {
auto sb = _produce_block(skip_time, false, skip_flag | 2);
validating_node->push_block( sb );
auto bs = validating_node->create_block_state_future( sb );
validating_node->push_block( bs );

return sb;
}
Expand All @@ -383,15 +384,15 @@ namespace eosio { namespace testing {
}

void validate_push_block(const signed_block_ptr& sb) {
validating_node->push_block( sb );
auto bs = validating_node->create_block_state_future( sb );
validating_node->push_block( bs );
}

signed_block_ptr produce_empty_block( fc::microseconds skip_time = fc::milliseconds(config::block_interval_ms), uint32_t skip_flag = 0 /*skip_missed_block_penalty*/ )override {
control->abort_block();
auto sb = _produce_block(skip_time, true, skip_flag | 2);
validating_node->push_block( sb );


auto bs = validating_node->create_block_state_future( sb );
validating_node->push_block( bs );

return sb;
}
Expand Down
6 changes: 4 additions & 2 deletions libraries/testing/tester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,9 @@ namespace eosio { namespace testing {
}

signed_block_ptr base_tester::push_block(signed_block_ptr b) {
auto bs = control->create_block_state_future(b);
control->abort_block();
control->push_block(b);
control->push_block(bs);

auto itr = last_produced_block.find(b->producer);
if (itr == last_produced_block.end() || block_header::num_from_id(b->id()) > block_header::num_from_id(itr->second)) {
Expand Down Expand Up @@ -795,8 +796,9 @@ namespace eosio { namespace testing {
for( int i = 1; i <= a.control->head_block_num(); ++i ) {
auto block = a.control->fetch_block_by_number(i);
if( block ) { //&& !b.control->is_known_block(block->id()) ) {
auto bs = b.control->create_block_state_future( block );
b.control->abort_block();
b.control->push_block(block); //, eosio::chain::validation_steps::created_block);
b.control->push_block(bs); //, eosio::chain::validation_steps::created_block);
}
}
};
Expand Down
Loading

0 comments on commit b828413

Please sign in to comment.