From 589199a1af8d6f8d6e69a83e44f264b3dc39eea2 Mon Sep 17 00:00:00 2001 From: Ravin Perera <33562092+ravinsp@users.noreply.github.com> Date: Wed, 26 Jan 2022 18:51:01 +0530 Subject: [PATCH] Passed unl stats to contract. (#357) --- examples/c_contract/echo_contract.c | 2 +- examples/c_contract/hotpocket_contract.h | 73 +++++++++++++++++------- src/consensus.cpp | 2 +- src/unl.cpp | 49 ++++++++++------ src/unl.hpp | 4 +- 5 files changed, 89 insertions(+), 41 deletions(-) diff --git a/examples/c_contract/echo_contract.c b/examples/c_contract/echo_contract.c index f7a0115f..2448c23e 100644 --- a/examples/c_contract/echo_contract.c +++ b/examples/c_contract/echo_contract.c @@ -61,7 +61,7 @@ int main(int argc, char **argv) // struct hp_config *config = hp_get_config(); // hp_set_config_string(&config->version, "2.0", 4); // config->round_limits.user_input_bytes = 1024; - // struct hp_unl_node new_unl[2] = {{"ed726f9f536904b125bdca10bbdd1e66591b274799b92ac8bcfc75bf45d7da4c0f"}, + // struct hp_pubkey new_unl[2] = {{"ed726f9f536904b125bdca10bbdd1e66591b274799b92ac8bcfc75bf45d7da4c0f"}, // {"ed3e63992d62804ea0c182e5b22fe43c4b652fbbf068ec7520f3020f4c3771416a"}}; // hp_set_config_unl(config, new_unl, 2); // hp_update_config(config); diff --git a/examples/c_contract/hotpocket_contract.h b/examples/c_contract/hotpocket_contract.h index a63e7227..36b62dcf 100644 --- a/examples/c_contract/hotpocket_contract.h +++ b/examples/c_contract/hotpocket_contract.h @@ -108,10 +108,15 @@ struct hp_user_inputs_collection size_t count; }; +struct hp_pubkey +{ + char data[HP_KEY_SIZE + 1]; // +1 for null char. +}; + // Represents a user that is connected to HP cluster. struct hp_user { - char pubkey[HP_KEY_SIZE + 1]; // +1 for null char. + struct hp_pubkey pubkey; int outfd; struct hp_user_inputs_collection inputs; }; @@ -119,7 +124,8 @@ struct hp_user // Represents a node that's part of unl. struct hp_unl_node { - char pubkey[HP_KEY_SIZE + 1]; // +1 for null char. + struct hp_pubkey pubkey; + uint64_t active_on; }; struct hp_users_collection @@ -129,6 +135,12 @@ struct hp_users_collection int in_fd; }; +struct hp_pubkey_collection +{ + struct hp_pubkey *list; + size_t count; +}; + struct hp_unl_collection { struct hp_unl_node *list; @@ -155,7 +167,7 @@ struct hp_round_limits_config struct hp_config { char *version; - struct hp_unl_collection unl; + struct hp_pubkey_collection unl; char *bin_path; char *bin_args; char *environment; @@ -202,7 +214,7 @@ struct hp_config *hp_get_config(); int hp_update_config(const struct hp_config *config); int hp_update_peers(const char *add_peers[], const size_t add_peers_count, const char *remove_peers[], const size_t remove_peers_count); void hp_set_config_string(char **config_str, const char *value, const size_t value_size); -void hp_set_config_unl(struct hp_config *config, const struct hp_unl_node *new_unl, const size_t new_unl_count); +void hp_set_config_unl(struct hp_config *config, const struct hp_pubkey *new_unl, const size_t new_unl_count); void hp_free_config(struct hp_config *config); void __hp_parse_args_json(const struct json_object_s *object); @@ -493,20 +505,21 @@ int hp_update_config(const struct hp_config *config) { for (size_t i = 0; i < config->unl.count; i++) { - const size_t pubkey_len = strlen(config->unl.list[i].pubkey); + const char *pubkey = config->unl.list[i].data; + const size_t pubkey_len = strlen(pubkey); if (pubkey_len == 0) __HP_UPDATE_CONFIG_ERROR("Unl pubkey cannot be empty."); if (pubkey_len != HP_KEY_SIZE) __HP_UPDATE_CONFIG_ERROR("Unl pubkey invalid. Invalid length."); - if (config->unl.list[i].pubkey[0] != 'e' || config->unl.list[i].pubkey[1] != 'd') + if (pubkey[0] != 'e' || pubkey[1] != 'd') __HP_UPDATE_CONFIG_ERROR("Unl pubkey invalid. Invalid format."); // Checking the validity of hexadecimal portion. (without 'ed'). for (size_t j = 2; j < HP_KEY_SIZE; j++) { - const char current_char = config->unl.list[i].pubkey[j]; + const char current_char = pubkey[j]; if ((current_char < 'A' || current_char > 'F') && (current_char < 'a' || current_char > 'f') && (current_char < '0' || current_char > '9')) __HP_UPDATE_CONFIG_ERROR("Unl pubkey invalid. Invalid character."); } @@ -564,13 +577,13 @@ void hp_set_config_string(char **config_str, const char *value, const size_t val /** * Populates the config unl list with the specified values. * @param config The config struct to populate the unl to. - * @param new_unl Pointer to the new unl node array. - * @param new_unl_count No. of entries in the new unl node array. + * @param new_unl Pointer to array of unl pubkeys. + * @param new_unl_count No. of entries in the new unl pubkey array. */ -void hp_set_config_unl(struct hp_config *config, const struct hp_unl_node *new_unl, const size_t new_unl_count) +void hp_set_config_unl(struct hp_config *config, const struct hp_pubkey *new_unl, const size_t new_unl_count) { - const size_t mem_size = sizeof(struct hp_unl_node) * new_unl_count; - config->unl.list = (struct hp_unl_node *)realloc(config->unl.list, mem_size); + const size_t mem_size = sizeof(struct hp_pubkey) * new_unl_count; + config->unl.list = (struct hp_pubkey *)realloc(config->unl.list, mem_size); memcpy(config->unl.list, new_unl, mem_size); config->unl.count = new_unl_count; } @@ -728,7 +741,7 @@ int __hp_write_to_patch_file(const int fd, const struct hp_config *config) strncpy(unl_buf + pos, "\n ", 9); pos += 9; unl_buf[pos++] = '"'; - strncpy(unl_buf + pos, config->unl.list[i].pubkey, HP_KEY_SIZE); + strncpy(unl_buf + pos, config->unl.list[i].data, HP_KEY_SIZE); pos += HP_KEY_SIZE; unl_buf[pos++] = '"'; } @@ -825,14 +838,14 @@ void __hp_populate_patch_from_json_object(struct hp_config *config, const struct const size_t unl_count = unl_array->length; config->unl.count = unl_count; - config->unl.list = unl_count ? (struct hp_unl_node *)malloc(sizeof(struct hp_unl_node) * unl_count) : NULL; + config->unl.list = unl_count ? (struct hp_pubkey *)malloc(sizeof(struct hp_pubkey) * unl_count) : NULL; if (unl_count > 0) { struct json_array_element_s *unl_elem = unl_array->start; for (int i = 0; i < unl_count; i++) { - __HP_ASSIGN_STRING(config->unl.list[i].pubkey, unl_elem); + __HP_ASSIGN_STRING(config->unl.list[i].data, unl_elem); unl_elem = unl_elem->next; } } @@ -981,7 +994,7 @@ void __hp_parse_args_json(const struct json_object_s *object) for (int i = 0; i < user_count; i++) { struct hp_user *user = &cctx->users.list[i]; - memcpy(user->pubkey, user_elem->name->string, HP_KEY_SIZE); + memcpy(user->pubkey.data, user_elem->name->string, HP_KEY_SIZE); if (user_elem->value->type == json_type_array) { @@ -1020,20 +1033,38 @@ void __hp_parse_args_json(const struct json_object_s *object) } else if (strcmp(k->string, "unl") == 0) { - if (elem->value->type == json_type_array) + // unl is an object with pubkeys as keys. Each key contains an object with that node statistics. + if (elem->value->type == json_type_object) { - const struct json_array_s *unl_array = (struct json_array_s *)elem->value->payload; - const size_t unl_count = unl_array->length; + const struct json_object_s *unl_obj = (struct json_object_s *)elem->value->payload; + const size_t unl_count = unl_obj->length; cctx->unl.count = unl_count; cctx->unl.list = unl_count ? (struct hp_unl_node *)malloc(sizeof(struct hp_unl_node) * unl_count) : NULL; if (unl_count > 0) { - struct json_array_element_s *unl_elem = unl_array->start; + struct json_object_element_s *unl_elem = unl_obj->start; for (int i = 0; i < unl_count; i++) { - __HP_ASSIGN_STRING(cctx->unl.list[i].pubkey, unl_elem); + // Each element(key) is named by the pubkey. + strncpy(cctx->unl.list[i].pubkey.data, unl_elem->name->string, unl_elem->name->string_size); + + if (unl_elem->value->type == json_type_object) + { + const struct json_object_s *stat_obj = (struct json_object_s *)unl_elem->value->payload; + struct json_object_element_s *stat_elem = stat_obj->start; + do + { + const struct json_string_s *k = stat_elem->name; + if (strcmp(k->string, "active_on") == 0) + { + __HP_ASSIGN_UINT64(cctx->unl.list[i].active_on, stat_elem); + } + stat_elem = stat_elem->next; + } while (stat_elem); + } + unl_elem = unl_elem->next; } } diff --git a/src/consensus.cpp b/src/consensus.cpp index 14c0c270..f2e8f39c 100644 --- a/src/consensus.cpp +++ b/src/consensus.cpp @@ -487,7 +487,7 @@ namespace consensus } // Provide latest roundtime information to unl statistics. - unl::update_time_config_stats(collected_proposals); + unl::update_unl_stats(collected_proposals); // Move collected propsals to candidate set of proposals. for (const auto &p : collected_proposals) diff --git a/src/unl.cpp b/src/unl.cpp index 662082b2..47ff4cd3 100644 --- a/src/unl.cpp +++ b/src/unl.cpp @@ -10,8 +10,14 @@ */ namespace unl { - std::map list; // List of binary pubkeys of UNL and their latest reported time configs. - std::string json_list; // Stringified json array of UNL. (To be fed into the contract args) + struct node_stat + { + uint32_t time_config = 0; // Roundtime config of this node. + uint64_t active_on = 0; // Latest timestamp we received a proposal from this node. + }; + + std::map list; // List of binary pubkeys of UNL nodes and their statistics. + std::string json_list; // Stringified json array of UNL. (To be fed into the contract args) std::shared_mutex unl_mutex; /** @@ -81,18 +87,27 @@ namespace unl } /** - * Updates unl pubkey-->time config information using the specified list of proposals. + * Updates unl stats using the specified list of proposals. */ - void update_time_config_stats(const std::list &proposals) + void update_unl_stats(const std::list &proposals) { std::unique_lock lock(unl_mutex); + bool changes_made = false; for (const auto &p : proposals) { const auto itr = list.find(p.pubkey); if (itr != list.end()) - itr->second = p.time_config; + { + changes_made = true; + itr->second.active_on = p.recv_timestamp; + itr->second.time_config = p.time_config; + } } + + // Update the prepared json list which will be fed into contract args. + if (changes_made) + json_list = prepare_json_list(); } /** @@ -112,14 +127,14 @@ namespace unl for (auto itr = list.begin(); itr != list.end(); itr++) { // If time config is 0, attempt to get from peer connection (if available). - if (itr->second == 0) + if (itr->second.time_config == 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_time_config; + itr->second.time_config = peer_itr->second->reported_time_config; } - const uint32_t time_config = itr->second; + const uint32_t time_config = itr->second.time_config; if (time_config > 0) time_config_votes[time_config]++; } @@ -167,7 +182,7 @@ namespace unl { if (list.count(pubkey) == 0) { - list.emplace(pubkey, 0); + list.emplace(pubkey, node_stat{}); changes_made = true; } } @@ -176,7 +191,7 @@ namespace unl return false; // Update the prepared json list which will be fed into contract args. - json_list = prepare_json_list(conf::cfg.contract.unl); + json_list = prepare_json_list(); // Update the own node's unl status. conf::cfg.node.is_unl = (list.count(conf::cfg.node.public_key) == 1); @@ -184,19 +199,21 @@ namespace unl return true; // Changes made. } - const std::string prepare_json_list(const std::set &new_list) + const std::string prepare_json_list() { std::ostringstream os; - os << "["; - for (auto pk = new_list.begin(); pk != new_list.end(); pk++) + os << "{"; + for (auto node = list.begin(); node != list.end(); node++) { - if (pk != new_list.begin()) + if (node != list.begin()) os << ","; // Trailing comma separator for previous element. // Convert binary pubkey into hex. - os << "\"" << util::to_hex(*pk) << "\""; + os << "\"" << util::to_hex(node->first) << "\":{" + << "\"active_on\":" << node->second.active_on + << "}"; } - os << "]"; + os << "}"; return os.str(); } diff --git a/src/unl.hpp b/src/unl.hpp index 32d3cef1..26c224f7 100644 --- a/src/unl.hpp +++ b/src/unl.hpp @@ -17,10 +17,10 @@ namespace unl bool exists(const std::string &bin_pubkey); int init(); void update_unl_changes_from_patch(); - void update_time_config_stats(const std::list &proposals); + void update_unl_stats(const std::list &proposals); uint32_t get_majority_time_config(); bool merge_latest_unl_config(); - const std::string prepare_json_list(const std::set &new_list); + const std::string prepare_json_list(); } // namespace unl