Skip to content

Commit

Permalink
GH-1980 Implement proposer policy change
Browse files Browse the repository at this point in the history
  • Loading branch information
heifner committed Jan 11, 2024
1 parent 59532df commit d2af863
Show file tree
Hide file tree
Showing 12 changed files with 290 additions and 156 deletions.
23 changes: 20 additions & 3 deletions libraries/chain/block_header_state.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
#include <eosio/chain/block_header_state.hpp>
#include <eosio/chain/block_header_state_utils.hpp>
#include <eosio/chain/hotstuff/instant_finality_extension.hpp>
#include <eosio/chain/hotstuff/proposer_policy.hpp>
#include <eosio/chain/exceptions.hpp>
#include <limits>

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
Expand Down Expand Up @@ -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)
Expand Down
239 changes: 166 additions & 73 deletions libraries/chain/controller.cpp

Large diffs are not rendered by default.

16 changes: 7 additions & 9 deletions libraries/chain/include/eosio/chain/block_header_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<checksum256_type>(building_block::trx_mroot_or_receipt_digests)
digest_type action_mroot; // Compute root from building_block::action_receipt_digests
std::optional<proposer_policy> new_proposer_policy; // Comes from building_block::new_proposer_policy
std::shared_ptr<proposer_policy> new_proposer_policy; // Comes from building_block::new_proposer_policy
std::optional<finalizer_policy> new_finalizer_policy; // Comes from building_block::new_finalizer_policy
std::optional<qc_info_t> qc_info; // Comes from traversing branch from parent and calling get_best_qc()
// assert(qc->block_num <= num_from_id(previous));
Expand All @@ -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<uint32_t, proposer_policy_ptr> proposer_policies;
// block time when proposer_policy will become active
flat_map<block_timestamp_type, proposer_policy_ptr> proposer_policies;
flat_map<uint32_t, finalizer_policy_ptr> finalizer_policies;

// ------ functions -----------------------------------------------------------------
Expand All @@ -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
Expand All @@ -77,10 +77,8 @@ struct block_header_state {
}

flat_set<digest_type> 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<producer_authority_schedule>& new_pending_producer_schedule() { static std::optional<producer_authority_schedule> x; return x; } // [greg todo]
signed_block_header make_block_header(const checksum256_type& transaction_mroot,
const checksum256_type& action_mroot,
const std::optional<producer_authority_schedule>& new_producers,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<producer_authority>& producers, block_timestamp_type t) {
Expand Down
2 changes: 1 addition & 1 deletion libraries/chain/include/eosio/chain/block_state_legacy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<transaction_metadata_ptr>& trxs_metas() const { return _cached_trxs; }


Expand Down
9 changes: 7 additions & 2 deletions libraries/chain/include/eosio/chain/controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<producer_authority_schedule> 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<producer_authority_schedule> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@ struct instant_finality_extension : fc::reflect_init {
instant_finality_extension() = default;
instant_finality_extension(std::optional<qc_info_t> qc_info,
std::optional<finalizer_policy> new_finalizer_policy,
std::optional<proposer_policy> new_proposer_policy) :
std::shared_ptr<proposer_policy> new_proposer_policy) :
qc_info(qc_info),
new_finalizer_policy(std::move(new_finalizer_policy)),
new_proposer_policy(std::move(new_proposer_policy))
{}

void reflector_init();

std::optional<qc_info_t> qc_info;
std::optional<finalizer_policy> new_finalizer_policy;
std::optional<proposer_policy> new_proposer_policy;
std::optional<qc_info_t> qc_info;
std::optional<finalizer_policy> new_finalizer_policy;
std::shared_ptr<proposer_policy> new_proposer_policy;
};

} /// eosio::chain
Expand Down
6 changes: 3 additions & 3 deletions plugins/chain_plugin/chain_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion plugins/net_plugin/net_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

Expand Down
8 changes: 4 additions & 4 deletions unittests/block_header_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<finalizer_policy>{}, std::optional<proposer_policy>{}} )
fc::raw::pack( instant_finality_extension{qc_info_t{last_qc_block_num, is_last_qc_strong}, std::optional<finalizer_policy>{}, std::shared_ptr<proposer_policy>{}} )
);

std::optional<block_header_extension> ext = header.extract_header_extension(instant_finality_extension::extension_id());
Expand All @@ -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<proposer_policy>{}} )
);

std::vector<finalizer_authority> finalizers { {"test description", 50, fc::crypto::blslib::bls_public_key{"PUB_BLS_MPPeebAPxt/ibL2XPuZVGpADjGn+YEVPPoYmTZeBD6Ok2E19M8SnmDGSdZBf2qwSuJim+8H83EsTpEn3OiStWBiFeJYfVRLlEsZuSF0SYYwtVteY48n+KeE1IWzlSAkSyBqiGA==" }} };
Expand All @@ -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<proposer_policy>(1, block_timestamp_type{200}, producer_authority_schedule{} );

emplace_extension(
header.header_extensions,
Expand All @@ -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<proposer_policy>(1, block_timestamp_type{200}, producer_authority_schedule{} );

emplace_extension(
header.header_extensions,
Expand Down
10 changes: 7 additions & 3 deletions unittests/chain_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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<block_signing_authority_v0>(prod.authority).threshold, expected_threshold);
for(const auto& key : std::get<block_signing_authority_v0>(prod.authority).keys){
BOOST_REQUIRE_EQUAL(key.key, new_key);
Expand Down
Loading

0 comments on commit d2af863

Please sign in to comment.