From eeeab17477a635fcff7f2d294dff8ecf25449b10 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Wed, 19 Aug 2015 13:07:13 -0400 Subject: [PATCH] Polish out-of-order-block handling, write test case --- libraries/chain/fork_database.cpp | 12 ++--- .../include/graphene/chain/fork_database.hpp | 2 + tests/tests/block_tests.cpp | 53 +++++++++++++++++++ 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index f867e69a..dcd48be1 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -51,7 +51,7 @@ shared_ptr fork_database::push_block(const signed_block& b) auto item = std::make_shared(b); try { _push_block(item); - } + } catch ( const unlinkable_block_exception& e ) { wlog( "Pushing block to fork database that failed to link." ); @@ -65,13 +65,14 @@ void fork_database::_push_block(const item_ptr& item) if( _head ) // make sure the block is within the range that we are caching { FC_ASSERT( item->num > std::max( 0, int64_t(_head->num) - (_max_size) ) ); - FC_ASSERT( item->num < _head->num + 32 ); + FC_ASSERT( item->num < _head->num + MAX_BLOCK_REORDERING ); } if( _head && item->previous_id() != block_id_type() ) { - auto itr = _index.get().find(item->previous_id()); - GRAPHENE_ASSERT(itr != _index.get().end(), unlinkable_block_exception, "block does not link to known chain"); + auto& index = _index.get(); + auto itr = index.find(item->previous_id()); + GRAPHENE_ASSERT(itr != index.end(), unlinkable_block_exception, "block does not link to known chain"); FC_ASSERT(!(*itr)->invalid); item->prev = *itr; } @@ -97,14 +98,13 @@ void fork_database::_push_block(const item_ptr& item) void fork_database::_push_next( const item_ptr& new_item ) { auto& prev_idx = _unlinked_index.get(); - vector newly_inserted; auto itr = prev_idx.find( new_item->id ); while( itr != prev_idx.end() ) { auto tmp = *itr; prev_idx.erase( itr ); - _push_block( tmp ); + _push_block( tmp ); itr = prev_idx.find( new_item->id ); } diff --git a/libraries/chain/include/graphene/chain/fork_database.hpp b/libraries/chain/include/graphene/chain/fork_database.hpp index a2144c41..34801b1a 100644 --- a/libraries/chain/include/graphene/chain/fork_database.hpp +++ b/libraries/chain/include/graphene/chain/fork_database.hpp @@ -63,6 +63,8 @@ namespace graphene { namespace chain { { public: typedef vector branch_type; + /// The maximum number of blocks that may be skipped in an out-of-order push + const static int MAX_BLOCK_REORDERING = 32; fork_database(); void reset(); diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index 7af5fc91..427fdd51 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -276,6 +276,59 @@ BOOST_AUTO_TEST_CASE( fork_blocks ) } } +BOOST_AUTO_TEST_CASE( out_of_order_blocks ) +{ + try { + fc::temp_directory data_dir1( graphene::utilities::temp_directory_path() ); + fc::temp_directory data_dir2( graphene::utilities::temp_directory_path() ); + + database db1; + db1.open(data_dir1.path(), make_genesis); + database db2; + db2.open(data_dir2.path(), make_genesis); + BOOST_CHECK( db1.get_chain_id() == db2.get_chain_id() ); + + auto init_account_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) ); + auto b1 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + auto b2 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + auto b3 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + auto b4 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + auto b5 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + auto b6 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + auto b7 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + auto b8 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + auto b9 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + auto b10 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + auto b11 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + auto b12 = db1.generate_block(db1.get_slot_time(1), db1.get_scheduled_witness(1).first, init_account_priv_key, database::skip_nothing); + BOOST_CHECK_EQUAL(db1.head_block_num(), 12); + BOOST_CHECK_EQUAL(db2.head_block_num(), 0); + PUSH_BLOCK( db2, b1 ); + BOOST_CHECK_EQUAL(db2.head_block_num(), 1); + PUSH_BLOCK( db2, b3 ); + BOOST_CHECK_EQUAL(db2.head_block_num(), 1); + PUSH_BLOCK( db2, b2 ); + BOOST_CHECK_EQUAL(db2.head_block_num(), 3); + PUSH_BLOCK( db2, b5 ); + PUSH_BLOCK( db2, b6 ); + PUSH_BLOCK( db2, b7 ); + BOOST_CHECK_EQUAL(db2.head_block_num(), 3); + PUSH_BLOCK( db2, b4 ); + BOOST_CHECK_EQUAL(db2.head_block_num(), 7); + PUSH_BLOCK( db2, b8 ); + BOOST_CHECK_EQUAL(db2.head_block_num(), 8); + PUSH_BLOCK( db2, b11 ); + PUSH_BLOCK( db2, b10 ); + PUSH_BLOCK( db2, b12 ); + BOOST_CHECK_EQUAL(db2.head_block_num(), 8); + PUSH_BLOCK( db2, b9 ); + BOOST_CHECK_EQUAL(db2.head_block_num(), 12); + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + BOOST_AUTO_TEST_CASE( undo_pending ) { try {