From 2a4a7d3b342f28811f119323ccef801bc6ac5578 Mon Sep 17 00:00:00 2001 From: Udith Indrakantha <45740208+Udith-Gayan@users.noreply.github.com> Date: Fri, 22 Jul 2022 11:36:06 +0530 Subject: [PATCH] Restructuring hp.cfg consensus and npl blocks (#365) --- src/conf.cpp | 83 +++++++++++++++++++----------- src/conf.hpp | 28 ++++++++-- src/consensus.cpp | 22 ++++---- src/hpfs/hpfs_mount.hpp | 2 +- src/ledger/ledger_query.cpp | 2 +- src/msg/bson/usrmsg_bson.cpp | 2 +- src/msg/fbuf/p2pmsg_conversion.cpp | 2 +- src/msg/json/usrmsg_json.cpp | 2 +- src/p2p/p2p.cpp | 2 +- src/p2p/peer_comm_session.cpp | 4 +- src/sc/sc.cpp | 2 +- 11 files changed, 95 insertions(+), 56 deletions(-) diff --git a/src/conf.cpp b/src/conf.cpp index af7b688e..af825a5a 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -22,13 +22,14 @@ namespace conf constexpr int FILE_PERMS = 0644; constexpr uint32_t MAX_ROUND_TIME = 3600000; constexpr uint32_t MAX_STAGE_SLICE = 33; + constexpr uint32_t MAX_THRESHOLD = 100; constexpr const char *ROLE_OBSERVER = "observer"; constexpr const char *ROLE_VALIDATOR = "validator"; constexpr const char *HISTORY_FULL = "full"; constexpr const char *HISTORY_CUSTOM = "custom"; - constexpr const char *PUBLIC = "public"; - constexpr const char *PRIVATE = "private"; + constexpr const char *MODE_PUBLIC = "public"; + constexpr const char *MODE_PRIVATE = "private"; bool init_success = false; @@ -145,9 +146,9 @@ namespace conf return -1; } - //Create config file with default settings. + // Create config file with default settings. - //We populate the in-memory struct with default settings and then save it to the file. + // We populate the in-memory struct with default settings and then save it to the file. { hp_config cfg = {}; @@ -166,13 +167,14 @@ namespace conf cfg.contract.log.max_mbytes_per_file = 5; cfg.contract.log.max_file_count = 10; cfg.contract.version = "1.0"; - //Add self pubkey to the unl. + // Add self pubkey to the unl. cfg.contract.unl.emplace(cfg.node.public_key); cfg.contract.bin_path = ""; - cfg.contract.roundtime = 1000; - cfg.contract.stage_slice = 25; - cfg.contract.is_consensus_public = false; - cfg.contract.is_npl_public = false; + cfg.contract.consensus.roundtime = 1000; + cfg.contract.consensus.stage_slice = 25; + cfg.contract.consensus.mode = MODE::PRIVATE; + cfg.contract.consensus.threshold = 80; + cfg.contract.npl.mode = MODE::PRIVATE; cfg.contract.max_input_ledger_offset = 10; cfg.mesh.port = 22860; @@ -192,7 +194,7 @@ namespace conf cfg.log.loggers.emplace("console"); cfg.log.loggers.emplace("file"); - //Save the default settings into the config file. + // Save the default settings into the config file. if (write_config(cfg) != 0) return -1; } @@ -642,8 +644,9 @@ namespace conf // Other required fields. bool fields_invalid = false; - fields_invalid |= cfg.contract.roundtime == 0 && std::cerr << "Invalid value for roundtime.\n"; - fields_invalid |= cfg.contract.stage_slice == 0 && std::cerr << "Invalid value for stage slice.\n"; + fields_invalid |= cfg.contract.consensus.roundtime == 0 && std::cerr << "Invalid value for roundtime.\n"; + fields_invalid |= cfg.contract.consensus.stage_slice == 0 && std::cerr << "Invalid value for stage slice.\n"; + fields_invalid |= cfg.contract.consensus.threshold == 0 && std::cerr << "Invalid value for threshold.\n"; fields_invalid |= cfg.contract.unl.empty() && std::cerr << "Invalid value for unl. Unl list cannot be empty.\n"; fields_invalid |= cfg.contract.id.empty() && std::cerr << "Invalid value for contract id.\n"; fields_invalid |= cfg.mesh.port == 0 && std::cerr << "Invalid value for mesh port.\n"; @@ -687,7 +690,7 @@ namespace conf } } - //Sign and verify a sample message to ensure we have a matching signing key pair. + // Sign and verify a sample message to ensure we have a matching signing key pair. const std::string msg = "hotpocket"; const std::string sig = crypto::sign(msg, cfg.node.private_key); if (crypto::verify(msg, sig, cfg.node.public_key) != 0) @@ -760,7 +763,7 @@ namespace conf * Convert string to Log Severity enum type. * @param severity log severity code. * @return log severity type. - */ + */ LOG_SEVERITY get_loglevel_type(std::string_view severity) { if (severity == "dbg") @@ -788,7 +791,7 @@ namespace conf /** * Populate patch.cfg in hpfs using current values in config. * @return Returns -1 on error and 0 on successful update. - */ + */ int populate_patch_config() { jsoncons::ojson jdoc; @@ -803,7 +806,7 @@ namespace conf * we also persist it to hp.cfg so that both config files are consistent with each other. * @param hpfs_session_name The current hpfs session hosting the patch config. * @return -1 on error and 0 in successful update. - */ + */ int apply_patch_config(std::string_view hpfs_session_name) { const std::string path = sc::contract_fs.physical_path(hpfs_session_name, sc::PATCH_FILE_PATH); @@ -886,7 +889,7 @@ namespace conf * Locks the config file. If already locked means there's another hpcore instance running in the same directory. * If so, log error and return, Otherwise lock the config. * @return Returns 0 if lock is successfully acquired, -1 on error. - */ + */ int set_config_lock() { ctx.config_fd = open(ctx.config_file.data(), O_RDWR, 444); @@ -910,7 +913,7 @@ namespace conf /** * Releases the config file and closes the opened file descriptor. * @return Returns 0 if lock is successfully acquired, -1 on error. - */ + */ int release_config_lock() { const int res = util::release_lock(ctx.config_fd, ctx.config_lock); @@ -949,12 +952,19 @@ namespace conf jdoc.insert_or_assign("bin_path", contract.bin_path); jdoc.insert_or_assign("bin_args", contract.bin_args); jdoc.insert_or_assign("environment", contract.environment); - jdoc.insert_or_assign("roundtime", contract.roundtime.load()); - jdoc.insert_or_assign("stage_slice", contract.stage_slice.load()); - jdoc.insert_or_assign("consensus", contract.is_consensus_public ? PUBLIC : PRIVATE); - jdoc.insert_or_assign("npl", contract.is_npl_public ? PUBLIC : PRIVATE); jdoc.insert_or_assign("max_input_ledger_offset", contract.max_input_ledger_offset); + jsoncons::ojson consensus; + consensus.insert_or_assign("mode", contract.consensus.mode == MODE::PUBLIC ? MODE_PUBLIC : MODE_PRIVATE); + consensus.insert_or_assign("roundtime", contract.consensus.roundtime.load()); + consensus.insert_or_assign("stage_slice", contract.consensus.stage_slice.load()); + consensus.insert_or_assign("threshold", contract.consensus.threshold); + jdoc.insert_or_assign("consensus", consensus); + + jsoncons::ojson npl; + npl.insert_or_assign("mode", contract.npl.mode == MODE::PUBLIC ? MODE_PUBLIC : MODE_PRIVATE); + jdoc.insert_or_assign("npl", npl); + jsoncons::ojson round_limits; round_limits.insert_or_assign("user_input_bytes", contract.round_limits.user_input_bytes); round_limits.insert_or_assign("user_output_bytes", contract.round_limits.user_output_bytes); @@ -1021,6 +1031,7 @@ namespace conf return -1; } + jpath = "contract.unl"; contract.unl.clear(); for (auto &nodepk : jdoc["unl"].array_range()) { @@ -1042,35 +1053,45 @@ namespace conf contract.bin_path = jdoc["bin_path"].as(); contract.bin_args = jdoc["bin_args"].as(); contract.environment = jdoc["environment"].as(); + contract.max_input_ledger_offset = jdoc["max_input_ledger_offset"].as(); - contract.roundtime = jdoc["roundtime"].as(); - if (contract.roundtime < 1 || contract.roundtime > MAX_ROUND_TIME) + + jpath = "contract.consensus"; + contract.consensus.roundtime = jdoc["consensus"]["roundtime"].as(); + if (contract.consensus.roundtime < 1 || contract.consensus.roundtime > MAX_ROUND_TIME) { std::cerr << "Round time must be between 1 and " << MAX_ROUND_TIME << "ms inclusive.\n"; return -1; } - contract.stage_slice = jdoc["stage_slice"].as(); - if (contract.stage_slice < 1 || contract.stage_slice > MAX_STAGE_SLICE) + contract.consensus.stage_slice = jdoc["consensus"]["stage_slice"].as(); + if (contract.consensus.stage_slice < 1 || contract.consensus.stage_slice > MAX_STAGE_SLICE) { std::cerr << "Stage slice must be between 1 and " << MAX_STAGE_SLICE << " percent inclusive.\n"; return -1; } - if (jdoc["consensus"] != PUBLIC && jdoc["consensus"] != PRIVATE) + contract.consensus.threshold = jdoc["consensus"]["threshold"].as(); + if (contract.consensus.threshold < 1 || contract.consensus.threshold > MAX_THRESHOLD) + { + std::cerr << "Threshold must be between 1 and " << MAX_THRESHOLD << " percent inclusive.\n"; + return -1; + } + + if (jdoc["consensus"]["mode"].as() != MODE_PUBLIC && jdoc["consensus"]["mode"].as() != MODE_PRIVATE) { std::cerr << "Invalid consensus flag configured. Valid values: public|private\n"; return -1; } - contract.is_consensus_public = jdoc["consensus"] == PUBLIC; + contract.consensus.mode = jdoc["consensus"]["mode"].as() == MODE_PUBLIC ? MODE::PUBLIC : MODE::PRIVATE; - if (jdoc["npl"] != PUBLIC && jdoc["npl"] != PRIVATE) + jpath = "contract.npl"; + if (jdoc["npl"]["mode"].as() != MODE_PUBLIC && jdoc["npl"]["mode"].as() != MODE_PRIVATE) { std::cerr << "Invalid npl flag configured. Valid values: public|private\n"; return -1; } - contract.is_npl_public = jdoc["npl"] == PUBLIC; - contract.max_input_ledger_offset = jdoc["max_input_ledger_offset"].as(); + contract.npl.mode = jdoc["npl"]["mode"].as() == MODE_PUBLIC ? MODE::PUBLIC : MODE::PRIVATE; jpath = "contract.round_limits"; contract.round_limits.user_input_bytes = jdoc["round_limits"]["user_input_bytes"].as(); diff --git a/src/conf.hpp b/src/conf.hpp index b0155086..11062794 100644 --- a/src/conf.hpp +++ b/src/conf.hpp @@ -12,7 +12,7 @@ namespace conf { constexpr size_t CONCURRENT_READ_REQUEST_MAX_LIMIT = 32; -#define CURRENT_TIME_CONFIG ((conf::cfg.contract.roundtime * 100) + conf::cfg.contract.stage_slice) +#define CURRENT_TIME_CONFIG ((conf::cfg.contract.consensus.roundtime * 100) + conf::cfg.contract.consensus.stage_slice) // Struct to represent ip and port of the peer. struct peer_ip_port @@ -124,6 +124,13 @@ namespace conf CUSTOM }; + // Broadcasting mode of consensus and npl + enum MODE + { + PUBLIC, + PRIVATE + }; + // Max number of shards to keep for primary and raw shards. struct history_configuration { @@ -162,6 +169,19 @@ namespace conf size_t max_file_count = 0; // Max no. of log files to keep. }; + struct consensus_config + { + MODE mode; // If PUBLIC, consensus are broadcasted to non-unl nodes as well. + std::atomic roundtime = 0; // Consensus round time in ms (max: 3,600,000). + std::atomic stage_slice = 0; // Percentage slice of round time that stages 0,1,2 get (max: 33). + uint16_t threshold = 0; + }; + + struct npl_config + { + MODE mode; // If PUBLIC, npl messages are broadcasted to non-unl nodes as well. + }; + struct contract_config { std::string id; // Contract guid. @@ -174,11 +194,9 @@ namespace conf std::string bin_path; // Full path to the contract binary. std::string bin_args; // CLI arguments to pass to the contract binary. std::string environment; // Environment variables to be passed into contract. - std::atomic roundtime = 0; // Consensus round time in ms (max: 3,600,000). - std::atomic stage_slice = 0; // Percentage slice of round time that stages 0,1,2 get (max: 33). - bool is_consensus_public = false; // If true, consensus are broadcasted to non-unl nodes as well. - bool is_npl_public = false; // If true, npl messages are broadcasted to non-unl nodes as well. uint16_t max_input_ledger_offset; // Maximum ledger sequence number offset that can be specified in the input. + consensus_config consensus; + npl_config npl; round_limits_config round_limits; // Config element which are initialized in memory (This is not directly loaded from the config file) diff --git a/src/consensus.cpp b/src/consensus.cpp index 38ed22c8..ba2d3094 100644 --- a/src/consensus.cpp +++ b/src/consensus.cpp @@ -510,7 +510,7 @@ namespace consensus // If we are in sync, consider proposals from previous stage only. bool keep_candidate = false; - if (ctx.round_start_time >= cp.time && (ctx.round_start_time - cp.time) <= conf::cfg.contract.roundtime) + if (ctx.round_start_time >= cp.time && (ctx.round_start_time - cp.time) <= conf::cfg.contract.consensus.roundtime) { if (!in_sync) keep_candidate = true; @@ -692,11 +692,11 @@ namespace consensus if (ctx.stage == 0) { // This gets the start time of current round window. Stage 0 must start in the window after that. - const uint64_t previous_round_start = (((uint64_t)((now - ctx.round_boundry_offset) / conf::cfg.contract.roundtime)) * conf::cfg.contract.roundtime) + ctx.round_boundry_offset; + const uint64_t previous_round_start = (((uint64_t)((now - ctx.round_boundry_offset) / conf::cfg.contract.consensus.roundtime)) * conf::cfg.contract.consensus.roundtime) + ctx.round_boundry_offset; // Stage 0 must start in the next round window. // (This makes sure stage 3 gets whichever the remaining time in the round after stages 0,1,2) - ctx.round_start_time = previous_round_start + conf::cfg.contract.roundtime; + ctx.round_start_time = previous_round_start + conf::cfg.contract.consensus.roundtime; const uint64_t to_wait = ctx.round_start_time - now; LOG_DEBUG << "Waiting " << to_wait << "ms for next round stage 0."; @@ -777,7 +777,7 @@ namespace consensus flatbuffers::FlatBufferBuilder fbuf; p2pmsg::create_msg_from_proposal(fbuf, p); - p2p::broadcast_message(fbuf, true, false, !conf::cfg.contract.is_consensus_public, 1); // Use high priority send. + p2p::broadcast_message(fbuf, true, false, conf::cfg.contract.consensus.mode != conf::MODE::PUBLIC, 1); // Use high priority send. LOG_DEBUG << "Proposed-s" << std::to_string(p.stage) << " u/i/t:" << p.users.size() @@ -958,7 +958,7 @@ namespace consensus { // Vote for times. // Everyone votes on the discreet time, as long as it's not in the future and within 2 round times. - if (time_now > cp.time && (time_now - cp.time) <= (conf::cfg.contract.roundtime * 2)) + if (time_now > cp.time && (time_now - cp.time) <= (conf::cfg.contract.consensus.roundtime * 2)) increment(votes.time, cp.time); // Vote for round nonce. @@ -1462,19 +1462,19 @@ namespace consensus LOG_INFO << "New time config detected:" << majority_time_config << " previous:" << CURRENT_TIME_CONFIG; // Time config is a single value derived from roundtime*100 + stage_slice. Here we derive back the original components. - conf::cfg.contract.roundtime = (majority_time_config / 100); - conf::cfg.contract.stage_slice = majority_time_config - (conf::cfg.contract.roundtime * 100); + conf::cfg.contract.consensus.roundtime = (majority_time_config / 100); + conf::cfg.contract.consensus.stage_slice = majority_time_config - (conf::cfg.contract.consensus.roundtime * 100); } // We allocate configured stage slice for stages 1, 2, 3. Stage 0 gets the entire remaining time from the round window. - ctx.stage123_duration = conf::cfg.contract.roundtime * conf::cfg.contract.stage_slice / 100; - ctx.stage0_duration = conf::cfg.contract.roundtime - (ctx.stage123_duration * 3); - ctx.stage_reset_wait_threshold = conf::cfg.contract.roundtime / 10; + ctx.stage123_duration = conf::cfg.contract.consensus.roundtime * conf::cfg.contract.consensus.stage_slice / 100; + ctx.stage0_duration = conf::cfg.contract.consensus.roundtime - (ctx.stage123_duration * 3); + ctx.stage_reset_wait_threshold = conf::cfg.contract.consensus.roundtime / 10; // We use a time window boundry offset based on contract id to vary the window boundries between // different contracts with same round time. std::hash str_hasher; - ctx.round_boundry_offset = str_hasher(conf::cfg.contract.id) % conf::cfg.contract.roundtime; + ctx.round_boundry_offset = str_hasher(conf::cfg.contract.id) % conf::cfg.contract.consensus.roundtime; } } // namespace consensus diff --git a/src/hpfs/hpfs_mount.hpp b/src/hpfs/hpfs_mount.hpp index 30a3af1c..ceb51838 100644 --- a/src/hpfs/hpfs_mount.hpp +++ b/src/hpfs/hpfs_mount.hpp @@ -24,7 +24,7 @@ namespace hpfs inline uint32_t get_request_resubmit_timeout() { - return conf::cfg.contract.roundtime * 0.7; + return conf::cfg.contract.consensus.roundtime * 0.7; } const util::h32 get_root_hash(const util::h32 &child_one, const util::h32 &child_two); diff --git a/src/ledger/ledger_query.cpp b/src/ledger/ledger_query.cpp index 40be6724..86e3ffdc 100644 --- a/src/ledger/ledger_query.cpp +++ b/src/ledger/ledger_query.cpp @@ -45,7 +45,7 @@ namespace ledger::query if (seq_q.inputs || seq_q.outputs) { // Do not return other users' blobs if consensus is private. - const std::string filter_user = conf::cfg.contract.is_consensus_public ? "" : std::string(user_pubkey); + const std::string filter_user = conf::cfg.contract.consensus.mode == conf::MODE::PUBLIC ? "" : std::string(user_pubkey); for (ledger_record &ledger : ledgers) { diff --git a/src/msg/bson/usrmsg_bson.cpp b/src/msg/bson/usrmsg_bson.cpp index 2300886c..7b7e26df 100644 --- a/src/msg/bson/usrmsg_bson.cpp +++ b/src/msg/bson/usrmsg_bson.cpp @@ -50,7 +50,7 @@ namespace msg::usrmsg::bson encoder.key(msg::usrmsg::FLD_VOTE_STATUS); encoder.string_value(msg::usrmsg::VOTE_STATUSES[vote_status]); encoder.key(msg::usrmsg::FLD_ROUND_TIME); - encoder.uint64_value(conf::cfg.contract.roundtime); + encoder.uint64_value(conf::cfg.contract.consensus.roundtime); encoder.key(msg::usrmsg::FLD_CONTARCT_EXECUTION_ENABLED); encoder.bool_value(conf::cfg.contract.execute); encoder.key(msg::usrmsg::FLD_READ_REQUESTS_ENABLED); diff --git a/src/msg/fbuf/p2pmsg_conversion.cpp b/src/msg/fbuf/p2pmsg_conversion.cpp index f250c667..b6bd7fc8 100644 --- a/src/msg/fbuf/p2pmsg_conversion.cpp +++ b/src/msg/fbuf/p2pmsg_conversion.cpp @@ -42,7 +42,7 @@ namespace msg::fbuf::p2pmsg if (session && session->challenge_status == comm::CHALLENGE_STATUS::CHALLENGE_VERIFIED && message.size() <= MAX_SIZE_FOR_TIME_CHECK) { const uint64_t time_now = util::get_epoch_milliseconds(); - if (p2p_msg->created_on() < (time_now - (conf::cfg.contract.roundtime * 3))) + if (p2p_msg->created_on() < (time_now - (conf::cfg.contract.consensus.roundtime * 3))) { LOG_DEBUG << "Peer message is too old. type:" << p2p_msg->content_type() << " from:" << (session ? session->display_name() : ""); return p2p::peer_message_info{NULL, P2PMsgContent_NONE, 0}; diff --git a/src/msg/json/usrmsg_json.cpp b/src/msg/json/usrmsg_json.cpp index 069854c9..4b4ac57b 100644 --- a/src/msg/json/usrmsg_json.cpp +++ b/src/msg/json/usrmsg_json.cpp @@ -180,7 +180,7 @@ namespace msg::usrmsg::json msg += SEP_COMMA; msg += msg::usrmsg::FLD_ROUND_TIME; msg += SEP_COLON_NOQUOTE; - msg += std::to_string(conf::cfg.contract.roundtime); + msg += std::to_string(conf::cfg.contract.consensus.roundtime); msg += SEP_COMMA_NOQUOTE; msg += msg::usrmsg::FLD_CONTARCT_EXECUTION_ENABLED; msg += SEP_COLON_NOQUOTE; diff --git a/src/p2p/p2p.cpp b/src/p2p/p2p.cpp index b39b7b4c..046e314a 100644 --- a/src/p2p/p2p.cpp +++ b/src/p2p/p2p.cpp @@ -260,7 +260,7 @@ namespace p2p { // Checking the time to live of the message. The time to live for forwarding is three times the round time. const uint64_t time_now = util::get_epoch_milliseconds(); - if (originated_on < (time_now - (conf::cfg.contract.roundtime * 3))) + if (originated_on < (time_now - (conf::cfg.contract.consensus.roundtime * 3))) { LOG_DEBUG << "Peer message is too old for forwarding. type:" << msg_type << " from:" << session.display_name(); return false; diff --git a/src/p2p/peer_comm_session.cpp b/src/p2p/peer_comm_session.cpp index 6bfcd152..1dfea3f9 100644 --- a/src/p2p/peer_comm_session.cpp +++ b/src/p2p/peer_comm_session.cpp @@ -92,8 +92,8 @@ namespace p2p { // Npl messages and consensus proposals are forwarded only to unl nodes if relavent flags (npl and consensus) are set to private. // If consensus and npl flags are public, these messages are forward to all the connected nodes. - const bool unl_only = (!conf::cfg.contract.is_npl_public && mi.type == p2pmsg::P2PMsgContent_NplMsg) || - (!conf::cfg.contract.is_consensus_public && mi.type == p2pmsg::P2PMsgContent_ProposalMsg); + const bool unl_only = (conf::cfg.contract.npl.mode != conf::MODE::PUBLIC && mi.type == p2pmsg::P2PMsgContent_NplMsg) || + (conf::cfg.contract.consensus.mode != conf::MODE::PUBLIC && mi.type == p2pmsg::P2PMsgContent_ProposalMsg); if (need_consensus_msg_forwarding) { // Forward messages received by weakly connected nodes to other peers. diff --git a/src/sc/sc.cpp b/src/sc/sc.cpp index 2dd0d555..7c98c2fc 100644 --- a/src/sc/sc.cpp +++ b/src/sc/sc.cpp @@ -723,7 +723,7 @@ namespace sc { flatbuffers::FlatBufferBuilder fbuf; msg::fbuf::p2pmsg::create_msg_from_npl_output(fbuf, output, ledger::ctx.get_lcl_id()); - p2p::broadcast_message(fbuf, true, false, !conf::cfg.contract.is_npl_public, 1); // Use high priority send. + p2p::broadcast_message(fbuf, true, false, conf::cfg.contract.npl.mode != conf::MODE::PUBLIC, 1); // Use high priority send. } }