Skip to content

Commit

Permalink
Merge pull request #188 from AntelopeIO/GH-182-gpo
Browse files Browse the repository at this point in the history
Store proposed producers and proposed finalizers in building block
  • Loading branch information
heifner authored May 23, 2024
2 parents 78fbcf2 + 1c4fb16 commit c1a4895
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 149 deletions.
202 changes: 74 additions & 128 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,6 @@ R apply_l(const block_handle& bh, F&& f) {
struct completed_block {
block_handle bsp;

bool is_legacy() const { return std::holds_alternative<block_state_legacy_ptr>(bsp.internal()); }

deque<transaction_metadata_ptr> extract_trx_metas() {
return apply<deque<transaction_metadata_ptr>>(bsp, [](auto& bsp) { return bsp->extract_trxs_metas(); });
}
Expand Down Expand Up @@ -258,8 +256,6 @@ struct assembled_block {

std::variant<assembled_block_legacy, assembled_block_if> v;

bool is_legacy() const { return std::holds_alternative<assembled_block_legacy>(v); }

template <class R, class F>
R apply_legacy(F&& f) {
if constexpr (std::is_same_v<void, R>)
Expand Down Expand Up @@ -398,6 +394,7 @@ struct building_block {
deque<transaction_receipt> pending_trx_receipts;
checksum_or_digests trx_mroot_or_receipt_digests {digests_t{}};
action_digests_t action_receipt_digests;
trx_block_context trx_blk_context;

building_block_common(const vector<digest_type>& new_protocol_feature_activations,
action_digests_t::store_which_t store_which) :
Expand Down Expand Up @@ -492,7 +489,7 @@ struct building_block {

// 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 {
std::optional<uint32_t> get_next_proposer_schedule_version(const std::vector<producer_authority>& producers) const {
assert(active_proposer_policy);

auto get_next_sched = [&]() -> const producer_authority_schedule& {
Expand All @@ -506,7 +503,7 @@ struct building_block {

const producer_authority_schedule& lhs = get_next_sched();
auto v = lhs.version;
if (!std::equal(lhs.producers.begin(), lhs.producers.end(), producers.begin(), producers.end())) {
if (lhs.producers != producers) {
++v;
return std::optional<uint32_t>{v};
}
Expand All @@ -529,8 +526,6 @@ struct building_block {
v(building_block_if(prev, input, action_digests_t::store_which_t::savanna))
{}

bool is_legacy() const { return std::holds_alternative<building_block_legacy>(v); }

// apply legacy, building_block_legacy
template <class R, class F>
R apply_l(F&& f) {
Expand All @@ -552,6 +547,16 @@ struct building_block {
[&](building_block_if& bb) -> R { return std::forward<F>(f)(bb); }}, v);
}

template <class R, class F, class S>
R apply(F&& f, S&& s) {
if constexpr (std::is_same_v<void, R>)
std::visit(overloaded{[&](building_block_legacy& bb) { std::forward<F>(f)(bb); },
[&](building_block_if& bb) { std::forward<S>(s)(bb); }}, v);
else
return std::visit(overloaded{[&](building_block_legacy& bb) -> R { return std::forward<F>(f)(bb); },
[&](building_block_if& bb) -> R { return std::forward<S>(s)(bb); }}, v);
}

deque<transaction_metadata_ptr> extract_trx_metas() {
return std::visit([](auto& bb) { return std::move(bb.pending_trx_metas); }, v);
}
Expand Down Expand Up @@ -596,7 +601,7 @@ struct building_block {
v);
}

std::optional<uint32_t> get_next_proposer_schedule_version(const shared_vector<shared_producer_authority>& producers) const {
std::optional<uint32_t> get_next_proposer_schedule_version(const std::vector<producer_authority>& producers) const {
return std::visit(
overloaded{[](const building_block_legacy&) -> std::optional<uint32_t> { return std::nullopt; },
[&](const building_block_if& bb) -> std::optional<uint32_t> {
Expand Down Expand Up @@ -663,18 +668,6 @@ struct building_block {
v);
}

void update_finalizer_policy_generation(finalizer_policy& new_finalizer_policy) {
apply<void>(
overloaded{
[&](building_block::building_block_legacy& bb_legacy) -> void {
new_finalizer_policy.generation = 1;
},
[&](building_block::building_block_if& bb_savanna) -> void {
new_finalizer_policy.generation = bb_savanna.parent.finalizer_policy_generation + 1;
}
});
}

qc_data_t get_qc_data(fork_database& fork_db, const block_state& parent) {
// find most recent ancestor block that has a QC by traversing fork db
// branch from parent
Expand Down Expand Up @@ -881,8 +874,6 @@ struct pending_state {
_db_session.push();
}

bool is_legacy() const { return std::visit([](const auto& stage) { return stage.is_legacy(); }, _block_stage); }

const block_signing_authority& pending_block_signing_authority() const {
return std::visit(
[](const auto& stage) -> const block_signing_authority& { return stage.pending_block_signing_authority(); },
Expand All @@ -907,7 +898,7 @@ struct pending_state {
_block_stage);
}

std::optional<uint32_t> get_next_proposer_schedule_version(const shared_vector<shared_producer_authority>& producers) const {
std::optional<uint32_t> get_next_proposer_schedule_version(const std::vector<producer_authority>& producers) const {
return std::visit(overloaded{
[&](const building_block& stage) -> std::optional<uint32_t> {
return stage.get_next_proposer_schedule_version(producers);
Expand Down Expand Up @@ -994,7 +985,6 @@ struct controller_impl {
});
}};

int64_t set_proposed_producers( vector<producer_authority> producers );
int64_t set_proposed_producers_legacy( vector<producer_authority> producers );

protocol_feature_activation_set_ptr head_activated_protocol_features() const {
Expand Down Expand Up @@ -3165,55 +3155,36 @@ struct controller_impl {
);
resource_limits.process_block_usage(bb.block_num());

// Any proposer policy?
auto process_new_proposer_policy = [&](auto&) -> std::optional<proposer_policy> {
std::optional<proposer_policy> new_proposer_policy;
const auto& gpo = db.get<global_property_object>();
if (gpo.proposed_schedule_block_num) {
std::optional<uint32_t> version = pending->get_next_proposer_schedule_version(gpo.proposed_schedule.producers);
if (version) {
new_proposer_policy.emplace();
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));
// Any proposer policy or finalizer policy?
std::optional<finalizer_policy> new_finalizer_policy;
std::optional<proposer_policy> new_proposer_policy;
bb.apply<void>(
overloaded{
[&](building_block::building_block_legacy& bb) -> void {
// Make sure new_finalizer_policy is set only once in Legacy
if (bb.trx_blk_context.proposed_fin_pol_block_num && !bb.pending_block_header_state.is_if_transition_block()) {
new_finalizer_policy = std::move(bb.trx_blk_context.proposed_fin_pol);
new_finalizer_policy->generation = 1;
}
},
[&](building_block::building_block_if& bb) -> void {
if (bb.trx_blk_context.proposed_fin_pol_block_num) {
new_finalizer_policy = std::move(bb.trx_blk_context.proposed_fin_pol);
new_finalizer_policy->generation = bb.parent.finalizer_policy_generation + 1;
}
if (bb.trx_blk_context.proposed_schedule_block_num) {
std::optional<uint32_t> version = pending->get_next_proposer_schedule_version(bb.trx_blk_context.proposed_schedule.producers);
if (version) {
new_proposer_policy.emplace();
new_proposer_policy->active_time = detail::get_next_next_round_block_time(bb.timestamp);
new_proposer_policy->proposer_schedule = std::move(bb.trx_blk_context.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;
};
auto new_proposer_policy = apply_s<std::optional<proposer_policy>>(chain_head, process_new_proposer_policy);

// Any finalizer policy?
std::optional<finalizer_policy> new_finalizer_policy = std::nullopt;
const auto& gpo = db.get<global_property_object>();
if (gpo.proposed_fin_pol_block_num.has_value()) {
new_finalizer_policy = gpo.proposed_fin_pol;

db.modify( gpo, [&]( auto& gp ) {
gp.proposed_fin_pol_block_num = std::nullopt;
gp.proposed_fin_pol.generation = 0;
gp.proposed_fin_pol.finalizers.clear();
});
}

// Make sure new_finalizer_policy is set only once in Legacy
bb.apply_l<void>([&](building_block::building_block_legacy& bl) {
if (bl.pending_block_header_state.is_if_transition_block()) {
new_finalizer_policy = std::nullopt;
}
});

// Update generation
if (new_finalizer_policy.has_value()) {
bb.update_finalizer_policy_generation(*new_finalizer_policy);
}

auto assembled_block =
bb.assemble_block(thread_pool.get_executor(),
Expand Down Expand Up @@ -3336,32 +3307,30 @@ struct controller_impl {
pending->push();
}

void set_proposed_finalizers(finalizer_policy&& fin_pol) {
void apply_trx_block_context(trx_block_context& trx_blk_context) {
assert(pending); // has to exist and be building_block since called from host function
assert(std::holds_alternative<building_block>(pending->_block_stage));
auto& bb = std::get<building_block>(pending->_block_stage);

// Use global_property_object instead of building_block so that if the
// transaction fails it will be rolledback.
auto cur_block_num = chain_head.block_num() + 1;
auto& gpo = db.get<global_property_object>();
db.modify( gpo, [&]( auto& gp ) {
gp.proposed_fin_pol_block_num = cur_block_num;
gp.proposed_fin_pol = std::move(fin_pol);
});
// Savanna uses new algorithm for proposer schedule change
// Prevent any in-flight legacy proposer schedule changes when finalizers are first proposed
if (trx_blk_context.proposed_fin_pol_block_num) {
bb.apply_l<void>([&](building_block::building_block_legacy& bl) {
const auto& gpo = db.get<global_property_object>();
if (gpo.proposed_schedule_block_num) {
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();
});
}
bl.new_pending_producer_schedule = {};
bl.pending_block_header_state.prev_pending_schedule.schedule.producers.clear();
});
}

bb.apply_l<void>([&](building_block::building_block_legacy& bl) {
// Savanna uses new algorithm for proposer schedule change, prevent any in-flight legacy proposer schedule changes
const auto& gpo = db.get<global_property_object>();
if (gpo.proposed_schedule_block_num) {
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();
});
}
bl.new_pending_producer_schedule = {};
bl.pending_block_header_state.prev_pending_schedule.schedule.producers.clear();
bb.apply<void>([&](auto& b) {
b.trx_blk_context.apply(std::move(trx_blk_context));
});
}

Expand Down Expand Up @@ -5274,39 +5243,16 @@ bool controller::is_writing_snapshot() const {
return my->writing_snapshot.load(std::memory_order_acquire);
}

int64_t controller::set_proposed_producers( vector<producer_authority> producers ) {
int64_t controller::set_proposed_producers( transaction_context& trx_context, vector<producer_authority> producers ) {
assert(my->pending);
if (my->pending->is_legacy()) {
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<producer_authority> producers ) {
// Savanna sets the global_property_object.proposed_schedule similar to legacy, but it is only set during the building of the block.
// global_property_object is used instead of building_block so that if the transaction fails it is rolledback.

if (producers.empty())
return -1; // INSTANT_FINALITY depends on DISALLOW_EMPTY_PRODUCER_SCHEDULE

EOS_ASSERT(producers.size() <= config::max_proposers, wasm_execution_error,
"Producer schedule exceeds the maximum proposer count for this chain");
assert(pending);

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

// 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 std::numeric_limits<uint32_t>::max();
assert(std::holds_alternative<building_block>(my->pending->_block_stage));
auto& bb = std::get<building_block>(my->pending->_block_stage);
return bb.apply<int64_t>([&](building_block::building_block_legacy&) {
return my->set_proposed_producers_legacy(std::move(producers));
},
[&](building_block::building_block_if&) {
return trx_context.set_proposed_producers(std::move(producers));
});
}

int64_t controller_impl::set_proposed_producers_legacy( vector<producer_authority> producers ) {
Expand Down Expand Up @@ -5357,7 +5303,7 @@ int64_t controller_impl::set_proposed_producers_legacy( vector<producer_authorit
// The check for if there is a finalizer policy set is required because
// is_if_transition_block() is set in assemble_block so it is not set
// for the if genesis block.
return bl.pending_block_header_state.is_if_transition_block() || gpo.proposed_fin_pol_block_num.has_value();
return bl.pending_block_header_state.is_if_transition_block() || bl.trx_blk_context.proposed_fin_pol_block_num.has_value();
});
if (transition_block)
return -1;
Expand All @@ -5375,8 +5321,8 @@ int64_t controller_impl::set_proposed_producers_legacy( vector<producer_authorit
return version;
}

void controller::set_proposed_finalizers( finalizer_policy&& fin_pol ) {
my->set_proposed_finalizers(std::move(fin_pol));
void controller::apply_trx_block_context(trx_block_context& trx_blk_context) {
my->apply_trx_block_context(trx_blk_context);
}

// called from net threads
Expand Down
9 changes: 6 additions & 3 deletions libraries/chain/include/eosio/chain/controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ namespace eosio::chain {
using chainbase::pinnable_mapped_file;
using boost::signals2::signal;

class transaction_context;
struct trx_block_context;
class dynamic_global_property_object;
class global_property_object;
class permission_object;
Expand Down Expand Up @@ -329,10 +331,11 @@ namespace eosio::chain {

bool is_known_unexpired_transaction( const transaction_id_type& id) const;

int64_t set_proposed_producers( vector<producer_authority> producers );
// called by host function
int64_t set_proposed_producers( transaction_context& trx_context, vector<producer_authority> producers );

void apply_trx_block_context( trx_block_context& trx_blk_context );

// called by host function set_finalizers
void set_proposed_finalizers( finalizer_policy&& fin_pol );
// called from net threads
void process_vote_message( uint32_t connection_id, const vote_message_ptr& msg );
// thread safe, for testing
Expand Down
12 changes: 0 additions & 12 deletions libraries/chain/include/eosio/chain/global_property_object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,6 @@ namespace eosio::chain {
chain_config configuration;
chain_id_type chain_id;
wasm_config wasm_configuration;
// Note: proposed_fin_pol_block_num and proposed_fin_pol are used per block
// and reset after uses. They do not need to be stored in snapshots.
std::optional<block_num_type> proposed_fin_pol_block_num;
finalizer_policy proposed_fin_pol;

// For snapshot_global_property_object_v2 and initialize_from( const T& legacy )
template<typename T>
Expand All @@ -115,10 +111,6 @@ namespace eosio::chain {
} else {
wasm_configuration = legacy.wasm_configuration;
}

// proposed_fin_pol_block_num and proposed_fin_pol are set to default values.
proposed_fin_pol_block_num = std::nullopt;
proposed_fin_pol = finalizer_policy{};
}

// For snapshot_global_property_object v3, v4, and v5
Expand Down Expand Up @@ -162,10 +154,6 @@ namespace eosio::chain {
value.configuration = row.configuration;
value.chain_id = row.chain_id;
value.wasm_configuration = row.wasm_configuration;
// Snapshot does not contain proposed_fin_pol_block_num and proposed_fin_pol.
// Return their default values
value.proposed_fin_pol_block_num = std::nullopt;
value.proposed_fin_pol = finalizer_policy{};
}
};
}
Expand Down
Loading

0 comments on commit c1a4895

Please sign in to comment.