From 77dd8a93590dc8c770029076c72705dc8fe34898 Mon Sep 17 00:00:00 2001 From: Srdjan Obucina Date: Mon, 2 Sep 2019 15:03:00 +0200 Subject: [PATCH] GRPH-76 - Short-cut long sequences of missed blocks Fixes database::update_global_dynamic_data to speed up counting missed blocks. (This also fixes a minor issue with counting - the previous algorithm would skip missed blocks for the witness who signed the first block after the gap.) --- libraries/chain/db_block.cpp | 3 +- libraries/chain/db_update.cpp | 39 +++---------------- libraries/chain/db_witness_schedule.cpp | 16 ++++++++ .../chain/include/graphene/chain/database.hpp | 5 ++- tests/tests/block_tests.cpp | 33 ++++++++++++++++ 5 files changed, 60 insertions(+), 36 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index 9b2c7f36..e9124594 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -612,7 +612,8 @@ void database::_apply_block( const signed_block& next_block ) if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM) update_witness_schedule(next_block); - update_global_dynamic_data(next_block); + const uint32_t missed = update_witness_missed_blocks( next_block ); + update_global_dynamic_data( next_block, missed ); update_signing_witness(signing_witness, next_block); update_last_irreversible_block(); diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index ad98837e..090a0a23 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -43,43 +43,13 @@ namespace graphene { namespace chain { -void database::update_global_dynamic_data( const signed_block& b ) +void database::update_global_dynamic_data( const signed_block& b, const uint32_t missed_blocks ) { const dynamic_global_property_object& _dgp = dynamic_global_property_id_type(0)(*this); const global_property_object& gpo = get_global_properties(); - uint32_t missed_blocks = get_slot_at_time( b.timestamp ); - -//#define DIRTY_TRICK // problem with missed_blocks can occur when "maintenance_interval" set to few minutes -#ifdef DIRTY_TRICK - if (missed_blocks != 0) { -#else - assert( missed_blocks != 0 ); -#endif -// bad if-condition, this code needs to execute for both shuffled and rng algorithms -// if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) -// { - missed_blocks--; - for( uint32_t i = 0; i < missed_blocks; ++i ) { - const auto& witness_missed = get_scheduled_witness( i+1 )(*this); - if( witness_missed.id != b.witness ) { - /* - const auto& witness_account = witness_missed.witness_account(*this); - if( (fc::time_point::now() - b.timestamp) < fc::seconds(30) ) - wlog( "Witness ${name} missed block ${n} around ${t}", ("name",witness_account.name)("n",b.block_num())("t",b.timestamp) ); - */ - - modify( witness_missed, [&]( witness_object& w ) { - w.total_missed++; - }); - } - } -// } -#ifdef DIRTY_TRICK - } -#endif // dynamic global properties updating - modify( _dgp, [&]( dynamic_global_property_object& dgp ){ + modify( _dgp, [&b,this,missed_blocks]( dynamic_global_property_object& dgp ){ secret_hash_type::encoder enc; fc::raw::pack( enc, dgp.random ); fc::raw::pack( enc, b.previous_secret ); @@ -87,9 +57,10 @@ void database::update_global_dynamic_data( const signed_block& b ) _random_number_generator = fc::hash_ctr_rng(dgp.random.data()); - if( BOOST_UNLIKELY( b.block_num() == 1 ) ) + const uint32_t block_num = b.block_num(); + if( BOOST_UNLIKELY( block_num == 1 ) ) dgp.recently_missed_count = 0; - else if( _checkpoints.size() && _checkpoints.rbegin()->first >= b.block_num() ) + else if( _checkpoints.size() && _checkpoints.rbegin()->first >= block_num ) dgp.recently_missed_count = 0; else if( missed_blocks ) dgp.recently_missed_count += GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT*missed_blocks; diff --git a/libraries/chain/db_witness_schedule.cpp b/libraries/chain/db_witness_schedule.cpp index 3a2378a9..e12c81dc 100644 --- a/libraries/chain/db_witness_schedule.cpp +++ b/libraries/chain/db_witness_schedule.cpp @@ -226,6 +226,22 @@ void database::update_witness_schedule(const signed_block& next_block) idump( ( double(total_time/1000000.0)/calls) ); } +uint32_t database::update_witness_missed_blocks( const signed_block& b ) +{ + uint32_t missed_blocks = get_slot_at_time( b.timestamp ); + FC_ASSERT( missed_blocks != 0, "Trying to push double-produced block onto current block?!" ); + missed_blocks--; + const auto& witnesses = witness_schedule_id_type()(*this).current_shuffled_witnesses; + if( missed_blocks < witnesses.size() ) + for( uint32_t i = 0; i < missed_blocks; ++i ) { + const auto& witness_missed = get_scheduled_witness( i+1 )(*this); + modify( witness_missed, []( witness_object& w ) { + w.total_missed++; + }); + } + return missed_blocks; +} + uint32_t database::witness_participation_rate()const { const global_property_object& gpo = get_global_properties(); diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 0c6dcb0f..84d1ea9b 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -488,8 +488,11 @@ namespace graphene { namespace chain { const witness_object& _validate_block_header( const signed_block& next_block )const; void create_block_summary(const signed_block& next_block); + //////////////////// db_witness_schedule.cpp //////////////////// + uint32_t update_witness_missed_blocks( const signed_block& b ); + //////////////////// db_update.cpp //////////////////// - void update_global_dynamic_data( const signed_block& b ); + void update_global_dynamic_data( const signed_block& b, const uint32_t missed_blocks ); void update_signing_witness(const witness_object& signing_witness, const signed_block& new_block); void update_last_irreversible_block(); void clear_expired_transactions(); diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index 07609d4b..53e99e5f 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include @@ -1288,18 +1289,50 @@ BOOST_AUTO_TEST_CASE( genesis_reserve_ids ) } } +BOOST_FIXTURE_TEST_CASE( miss_some_blocks, database_fixture ) +{ try { + std::vector witnesses = witness_schedule_id_type()(db).current_shuffled_witnesses; + BOOST_CHECK_EQUAL( 10, witnesses.size() ); + // database_fixture constructor calls generate_block once, signed by witnesses[0] + generate_block(); // witnesses[1] + generate_block(); // witnesses[2] + for( const auto& id : witnesses ) + BOOST_CHECK_EQUAL( 0, id(db).total_missed ); + // generate_blocks generates another block *now* (witnesses[3]) + // and one at now+10 blocks (witnesses[12%10]) + generate_blocks( db.head_block_time() + db.get_global_properties().parameters.block_interval * 10, true ); + // i. e. 8 blocks are missed in between by witness[4..11%10] + for( uint32_t i = 0; i < witnesses.size(); i++ ) + BOOST_CHECK_EQUAL( (i+7) % 10 < 2 ? 0 : 1, witnesses[i](db).total_missed ); +} FC_LOG_AND_RETHROW() } + BOOST_FIXTURE_TEST_CASE( miss_many_blocks, database_fixture ) { try { + auto get_misses = []( database& db ) { + std::map< witness_id_type, uint32_t > misses; + for( const auto& witness_id : witness_schedule_id_type()(db).current_shuffled_witnesses ) + misses[witness_id] = witness_id(db).total_missed; + return misses; + }; generate_block(); generate_block(); generate_block(); + auto missed_before = get_misses( db ); // miss 10 maintenance intervals generate_blocks( db.get_dynamic_global_properties().next_maintenance_time + db.get_global_properties().parameters.maintenance_interval * 10, true ); generate_block(); generate_block(); generate_block(); + auto missed_after = get_misses( db ); + BOOST_CHECK_EQUAL( missed_before.size(), missed_after.size() ); + for( const auto& miss : missed_before ) + { + const auto& after = missed_after.find( miss.first ); + BOOST_REQUIRE( after != missed_after.end() ); + BOOST_CHECK_EQUAL( miss.second, after->second ); + } } catch (fc::exception& e) {