From a347e9764908e6af252e881adc91a1a649437e27 Mon Sep 17 00:00:00 2001 From: satyakoneru Date: Tue, 24 Dec 2019 00:30:49 +1100 Subject: [PATCH] SON207 - Introduce scheduling for SONs similar to witnesses (#251) --- libraries/chain/db_block.cpp | 18 ++- libraries/chain/db_init.cpp | 24 +++ libraries/chain/db_maint.cpp | 11 +- libraries/chain/db_witness_schedule.cpp | 142 ++++++++++++++++++ .../chain/include/graphene/chain/database.hpp | 18 +++ .../include/graphene/chain/protocol/types.hpp | 6 +- .../chain/witness_schedule_object.hpp | 57 +++++++ 7 files changed, 266 insertions(+), 10 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index dafdc3ff..c6b4564c 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -649,8 +649,13 @@ void database::_apply_block( const signed_block& next_block ) _current_virtual_op = 0; } - if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM) - update_witness_schedule(next_block); + if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM) { + update_witness_schedule(next_block); + if(global_props.active_sons.size() > 0) { + update_son_schedule(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); @@ -678,8 +683,13 @@ void database::_apply_block( const signed_block& next_block ) // update_global_dynamic_data() as perhaps these methods only need // to be called for header validation? update_maintenance_flag( maint_needed ); - if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) - update_witness_schedule(); + if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) { + update_witness_schedule(); + if(global_props.active_sons.size() > 0) { + update_son_schedule(); + } + } + if( !_node_property_object.debug_updates.empty() ) apply_debug_updates(); diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index c4ddad18..31c0dcd5 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -314,6 +314,7 @@ void database::initialize_indexes() add_index< primary_index> >(); add_index< primary_index > >(); add_index< primary_index > >(); + add_index< primary_index > >(); add_index< primary_index > >(); add_index< primary_index< special_authority_index > >(); add_index< primary_index< buyback_index > >(); @@ -947,6 +948,29 @@ void database::init_genesis(const genesis_state_type& genesis_state) }); assert( wso.id == witness_schedule_id_type() ); + // Initialize witness schedule +#ifndef NDEBUG + const son_schedule_object& sso = +#endif + create([&](son_schedule_object& _sso) + { + // for scheduled + memset(_sso.rng_seed.begin(), 0, _sso.rng_seed.size()); + + witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV); + + auto init_witnesses = get_global_properties().active_witnesses; + + _sso.scheduler = son_scheduler(); + _sso.scheduler._min_token_count = std::max(int(init_witnesses.size()) / 2, 1); + + + _sso.last_scheduling_block = 0; + + _sso.recent_slots_filled = fc::uint128::max_value(); + }); + assert( sso.id == son_schedule_id_type() ); + // Enable fees modify(get_global_properties(), [&genesis_state](global_property_object& p) { p.parameters.current_fees = genesis_state.initial_parameters.current_fees; diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index cae17eda..8fb72566 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -455,11 +455,12 @@ void database::update_active_sons() }); }); - //const witness_schedule_object& wso = witness_schedule_id_type()(*this); - //modify(wso, [&](witness_schedule_object& _wso) - //{ - // _wso.scheduler.update(gpo.active_witnesses); - //}); + const son_schedule_object& sso = son_schedule_id_type()(*this); + modify(sso, [&](son_schedule_object& _sso) + { + flat_set active_sons(gpo.active_sons.begin(), gpo.active_sons.end()); + _sso.scheduler.update(active_sons); + }); } FC_CAPTURE_AND_RETHROW() } void database::initialize_budget_record( fc::time_point_sec now, budget_record& rec )const diff --git a/libraries/chain/db_witness_schedule.cpp b/libraries/chain/db_witness_schedule.cpp index e12c81dc..3ce11443 100644 --- a/libraries/chain/db_witness_schedule.cpp +++ b/libraries/chain/db_witness_schedule.cpp @@ -26,6 +26,7 @@ #include #include #include +#include namespace graphene { namespace chain { @@ -72,6 +73,47 @@ witness_id_type database::get_scheduled_witness( uint32_t slot_num )const return wid; } +son_id_type database::get_scheduled_son( uint32_t slot_num )const +{ + son_id_type sid; + const global_property_object& gpo = get_global_properties(); + if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) + { + const dynamic_global_property_object& dpo = get_dynamic_global_properties(); + const son_schedule_object& sso = son_schedule_id_type()(*this); + uint64_t current_aslot = dpo.current_aslot + slot_num; + return sso.current_shuffled_sons[ current_aslot % sso.current_shuffled_sons.size() ]; + } + if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM && + slot_num != 0 ) + { + const son_schedule_object& sso = son_schedule_id_type()(*this); + // ask the near scheduler who goes in the given slot + bool slot_is_near = sso.scheduler.get_slot(slot_num-1, sid); + if(! slot_is_near) + { + // if the near scheduler doesn't know, we have to extend it to + // a far scheduler. + // n.b. instantiating it is slow, but block gaps long enough to + // need it are likely pretty rare. + + witness_scheduler_rng far_rng(sso.rng_seed.begin(), GRAPHENE_FAR_SCHEDULE_CTR_IV); + + far_future_son_scheduler far_scheduler = + far_future_son_scheduler(sso.scheduler, far_rng); + if(!far_scheduler.get_slot(slot_num-1, sid)) + { + // no scheduled son -- somebody set up us the bomb + // n.b. this code path is impossible, the present + // implementation of far_future_son_scheduler + // returns true unconditionally + assert( false ); + } + } + } + return sid; +} + fc::time_point_sec database::get_slot_time(uint32_t slot_num)const { if( slot_num == 0 ) @@ -146,6 +188,41 @@ void database::update_witness_schedule() } } +void database::update_son_schedule() +{ + const son_schedule_object& sso = son_schedule_id_type()(*this); + const global_property_object& gpo = get_global_properties(); + + if( head_block_num() % gpo.active_sons.size() == 0 ) + { + modify( sso, [&]( son_schedule_object& _sso ) + { + _sso.current_shuffled_sons.clear(); + _sso.current_shuffled_sons.reserve( gpo.active_sons.size() ); + + for( const son_id_type& w : gpo.active_sons ) + _sso.current_shuffled_sons.push_back( w ); + + auto now_hi = uint64_t(head_block_time().sec_since_epoch()) << 32; + for( uint32_t i = 0; i < _sso.current_shuffled_sons.size(); ++i ) + { + /// High performance random generator + /// http://xorshift.di.unimi.it/ + uint64_t k = now_hi + uint64_t(i)*2685821657736338717ULL; + k ^= (k >> 12); + k ^= (k << 25); + k ^= (k >> 27); + k *= 2685821657736338717ULL; + + uint32_t jmax = _sso.current_shuffled_sons.size() - i; + uint32_t j = i + k%jmax; + std::swap( _sso.current_shuffled_sons[i], + _sso.current_shuffled_sons[j] ); + } + }); + } +} + vector database::get_near_witness_schedule()const { const witness_schedule_object& wso = witness_schedule_id_type()(*this); @@ -226,6 +303,71 @@ void database::update_witness_schedule(const signed_block& next_block) idump( ( double(total_time/1000000.0)/calls) ); } +void database::update_son_schedule(const signed_block& next_block) +{ + auto start = fc::time_point::now(); + const global_property_object& gpo = get_global_properties(); + const son_schedule_object& sso = get(son_schedule_id_type()); + uint32_t schedule_needs_filled = gpo.active_sons.size(); + uint32_t schedule_slot = get_slot_at_time(next_block.timestamp); + + // We shouldn't be able to generate _pending_block with timestamp + // in the past, and incoming blocks from the network with timestamp + // in the past shouldn't be able to make it this far without + // triggering FC_ASSERT elsewhere + + assert( schedule_slot > 0 ); + + son_id_type first_son; + bool slot_is_near = sso.scheduler.get_slot( schedule_slot-1, first_son ); + + son_id_type son; + + const dynamic_global_property_object& dpo = get_dynamic_global_properties(); + + assert( dpo.random.data_size() == witness_scheduler_rng::seed_length ); + assert( witness_scheduler_rng::seed_length == sso.rng_seed.size() ); + + modify(sso, [&](son_schedule_object& _sso) + { + _sso.slots_since_genesis += schedule_slot; + witness_scheduler_rng rng(sso.rng_seed.data, _sso.slots_since_genesis); + + _sso.scheduler._min_token_count = std::max(int(gpo.active_sons.size()) / 2, 1); + + if( slot_is_near ) + { + uint32_t drain = schedule_slot; + while( drain > 0 ) + { + if( _sso.scheduler.size() == 0 ) + break; + _sso.scheduler.consume_schedule(); + --drain; + } + } + else + { + _sso.scheduler.reset_schedule( first_son ); + } + while( !_sso.scheduler.get_slot(schedule_needs_filled, son) ) + { + if( _sso.scheduler.produce_schedule(rng) & emit_turn ) + memcpy(_sso.rng_seed.begin(), dpo.random.data(), dpo.random.data_size()); + } + _sso.last_scheduling_block = next_block.block_num(); + _sso.recent_slots_filled = ( + (_sso.recent_slots_filled << 1) + + 1) << (schedule_slot - 1); + }); + auto end = fc::time_point::now(); + static uint64_t total_time = 0; + static uint64_t calls = 0; + total_time += (end - start).count(); + if( ++calls % 1000 == 0 ) + 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 ); diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 1e989a21..719c6240 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -240,6 +240,22 @@ namespace graphene { namespace chain { */ witness_id_type get_scheduled_witness(uint32_t slot_num)const; + /** + * @brief Get the son scheduled for block production in a slot. + * + * slot_num always corresponds to a time in the future. + * + * If slot_num == 1, returns the next scheduled son. + * If slot_num == 2, returns the next scheduled son after + * 1 block gap. + * + * Use the get_slot_time() and get_slot_at_time() functions + * to convert between slot_num and timestamp. + * + * Passing slot_num == 0 returns GRAPHENE_NULL_WITNESS + */ + son_id_type get_scheduled_son(uint32_t slot_num)const; + /** * Get the time at which the given slot occurs. * @@ -263,6 +279,8 @@ namespace graphene { namespace chain { vector get_near_witness_schedule()const; void update_witness_schedule(); void update_witness_schedule(const signed_block& next_block); + void update_son_schedule(); + void update_son_schedule(const signed_block& next_block); void check_lottery_end_by_participants( asset_id_type asset_id ); void check_ending_lotteries(); diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 463c862f..5b040850 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -177,7 +177,8 @@ namespace graphene { namespace chain { impl_global_betting_statistics_object_type, impl_lottery_balance_object_type, impl_sweeps_vesting_balance_object_type, - impl_son_statistics_object_type + impl_son_statistics_object_type, + impl_son_schedule_object_type }; //typedef fc::unsigned_int object_id_type; @@ -264,6 +265,7 @@ namespace graphene { namespace chain { class lottery_balance_object; class sweeps_vesting_balance_object; class son_statistics_object; + class son_schedule_object; typedef object_id< implementation_ids, impl_global_property_object_type, global_property_object> global_property_id_type; typedef object_id< implementation_ids, impl_dynamic_global_property_object_type, dynamic_global_property_object> dynamic_global_property_id_type; @@ -293,6 +295,7 @@ namespace graphene { namespace chain { typedef object_id< implementation_ids, impl_lottery_balance_object_type, lottery_balance_object > lottery_balance_id_type; typedef object_id< implementation_ids, impl_sweeps_vesting_balance_object_type, sweeps_vesting_balance_object> sweeps_vesting_balance_id_type; typedef object_id< implementation_ids, impl_son_statistics_object_type, son_statistics_object > son_statistics_id_type; + typedef object_id< implementation_ids, impl_son_schedule_object_type, son_schedule_object> son_schedule_id_type; typedef fc::array symbol_type; typedef fc::ripemd160 block_id_type; @@ -453,6 +456,7 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type, (impl_lottery_balance_object_type) (impl_sweeps_vesting_balance_object_type) (impl_son_statistics_object_type) + (impl_son_schedule_object_type) ) FC_REFLECT_TYPENAME( graphene::chain::share_type ) diff --git a/libraries/chain/include/graphene/chain/witness_schedule_object.hpp b/libraries/chain/include/graphene/chain/witness_schedule_object.hpp index e4c4bb51..fc7d6d10 100644 --- a/libraries/chain/include/graphene/chain/witness_schedule_object.hpp +++ b/libraries/chain/include/graphene/chain/witness_schedule_object.hpp @@ -31,6 +31,7 @@ namespace graphene { namespace chain { class witness_schedule_object; +class son_schedule_object; typedef hash_ctr_rng< /* HashClass = */ fc::sha256, @@ -53,6 +54,22 @@ typedef generic_far_future_witness_scheduler< /* debug = */ true > far_future_witness_scheduler; +typedef generic_witness_scheduler< + /* WitnessID = */ son_id_type, + /* RNG = */ witness_scheduler_rng, + /* CountType = */ decltype( chain_parameters::maximum_son_count ), + /* OffsetType = */ uint32_t, + /* debug = */ true + > son_scheduler; + +typedef generic_far_future_witness_scheduler< + /* WitnessID = */ son_id_type, + /* RNG = */ witness_scheduler_rng, + /* CountType = */ decltype( chain_parameters::maximum_son_count ), + /* OffsetType = */ uint32_t, + /* debug = */ true + > far_future_son_scheduler; + class witness_schedule_object : public graphene::db::abstract_object { public: @@ -73,6 +90,26 @@ class witness_schedule_object : public graphene::db::abstract_object +{ + public: + static const uint8_t space_id = implementation_ids; + static const uint8_t type_id = impl_son_schedule_object_type; + + vector< son_id_type > current_shuffled_sons; + + son_scheduler scheduler; + uint32_t last_scheduling_block; + uint64_t slots_since_genesis = 0; + fc::array< char, sizeof(secret_hash_type) > rng_seed; + + /** + * Not necessary for consensus, but used for figuring out the participation rate. + * The nth bit is 0 if the nth slot was unfilled, else it is 1. + */ + fc::uint128 recent_slots_filled; +}; + } } @@ -96,3 +133,23 @@ FC_REFLECT_DERIVED( (recent_slots_filled) (current_shuffled_witnesses) ) +FC_REFLECT( graphene::chain::son_scheduler, + (_turns) + (_tokens) + (_min_token_count) + (_ineligible_waiting_for_token) + (_ineligible_no_turn) + (_eligible) + (_schedule) + (_lame_duck) + ) +FC_REFLECT_DERIVED( + graphene::chain::son_schedule_object, + (graphene::db::object), + (scheduler) + (last_scheduling_block) + (slots_since_genesis) + (rng_seed) + (recent_slots_filled) + (current_shuffled_sons) +)