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

Start block state creation early #6167

Merged
merged 17 commits into from
Nov 13, 2018
Merged

Conversation

heifner
Copy link
Contributor

@heifner heifner commented Oct 26, 2018

Change Description

  • controller::push_block() now takes a future to a block state that can be created by controller::create_state_block_future(). This allows the creation of the block state to begin earlier (before abort block) including the verfication of block signee.
  • Added a controller::replay_push_block for use during replay since futures would only slow that process down (block signee is not verified during normal replay).
  • Capture weak_ptr for futures so they do not keep shared_ptr alive

try {
auto& b = bs->block;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be made "const auto&"? (quick perusal looks like it could, but hard to search with variable "b")

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() ) );
result.verify_signee();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If my brain is following this, I think it would be better that this block assert that !block_signing_key_future.valid() and call verify_signee(signee()) (see comment on verify_signee() )

if( block_signing_key_future.valid() ) {
// if block signing verification delayed for light validation then verify it here
auto signee = block_signing_key_future.get();
EOS_ASSERT( block_signing_key == signee, wrong_signing_key, "block signed by wrong key",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this check a function verify_signee(... signee) { ...}?

And make verify_signee do an assert on block_signing_key_future.valid() and make this method just do the future signature processing, see other comment above.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should handle case where signee == decltype(block_signing_key_future){} differently, since we don't know if the block is signed by wrong key, we just don't have the header state anymore to know who the signee is.

@@ -1263,7 +1274,15 @@ struct controller_impl {
EOS_ASSERT( s != controller::block_status::incomplete, block_validate_exception, "invalid block status for a completed 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 );
auto new_header_state = fork_db.add( b, trust || conf.block_validation_mode == validation_mode::LIGHT );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like that a flag that we are calling trust in fork_database::add and block_state::next is really "trust or light validation", we either need to come up with a name that means that or give it a horrible name like trust_or_light_validation so that someone is expected to look at what is set into that flag before they can correctly use it.

@heifner
Copy link
Contributor Author

heifner commented Oct 30, 2018

Tieing block signee delayed verification to light validation is the opposite of what this should be tied to. Light validation implies you are doing the same validation as a light client. Block signature verification is not something you want to postpone in that mode.

Copy link
Contributor

@brianjohnson5972 brianjohnson5972 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes were requested, see @heifner 's comment

@heifner heifner changed the title Light block verification Start block state creation early Oct 31, 2018
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 );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling fork_database::add( block_state_ptr ) here instead of the original fork_database::add( signed_block_ptr bool ) skips some checks that were done in the original version.

create_block_state_future replicates those fork_database::add( signed_block_ptr, bool) checks, but it does so at a different time (and therefore potentially with different state of the fork database) than the call to fork_db.add here.

One of the two necessary checks, checking for duplicate blocks, is already handled in fork_database::add( block_state_ptr ) with the assertion that inserted.second is true.

The other check that is necessary to again here (so it could be done in fork_database::add( block_state_ptr )) is to ensure that the block still links to an existing block in the fork database. Without that check, it is possible to add a block into the fork database that does not link. For example, a user of controller could in theory:

  1. Call create_block_state_future on two different blocks A and B where both currently link to existing blocks in the fork database and where B builds off of the current head.
  2. Then calls push_block with the future of block A which causes: a fork switch, LIB advancement, and purging of orphaned branches which may include the block that the block B builds off of.
  3. Then calls push_block with the future of block B.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current code does not have more than one create_block_state_future in play at a time. It is single threaded from producer plugin on_incoming_block to push_block. The create_block_state_future creation of block_state which includes digest, merkle, and signee verification can run at the same time as abort_block but the rest of the order is all single threaded.

Now if we updated the code so that more than one create_block_state_future could be in flight at a time then that could certainly be a problem. That would take some work as it does verify previous is in the fork_db. Could create a version of create_block_state_future that takes the previous block so that a whole chain of blocks could be processed at once and then verify in fork_db add that the previous is there. However, that would require changing net_plugin to receive more than one block at a time or queue up blocks which would increase latency which is what this is attempting to reduce.

That said the extra check doesn't hurt and should be quick, so I'll go ahead and add it.

@wanderingbort wanderingbort merged commit b828413 into develop Nov 13, 2018
@wanderingbort wanderingbort deleted the light-block-verification branch November 13, 2018 16:56
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants