Skip to content

Commit

Permalink
Merge pull request #500 from AntelopeIO/GH-479-get-finalizer-info
Browse files Browse the repository at this point in the history
Fix v1/chain/get_finalizer_info to update votes
  • Loading branch information
heifner authored Aug 8, 2024
2 parents 6837655 + 06921a3 commit 9b03495
Show file tree
Hide file tree
Showing 11 changed files with 140 additions and 100 deletions.
4 changes: 4 additions & 0 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5879,6 +5879,10 @@ void controller::set_node_finalizer_keys(const bls_pub_priv_key_map_t& finalizer
my->set_node_finalizer_keys(finalizer_keys);
}

bool controller::is_node_finalizer_key(const bls_public_key& key) const {
return my->my_finalizers.contains(key);
}

/// Protocol feature activation handlers:

template<>
Expand Down
3 changes: 3 additions & 0 deletions libraries/chain/include/eosio/chain/controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,9 @@ namespace eosio::chain {
void code_block_num_last_used(const digest_type& code_hash, uint8_t vm_type, uint8_t vm_version, uint32_t block_num);
void set_node_finalizer_keys(const bls_pub_priv_key_map_t& finalizer_keys);

// is the bls key a registered finalizer key of this node, thread safe
bool is_node_finalizer_key(const bls_public_key& key) const;

private:
friend class apply_context;
friend class transaction_context;
Expand Down
1 change: 1 addition & 0 deletions libraries/chain/include/eosio/chain/finality/finalizer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ namespace eosio::chain {

size_t size() const { return finalizers.size(); } // doesn't change, thread safe
bool empty() const { return finalizers.empty(); } // doesn't change, thread safe
bool contains(const bls_public_key& pub_key) const { return finalizers.contains(pub_key); } // doesn't change, thread safe

template<typename F>
bool all_of_public_keys(F&& f) const { // only access keys which do not change, thread safe
Expand Down
16 changes: 14 additions & 2 deletions plugins/chain_plugin/chain_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,14 @@ void chain_plugin_impl::plugin_initialize(const variables_map& options) {
}
}

// only enable last tracked votes if chain_api_plugin enabled, if no http endpoint, no reason to track
bool last_tracked_votes_enabled = false;
if (options.count("plugin")) {
const auto& v = options.at("plugin").as<std::vector<std::string>>();
last_tracked_votes_enabled = std::ranges::any_of(v, [](const std::string& p) { return p.find("eosio::chain_api_plugin") != std::string::npos; });
}
_last_tracked_votes.emplace(*chain, last_tracked_votes_enabled);

// initialize deep mind logging
if ( options.at( "deep-mind" ).as<bool>() ) {
// The actual `fc::dmlog_appender` implementation that is currently used by deep mind
Expand Down Expand Up @@ -1151,8 +1159,6 @@ void chain_plugin_impl::plugin_startup()
} FC_LOG_AND_DROP(("Unable to enable account queries"));
}

_last_tracked_votes.emplace(*chain);

} FC_CAPTURE_AND_RETHROW() }

void chain_plugin::plugin_startup() {
Expand Down Expand Up @@ -2728,6 +2734,12 @@ const controller::config& chain_plugin::chain_config() const {
EOS_ASSERT(my->chain_config.has_value(), plugin_exception, "chain_config not initialized");
return *my->chain_config;
}

void chain_plugin::register_update_vote_block_metrics(std::function<void(chain_apis::tracked_votes::vote_block_metrics&&)>&& m) {
assert(my->_last_tracked_votes);
my->_last_tracked_votes->register_update_vote_block_metrics(std::move(m));
}

} // namespace eosio

