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

Add endpoint to chain_api that reports active (and pending if different) finalizer policies and each finalizers last tracked vote #453

Merged
merged 31 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
07a6e5c
Implement tracked_votes cache
linh2931 Jul 31, 2024
9350290
Add additional fields to vote_metrics
linh2931 Jul 31, 2024
f11ac74
Implement get_finalizer_info
linh2931 Jul 31, 2024
69f3822
Add head_pending_finalizer_policy() and update vote_metrics()
linh2931 Jul 31, 2024
a24233f
Add new test file plugin_http_api_test_savanna.py for Savanna specifi…
linh2931 Jul 31, 2024
c066ef5
Test get_finalizer_info in Legacy
linh2931 Jul 31, 2024
df7a7b1
Update tests to accommodate chain_apis::read_only constructor changes
linh2931 Jul 31, 2024
771a3d3
Remove unneeded imports and improve comments
linh2931 Jul 31, 2024
36eeddc
Merge branch 'main' into get_finalizers_info_rpc
linh2931 Jul 31, 2024
cf42e72
Add missing voted_policy_generation to the output of get_finalizer_info
linh2931 Jul 31, 2024
ef49d53
Merge branch 'main' into get_finalizers_info_rpc
linh2931 Jul 31, 2024
32f547d
Do not compare type() with None
linh2931 Jul 31, 2024
97c72ad
Increase timeout of plugin_http_api_test_savanna
linh2931 Jul 31, 2024
2ecd093
Increase timeouts for plugin_http_api_test, plugin_http_api_test_sava…
linh2931 Jul 31, 2024
46b6d1d
Return an empty pending_finalizer_policy instead of nothing if it doe…
linh2931 Aug 1, 2024
a89b992
Revert the change of timeout increase for plugin_http_api_test, plugi…
linh2931 Aug 1, 2024
ffe8f62
Use passed in is_strong to set is_vote_strong in vote_info
linh2931 Aug 1, 2024
209a7e7
Add specifier to single parameter constructors tracked_votes and tra…
linh2931 Aug 1, 2024
3939e91
Use bls_public_
linh2931 Aug 1, 2024
a736e44
No need to use finalizer_policy_with_string_key for to_variant
linh2931 Aug 1, 2024
1d96c03
Add a try-catch to on_accepted_block()
linh2931 Aug 1, 2024
3e456fe
Small improvements from review comments
linh2931 Aug 1, 2024
466df35
Correct voted policy generation calculation
linh2931 Aug 1, 2024
a1624c6
Add target block number to missing votes logging
linh2931 Aug 1, 2024
76d123d
Add detailed description to each field of get_finalizer_info_result
linh2931 Aug 1, 2024
451e0f1
Update logging for not_votes
linh2931 Aug 1, 2024
1f74929
Fix an indentation
linh2931 Aug 1, 2024
1422630
Rename fin_auth_ele to fin_auth
linh2931 Aug 1, 2024
42a361c
Fix indentation in comment of vote_metrics
linh2931 Aug 2, 2024
7e4dbf5
Merge branch 'main' into get_finalizers_info_rpc
linh2931 Aug 2, 2024
f5a5022
Use better descriptive names for the fields in vote_info, place as t…
linh2931 Aug 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3739,7 +3739,15 @@ struct controller_impl {
block_state_ptr bsp = fork_db_fetch_bsp_on_branch_by_num(id, qc.block_num);
if (!bsp)
return {};
return bsp->aggregating_qc.vote_metrics(qc);

// Get voting metrics from QC
auto result = bsp->aggregating_qc.vote_metrics(qc);

// Populate block related information
result.voted_for_block_id = bsp->id();
result.voted_for_block_timestamp = bsp->timestamp();

return result;
}


Expand Down Expand Up @@ -5465,6 +5473,12 @@ finalizer_policy_ptr controller::head_active_finalizer_policy()const {
});
}

finalizer_policy_ptr controller::head_pending_finalizer_policy()const {
return block_handle_accessor::apply_s<finalizer_policy_ptr>(my->chain_head, [](const auto& head) {
return (head->pending_finalizer_policy ? head->pending_finalizer_policy->second : nullptr);
});
}

qc_vote_metrics_t controller::vote_metrics(const block_id_type& id, const qc_t& qc) const {
return my->vote_metrics(id, qc);
}
Expand Down
14 changes: 12 additions & 2 deletions libraries/chain/finality/qc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,15 @@ qc_vote_metrics_t aggregating_qc_t::vote_metrics(const qc_t& qc) const {
size_t added = 0;
for (size_t i = 0; i < votes.size(); ++i) {
if (votes[i]) {
results.insert(finalizer_authority_ptr{finalizer_policy, &finalizer_policy->finalizers[i]}); // use aliasing shared_ptr constructor
results.insert(qc_vote_metrics_t::fin_auth{
.fin_auth = finalizer_authority_ptr{finalizer_policy, &finalizer_policy->finalizers[i]}, // use aliasing shared_ptr constructor

// add_policy_votes and add_votes in turn is called on
// pending_finalizer_policy after on active_finalizer_policy.
// Therefore pending_finalizer_policy generation will be used
// for generation if the finalizer votes on both active and
// pending finalizer policies.
.generation = finalizer_policy->generation});
++added;
}
}
Expand Down Expand Up @@ -490,7 +498,9 @@ qc_vote_metrics_t::fin_auth_set_t aggregating_qc_t::missing_votes(const qc_t& qc
assert(!other_votes || other_votes->size() == finalizers.size());
for (size_t i = 0; i < votes.size(); ++i) {
if (!votes[i] && !check_other(other_votes, i)) {
not_voted.insert(finalizer_authority_ptr{finalizer_policy, &finalizers[i]}); // use aliasing shared_ptr constructor
not_voted.insert(qc_vote_metrics_t::fin_auth{
.fin_auth = finalizer_authority_ptr{finalizer_policy, &finalizers[i]}, // use aliasing shared_ptr constructor
.generation = finalizer_policy->generation});
}
}
};
Expand Down
5 changes: 2 additions & 3 deletions libraries/chain/include/eosio/chain/controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,9 +276,8 @@ namespace eosio::chain {
// post-instant-finality this always returns nullptr
const producer_authority_schedule* pending_producers_legacy()const;

// returns nullptr pre-savanna
finalizer_policy_ptr head_active_finalizer_policy()const;
// returns nullptr pre-savanna, thread-safe, block_num according to branch corresponding to id
finalizer_policy_ptr head_active_finalizer_policy()const; // returns nullptr pre-savanna
finalizer_policy_ptr head_pending_finalizer_policy()const; // returns nullptr pre-savanna

/// Return the vote metrics for qc.block_num
/// thread-safe
Expand Down
20 changes: 14 additions & 6 deletions libraries/chain/include/eosio/chain/finality/qc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,16 +195,24 @@ namespace eosio::chain {

// finalizer authority of strong, weak, or missing votes
struct qc_vote_metrics_t {
struct fin_auth {
finalizer_authority_ptr fin_auth;
// If the finalizer votes in both active and pending policies,
// use pending finalizer policy's generation.
uint32_t generation{0};
};
struct fin_auth_less {
bool operator()(const finalizer_authority_ptr& lhs, const finalizer_authority_ptr& rhs) const {
return lhs->public_key < rhs->public_key;
bool operator()(const fin_auth& lhs, const fin_auth& rhs) const {
return lhs.fin_auth->public_key < rhs.fin_auth->public_key;
};
};
using fin_auth_set_t = std::set<finalizer_authority_ptr, fin_auth_less>;
using fin_auth_set_t = std::set<fin_auth, fin_auth_less>;

fin_auth_set_t strong_votes;
fin_auth_set_t weak_votes;
fin_auth_set_t missing_votes;
fin_auth_set_t strong_votes;
fin_auth_set_t weak_votes;
fin_auth_set_t missing_votes;
block_timestamp_type voted_for_block_timestamp;
block_id_type voted_for_block_id;
};

/**
Expand Down
1 change: 1 addition & 0 deletions plugins/chain_api_plugin/chain_api_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ void chain_api_plugin::plugin_startup() {
CHAIN_RO_CALL(get_abi, 200, http_params_types::params_required),
CHAIN_RO_CALL(get_raw_code_and_abi, 200, http_params_types::params_required),
CHAIN_RO_CALL(get_raw_abi, 200, http_params_types::params_required),
CHAIN_RO_CALL(get_finalizer_info, 200, http_params_types::no_params),
CHAIN_RO_CALL_POST(get_table_rows, chain_apis::read_only::get_table_rows_result, 200, http_params_types::params_required),
CHAIN_RO_CALL(get_table_by_scope, 200, http_params_types::params_required),
CHAIN_RO_CALL(get_currency_balance, 200, http_params_types::params_required),
Expand Down
1 change: 1 addition & 0 deletions plugins/chain_plugin/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ add_library( chain_plugin
trx_finality_status_processing.cpp
chain_plugin.cpp
trx_retry_db.cpp
tracked_votes.cpp
${HEADERS} )

if(EOSIO_ENABLE_DEVELOPER_OPTIONS)
Expand Down
49 changes: 48 additions & 1 deletion plugins/chain_plugin/chain_plugin.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <eosio/chain_plugin/chain_plugin.hpp>
#include <eosio/chain_plugin/trx_retry_db.hpp>
#include <eosio/chain_plugin/tracked_votes.hpp>
#include <eosio/chain/block_log.hpp>
#include <eosio/chain/exceptions.hpp>
#include <eosio/chain/authorization_manager.hpp>
Expand Down Expand Up @@ -197,6 +198,7 @@ class chain_plugin_impl {
std::optional<chain_apis::account_query_db> _account_query_db;
std::optional<chain_apis::trx_retry_db> _trx_retry_db;
chain_apis::trx_finality_status_processing_ptr _trx_finality_status_processing;
std::optional<chain_apis::tracked_votes> _last_tracked_votes;

static void handle_guard_exception(const chain::guard_exception& e);
void do_hard_replay(const variables_map& options);
Expand Down Expand Up @@ -1040,6 +1042,10 @@ void chain_plugin_impl::plugin_initialize(const variables_map& options) {
_trx_finality_status_processing->signal_accepted_block(block, id);
}

if (_last_tracked_votes) {
_last_tracked_votes->on_accepted_block(block, id);
}

accepted_block_channel.publish( priority::high, t );
} );

Expand Down Expand Up @@ -1141,6 +1147,7 @@ void chain_plugin_impl::plugin_startup()
} FC_LOG_AND_DROP(("Unable to enable account queries"));
}

_last_tracked_votes.emplace(*chain);

} FC_CAPTURE_AND_RETHROW() }

Expand Down Expand Up @@ -1190,7 +1197,7 @@ chain_apis::read_write chain_plugin::get_read_write_api(const fc::microseconds&
}

chain_apis::read_only chain_plugin::get_read_only_api(const fc::microseconds& http_max_response_time) const {
return chain_apis::read_only(chain(), my->_account_query_db, get_abi_serializer_max_time(), http_max_response_time, my->_trx_finality_status_processing.get());
return chain_apis::read_only(chain(), my->_account_query_db, my->_last_tracked_votes, get_abi_serializer_max_time(), http_max_response_time, my->_trx_finality_status_processing.get());
}


Expand Down Expand Up @@ -1736,6 +1743,46 @@ fc::variant get_global_row( const database& db, const abi_def& abi, const abi_se
return abis.binary_to_variant(abis.get_table_type("global"_n), data, abi_serializer::create_yield_function( abi_serializer_max_time_us ), shorten_abi_errors );
}

read_only::get_finalizer_info_result read_only::get_finalizer_info( const read_only::get_finalizer_info_params& p, const fc::time_point& ) const {
read_only::get_finalizer_info_result result;

// Finalizer keys present in active_finalizer_policy and pending_finalizer_policy.
// Use std::set for eliminating duplications.
std::set<fc::crypto::blslib::bls_public_key> finalizer_keys;

// Populate a particular finalizer policy
auto add_policy_to_result = [&](const finalizer_policy_ptr& from_policy, fc::variant& to_policy) {
if (from_policy) {
// Use string format of public key for easy uses
to_variant(*from_policy, to_policy);

for (const auto& f: from_policy->finalizers) {
finalizer_keys.insert(f.public_key);
}
}
};

// Populate active_finalizer_policy and pending_finalizer_policy
add_policy_to_result(db.head_active_finalizer_policy(), result.active_finalizer_policy);
add_policy_to_result(db.head_pending_finalizer_policy(), result.pending_finalizer_policy);

// Populate last_tracked_votes
if (last_tracked_votes) {
for (const auto& k: finalizer_keys) {
if (const auto& v = last_tracked_votes->get_last_vote_info(k)) {
result.last_tracked_votes.emplace_back(*v);
}
}
}

// Sort last_tracked_votes by description
std::sort( result.last_tracked_votes.begin(), result.last_tracked_votes.end(), []( const tracked_votes::vote_info& lhs, const tracked_votes::vote_info& rhs ) {
return lhs.description < rhs.description;
});

return result;
}

read_only::get_producers_result
read_only::get_producers( const read_only::get_producers_params& params, const fc::time_point& deadline ) const try {
abi_def abi = eosio::chain_apis::get_abi(db, config::system_account_name);
Expand Down
23 changes: 23 additions & 0 deletions plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <eosio/chain_plugin/account_query_db.hpp>
#include <eosio/chain_plugin/trx_retry_db.hpp>
#include <eosio/chain_plugin/trx_finality_status_processing.hpp>
#include <eosio/chain_plugin/tracked_votes.hpp>

#include <eosio/chain/application.hpp>
#include <eosio/chain/asset.hpp>
Expand Down Expand Up @@ -139,6 +140,7 @@ class api_base {
class read_only : public api_base {
const controller& db;
const std::optional<account_query_db>& aqdb;
const std::optional<tracked_votes>& last_tracked_votes;
const fc::microseconds abi_serializer_max_time;
const fc::microseconds http_max_response_time;
bool shorten_abi_errors = true;
Expand All @@ -149,10 +151,12 @@ class read_only : public api_base {
static const string KEYi64;

read_only(const controller& db, const std::optional<account_query_db>& aqdb,
const std::optional<tracked_votes>& last_tracked_votes,
const fc::microseconds& abi_serializer_max_time, const fc::microseconds& http_max_response_time,
const trx_finality_status_processing* trx_finality_status_proc)
: db(db)
, aqdb(aqdb)
, last_tracked_votes(last_tracked_votes)
, abi_serializer_max_time(abi_serializer_max_time)
, http_max_response_time(http_max_response_time)
, trx_finality_status_proc(trx_finality_status_proc) {
Expand Down Expand Up @@ -486,6 +490,22 @@ class read_only : public api_base {

fc::variant get_currency_stats( const get_currency_stats_params& params, const fc::time_point& deadline )const;

struct get_finalizer_info_params {
};

struct get_finalizer_info_result {
fc::variant active_finalizer_policy; // current active policy
fc::variant pending_finalizer_policy; // current pending policy. Empty if not existing

// Last tracked vote information for each of the finalizers in
// active_finalizer_policy and pending_finalizer_policy.
// if a finalizer votes on both active_finalizer_policy and pending_finalizer_policy,
// the vote information on pending_finalizer_policy is used.
std::vector<tracked_votes::vote_info> last_tracked_votes;
};

get_finalizer_info_result get_finalizer_info( const get_finalizer_info_params& params, const fc::time_point& deadline )const;

struct get_producers_params {
bool json = false;
string lower_bound;
Expand Down Expand Up @@ -1047,6 +1067,9 @@ FC_REFLECT( eosio::chain_apis::read_only::get_currency_balance_params, (code)(ac
FC_REFLECT( eosio::chain_apis::read_only::get_currency_stats_params, (code)(symbol));
FC_REFLECT( eosio::chain_apis::read_only::get_currency_stats_result, (supply)(max_supply)(issuer));

FC_REFLECT_EMPTY( eosio::chain_apis::read_only::get_finalizer_info_params )
FC_REFLECT( eosio::chain_apis::read_only::get_finalizer_info_result, (active_finalizer_policy)(pending_finalizer_policy)(last_tracked_votes) );

FC_REFLECT( eosio::chain_apis::read_only::get_producers_params, (json)(lower_bound)(limit)(time_limit_ms) )
FC_REFLECT( eosio::chain_apis::read_only::get_producers_result, (rows)(total_producer_vote_weight)(more) );

Expand Down
50 changes: 50 additions & 0 deletions plugins/chain_plugin/include/eosio/chain_plugin/tracked_votes.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#pragma once
#include <eosio/chain/types.hpp>
#include <eosio/chain/block.hpp>
#include <eosio/chain/controller.hpp>

namespace eosio::chain_apis {
/**
* This class manages the ephemeral data that is needed by `get_finalizers_info` RPC call.
* There is no persistence and the cache is recreated when the class is instantiated
* based on the current state of the chain.
*/
class tracked_votes {
public:

/**
* 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 chain - controller to read data from
*/
explicit tracked_votes( const class eosio::chain::controller& chain );
~tracked_votes();

/**
* vote information for a given finalizer.
*/
struct vote_info {
std::string description; // voting finalizer's description
std::string public_key; // voting finalizer's public key
bool is_vote_strong{false}; // indicating the vote is strong or not
uint32_t finalizer_policy_generation{0}; // the generation of finalizer policy being used to vote
chain::block_id_type voted_for_block_id; // block id of the block being voted
uint32_t voted_for_block_num{0}; // block number of the block being voted
fc::time_point voted_for_block_timestamp; // block timestamp of the block being voted
};

// 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);

// 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;

private:
std::unique_ptr<struct tracked_votes_impl> _impl;
};

}

FC_REFLECT( eosio::chain_apis::tracked_votes::vote_info, (description)(public_key)(is_vote_strong)(finalizer_policy_generation)(voted_for_block_id)(voted_for_block_num)(voted_for_block_timestamp))
Loading