mirror of
https://github.com/EvernodeXRPL/hpcore.git
synced 2026-04-29 15:37:59 +00:00
Dynamic roundtime detection. (#244)
When consensus is unreliable detect roundtime based on roundtime reported by peers.
This commit is contained in:
183
src/unl.cpp
183
src/unl.cpp
@@ -9,10 +9,9 @@
|
||||
*/
|
||||
namespace unl
|
||||
{
|
||||
std::set<std::string> list; // List of binary pubkeys of UNL.
|
||||
std::string json_list; // Stringified json array of UNL. (To be fed into the contract args)
|
||||
std::map<std::string, uint16_t> list; // List of binary pubkeys of UNL and their latest reported roundtime.
|
||||
std::string json_list; // Stringified json array of UNL. (To be fed into the contract args)
|
||||
std::shared_mutex unl_mutex;
|
||||
std::string hash;
|
||||
|
||||
/**
|
||||
* Performs startup activitites related to unl list.
|
||||
@@ -24,11 +23,8 @@ namespace unl
|
||||
return -1;
|
||||
|
||||
std::unique_lock lock(unl_mutex);
|
||||
list = conf::cfg.contract.unl;
|
||||
// Update the own node's unl status.
|
||||
conf::cfg.node.is_unl = (list.find(conf::cfg.node.public_key) != list.end());
|
||||
update_json_list();
|
||||
hash = calculate_hash(list);
|
||||
update_unl_list(conf::cfg.contract.unl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -38,13 +34,16 @@ namespace unl
|
||||
return list.size();
|
||||
}
|
||||
|
||||
std::set<std::string> get()
|
||||
const std::set<std::string> get()
|
||||
{
|
||||
std::shared_lock lock(unl_mutex);
|
||||
return list;
|
||||
std::set<std::string> ret;
|
||||
for (auto [pubkey, roundtime] : list)
|
||||
ret.emplace(std::move(pubkey));
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string get_json()
|
||||
const std::string get_json()
|
||||
{
|
||||
std::shared_lock lock(unl_mutex);
|
||||
return json_list;
|
||||
@@ -61,34 +60,6 @@ namespace unl
|
||||
return list.find(bin_pubkey) != list.end();
|
||||
}
|
||||
|
||||
void update_json_list()
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "[";
|
||||
for (auto pk = list.begin(); pk != list.end(); pk++)
|
||||
{
|
||||
if (pk != list.begin())
|
||||
os << ","; // Trailing comma separator for previous element.
|
||||
|
||||
// Convert binary pubkey into hex.
|
||||
os << "\"" << util::to_hex(*pk) << "\"";
|
||||
}
|
||||
os << "]";
|
||||
json_list = os.str();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate hash of the given set.
|
||||
* @param unl_list UNL list.
|
||||
* @return Returns the generated hash of the given list.
|
||||
*/
|
||||
std::string calculate_hash(const std::set<std::string> &new_list)
|
||||
{
|
||||
std::vector<std::string_view> unl_vector(new_list.begin(), new_list.end());
|
||||
return crypto::get_hash(unl_vector);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace the unl list from the latest unl list from patch file.
|
||||
*/
|
||||
@@ -97,17 +68,7 @@ namespace unl
|
||||
bool is_unl_list_changed = false;
|
||||
{
|
||||
std::unique_lock lock(unl_mutex);
|
||||
const std::string updated_hash = calculate_hash(conf::cfg.contract.unl);
|
||||
if (hash != updated_hash)
|
||||
{
|
||||
hash = updated_hash;
|
||||
list = conf::cfg.contract.unl;
|
||||
update_json_list();
|
||||
|
||||
// Update the own node's unl status.
|
||||
conf::cfg.node.is_unl = (list.find(conf::cfg.node.public_key) != list.end());
|
||||
is_unl_list_changed = true;
|
||||
}
|
||||
is_unl_list_changed = update_unl_list(conf::cfg.contract.unl);
|
||||
}
|
||||
|
||||
// Update the is_unl flag of peer sessions.
|
||||
@@ -115,8 +76,128 @@ namespace unl
|
||||
if (is_unl_list_changed)
|
||||
{
|
||||
p2p::update_unl_connections();
|
||||
usr::announce_unl_list(list);
|
||||
usr::announce_unl_list(conf::cfg.contract.unl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates unl pubkey-roundtime information using the specified list of proposals.
|
||||
*/
|
||||
void update_roundtime_stats(const std::list<p2p::proposal> &proposals)
|
||||
{
|
||||
std::unique_lock lock(unl_mutex);
|
||||
|
||||
for (const auto &p : proposals)
|
||||
{
|
||||
const auto itr = list.find(p.pubkey);
|
||||
if (itr != list.end())
|
||||
itr->second = p.roundtime;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the majority roundtime reported among the unl.
|
||||
*/
|
||||
uint16_t get_majority_roundtime()
|
||||
{
|
||||
std::unique_lock lock(unl_mutex);
|
||||
|
||||
// Vote and find majority roundtime within the unl.
|
||||
// Fill any 0 roundtimes with information from peer connections.
|
||||
std::map<uint16_t, uint32_t> roundtime_votes;
|
||||
|
||||
{
|
||||
std::scoped_lock<std::mutex> lock(p2p::ctx.peer_connections_mutex);
|
||||
|
||||
for (auto itr = list.begin(); itr != list.end(); itr++)
|
||||
{
|
||||
// If roundtime is 0, attempt to get from peer connection (if available).
|
||||
if (itr->second == 0)
|
||||
{
|
||||
const auto peer_itr = p2p::ctx.peer_connections.find(itr->first);
|
||||
if (peer_itr != p2p::ctx.peer_connections.end())
|
||||
itr->second = peer_itr->second->reported_roundtime;
|
||||
}
|
||||
|
||||
const uint16_t roundtime = itr->second;
|
||||
if (roundtime > 0)
|
||||
roundtime_votes[roundtime]++;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the majority vote.
|
||||
uint32_t highest_votes = 0;
|
||||
uint16_t majority_roundtime = 0;
|
||||
for (const auto [roundtime, num_votes] : roundtime_votes)
|
||||
{
|
||||
if (num_votes > highest_votes)
|
||||
{
|
||||
highest_votes = num_votes;
|
||||
majority_roundtime = roundtime;
|
||||
}
|
||||
}
|
||||
|
||||
return majority_roundtime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the unl list using the provided new list.
|
||||
* @return Whether or not any unl list changes were made.
|
||||
*/
|
||||
bool update_unl_list(const std::set<std::string> &new_list)
|
||||
{
|
||||
bool changes_made = false;
|
||||
|
||||
// Erase any pubkeys from current unl list that does not exist in new config.
|
||||
for (auto itr = list.begin(); itr != list.end();)
|
||||
{
|
||||
if (conf::cfg.contract.unl.count(itr->first) == 0)
|
||||
{
|
||||
itr = list.erase(itr);
|
||||
changes_made = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
itr++;
|
||||
}
|
||||
}
|
||||
|
||||
// Add any pubkeys that are not in current unl list.
|
||||
for (const std::string pubkey : conf::cfg.contract.unl)
|
||||
{
|
||||
if (list.count(pubkey) == 0)
|
||||
{
|
||||
list.emplace(pubkey, 0);
|
||||
changes_made = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!changes_made)
|
||||
return false;
|
||||
|
||||
// Update the prepared json list which will be fed into contract args.
|
||||
json_list = prepare_json_list(new_list);
|
||||
|
||||
// Update the own node's unl status.
|
||||
conf::cfg.node.is_unl = (list.count(conf::cfg.node.public_key) == 1);
|
||||
|
||||
return true; // Changes made.
|
||||
}
|
||||
|
||||
const std::string prepare_json_list(const std::set<std::string> &new_list)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "[";
|
||||
for (auto pk = new_list.begin(); pk != new_list.end(); pk++)
|
||||
{
|
||||
if (pk != new_list.begin())
|
||||
os << ","; // Trailing comma separator for previous element.
|
||||
|
||||
// Convert binary pubkey into hex.
|
||||
os << "\"" << util::to_hex(*pk) << "\"";
|
||||
}
|
||||
os << "]";
|
||||
return os.str();
|
||||
}
|
||||
|
||||
} // namespace unl
|
||||
|
||||
Reference in New Issue
Block a user