From d2af8636c116a66d530af34bc3565f73b7ed29eb Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Thu, 11 Jan 2024 15:53:22 -0600 Subject: [PATCH] GH-1980 Implement proposer policy change --- libraries/chain/block_header_state.cpp | 23 +- libraries/chain/controller.cpp | 239 ++++++++++++------ .../eosio/chain/block_header_state.hpp | 16 +- .../eosio/chain/block_header_state_utils.hpp | 6 +- .../eosio/chain/block_state_legacy.hpp | 2 +- .../chain/include/eosio/chain/controller.hpp | 9 +- .../hotstuff/instant_finality_extension.hpp | 8 +- plugins/chain_plugin/chain_plugin.cpp | 6 +- plugins/net_plugin/net_plugin.cpp | 4 +- unittests/block_header_tests.cpp | 8 +- unittests/chain_tests.cpp | 10 +- unittests/producer_schedule_tests.cpp | 115 +++++---- 12 files changed, 290 insertions(+), 156 deletions(-) diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index 4a6161269c..d52782411d 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -1,13 +1,14 @@ #include #include #include +#include #include #include namespace eosio::chain { producer_authority block_header_state::get_scheduled_producer(block_timestamp_type t) const { - return detail::get_scheduled_producer(proposer_policy->proposer_schedule.producers, t); + return detail::get_scheduled_producer(active_proposer_policy->proposer_schedule.producers, t); } #warning Add last_proposed_finalizer_policy_generation to snapshot_block_header_state_v3, see header file TODO @@ -61,13 +62,29 @@ block_header_state block_header_state::next(block_header_state_input& input) con result.header = block_header { .timestamp = input.timestamp, // [greg todo] do we have to do the slot++ stuff from the legacy version? .producer = input.producer, + .confirmed = hs_block_confirmed, // todo: consider 0 instead .previous = input.parent_id, .transaction_mroot = input.transaction_mroot, .action_mroot = input.action_mroot, - //.schedule_version = ?, [greg todo] - //.new_producers = ? [greg todo] + .schedule_version = header.schedule_version }; + if(!proposer_policies.empty()) { + auto it = proposer_policies.begin(); + if (it->first <= input.timestamp) { + result.active_proposer_policy = it->second; + result.header.schedule_version = header.schedule_version + 1; + result.active_proposer_policy->proposer_schedule.version = result.header.schedule_version; + result.proposer_policies = { ++it, proposer_policies.end() }; + } else { + result.proposer_policies = proposer_policies; + } + } + if (input.new_proposer_policy) { + // called when assembling the block + result.proposer_policies[result.header.timestamp] = input.new_proposer_policy; + } + // core // ---- if (input.qc_info) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index d5b2857a14..d52673bb2e 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +43,7 @@ #include #include #include +#include namespace eosio::chain { @@ -184,9 +186,14 @@ struct block_data_t { const producer_authority_schedule& head_active_schedule_auth() { return std::visit([](const auto& bd) -> const producer_authority_schedule& { return bd.head->active_schedule_auth(); }, v); } - - const producer_authority_schedule& head_pending_schedule_auth() { - return std::visit([](const auto& bd) -> const producer_authority_schedule& { return bd.head->pending_schedule_auth(); }, v); + + const producer_authority_schedule* head_pending_schedule_auth_legacy() { + return std::visit(overloaded{ + [](const block_data_legacy_t& bd) -> const producer_authority_schedule* { + return bd.head->pending_schedule_auth(); + }, + [](const block_data_new_t&) -> const producer_authority_schedule* { return nullptr; } + }, v); } const block_id_type& head_block_id() const { @@ -201,6 +208,29 @@ struct block_data_t { return std::visit([](const auto& bd) -> const signed_block_ptr& { return bd.head->block; }, v); } + void replace_producer_keys( const public_key_type& key ) { + ilog("Replace producer keys with ${k}", ("k", key)); + + std::visit( + overloaded{ + [&](const block_data_legacy_t& bd) { + auto version = bd.head->pending_schedule.schedule.version; + bd.head->pending_schedule = {}; + bd.head->pending_schedule.schedule.version = version; + for (auto& prod: bd.head->active_schedule.producers ) { + ilog("${n}", ("n", prod.producer_name)); + std::visit([&](auto &auth) { + auth.threshold = 1; + auth.keys = {key_weight{key, 1}}; + }, prod.authority); + } + }, + [](const block_data_new_t&) { + // TODO IF: add instant-finality implementation, will need to replace finalizers as well + } + }, v); + } + // --------------- access fork_db head ---------------------------------------------------------------------- bool fork_db_has_head() const { return std::visit([&](const auto& bd) { return !!bd.fork_db_head(); }, v); @@ -300,8 +330,20 @@ struct completed_block { return std::visit([](const auto& bsp) -> const producer_authority_schedule& { return bsp->active_schedule_auth(); }, bsp); } - const producer_authority_schedule& pending_producers() const { - return std::visit([](const auto& bsp) -> const producer_authority_schedule& { return bsp->pending_schedule_auth();}, bsp); + const producer_authority_schedule* next_producers() const { + return std::visit(overloaded{ + [](const block_state_legacy_ptr& bsp) -> const producer_authority_schedule* { return bsp->pending_schedule_auth();}, + [](const block_state_ptr& bsp) -> const producer_authority_schedule* { + return bsp->proposer_policies.empty() ? nullptr : &bsp->proposer_policies.begin()->second->proposer_schedule; + } + }, bsp); + } + + const producer_authority_schedule* pending_producers_legacy() const { + return std::visit(overloaded{ + [](const block_state_legacy_ptr& bsp) -> const producer_authority_schedule* { return &bsp->pending_schedule.schedule; }, + [](const block_state_ptr&) -> const producer_authority_schedule* { return nullptr; } + }, bsp); } bool is_protocol_feature_activated(const digest_type& digest) const { @@ -416,16 +458,28 @@ struct assembled_block { v); } - using opt_pas = const std::optional; + const producer_authority_schedule* next_producers() const { + return std::visit(overloaded{ + [](const assembled_block_dpos& ab) -> const producer_authority_schedule* { + return ab.new_producer_authority_cache.has_value() ? &ab.new_producer_authority_cache.value() : nullptr; + }, + [](const assembled_block_if& ab) -> const producer_authority_schedule* { + return ab.bhs.proposer_policies.empty() ? nullptr : &ab.bhs.proposer_policies.begin()->second->proposer_schedule; + } + }, + v); + } - opt_pas& pending_producers() const { - return std::visit( - overloaded{[](const assembled_block_dpos& ab) -> opt_pas& { return ab.new_producer_authority_cache; }, - [](const assembled_block_if& ab) -> opt_pas& { - static opt_pas empty; - return empty; // [greg todo] - }}, - v); + const producer_authority_schedule* pending_producers_legacy() const { + return std::visit(overloaded{ + [](const assembled_block_dpos& ab) -> const producer_authority_schedule* { + return ab.new_producer_authority_cache.has_value() ? &ab.new_producer_authority_cache.value() : nullptr; + }, + [](const assembled_block_if&) -> const producer_authority_schedule* { + return nullptr; + } + }, + v); } const block_signing_authority& pending_block_signing_authority() const { @@ -532,7 +586,7 @@ struct building_block { const uint32_t block_num; // Cached: parent.block_num() + 1 // Members below (as well as non-const members of building_block_common) start from initial state and are mutated as the block is built. - std::optional new_proposer_policy; + std::shared_ptr new_proposer_policy; building_block_if(const block_header_state& parent, const building_block_input& input) : building_block_common(input.new_protocol_feature_activations) @@ -541,7 +595,7 @@ struct building_block { , timestamp(input.timestamp) , active_producer_authority{input.producer, [&]() -> block_signing_authority { - const auto& pas = parent.proposer_policy->proposer_schedule; + const auto& pas = parent.active_proposer_policy->proposer_schedule; for (const auto& pa : pas.producers) if (pa.producer_name == input.producer) return pa.authority; @@ -549,7 +603,7 @@ struct building_block { return {}; }()} , prev_activated_protocol_features(parent.activated_protocol_features) - , active_proposer_policy(parent.proposer_policy) + , active_proposer_policy(parent.active_proposer_policy) , block_num(parent.block_num() + 1) {} bool is_protocol_feature_activated(const digest_type& digest) const { @@ -558,6 +612,14 @@ struct building_block { uint32_t get_block_num() const { return block_num; } + uint32_t get_next_proposer_schedule_version() const { + if (!parent.proposer_policies.empty()) { + return (--parent.proposer_policies.end())->second->proposer_schedule.version + 1; + } + assert(active_proposer_policy); + return active_proposer_policy->proposer_schedule.version + 1; + } + }; std::variant v; @@ -599,6 +661,19 @@ struct building_block { std::visit([&](auto& bb) { bb.new_finalizer_policy = fin_pol; }, v); } + int64_t set_proposed_producers( std::vector producers ) { + return std::visit( + overloaded{[](building_block_dpos&) -> int64_t { return -1; }, + [&](building_block_if& bb) -> int64_t { + bb.new_proposer_policy = std::make_shared(); + bb.new_proposer_policy->active_time = detail::get_next_next_round_block_time(bb.timestamp); + bb.new_proposer_policy->proposer_schedule.producers = std::move(producers); + bb.new_proposer_policy->proposer_schedule.version = bb.get_next_proposer_schedule_version(); + return bb.new_proposer_policy->proposer_schedule.version; + }}, + v); + } + deque extract_trx_metas() { return std::visit([](auto& bb) { return std::move(bb.pending_trx_metas); }, v); } @@ -681,15 +756,30 @@ struct building_block { v); } - const producer_authority_schedule& pending_producers() const { - return std::visit(overloaded{[](const building_block_dpos& bb) -> const producer_authority_schedule& { + const producer_authority_schedule* next_producers() const { + return std::visit(overloaded{[](const building_block_dpos& bb) -> const producer_authority_schedule* { if (bb.new_pending_producer_schedule) - return *bb.new_pending_producer_schedule; - return bb.pending_block_header_state.prev_pending_schedule.schedule; + return &bb.new_pending_producer_schedule.value(); + return &bb.pending_block_header_state.prev_pending_schedule.schedule; }, - [](const building_block_if& bb) -> const producer_authority_schedule& { - static producer_authority_schedule empty; - return empty; // [greg todo] + [](const building_block_if& bb) -> const producer_authority_schedule* { + if (!bb.parent.proposer_policies.empty()) + return &bb.parent.proposer_policies.begin()->second->proposer_schedule; + if (bb.new_proposer_policy) + return &bb.new_proposer_policy->proposer_schedule; + return nullptr; + }}, + v); + } + + const producer_authority_schedule* pending_producers_legacy() const { + return std::visit(overloaded{[](const building_block_dpos& bb) -> const producer_authority_schedule* { + if (bb.new_pending_producer_schedule) + return &bb.new_pending_producer_schedule.value(); + return &bb.pending_block_header_state.prev_pending_schedule.schedule; + }, + [](const building_block_if&) -> const producer_authority_schedule* { + return nullptr; }}, v); } @@ -761,7 +851,7 @@ struct building_block { }; block_header_state_input bhs_input{ - bb_input, transaction_mroot, action_mroot, bb.new_proposer_policy, bb.new_finalizer_policy, + bb_input, transaction_mroot, action_mroot, std::move(bb.new_proposer_policy), std::move(bb.new_finalizer_policy), qc_data ? qc_data->qc_info : std::optional{} }; assembled_block::assembled_block_if ab{bb.active_producer_authority, bb.parent.next(bhs_input), @@ -830,18 +920,19 @@ struct pending_state { [](const auto& stage) -> const producer_authority_schedule& { return stage.active_producers(); }, _block_stage); } - -#if 0 - // [greg todo] maybe we don't need this and we can have the implementation in controller::pending_producers() - const producer_authority_schedule& pending_producers() const { + + const producer_authority_schedule* pending_producers_legacy() const { return std::visit( - overloaded{ - [](const building_block& bb) -> const producer_authority_schedule& { return bb.pending_producers(); }, - [](const assembled_block& ab) -> const producer_authority_schedule& { return ab.pending_producers(); }, - [](const completed_block& cb) -> const producer_authority_schedule& { return cb.pending_producers(); }}, + [](const auto& stage) -> const producer_authority_schedule* { return stage.pending_producers_legacy(); }, _block_stage); } -#endif + + const producer_authority_schedule* next_producers()const { + return std::visit( + [](const auto& stage) -> const producer_authority_schedule* { return stage.next_producers(); }, + _block_stage); + } + }; struct controller_impl { @@ -896,6 +987,8 @@ struct controller_impl { map< account_name, map > apply_handlers; unordered_map< builtin_protocol_feature_t, std::function, enum_hash > protocol_feature_activation_handlers; + int64_t set_proposed_producers( vector producers ); + int64_t set_proposed_producers_legacy( vector producers ); void pop_block() { uint32_t prev_block_num = block_data.pop_block(); @@ -3932,10 +4025,28 @@ void controller::write_snapshot( const snapshot_writer_ptr& snapshot ) { } int64_t controller::set_proposed_producers( vector producers ) { - const auto& gpo = get_global_properties(); + assert(my->pending); + if (my->pending->is_dpos()) { + return my->set_proposed_producers_legacy(std::move(producers)); + } else { + return my->set_proposed_producers(std::move(producers)); + } +} + +int64_t controller_impl::set_proposed_producers( vector producers ) { + // TODO: zero out gpo.proposed_schedule_block_num and gpo.proposed_schedule on instant finality enabled + if (producers.empty()) + return -1; + + auto& bb = std::get(pending->_block_stage); + return bb.set_proposed_producers(std::move(producers)); +} + +int64_t controller_impl::set_proposed_producers_legacy( vector producers ) { + const auto& gpo = self.get_global_properties(); auto cur_block_num = head_block_num() + 1; - if( producers.size() == 0 && is_builtin_activated( builtin_protocol_feature_t::disallow_empty_producer_schedule ) ) { + if( producers.size() == 0 && self.is_builtin_activated( builtin_protocol_feature_t::disallow_empty_producer_schedule ) ) { return -1; } @@ -3953,17 +4064,18 @@ int64_t controller::set_proposed_producers( vector producers decltype(sch.producers.cend()) end; decltype(end) begin; - const auto& pending_sch = pending_producers(); + const auto* pending_sch = self.pending_producers_legacy(); + assert(pending_sch); // can't be null during dpos - if( pending_sch.producers.size() == 0 ) { - const auto& active_sch = active_producers(); + if( pending_sch->producers.size() == 0 ) { + const auto& active_sch = self.active_producers(); begin = active_sch.producers.begin(); end = active_sch.producers.end(); sch.version = active_sch.version + 1; } else { - begin = pending_sch.producers.begin(); - end = pending_sch.producers.end(); - sch.version = pending_sch.version + 1; + begin = pending_sch->producers.begin(); + end = pending_sch->producers.end(); + sch.version = pending_sch->version + 1; } if( std::equal( producers.begin(), producers.end(), begin, end ) ) @@ -3975,7 +4087,7 @@ int64_t controller::set_proposed_producers( vector producers ilog( "proposed producer schedule with version ${v}", ("v", version) ); - my->db.modify( gpo, [&]( auto& gp ) { + db.modify( gpo, [&]( auto& gp ) { gp.proposed_schedule_block_num = cur_block_num; gp.proposed_schedule = sch; }); @@ -4022,25 +4134,14 @@ const producer_authority_schedule& controller::head_active_producers()const { return my->block_data.head_active_schedule_auth(); } -const producer_authority_schedule& controller::pending_producers()const { - if( !(my->pending) ) - return my->block_data.head_pending_schedule_auth(); // [greg todo] implement pending_producers correctly for IF mode - - if( std::holds_alternative(my->pending->_block_stage) ) - return std::get(my->pending->_block_stage).pending_producers(); - - if( std::holds_alternative(my->pending->_block_stage) ) { - const auto& pp = std::get(my->pending->_block_stage).pending_producers(); - if( pp ) { - return *pp; - } - } +const producer_authority_schedule* controller::pending_producers_legacy()const { + if( !(my->pending) ) + return my->block_data.head_pending_schedule_auth_legacy(); - const auto& bb = std::get(my->pending->_block_stage); - return bb.pending_producers(); + return my->pending->pending_producers_legacy(); } -std::optional controller::proposed_producers()const { +std::optional controller::proposed_producers_legacy()const { const auto& gpo = get_global_properties(); if( !gpo.proposed_schedule_block_num ) return std::optional(); @@ -4048,6 +4149,10 @@ std::optional controller::proposed_producers()const return producer_authority_schedule::from_shared(gpo.proposed_schedule); } +const producer_authority_schedule* controller::next_producers()const { + return my->pending->next_producers(); +} + bool controller::light_validation_allowed() const { if (!my->pending || my->in_trx_requiring_checks) { return false; @@ -4391,26 +4496,14 @@ std::optional controller::extract_chain_id_from_db( const path& s void controller::replace_producer_keys( const public_key_type& key ) { ilog("Replace producer keys with ${k}", ("k", key)); + // can be done even after instant-finality, will be no-op then mutable_db().modify( db().get(), [&]( auto& gp ) { gp.proposed_schedule_block_num = {}; gp.proposed_schedule.version = 0; gp.proposed_schedule.producers.clear(); }); - - auto replace_keys = [&key](auto& fork_db, auto& head) { - auto version = head->pending_schedule.schedule.version; - head->pending_schedule = {}; - head->pending_schedule.schedule.version = version; - for (auto& prod: head->active_schedule.producers ) { - ilog("${n}", ("n", prod.producer_name)); - std::visit([&](auto &auth) { - auth.threshold = 1; - auth.keys = {key_weight{key, 1}}; - }, prod.authority); - } - }; - my->block_data.apply_dpos(replace_keys); // [greg todo]: make it work with `apply` instead of `apply_dpos` + my->block_data.replace_producer_keys(key); } void controller::replace_account_keys( name account, name permission, const public_key_type& key ) { diff --git a/libraries/chain/include/eosio/chain/block_header_state.hpp b/libraries/chain/include/eosio/chain/block_header_state.hpp index da9e525041..c565cbf273 100644 --- a/libraries/chain/include/eosio/chain/block_header_state.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state.hpp @@ -29,7 +29,7 @@ struct qc_data_t { struct block_header_state_input : public building_block_input { digest_type transaction_mroot; // Comes from std::get(building_block::trx_mroot_or_receipt_digests) digest_type action_mroot; // Compute root from building_block::action_receipt_digests - std::optional new_proposer_policy; // Comes from building_block::new_proposer_policy + std::shared_ptr new_proposer_policy; // Comes from building_block::new_proposer_policy std::optional new_finalizer_policy; // Comes from building_block::new_finalizer_policy std::optional qc_info; // Comes from traversing branch from parent and calling get_best_qc() // assert(qc->block_num <= num_from_id(previous)); @@ -54,10 +54,11 @@ struct block_header_state { incremental_merkle_tree proposal_mtree; incremental_merkle_tree finality_mtree; - finalizer_policy_ptr finalizer_policy; // finalizer set + threshold + generation, supports `digest()` - proposer_policy_ptr proposer_policy; // producer authority schedule, supports `digest()` + finalizer_policy_ptr active_finalizer_policy; // finalizer set + threshold + generation, supports `digest()` + proposer_policy_ptr active_proposer_policy; // producer authority schedule, supports `digest()` - flat_map proposer_policies; + // block time when proposer_policy will become active + flat_map proposer_policies; flat_map finalizer_policies; // ------ functions ----------------------------------------------------------------- @@ -66,9 +67,8 @@ struct block_header_state { account_name producer() const { return header.producer; } const block_id_type& previous() const { return header.previous; } uint32_t block_num() const { return block_header::num_from_id(previous()) + 1; } - const producer_authority_schedule& active_schedule_auth() const { return proposer_policy->proposer_schedule; } - const producer_authority_schedule& pending_schedule_auth() const { return proposer_policies.rbegin()->second->proposer_schedule; } // [greg todo] - + const producer_authority_schedule& active_schedule_auth() const { return active_proposer_policy->proposer_schedule; } + block_header_state next(block_header_state_input& data) const; // block descending from this need the provided qc in the block extension @@ -77,10 +77,8 @@ struct block_header_state { } flat_set get_activated_protocol_features() const { return activated_protocol_features->protocol_features; } - detail::schedule_info prev_pending_schedule() const; producer_authority get_scheduled_producer(block_timestamp_type t) const; uint32_t active_schedule_version() const; - std::optional& new_pending_producer_schedule() { static std::optional x; return x; } // [greg todo] signed_block_header make_block_header(const checksum256_type& transaction_mroot, const checksum256_type& action_mroot, const std::optional& new_producers, diff --git a/libraries/chain/include/eosio/chain/block_header_state_utils.hpp b/libraries/chain/include/eosio/chain/block_header_state_utils.hpp index 91237d4709..b7dabf9a35 100644 --- a/libraries/chain/include/eosio/chain/block_header_state_utils.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state_utils.hpp @@ -12,10 +12,10 @@ namespace eosio::chain::detail { return digest && protocol_features.find(*digest) != protocol_features.end(); } - inline uint32_t get_next_next_round_block_num(block_timestamp_type t, uint32_t block_num) { + inline block_timestamp_type get_next_next_round_block_time( block_timestamp_type t) { auto index = t.slot % config::producer_repetitions; // current index in current round - // (increment to the end of this round ) + next round - return block_num + (config::producer_repetitions - index) + config::producer_repetitions; + // (increment to the end of this round ) + next round + return block_timestamp_type{t.slot + (config::producer_repetitions - index) + config::producer_repetitions}; } inline producer_authority get_scheduled_producer(const vector& producers, block_timestamp_type t) { diff --git a/libraries/chain/include/eosio/chain/block_state_legacy.hpp b/libraries/chain/include/eosio/chain/block_state_legacy.hpp index b8c1876f21..f6c0ab35d5 100644 --- a/libraries/chain/include/eosio/chain/block_state_legacy.hpp +++ b/libraries/chain/include/eosio/chain/block_state_legacy.hpp @@ -42,7 +42,7 @@ namespace eosio { namespace chain { protocol_feature_activation_set_ptr get_activated_protocol_features() const { return activated_protocol_features; } const producer_authority_schedule& active_schedule_auth() const { return block_header_state_legacy_common::active_schedule; } - const producer_authority_schedule& pending_schedule_auth() const { return block_header_state_legacy::pending_schedule.schedule; } + const producer_authority_schedule* pending_schedule_auth() const { return &block_header_state_legacy::pending_schedule.schedule; } const deque& trxs_metas() const { return _cached_trxs; } diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index b563e4a5ca..5fae42731a 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -250,8 +250,13 @@ namespace eosio::chain { const producer_authority_schedule& active_producers()const; const producer_authority_schedule& head_active_producers()const; - const producer_authority_schedule& pending_producers()const; - std::optional proposed_producers()const; + // pending for pre-instant-finality, next proposed that will take affect, null if none are pending/proposed + const producer_authority_schedule* next_producers()const; + // post-instant-finality this always returns empty std::optional + std::optional proposed_producers_legacy()const; + // pre-instant-finality this always returns a valid producer_authority_schedule + // post-instant-finality this always returns nullptr + const producer_authority_schedule* pending_producers_legacy()const; // Called by qc_chain to indicate the current irreversible block num // After hotstuff is activated, this should be called on startup by qc_chain diff --git a/libraries/chain/include/eosio/chain/hotstuff/instant_finality_extension.hpp b/libraries/chain/include/eosio/chain/hotstuff/instant_finality_extension.hpp index a663e16f9d..a535414285 100644 --- a/libraries/chain/include/eosio/chain/hotstuff/instant_finality_extension.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff/instant_finality_extension.hpp @@ -17,7 +17,7 @@ struct instant_finality_extension : fc::reflect_init { instant_finality_extension() = default; instant_finality_extension(std::optional qc_info, std::optional new_finalizer_policy, - std::optional new_proposer_policy) : + std::shared_ptr new_proposer_policy) : qc_info(qc_info), new_finalizer_policy(std::move(new_finalizer_policy)), new_proposer_policy(std::move(new_proposer_policy)) @@ -25,9 +25,9 @@ struct instant_finality_extension : fc::reflect_init { void reflector_init(); - std::optional qc_info; - std::optional new_finalizer_policy; - std::optional new_proposer_policy; + std::optional qc_info; + std::optional new_finalizer_policy; + std::shared_ptr new_proposer_policy; }; } /// eosio::chain diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index ee3a564499..56761924a6 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -1800,9 +1800,9 @@ read_only::get_producers( const read_only::get_producers_params& params, const f read_only::get_producer_schedule_result read_only::get_producer_schedule( const read_only::get_producer_schedule_params& p, const fc::time_point& ) const { read_only::get_producer_schedule_result result; to_variant(db.active_producers(), result.active); - if(!db.pending_producers().producers.empty()) - to_variant(db.pending_producers(), result.pending); - auto proposed = db.proposed_producers(); + if (const auto* pending = db.next_producers()) // not applicable for instant-finality + to_variant(*pending, result.pending); + auto proposed = db.proposed_producers_legacy(); // empty for instant-finality if(proposed && !proposed->producers.empty()) to_variant(*proposed, result.proposed); return result; diff --git a/plugins/net_plugin/net_plugin.cpp b/plugins/net_plugin/net_plugin.cpp index bf8f7d0a07..967686ef4f 100644 --- a/plugins/net_plugin/net_plugin.cpp +++ b/plugins/net_plugin/net_plugin.cpp @@ -3929,7 +3929,9 @@ namespace eosio { } void net_plugin_impl::on_accepted_block() { - on_pending_schedule(chain_plug->chain().pending_producers()); + if (const auto* next_producers = chain_plug->chain().next_producers()) { + on_pending_schedule(*next_producers); + } on_active_schedule(chain_plug->chain().active_producers()); } diff --git a/unittests/block_header_tests.cpp b/unittests/block_header_tests.cpp index 48e7e39abb..ae7d31421c 100644 --- a/unittests/block_header_tests.cpp +++ b/unittests/block_header_tests.cpp @@ -24,7 +24,7 @@ BOOST_AUTO_TEST_CASE(instant_finality_extension_with_empty_values_test) emplace_extension( header.header_extensions, instant_finality_extension::extension_id(), - fc::raw::pack( instant_finality_extension{qc_info_t{last_qc_block_num, is_last_qc_strong}, std::optional{}, std::optional{}} ) + fc::raw::pack( instant_finality_extension{qc_info_t{last_qc_block_num, is_last_qc_strong}, std::optional{}, std::shared_ptr{}} ) ); std::optional ext = header.extract_header_extension(instant_finality_extension::extension_id()); @@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(instant_finality_extension_uniqueness_test) emplace_extension( header.header_extensions, instant_finality_extension::extension_id(), - fc::raw::pack( instant_finality_extension{qc_info_t{0, false}, {std::nullopt}, {std::nullopt}} ) + fc::raw::pack( instant_finality_extension{qc_info_t{0, false}, {std::nullopt}, std::shared_ptr{}} ) ); std::vector finalizers { {"test description", 50, fc::crypto::blslib::bls_public_key{"PUB_BLS_MPPeebAPxt/ibL2XPuZVGpADjGn+YEVPPoYmTZeBD6Ok2E19M8SnmDGSdZBf2qwSuJim+8H83EsTpEn3OiStWBiFeJYfVRLlEsZuSF0SYYwtVteY48n+KeE1IWzlSAkSyBqiGA==" }} }; @@ -54,7 +54,7 @@ BOOST_AUTO_TEST_CASE(instant_finality_extension_uniqueness_test) new_finalizer_policy.threshold = 100; new_finalizer_policy.finalizers = finalizers; - proposer_policy new_proposer_policy {1, block_timestamp_type{200}, {} }; + proposer_policy_ptr new_proposer_policy = std::make_shared(1, block_timestamp_type{200}, producer_authority_schedule{} ); emplace_extension( header.header_extensions, @@ -78,7 +78,7 @@ BOOST_AUTO_TEST_CASE(instant_finality_extension_with_values_test) new_finalizer_policy.threshold = 100; new_finalizer_policy.finalizers = finalizers; - proposer_policy new_proposer_policy {1, block_timestamp_type{200}, {} }; + proposer_policy_ptr new_proposer_policy = std::make_shared(1, block_timestamp_type{200}, producer_authority_schedule{} ); emplace_extension( header.header_extensions, diff --git a/unittests/chain_tests.cpp b/unittests/chain_tests.cpp index 40e49e5a04..45ad505115 100644 --- a/unittests/chain_tests.cpp +++ b/unittests/chain_tests.cpp @@ -27,11 +27,14 @@ BOOST_AUTO_TEST_CASE( replace_producer_keys ) try { } } - const auto old_pending_version = tester.control->pending_producers().version; + // TODO: Add test with instant-finality enabled + BOOST_REQUIRE(tester.control->pending_producers_legacy()); + const auto old_pending_version = tester.control->pending_producers_legacy()->version; const auto old_version = tester.control->active_producers().version; BOOST_REQUIRE_NO_THROW(tester.control->replace_producer_keys(new_key)); const auto new_version = tester.control->active_producers().version; - const auto pending_version = tester.control->pending_producers().version; + BOOST_REQUIRE(tester.control->pending_producers_legacy()); + const auto pending_version = tester.control->pending_producers_legacy()->version; // make sure version not been changed BOOST_REQUIRE(old_version == new_version); BOOST_REQUIRE(old_version == pending_version); @@ -44,7 +47,8 @@ BOOST_AUTO_TEST_CASE( replace_producer_keys ) try { const uint32_t expected_threshold = 1; const weight_type expected_key_weight = 1; - for(const auto& prod : tester.control->pending_producers().producers) { + BOOST_REQUIRE(tester.control->pending_producers_legacy()); + for(const auto& prod : tester.control->pending_producers_legacy()->producers) { BOOST_REQUIRE_EQUAL(std::get(prod.authority).threshold, expected_threshold); for(const auto& key : std::get(prod.authority).keys){ BOOST_REQUIRE_EQUAL(key.key, new_key); diff --git a/unittests/producer_schedule_tests.cpp b/unittests/producer_schedule_tests.cpp index dbfd9531ce..ad894303b1 100644 --- a/unittests/producer_schedule_tests.cpp +++ b/unittests/producer_schedule_tests.cpp @@ -137,12 +137,13 @@ BOOST_FIXTURE_TEST_CASE( producer_schedule_promotion_test, validating_tester ) t }; //wdump((fc::json::to_pretty_string(res))); wlog("set producer schedule to [alice,bob]"); - BOOST_REQUIRE_EQUAL( true, control->proposed_producers().has_value() ); - BOOST_CHECK_EQUAL( true, compare_schedules( sch1, *control->proposed_producers() ) ); - BOOST_CHECK_EQUAL( control->pending_producers().version, 0u ); + BOOST_REQUIRE_EQUAL( true, control->proposed_producers_legacy().has_value() ); + BOOST_CHECK_EQUAL( true, compare_schedules( sch1, *control->proposed_producers_legacy() ) ); + BOOST_REQUIRE(control->pending_producers_legacy()); + BOOST_CHECK_EQUAL( control->pending_producers_legacy()->version, 0u ); produce_block(); // Starts new block which promotes the proposed schedule to pending - BOOST_CHECK_EQUAL( control->pending_producers().version, 1u ); - BOOST_CHECK_EQUAL( true, compare_schedules( sch1, control->pending_producers() ) ); + BOOST_CHECK_EQUAL( control->pending_producers_legacy()->version, 1u ); + BOOST_CHECK_EQUAL( true, compare_schedules( sch1, *control->pending_producers_legacy() ) ); BOOST_CHECK_EQUAL( control->active_producers().version, 0u ); produce_block(); produce_block(); // Starts new block which promotes the pending schedule to active @@ -157,15 +158,16 @@ BOOST_FIXTURE_TEST_CASE( producer_schedule_promotion_test, validating_tester ) t producer_authority{"carol"_n, block_signing_authority_v0{1, {{get_public_key("carol"_n, "active"),1}}}} }; wlog("set producer schedule to [alice,bob,carol]"); - BOOST_REQUIRE_EQUAL( true, control->proposed_producers().has_value() ); - BOOST_CHECK_EQUAL( true, compare_schedules( sch2, *control->proposed_producers() ) ); + BOOST_REQUIRE_EQUAL( true, control->proposed_producers_legacy().has_value() ); + BOOST_CHECK_EQUAL( true, compare_schedules( sch2, *control->proposed_producers_legacy() ) ); produce_block(); produce_blocks(23); // Alice produces the last block of her first round. // Bob's first block (which advances LIB to Alice's last block) is started but not finalized. BOOST_REQUIRE_EQUAL( control->head_block_producer(), "alice"_n ); BOOST_REQUIRE_EQUAL( control->pending_block_producer(), "bob"_n ); - BOOST_CHECK_EQUAL( control->pending_producers().version, 2u ); + BOOST_REQUIRE(control->pending_producers_legacy()); + BOOST_CHECK_EQUAL( control->pending_producers_legacy()->version, 2u ); produce_blocks(12); // Bob produces his first 11 blocks BOOST_CHECK_EQUAL( control->active_producers().version, 1u ); @@ -202,12 +204,13 @@ BOOST_FIXTURE_TEST_CASE( producer_schedule_reduction, tester ) try { producer_authority{"carol"_n, block_signing_authority_v0{ 1, {{get_public_key("carol"_n, "active"),1}}}} }; wlog("set producer schedule to [alice,bob,carol]"); - BOOST_REQUIRE_EQUAL( true, control->proposed_producers().has_value() ); - BOOST_CHECK_EQUAL( true, compare_schedules( sch1, *control->proposed_producers() ) ); - BOOST_CHECK_EQUAL( control->pending_producers().version, 0u ); + BOOST_REQUIRE_EQUAL( true, control->proposed_producers_legacy().has_value() ); + BOOST_CHECK_EQUAL( true, compare_schedules( sch1, *control->proposed_producers_legacy() ) ); + BOOST_REQUIRE(control->pending_producers_legacy()); + BOOST_CHECK_EQUAL( control->pending_producers_legacy()->version, 0u ); produce_block(); // Starts new block which promotes the proposed schedule to pending - BOOST_CHECK_EQUAL( control->pending_producers().version, 1u ); - BOOST_CHECK_EQUAL( true, compare_schedules( sch1, control->pending_producers() ) ); + BOOST_CHECK_EQUAL( control->pending_producers_legacy()->version, 1u ); + BOOST_CHECK_EQUAL( true, compare_schedules( sch1, *control->pending_producers_legacy() ) ); BOOST_CHECK_EQUAL( control->active_producers().version, 0u ); produce_block(); produce_block(); // Starts new block which promotes the pending schedule to active @@ -221,13 +224,14 @@ BOOST_FIXTURE_TEST_CASE( producer_schedule_reduction, tester ) try { producer_authority{"bob"_n, block_signing_authority_v0{ 1, {{ get_public_key("bob"_n, "active"),1}}}} }; wlog("set producer schedule to [alice,bob]"); - BOOST_REQUIRE_EQUAL( true, control->proposed_producers().has_value() ); - BOOST_CHECK_EQUAL( true, compare_schedules( sch2, *control->proposed_producers() ) ); + BOOST_REQUIRE_EQUAL( true, control->proposed_producers_legacy().has_value() ); + BOOST_CHECK_EQUAL( true, compare_schedules( sch2, *control->proposed_producers_legacy() ) ); produce_blocks(48); BOOST_REQUIRE_EQUAL( control->head_block_producer(), "bob"_n ); BOOST_REQUIRE_EQUAL( control->pending_block_producer(), "carol"_n ); - BOOST_CHECK_EQUAL( control->pending_producers().version, 2u ); + BOOST_REQUIRE(control->pending_producers_legacy()); + BOOST_CHECK_EQUAL( control->pending_producers_legacy()->version, 2u ); produce_blocks(47); BOOST_CHECK_EQUAL( control->active_producers().version, 1u ); @@ -264,14 +268,16 @@ BOOST_AUTO_TEST_CASE( empty_producer_schedule_has_no_effect ) try { producer_authority{"bob"_n, block_signing_authority_v0{ 1, {{ get_public_key("bob"_n, "active"),1}}}} }; wlog("set producer schedule to [alice,bob]"); - BOOST_REQUIRE_EQUAL( true, c.control->proposed_producers().has_value() ); - BOOST_CHECK_EQUAL( true, compare_schedules( sch1, *c.control->proposed_producers() ) ); - BOOST_CHECK_EQUAL( c.control->pending_producers().producers.size(), 0u ); + BOOST_REQUIRE_EQUAL( true, c.control->proposed_producers_legacy().has_value() ); + BOOST_CHECK_EQUAL( true, compare_schedules( sch1, *c.control->proposed_producers_legacy() ) ); + BOOST_REQUIRE(c.control->pending_producers_legacy()); + BOOST_CHECK_EQUAL( c.control->pending_producers_legacy()->producers.size(), 0u ); // Start a new block which promotes the proposed schedule to pending c.produce_block(); - BOOST_CHECK_EQUAL( c.control->pending_producers().version, 1u ); - BOOST_CHECK_EQUAL( true, compare_schedules( sch1, c.control->pending_producers() ) ); + BOOST_REQUIRE(c.control->pending_producers_legacy()); + BOOST_CHECK_EQUAL( c.control->pending_producers_legacy()->version, 1u ); + BOOST_CHECK_EQUAL( true, compare_schedules( sch1, *c.control->pending_producers_legacy() ) ); BOOST_CHECK_EQUAL( c.control->active_producers().version, 0u ); // Start a new block which promotes the pending schedule to active @@ -282,22 +288,25 @@ BOOST_AUTO_TEST_CASE( empty_producer_schedule_has_no_effect ) try { res = c.set_producers_legacy( {} ); wlog("set producer schedule to []"); - BOOST_REQUIRE_EQUAL( true, c.control->proposed_producers().has_value() ); - BOOST_CHECK_EQUAL( c.control->proposed_producers()->producers.size(), 0u ); - BOOST_CHECK_EQUAL( c.control->proposed_producers()->version, 2u ); + BOOST_REQUIRE_EQUAL( true, c.control->proposed_producers_legacy().has_value() ); + BOOST_CHECK_EQUAL( c.control->proposed_producers_legacy()->producers.size(), 0u ); + BOOST_CHECK_EQUAL( c.control->proposed_producers_legacy()->version, 2u ); c.produce_blocks(12); - BOOST_CHECK_EQUAL( c.control->pending_producers().version, 1u ); + BOOST_REQUIRE(c.control->pending_producers_legacy()); + BOOST_CHECK_EQUAL( c.control->pending_producers_legacy()->version, 1u ); // Empty producer schedule does get promoted from proposed to pending c.produce_block(); - BOOST_CHECK_EQUAL( c.control->pending_producers().version, 2u ); - BOOST_CHECK_EQUAL( false, c.control->proposed_producers().has_value() ); + BOOST_REQUIRE(c.control->pending_producers_legacy()); + BOOST_CHECK_EQUAL( c.control->pending_producers_legacy()->version, 2u ); + BOOST_CHECK_EQUAL( false, c.control->proposed_producers_legacy().has_value() ); // However it should not get promoted from pending to active c.produce_blocks(24); BOOST_CHECK_EQUAL( c.control->active_producers().version, 1u ); - BOOST_CHECK_EQUAL( c.control->pending_producers().version, 2u ); + BOOST_REQUIRE(c.control->pending_producers_legacy()); + BOOST_CHECK_EQUAL( c.control->pending_producers_legacy()->version, 2u ); // Setting a new producer schedule should still use version 2 res = c.set_producers_legacy( {"alice"_n,"bob"_n,"carol"_n} ); @@ -307,15 +316,16 @@ BOOST_AUTO_TEST_CASE( empty_producer_schedule_has_no_effect ) try { producer_authority{"carol"_n, block_signing_authority_v0{ 1, {{get_public_key("carol"_n, "active"),1}}}} }; wlog("set producer schedule to [alice,bob,carol]"); - BOOST_REQUIRE_EQUAL( true, c.control->proposed_producers().has_value() ); - BOOST_CHECK_EQUAL( true, compare_schedules( sch2, *c.control->proposed_producers() ) ); - BOOST_CHECK_EQUAL( c.control->proposed_producers()->version, 2u ); + BOOST_REQUIRE_EQUAL( true, c.control->proposed_producers_legacy().has_value() ); + BOOST_CHECK_EQUAL( true, compare_schedules( sch2, *c.control->proposed_producers_legacy() ) ); + BOOST_CHECK_EQUAL( c.control->proposed_producers_legacy()->version, 2u ); // Produce enough blocks to promote the proposed schedule to pending, which it can do because the existing pending has zero producers c.produce_blocks(24); BOOST_CHECK_EQUAL( c.control->active_producers().version, 1u ); - BOOST_CHECK_EQUAL( c.control->pending_producers().version, 2u ); - BOOST_CHECK_EQUAL( true, compare_schedules( sch2, c.control->pending_producers() ) ); + BOOST_REQUIRE(c.control->pending_producers_legacy()); + BOOST_CHECK_EQUAL( c.control->pending_producers_legacy()->version, 2u ); + BOOST_CHECK_EQUAL( true, compare_schedules( sch2, *c.control->pending_producers_legacy() ) ); // Produce enough blocks to promote the pending schedule to active c.produce_blocks(24); @@ -342,12 +352,14 @@ BOOST_AUTO_TEST_CASE( producer_watermark_test ) try { producer_authority{"carol"_n, block_signing_authority_v0{ 1, {{c.get_public_key("carol"_n, "active"),1}}}} }; wlog("set producer schedule to [alice,bob,carol]"); - BOOST_REQUIRE_EQUAL( true, c.control->proposed_producers().has_value() ); - BOOST_CHECK_EQUAL( true, compare_schedules( sch1, *c.control->proposed_producers() ) ); - BOOST_CHECK_EQUAL( c.control->pending_producers().version, 0u ); + BOOST_REQUIRE_EQUAL( true, c.control->proposed_producers_legacy().has_value() ); + BOOST_CHECK_EQUAL( true, compare_schedules( sch1, *c.control->proposed_producers_legacy() ) ); + BOOST_REQUIRE(c.control->pending_producers_legacy()); + BOOST_CHECK_EQUAL( c.control->pending_producers_legacy()->version, 0u ); c.produce_block(); // Starts new block which promotes the proposed schedule to pending - BOOST_CHECK_EQUAL( c.control->pending_producers().version, 1u ); - BOOST_CHECK_EQUAL( true, compare_schedules( sch1, c.control->pending_producers() ) ); + BOOST_REQUIRE(c.control->pending_producers_legacy()); + BOOST_CHECK_EQUAL( c.control->pending_producers_legacy()->version, 1u ); + BOOST_CHECK_EQUAL( true, compare_schedules( sch1, *c.control->pending_producers_legacy() ) ); BOOST_CHECK_EQUAL( c.control->active_producers().version, 0u ); c.produce_block(); c.produce_block(); // Starts new block which promotes the pending schedule to active @@ -364,16 +376,18 @@ BOOST_AUTO_TEST_CASE( producer_watermark_test ) try { producer_authority{"bob"_n, block_signing_authority_v0{ 1, {{c.get_public_key("bob"_n, "active"),1}}}} }; wlog("set producer schedule to [alice,bob]"); - BOOST_REQUIRE_EQUAL( true, c.control->proposed_producers().has_value() ); - BOOST_CHECK_EQUAL( true, compare_schedules( sch2, *c.control->proposed_producers() ) ); + BOOST_REQUIRE_EQUAL( true, c.control->proposed_producers_legacy().has_value() ); + BOOST_CHECK_EQUAL( true, compare_schedules( sch2, *c.control->proposed_producers_legacy() ) ); produce_until_transition( c, "bob"_n, "carol"_n ); produce_until_transition( c, "alice"_n, "bob"_n ); - BOOST_CHECK_EQUAL( c.control->pending_producers().version, 2u ); + BOOST_REQUIRE(c.control->pending_producers_legacy()); + BOOST_CHECK_EQUAL( c.control->pending_producers_legacy()->version, 2u ); BOOST_CHECK_EQUAL( c.control->active_producers().version, 1u ); produce_until_transition( c, "carol"_n, "alice"_n ); - BOOST_CHECK_EQUAL( c.control->pending_producers().version, 2u ); + BOOST_REQUIRE(c.control->pending_producers_legacy()); + BOOST_CHECK_EQUAL( c.control->pending_producers_legacy()->version, 2u ); BOOST_CHECK_EQUAL( c.control->active_producers().version, 1u ); produce_until_transition( c, "bob"_n, "carol"_n ); @@ -388,8 +402,8 @@ BOOST_AUTO_TEST_CASE( producer_watermark_test ) try { res = c.set_producers( {"alice"_n,"bob"_n,"carol"_n} ); wlog("set producer schedule to [alice,bob,carol]"); - BOOST_REQUIRE_EQUAL( true, c.control->proposed_producers().has_value() ); - BOOST_CHECK_EQUAL( true, compare_schedules( sch1, *c.control->proposed_producers() ) ); + BOOST_REQUIRE_EQUAL( true, c.control->proposed_producers_legacy().has_value() ); + BOOST_CHECK_EQUAL( true, compare_schedules( sch1, *c.control->proposed_producers_legacy() ) ); produce_until_transition( c, "bob"_n, "alice"_n ); @@ -415,7 +429,8 @@ BOOST_AUTO_TEST_CASE( producer_watermark_test ) try { BOOST_CHECK_EQUAL( carol_itr->second, carol_last_produced_block_num ); } - BOOST_CHECK_EQUAL( c.control->pending_producers().version, 3u ); + BOOST_REQUIRE(c.control->pending_producers_legacy()); + BOOST_CHECK_EQUAL( c.control->pending_producers_legacy()->version, 3u ); BOOST_REQUIRE_EQUAL( c.control->active_producers().version, 2u ); produce_until_transition( c, "bob"_n, "alice"_n ); @@ -495,7 +510,7 @@ BOOST_FIXTURE_TEST_CASE( satisfiable_msig_test, validating_tester ) try { fc_exception_message_is( "producer schedule includes an unsatisfiable authority for alice" ) ); - BOOST_REQUIRE_EQUAL( false, control->proposed_producers().has_value() ); + BOOST_REQUIRE_EQUAL( false, control->proposed_producers_legacy().has_value() ); } FC_LOG_AND_RETHROW() @@ -514,7 +529,7 @@ BOOST_FIXTURE_TEST_CASE( duplicate_producers_test, validating_tester ) try { fc_exception_message_is( "duplicate producer name in producer schedule" ) ); - BOOST_REQUIRE_EQUAL( false, control->proposed_producers().has_value() ); + BOOST_REQUIRE_EQUAL( false, control->proposed_producers_legacy().has_value() ); } FC_LOG_AND_RETHROW() @@ -532,7 +547,7 @@ BOOST_FIXTURE_TEST_CASE( duplicate_keys_test, validating_tester ) try { fc_exception_message_is( "producer schedule includes a duplicated key for alice" ) ); - BOOST_REQUIRE_EQUAL( false, control->proposed_producers().has_value() ); + BOOST_REQUIRE_EQUAL( false, control->proposed_producers_legacy().has_value() ); // ensure that multiple producers are allowed to share keys vector sch2 = { @@ -541,7 +556,7 @@ BOOST_FIXTURE_TEST_CASE( duplicate_keys_test, validating_tester ) try { }; set_producer_schedule( sch2 ); - BOOST_REQUIRE_EQUAL( true, control->proposed_producers().has_value() ); + BOOST_REQUIRE_EQUAL( true, control->proposed_producers_legacy().has_value() ); } FC_LOG_AND_RETHROW() BOOST_AUTO_TEST_CASE( large_authority_overflow_test ) try { @@ -603,7 +618,7 @@ BOOST_AUTO_TEST_CASE( extra_signatures_test ) try { }; main.set_producer_schedule( sch1 ); - BOOST_REQUIRE_EQUAL( true, main.control->proposed_producers().has_value() ); + BOOST_REQUIRE_EQUAL( true, main.control->proposed_producers_legacy().has_value() ); main.block_signing_private_keys.emplace(get_public_key("alice"_n, "bs1"), get_private_key("alice"_n, "bs1")); main.block_signing_private_keys.emplace(get_public_key("alice"_n, "bs2"), get_private_key("alice"_n, "bs2"));