Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IF: Update new proposer policy algorithm #74

Merged
merged 12 commits into from
May 2, 2024
Merged
79 changes: 34 additions & 45 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -491,38 +491,27 @@ struct building_block {

uint32_t get_block_num() const { return block_num; }

// returns the next proposer schedule version and true if different
// if producers is not different then returns the current schedule version (or next schedule version)
std::tuple<uint32_t, bool> get_next_proposer_schedule_version(const vector<producer_authority>& producers) const {
// returns the next proposer schedule version if producers should be proposed in block
// if producers is not different then returns empty optional
std::optional<uint32_t> get_next_proposer_schedule_version(const shared_vector<shared_producer_authority>& producers) const {
assert(active_proposer_policy);

auto get_next_sched = [&]() -> const producer_authority_schedule& {
// if there are any policies already proposed but not active yet then they are what needs to be compared
if (!parent.proposer_policies.empty()) {
block_timestamp_type active_time = detail::get_next_next_round_block_time(timestamp);
if (auto itr = parent.proposer_policies.find(active_time); itr != parent.proposer_policies.cend()) {
// Same active time, a new proposer schedule will replace this entry, `next` therefore is the previous
if (itr != parent.proposer_policies.begin()) {
return (--itr)->second->proposer_schedule;
}
// no previous to what will be replaced, use active
return active_proposer_policy->proposer_schedule;
}
// will not replace any proposed policies, use next to become active
return parent.proposer_policies.begin()->second->proposer_schedule;
if (!parent.proposer_policies.empty()) { // proposed in-flight
// return the last proposed policy to use for comparison
return (--parent.proposer_policies.end())->second->proposer_schedule;
greg7mdp marked this conversation as resolved.
Show resolved Hide resolved
}

// none currently in-flight, use active
return active_proposer_policy->proposer_schedule;
};

const producer_authority_schedule& lhs = get_next_sched();

if (std::ranges::equal(lhs.producers, producers)) {
return {lhs.version, false};
auto v = lhs.version;
if (!std::equal(lhs.producers.begin(), lhs.producers.end(), producers.begin(), producers.end())) {
++v;
return std::optional<uint32_t>{v};
}

return {lhs.version + 1, true};
return std::nullopt;
}

};
Expand Down Expand Up @@ -613,10 +602,10 @@ struct building_block {
v);
}

std::tuple<uint32_t, bool> get_next_proposer_schedule_version(const vector<producer_authority>& producers) const {
std::optional<uint32_t> get_next_proposer_schedule_version(const shared_vector<shared_producer_authority>& producers) const {
return std::visit(
overloaded{[](const building_block_legacy&) -> std::tuple<uint32_t, bool> { return {-1, false}; },
[&](const building_block_if& bb) -> std::tuple<uint32_t, bool> {
overloaded{[](const building_block_legacy&) -> std::optional<uint32_t> { return std::nullopt; },
[&](const building_block_if& bb) -> std::optional<uint32_t> {
return bb.get_next_proposer_schedule_version(producers);
}
},
Expand Down Expand Up @@ -911,13 +900,13 @@ struct pending_state {
_block_stage);
}

std::tuple<uint32_t, bool> get_next_proposer_schedule_version(const vector<producer_authority>& producers) const {
std::optional<uint32_t> get_next_proposer_schedule_version(const shared_vector<shared_producer_authority>& producers) const {
return std::visit(overloaded{
[&](const building_block& stage) -> std::tuple<uint32_t, bool> {
[&](const building_block& stage) -> std::optional<uint32_t> {
return stage.get_next_proposer_schedule_version(producers);
},
[](const assembled_block&) -> std::tuple<uint32_t, bool> { assert(false); return {-1, false}; },
[](const completed_block&) -> std::tuple<uint32_t, bool> { assert(false); return {-1, false}; }
[](const assembled_block&) -> std::optional<uint32_t> { assert(false); return std::nullopt; },
[](const completed_block&) -> std::optional<uint32_t> { assert(false); return std::nullopt; }
},
_block_stage);
}
Expand Down Expand Up @@ -3142,23 +3131,29 @@ struct controller_impl {
resource_limits.process_block_usage(bb.block_num());

// Any proposer policy?
std::unique_ptr<proposer_policy> new_proposer_policy;
auto process_new_proposer_policy = [&](auto&) -> void {
auto process_new_proposer_policy = [&](auto&) -> std::unique_ptr<proposer_policy> {
std::unique_ptr<proposer_policy> new_proposer_policy;
const auto& gpo = db.get<global_property_object>();
if (gpo.proposed_schedule_block_num) {
new_proposer_policy = std::make_unique<proposer_policy>();
new_proposer_policy->active_time = detail::get_next_next_round_block_time(bb.timestamp());
new_proposer_policy->proposer_schedule = producer_authority_schedule::from_shared(gpo.proposed_schedule);
ilog("Scheduling proposer schedule change at ${t}: ${s}", ("t", new_proposer_policy->active_time)("s", new_proposer_policy->proposer_schedule));
std::optional<uint32_t> version = pending->get_next_proposer_schedule_version(gpo.proposed_schedule.producers);
if (version) {
new_proposer_policy = std::make_unique<proposer_policy>();
new_proposer_policy->active_time = detail::get_next_next_round_block_time(bb.timestamp());
new_proposer_policy->proposer_schedule = producer_authority_schedule::from_shared(gpo.proposed_schedule);
new_proposer_policy->proposer_schedule.version = *version;
ilog("Scheduling proposer schedule change at ${t}: ${s}",
("t", new_proposer_policy->active_time)("s", new_proposer_policy->proposer_schedule));
}

db.modify( gpo, [&]( auto& gp ) {
gp.proposed_schedule_block_num = std::optional<block_num_type>();
gp.proposed_schedule.version = 0;
gp.proposed_schedule.producers.clear();
});
}
return new_proposer_policy;
};
apply_s<void>(chain_head, process_new_proposer_policy);
auto new_proposer_policy = apply_s<std::unique_ptr<proposer_policy>>(chain_head, process_new_proposer_policy);

auto assembled_block =
bb.assemble_block(thread_pool.get_executor(), protocol_features.get_protocol_feature_set(), fork_db, std::move(new_proposer_policy),
Expand Down Expand Up @@ -5232,25 +5227,19 @@ int64_t controller_impl::set_proposed_producers( vector<producer_authority> prod

assert(pending);

auto [version, diff] = pending->get_next_proposer_schedule_version(producers);
if (!diff)
return version;

producer_authority_schedule sch;
sch.version = version;
// sch.version is set in assemble_block
sch.producers = std::move(producers);

ilog( "proposed producer schedule with version ${v}", ("v", sch.version) );

// overwrite any existing proposed_schedule set earlier in this block
// store schedule in gpo so it will be rolledback if transaction fails
auto cur_block_num = chain_head.block_num() + 1;
auto& gpo = db.get<global_property_object>();
db.modify( gpo, [&]( auto& gp ) {
gp.proposed_schedule_block_num = cur_block_num;
gp.proposed_schedule = sch;
});

return sch.version;
return std::numeric_limits<uint32_t>::max();
}

int64_t controller_impl::set_proposed_producers_legacy( vector<producer_authority> producers ) {
Expand Down
5 changes: 5 additions & 0 deletions libraries/chain/include/eosio/chain/block_header_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ struct block_header_state {
proposer_policy_ptr active_proposer_policy; // producer authority schedule, supports `digest()`

// block time when proposer_policy will become active
// current algorithm only two entries possible, for the next,next round and one for block round after that
// The active time is the next,next producer round. For example,
// round A [1,2,..12], next_round B [1,2,..12], next_next_round C [1,2,..12], D [1,2,..12]
// If proposed in A1, A2, .. A12 becomes active in C1
// If proposed in B1, B2, .. B12 becomes active in D1
flat_map<block_timestamp_type, proposer_policy_ptr> proposer_policies;

// track in-flight finalizer policies. This is a `multimap` because the same block number
Expand Down
6 changes: 4 additions & 2 deletions libraries/chain/include/eosio/chain/webassembly/interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ namespace webassembly {
*
* @param packed_producer_schedule - vector of producer keys
*
* @return -1 if proposing a new producer schedule was unsuccessful, otherwise returns the version of the new proposed schedule.
* @return pre-savanna: -1 if proposing a new producer schedule was unsuccessful, otherwise returns the version of the new proposed schedule.
* post-savanna: -1 if proposing a new producer schedule was unsuccessful, otherwise returns max uint32_t
*/
int64_t set_proposed_producers(legacy_span<const char> packed_producer_schedule);

Expand All @@ -169,7 +170,8 @@ namespace webassembly {
* @param packed_producer_format - format of the producer data blob.
* @param packed_producer_schedule - packed data of representing the producer schedule in the format indicated.
*
* @return -1 if proposing a new producer schedule was unsuccessful, otherwise returns the version of the new proposed schedule.
* @return pre-savanna: -1 if proposing a new producer schedule was unsuccessful, otherwise returns the version of the new proposed schedule.
* post-savanna: -1 if proposing a new producer schedule was unsuccessful, otherwise returns max uint32_t
*/
int64_t set_proposed_producers_ex(uint64_t packed_producer_format, legacy_span<const char> packed_producer_schedule);

Expand Down
Loading
Loading