Add storage server reachability field; test it with quorums (#820)

* Add storage server reachability field; test it with quorums

* Improve method names

* Address review comments
This commit is contained in:
Maxim Shishmarev 2019-09-02 13:06:15 +10:00 committed by Doyle
parent add6051a8f
commit b91e690a62
9 changed files with 120 additions and 5 deletions

View file

@ -2169,6 +2169,11 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::set_storage_server_peer_reachable(crypto::public_key const &pubkey, bool value)
{
return m_service_node_list.set_storage_server_peer_reachable(pubkey, value);
}
//-----------------------------------------------------------------------------------------------
bool core::update_blockchain_pruning()
{
return m_blockchain_storage.update_blockchain_pruning();

View file

@ -887,6 +887,11 @@ namespace cryptonote
*/
void record_checkpoint_vote(crypto::public_key const &pubkey, bool voted) { m_service_node_list.record_checkpoint_vote(pubkey, voted); }
/**
* @brief Record the reachability status of node's storage server
*/
bool set_storage_server_peer_reachable(crypto::public_key const &pubkey, bool value);
/// Time point at which the storage server last pinged us
std::atomic<time_t> m_last_storage_server_ping;
private:

View file

@ -1814,6 +1814,26 @@ namespace service_nodes
info.vote_index = (info.vote_index + 1) % info.votes.size();
}
bool service_node_list::set_storage_server_peer_reachable(crypto::public_key const &pubkey, bool value)
{
std::lock_guard<boost::recursive_mutex> lock(m_sn_mutex);
auto it = m_state.service_nodes_infos.find(pubkey);
if (it == m_state.service_nodes_infos.end()) {
LOG_PRINT_L2("No Service Node is known by this pubkey: " << pubkey);
return false;
} else {
proof_info &info = *it->second->proof;
if (info.storage_server_reachable != value) {
info.storage_server_reachable = value;
LOG_PRINT_L2("Setting reachability status for node " << pubkey << " as: " << (value ? "true" : "false"));
}
return true;
}
}
static quorum_manager quorum_for_serialization_to_quorum_manager(service_node_list::quorum_for_serialization const &source)
{
quorum_manager result = {};

View file

@ -47,6 +47,7 @@ namespace service_nodes
std::array<bool, CHECKPOINT_MIN_QUORUMS_NODE_MUST_VOTE_IN_BEFORE_DEREGISTER_CHECK> votes;
uint8_t vote_index = 0;
std::array<std::pair<uint32_t, uint64_t>, 2> public_ips = {}; // (not serialized)
bool storage_server_reachable = true;
proof_info() { votes.fill(true); }
// Called to update both actual and effective timestamp, i.e. when a proof is received
@ -260,6 +261,8 @@ namespace service_nodes
bool handle_uptime_proof (cryptonote::NOTIFY_UPTIME_PROOF::request const &proof);
void record_checkpoint_vote (crypto::public_key const &pubkey, bool voted);
bool set_storage_server_peer_reachable(crypto::public_key const &pubkey, bool value);
struct quorum_for_serialization
{
uint8_t version;

View file

@ -82,6 +82,11 @@ namespace service_nodes
result.uptime_proved = false;
}
if (!info.proof->storage_server_reachable) {
LOG_PRINT_L1("Service Node is not reachable for node: " << pubkey);
result.storage_server_reachable = false;
}
// IP change checks
const auto &ips = proof.public_ips;
if (ips[0].first && ips[1].first) {
@ -282,6 +287,18 @@ namespace service_nodes
passed = true;
}
if (test_results.uptime_proved && !test_results.storage_server_reachable &&
m_core.get_nettype() == cryptonote::MAINNET &&
m_obligations_height < HF_VERSION_12_CHECKPOINTING_SOFT_FORK_HEIGHT)
{
LOG_PRINT_L1("HF12 Checkpointing Pre-Soft Fork: Service node: "
<< node_key
<< " has unreachable storage server, it would have entered the "
"decommission phase");
passed = true;
}
new_state vote_for_state;
if (passed) {
if (info.is_decommissioned()) {

View file

@ -65,11 +65,12 @@ namespace service_nodes
};
struct service_node_test_results {
bool uptime_proved = true;
bool single_ip = true;
bool voted_in_checkpoints = true;
bool uptime_proved = true;
bool single_ip = true;
bool voted_in_checkpoints = true;
bool storage_server_reachable = true;
bool passed() const { return uptime_proved && voted_in_checkpoints; }
bool passed() const { return uptime_proved && voted_in_checkpoints && storage_server_reachable; }
};
class quorum_cop

View file

@ -2740,6 +2740,7 @@ namespace cryptonote
entry.service_node_version = {info.proof->version_major, info.proof->version_minor, info.proof->version_patch};
entry.public_ip = string_tools::get_ip_string_from_int32(info.proof->public_ip);
entry.storage_port = info.proof->storage_port;
entry.storage_server_reachable = info.proof->storage_server_reachable;
entry.contributors.reserve(info.contributors.size());
@ -3142,6 +3143,35 @@ namespace cryptonote
res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_report_peer_storage_server_status(const COMMAND_RPC_REPORT_PEER_SS_STATUS::request& req,
COMMAND_RPC_REPORT_PEER_SS_STATUS::response& res,
epee::json_rpc::error& error_resp,
const connection_context* ctx)
{
crypto::public_key pubkey;
if (!string_tools::hex_to_pod(req.pubkey, pubkey)) {
MERROR("Could not parse public key: " << req.pubkey);
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
error_resp.message = "Could not parse public key";
return false;
}
if (req.type == "reachability") {
if (!m_core.set_storage_server_peer_reachable(pubkey, req.passed)) {
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
error_resp.message = "Pubkey not found";
return false;
}
} else {
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
error_resp.message = "Unknown status type";
return false;
}
res.status = CORE_RPC_STATUS_OK;
return true;
}
} // namespace cryptonote

View file

@ -184,10 +184,11 @@ namespace cryptonote
MAP_JON_RPC_WE("get_all_service_nodes_keys", on_get_all_service_nodes_keys, COMMAND_RPC_GET_ALL_SERVICE_NODES_KEYS)
MAP_JON_RPC_WE("get_n_service_nodes", on_get_n_service_nodes, COMMAND_RPC_GET_N_SERVICE_NODES)
MAP_JON_RPC_WE("get_staking_requirement", on_get_staking_requirement, COMMAND_RPC_GET_STAKING_REQUIREMENT)
MAP_JON_RPC_WE("get_checkpoints", on_get_checkpoints, COMMAND_RPC_GET_CHECKPOINTS)
MAP_JON_RPC_WE("get_checkpoints", on_get_checkpoints, COMMAND_RPC_GET_CHECKPOINTS)
MAP_JON_RPC_WE_IF("perform_blockchain_test", on_perform_blockchain_test, COMMAND_RPC_PERFORM_BLOCKCHAIN_TEST, !m_restricted)
MAP_JON_RPC_WE_IF("storage_server_ping", on_storage_server_ping, COMMAND_RPC_STORAGE_SERVER_PING, !m_restricted)
MAP_JON_RPC_WE("get_service_nodes_state_changes", on_get_service_nodes_state_changes, COMMAND_RPC_GET_SN_STATE_CHANGES)
MAP_JON_RPC_WE_IF("report_peer_storage_server_status", on_report_peer_storage_server_status, COMMAND_RPC_REPORT_PEER_SS_STATUS, !m_restricted)
END_JSON_RPC_MAP()
END_URI_MAP2()
@ -277,6 +278,7 @@ namespace cryptonote
bool on_storage_server_ping(const COMMAND_RPC_STORAGE_SERVER_PING::request& req, COMMAND_RPC_STORAGE_SERVER_PING::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_checkpoints(const COMMAND_RPC_GET_CHECKPOINTS::request& req, COMMAND_RPC_GET_CHECKPOINTS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_service_nodes_state_changes(const COMMAND_RPC_GET_SN_STATE_CHANGES::request& req, COMMAND_RPC_GET_SN_STATE_CHANGES::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_report_peer_storage_server_status(const COMMAND_RPC_REPORT_PEER_SS_STATUS::request& req, COMMAND_RPC_REPORT_PEER_SS_STATUS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
//-----------------------
#if defined(LOKI_ENABLE_INTEGRATION_TEST_HOOKS)

View file

@ -2763,6 +2763,7 @@ namespace cryptonote
std::string operator_address; // The wallet address of the operator to which the operator cut of the staking reward is sent to.
std::string public_ip; // The public ip address of the service node
uint16_t storage_port; // The port number associated with the storage server
bool storage_server_reachable; // Whether the node's storage server has been reported as unreachable for a long time
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(service_node_pubkey)
@ -2786,6 +2787,7 @@ namespace cryptonote
KV_SERIALIZE(operator_address)
KV_SERIALIZE(public_ip)
KV_SERIALIZE(storage_port)
KV_SERIALIZE(storage_server_reachable)
END_KV_SERIALIZE_MAP()
};
@ -2843,6 +2845,7 @@ namespace cryptonote
bool operator_address;
bool public_ip;
bool storage_port;
bool storage_server_reachable;
bool block_hash;
bool height;
@ -2871,6 +2874,7 @@ namespace cryptonote
KV_SERIALIZE_OPT2(operator_address, false)
KV_SERIALIZE_OPT2(public_ip, false)
KV_SERIALIZE_OPT2(storage_port, false)
KV_SERIALIZE_OPT2(storage_server_reachable, false)
KV_SERIALIZE_OPT2(block_hash, false)
KV_SERIALIZE_OPT2(height, false)
KV_SERIALIZE_OPT2(target_height, false)
@ -2923,6 +2927,7 @@ namespace cryptonote
std::string operator_address; // The wallet address of the operator to which the operator cut of the staking reward is sent to.
std::string public_ip; // The public ip address of the service node
uint16_t storage_port; // The port number associated with the storage server
bool storage_server_reachable; // Whether the node's storage server has been reported as unreachable for a long time
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(service_node_pubkey);
@ -2946,6 +2951,7 @@ namespace cryptonote
KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(operator_address);
KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(public_ip);
KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(storage_port);
KV_SERIALIZE_ENTRY_FIELD_IF_REQUESTED(storage_server_reachable);
END_KV_SERIALIZE_MAP()
};
@ -3233,4 +3239,30 @@ namespace cryptonote
};
typedef epee::misc_utils::struct_init<response_t> response;
};
LOKI_RPC_DOC_INTROSPECT
struct COMMAND_RPC_REPORT_PEER_SS_STATUS
{
struct request
{
std::string type; // test type (currently used: ["reachability"])
std::string pubkey; // service node pubkey
bool passed; // whether the node is passing the test
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(type)
KV_SERIALIZE(pubkey)
KV_SERIALIZE(passed)
END_KV_SERIALIZE_MAP()
};
struct response
{
std::string status; // Generic RPC error code. "OK" is the success value.
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};
};
}