Config patch change detection. (#211)

* Applying patch file changes to hpcore runtime after patch file change detection.
* Removing unl sync functionality.
* Removing subjecting unl changeset to consensus.
This commit is contained in:
Savinda Senevirathne
2021-01-06 20:02:14 +05:30
committed by GitHub
parent c87ae6693d
commit bed8205ca8
22 changed files with 90 additions and 1033 deletions

View File

@@ -3,8 +3,6 @@
#include "conf.hpp"
#include "unl.hpp"
#include "crypto.hpp"
#include "p2p/p2p.hpp"
#include "./msg/fbuf/p2pmsg_helpers.hpp"
/**
* Manages the UNL public keys of this node.
@@ -15,15 +13,6 @@ namespace unl
std::string json_list; // Stringified json array of UNL. (To be fed into the contract args)
std::shared_mutex unl_mutex;
std::string hash;
sync_context sync_ctx;
bool init_success = false;
constexpr uint16_t SYNCER_IDLE_WAIT = 20; // unl syncer loop sleep time (milliseconds).
// Max no. of repetitive reqeust resubmissions before abandoning the sync.
constexpr uint16_t ABANDON_THRESHOLD = 10;
// No. of milliseconds to wait before resubmitting a request.
uint16_t REQUEST_RESUBMIT_TIMEOUT;
/**
* Performs startup activitites related to unl list.
@@ -40,21 +29,9 @@ namespace unl
conf::cfg.node.is_unl = (list.find(conf::cfg.node.public_key) != list.end());
update_json_list();
hash = calculate_hash(list);
sync_ctx.unl_sync_thread = std::thread(unl_syncer_loop);
REQUEST_RESUBMIT_TIMEOUT = conf::cfg.contract.roundtime;
init_success = true;
return 0;
}
void deinit()
{
if (init_success)
{
sync_ctx.is_shutting_down = true;
sync_ctx.unl_sync_thread.join();
}
}
size_t count()
{
std::shared_lock lock(unl_mutex);
@@ -84,75 +61,6 @@ namespace unl
return list.find(bin_pubkey) != list.end();
}
/**
* Called by consensus to apply unl changesets that reached consensus.
*/
void apply_changeset(const std::set<std::string> &additions, const std::set<std::string> &removals)
{
if (additions.empty() && removals.empty())
return;
bool is_updated = false;
{
std::unique_lock lock(unl_mutex);
for (const std::string &pubkey : additions)
{
const auto [ele, is_success] = list.emplace(pubkey);
if (is_success)
is_updated = true;
}
for (const std::string &pubkey : removals)
{
if (list.erase(pubkey))
is_updated = true;
}
if (is_updated)
{
update_json_list();
conf::persist_unl_update(list);
hash = calculate_hash(list);
LOG_INFO << "UNL updated. Count:" << list.size();
// Update the own node's unl status.
conf::cfg.node.is_unl = (list.find(conf::cfg.node.public_key) != list.end());
}
}
// Update the is_unl flag of peer sessions.
if (is_updated)
p2p::update_unl_connections();
}
/**
* Replace the unl list from the received new unl list after verifying it.
* @param new_list The received unl list from a random peer.
* @return Returns -1 on verification failure and 0 on successful replacement.
*/
int verify_and_replace(const std::set<std::string> &new_list)
{
const std::string new_unl_hash = calculate_hash(new_list);
if (new_unl_hash != sync_ctx.target_unl)
{
LOG_INFO << "Hash verification on received unl list failed.";
return -1;
}
{
std::unique_lock lock(unl_mutex);
list = new_list;
update_json_list();
conf::persist_unl_update(list);
hash = new_unl_hash;
// Update the own node's unl status.
conf::cfg.node.is_unl = (list.find(conf::cfg.node.public_key) != list.end());
}
// Update the is_unl flag of peer sessions.
p2p::update_unl_connections();
return 0;
}
void update_json_list()
{
std::ostringstream os;
@@ -169,12 +77,6 @@ namespace unl
json_list = os.str();
}
std::string get_hash()
{
std::shared_lock lock(unl_mutex);
return hash;
}
/**
* Calculate hash of the given set.
* @param unl_list UNL list.
@@ -186,165 +88,33 @@ namespace unl
return crypto::get_hash(unl_vector);
}
/**
* Set sync target to the given unl hash and start syncing.
* @param target_unl_hash The majority unl from the consensus.
* Replace the unl list from the latest unl list from patch file.
*/
void set_sync_target(std::string_view target_unl_hash)
void update_unl_changes_from_patch()
{
if (sync_ctx.is_shutting_down)
return;
std::scoped_lock<std::mutex> lock(sync_ctx.target_unl_mutex);
if (sync_ctx.target_unl != target_unl_hash)
bool is_unl_list_changed = false;
{
sync_ctx.is_syncing = true;
sync_ctx.target_unl = target_unl_hash;
sync_ctx.target_requested_on = 0;
sync_ctx.request_submissions = 0;
LOG_INFO << "unl sync: Syncing for target:" << util::to_hex(sync_ctx.target_unl).substr(0, 10) << " (current:" << util::to_hex(get_hash()).substr(0, 10) << ")";
}
}
/**
* Create and send unl request to random node from the unl list.
*/
void send_unl_sync_request()
{
const uint64_t time_now = util::get_epoch_milliseconds();
// Check whether we need to send any requests or abandon the sync due to timeout.
if ((sync_ctx.target_requested_on == 0) || // Initial request.
(time_now - sync_ctx.target_requested_on) > REQUEST_RESUBMIT_TIMEOUT) // Request resubmission.
{
if (sync_ctx.request_submissions < ABANDON_THRESHOLD)
std::unique_lock lock(unl_mutex);
const std::string updated_hash = calculate_hash(conf::cfg.contract.unl);
if (hash != updated_hash)
{
p2p::unl_sync_request unl_sync_message;
unl_sync_message.required_unl = sync_ctx.target_unl;
hash = updated_hash;
list = conf::cfg.contract.unl;
update_json_list();
flatbuffers::FlatBufferBuilder fbuf(1024);
p2pmsg::create_msg_from_unl_sync_request(fbuf, unl_sync_message);
std::string target_pubkey;
p2p::send_message_to_random_peer(fbuf, target_pubkey);
LOG_DEBUG << "UNL list requested from [" << target_pubkey.substr(0, 10) << "]. Required unl hash:" << util::to_hex(sync_ctx.target_unl).substr(0, 10);
sync_ctx.target_requested_on = time_now;
sync_ctx.request_submissions++;
}
else
{
LOG_INFO << "unl sync: Resubmission threshold exceeded. Abandoning sync.";
sync_ctx.clear_target();
}
}
}
/**
* Perform unl syncing and serving.
*/
void unl_syncer_loop()
{
util::mask_signal();
LOG_INFO << "unl sync: Worker started.";
while (!sync_ctx.is_shutting_down)
{
// Indicates whether any requests/responses were processed in the previous loop iteration.
bool prev_processed = false;
{
std::scoped_lock<std::mutex> lock(sync_ctx.target_unl_mutex);
if (!sync_ctx.target_unl.empty())
send_unl_sync_request();
if (!sync_ctx.target_unl.empty() && check_unl_sync_responses() == 1)
prev_processed = true;
}
if (check_unl_sync_requests() == 1)
prev_processed = true;
// Wait a small delay if there were no requests/responses processed during previous iteration.
if (!prev_processed)
util::sleep(SYNCER_IDLE_WAIT);
}
LOG_INFO << "unl sync: Worker stopped.";
}
/**
* Process any unl sync requests received.
* @return Returns 0 if no requests were processed and returns 1 if atleast one request is served.
*/
int check_unl_sync_requests()
{
// Move over the collected sync requests to the local list.
std::list<std::pair<std::string, p2p::unl_sync_request>> unl_requests;
{
std::scoped_lock<std::mutex>(sync_ctx.list_mutex);
unl_requests.splice(unl_requests.end(), sync_ctx.collected_unl_sync_requests);
}
const std::string unl_hash = get_hash();
std::shared_lock lock(unl_mutex);
for (const auto &[session_id, unl_request] : unl_requests)
{
// First check whether we are at the required unl state.
if (unl_request.required_unl != unl_hash)
continue;
p2p::unl_sync_response resp;
resp.requester_unl = unl_hash;
resp.unl_list = list;
flatbuffers::FlatBufferBuilder fbuf(1024);
p2pmsg::create_msg_from_unl_sync_response(fbuf, resp);
std::string_view msg = std::string_view(
reinterpret_cast<const char *>(fbuf.GetBufferPointer()), fbuf.GetSize());
// Find the peer that we should send the unl response to.
std::scoped_lock<std::mutex> lock(p2p::ctx.peer_connections_mutex);
const auto peer_itr = p2p::ctx.peer_connections.find(session_id);
if (peer_itr != p2p::ctx.peer_connections.end())
{
comm::comm_session *session = peer_itr->second;
session->send(msg);
conf::persist_unl_update(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;
}
}
return unl_requests.empty() ? 0 : 1;
}
/**
* Check for any unl sync responses received.
* @return Returns 0 if no responses were processed and returns 1 if atleast one response was processed.
*/
int check_unl_sync_responses()
{
// Move over the collected sync response to the local list.
std::list<p2p::unl_sync_response> unl_responses;
{
std::scoped_lock<std::mutex>(sync_ctx.list_mutex);
unl_responses.splice(unl_responses.end(), sync_ctx.collected_unl_sync_responses);
}
if (!sync_ctx.target_unl.empty())
{
// Scan any queued unl sync responses.
// Only process the first successful item which matches with our target unl.
for (const p2p::unl_sync_response &unl : unl_responses)
{
if (unl.requester_unl == sync_ctx.target_unl && verify_and_replace(unl.unl_list) != -1)
{
LOG_INFO << "unl sync: Sync complete. New unl:" << util::to_hex(sync_ctx.target_unl).substr(0, 10);
sync_ctx.clear_target();
}
}
}
return unl_responses.empty() ? 0 : 1;
// Update the is_unl flag of peer sessions.
if (is_unl_list_changed)
p2p::update_unl_connections();
}
} // namespace unl