FC_REFLECT( eosio::chain_apis::detail::ram_market_exchange_state_t, (ignore1)(ignore2)(ignore3)(core_symbol)(ignore4) )
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,9 @@ class chain_plugin : public plugin<chain_plugin> {
fc::variant get_log_trx(const transaction& trx) const;

const controller::config& chain_config() const;

void register_update_vote_block_metrics(std::function<void(chain_apis::tracked_votes::vote_block_metrics&&)>&&);

private:

unique_ptr<class chain_plugin_impl> my;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,21 @@ namespace eosio::chain_apis {
class tracked_votes {
public:

struct vote_block_metrics {
uint32_t block_num = 0;
std::vector<std::string> strong_votes;
std::vector<std::string> weak_votes;
std::vector<std::string> no_votes;
};

/**
* Instantiate a new tracked votes cache from the given chain controller
* The caller is expected to manage lifetimes such that this controller reference does not go stale
* for the life of the tracked votes cache
* @param tracking_enabled true if get_last_vote_info() vote tracking enabled
* @param chain - controller to read data from
*/
explicit tracked_votes( const class eosio::chain::controller& chain );
explicit tracked_votes( const class eosio::chain::controller& chain, bool tracking_enabled );
~tracked_votes();

/**
Expand All @@ -41,6 +49,9 @@ namespace eosio::chain_apis {
// Returns last vote information by a given finalizer
std::optional<vote_info> get_last_vote_info(const fc::crypto::blslib::bls_public_key& finalizer_pub_key) const;

// register prometheus callback
void register_update_vote_block_metrics(std::function<void(chain_apis::tracked_votes::vote_block_metrics&&)>&&);

private:
std::unique_ptr<struct tracked_votes_impl> _impl;
};
Expand Down
118 changes: 94 additions & 24 deletions plugins/chain_plugin/tracked_votes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,49 +10,74 @@ namespace eosio::chain_apis {
* Implementation details of the last tracked cache
*/
struct tracked_votes_impl {
explicit tracked_votes_impl(const chain::controller& controller)
:controller(controller)
explicit tracked_votes_impl(const chain::controller& controller, bool tracking_enabled)
: tracking_enabled(tracking_enabled)
, controller(controller)
{}

bool tracking_enabled = false;

// Cache to store last vote information for each known finalizer.
// A map of finalizer public key --> vote info.
std::map<fc::crypto::blslib::bls_public_key, tracked_votes::vote_info> last_votes;

// A handle to the controller.
const chain::controller& controller;

// prometheus support
std::function<void(tracked_votes::vote_block_metrics&&)> _update_vote_block_metrics;

// Called on accepted_block signal. Retrieve vote information from
// QC in the block and store it in last_votes.
void on_accepted_block( const chain::signed_block_ptr& block, const chain::block_id_type& id ) {
try {
if (!_update_vote_block_metrics && !tracking_enabled && !chain::vote_logger.is_enabled(fc::log_level::info))
return;

if (!block->contains_extension(chain::quorum_certificate_extension::extension_id())) {
fc_ilog(chain::vote_logger, "Block ${id}... #${n} @ ${t} produced by ${p}, latency: ${l}ms has no votes",
("id", id.str().substr(8, 16))("n", block->block_num())("t", block->timestamp)("p", block->producer)
("l", (fc::time_point::now() - block->timestamp).count() / 1000));
return;
}

// Retrieve vote information from QC
const auto& qc_ext = block->extract_extension<chain::quorum_certificate_extension>();
chain::qc_vote_metrics_t vm = controller.vote_metrics(id, qc_ext.qc);

auto track_votes = [&](const chain::qc_vote_metrics_t::fin_auth_set_t& finalizers, bool is_strong) {
for (auto& f: finalizers) {
assert(f.fin_auth);

tracked_votes::vote_info v_info {
.description = f.fin_auth->description,
.public_key = f.fin_auth->public_key.to_string(),
.is_vote_strong = is_strong,
.finalizer_policy_generation = f.generation,
.voted_for_block_id = vm.voted_for_block_id,
.voted_for_block_num = chain::block_header::num_from_id(vm.voted_for_block_id),
.voted_for_block_timestamp = vm.voted_for_block_timestamp
if (tracking_enabled || _update_vote_block_metrics) {
// Retrieve vote information from QC
const auto& qc_ext = block->extract_extension<chain::quorum_certificate_extension>();
chain::qc_vote_metrics_t vm = controller.vote_metrics(id, qc_ext.qc);

if (tracking_enabled) {
auto track_votes = [&](const chain::qc_vote_metrics_t::fin_auth_set_t& finalizers, bool is_strong) {
for (auto& f: finalizers) {
assert(f.fin_auth);

tracked_votes::vote_info v_info {
.description = f.fin_auth->description,
.public_key = f.fin_auth->public_key.to_string(),
.is_vote_strong = is_strong,
.finalizer_policy_generation = f.generation,
.voted_for_block_id = vm.voted_for_block_id,
.voted_for_block_num = chain::block_header::num_from_id(vm.voted_for_block_id),
.voted_for_block_timestamp = vm.voted_for_block_timestamp
};

last_votes[f.fin_auth->public_key] = std::move(v_info); // track the voting information for the finalizer
}
};

last_votes.emplace(f.fin_auth->public_key, std::move(v_info)); // track the voting information for the finalizer
track_votes(vm.strong_votes, true);
track_votes(vm.weak_votes, false);
}
if (_update_vote_block_metrics) {
update_vote_block_metrics(block->block_num(), vm);
}
};
log_missing_votes(block, id, vm.missing_votes, qc_ext.qc.block_num);
} else if (chain::vote_logger.is_enabled(fc::log_level::info)) {
const auto& qc_ext = block->extract_extension<chain::quorum_certificate_extension>();
auto missing = controller.missing_votes(id, qc_ext.qc);
log_missing_votes(block, id, missing, qc_ext.qc.block_num);
}

track_votes(vm.strong_votes, true);
track_votes(vm.weak_votes, false);
} FC_LOG_AND_DROP(("tracked_votes_impl on_accepted_block ERROR"));
}

Expand All @@ -65,10 +90,50 @@ namespace eosio::chain_apis {

return {};
}

void log_missing_votes(const chain::signed_block_ptr& block, const chain::block_id_type& id,
const chain::qc_vote_metrics_t::fin_auth_set_t& missing_votes,
uint32_t missed_block_num) const {
if (chain::vote_logger.is_enabled(fc::log_level::info)) {
auto now = fc::time_point::now();
if (now - block->timestamp < fc::minutes(5) || (block->block_num() % 1000 == 0)) {
std::string not_voted;
for (const auto& f : missing_votes) {
if (controller.is_node_finalizer_key(f.fin_auth->public_key)) {
fc_wlog(chain::vote_logger, "Local finalizer ${f} did not vote in block ${n} : ${id} for block ${m_n}",
("f", f.fin_auth->description)("n", block->block_num())("id", id.str().substr(8,16))("m_n", missed_block_num));
}
not_voted += f.fin_auth->description;
not_voted += ',';
}
if (!not_voted.empty()) {
not_voted.resize(not_voted.size() - 1); // remove ','
fc_ilog(chain::vote_logger, "Block ${id}... #${n} @ ${t} produced by ${p}, latency: ${l}ms has no votes for block #${m_n} from finalizers: ${v}",
("id", id.str().substr(8, 16))("n", block->block_num())("t", block->timestamp)("p", block->producer)
("l", (now - block->timestamp).count() / 1000)("v", not_voted)("m_n", missed_block_num));
}
}
}
}

// update prometheus metrics
void update_vote_block_metrics(chain::block_num_type block_num, const chain::qc_vote_metrics_t& vm) {
tracked_votes::vote_block_metrics m;
m.block_num = block_num;
m.strong_votes.resize(vm.strong_votes.size());
m.weak_votes.resize(vm.weak_votes.size());
m.no_votes.resize(vm.missing_votes.size());

std::ranges::transform(vm.strong_votes, m.strong_votes.begin(), [](const auto& f) { return f.fin_auth->description; });
std::ranges::transform(vm.weak_votes, m.weak_votes.begin(), [](const auto& f) { return f.fin_auth->description; });
std::ranges::transform(vm.missing_votes, m.no_votes.begin(), [](const auto& f) { return f.fin_auth->description; });
_update_vote_block_metrics(std::move(m));
}

}; // tracked_votes_impl

tracked_votes::tracked_votes( const chain::controller& controller )
:_impl(std::make_unique<tracked_votes_impl>(controller))
tracked_votes::tracked_votes( const chain::controller& controller, bool tracking_enabled )
:_impl(std::make_unique<tracked_votes_impl>(controller, tracking_enabled))
{
}

Expand All @@ -81,4 +146,9 @@ namespace eosio::chain_apis {
std::optional<tracked_votes::vote_info> tracked_votes::get_last_vote_info(const fc::crypto::blslib::bls_public_key& finalizer_pub_key) const {
return _impl->get_last_vote_info(finalizer_pub_key);
}

void tracked_votes::register_update_vote_block_metrics(std::function<void(chain_apis::tracked_votes::vote_block_metrics&&)>&& m) {
_impl->_update_vote_block_metrics = std::move(m);
}

} // namespace eosio::chain_apis
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,6 @@ class producer_plugin : public appbase::plugin<producer_plugin> {

static void set_test_mode(bool m) { test_mode_ = m; }

struct vote_block_metrics {
uint32_t block_num = 0;
std::vector<std::string> strong_votes;
std::vector<std::string> weak_votes;
std::vector<std::string> no_votes;
};

struct speculative_block_metrics {
account_name block_producer{};
uint32_t block_num = 0;
Expand Down Expand Up @@ -195,7 +188,6 @@ class producer_plugin : public appbase::plugin<producer_plugin> {
void register_update_produced_block_metrics(std::function<void(produced_block_metrics)>&&);
void register_update_speculative_block_metrics(std::function<void(speculative_block_metrics)>&&);
void register_update_incoming_block_metrics(std::function<void(incoming_block_metrics)>&&);
void register_update_vote_block_metrics(std::function<void(vote_block_metrics&&)>&&);

inline static bool test_mode_{false}; // to be moved into appbase (application_base)

Expand Down
Loading

0 comments on commit 9b03495

Please sign in to comment.