From 63e3050046af1c02f45d99a9e14979fcaedfad54 Mon Sep 17 00:00:00 2001 From: Savinda Senevirathne Date: Thu, 24 Dec 2020 11:40:02 +0530 Subject: [PATCH] Validating and displaying missing config fields in config read. (#205) --- src/conf.cpp | 333 +++++++++++++++++++++++++++++++-------------------- src/conf.hpp | 2 + 2 files changed, 208 insertions(+), 127 deletions(-) diff --git a/src/conf.cpp b/src/conf.cpp index 3aa2c859..75e9dd43 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -212,109 +212,142 @@ namespace conf } ifs.close(); - // Check whether the hp version is specified. - cfg.hp_version = d["hp_version"].as(); - if (cfg.hp_version.empty()) + try { - std::cerr << "Contract config HP version missing.\n"; - return -1; - } + // Check whether the hp version is specified. + cfg.hp_version = d["hp_version"].as(); + if (cfg.hp_version.empty()) + { + std::cerr << "Contract config HP version missing.\n"; + return -1; + } - // Check whether this config complies with the min version requirement. - int verresult = util::version_compare(cfg.hp_version, std::string(util::MIN_CONFIG_VERSION)); - if (verresult == -1) - { - std::cerr << "Config version too old. Minimum " - << util::MIN_CONFIG_VERSION << " required. " - << cfg.hp_version << " found.\n"; - return -1; + // Check whether this config complies with the min version requirement. + int verresult = util::version_compare(cfg.hp_version, std::string(util::MIN_CONFIG_VERSION)); + if (verresult == -1) + { + std::cerr << "Config version too old. Minimum " + << util::MIN_CONFIG_VERSION << " required. " + << cfg.hp_version << " found.\n"; + return -1; + } + else if (verresult == -2) + { + std::cerr << "Malformed version string.\n"; + return -1; + } } - else if (verresult == -2) + catch (const std::exception &e) { - std::cerr << "Malformed version string.\n"; + std::cerr << "Required config field hp_version missing at " << ctx.config_file << std::endl; return -1; } // node { - const jsoncons::json &node = d["node"]; - cfg.node.public_key_hex = node["public_key"].as(); - cfg.node.private_key_hex = node["private_key"].as(); - - // Convert the hex keys to binary. - cfg.node.public_key = util::to_bin(cfg.node.public_key_hex); - if (cfg.node.public_key.empty()) + try { - std::cerr << "Error decoding hex public key.\n"; + const jsoncons::json &node = d["node"]; + cfg.node.public_key_hex = node["public_key"].as(); + cfg.node.private_key_hex = node["private_key"].as(); + + // Convert the hex keys to binary. + cfg.node.public_key = util::to_bin(cfg.node.public_key_hex); + if (cfg.node.public_key.empty()) + { + std::cerr << "Error decoding hex public key.\n"; + return -1; + } + + cfg.node.private_key = util::to_bin(cfg.node.private_key_hex); + if (cfg.node.private_key.empty()) + { + std::cerr << "Error decoding hex private key.\n"; + return -1; + } + + if (node["role"] == ROLE_OBSERVER) + cfg.node.role = ROLE::OBSERVER; + else if (node["role"] == ROLE_VALIDATOR) + cfg.node.role = ROLE::VALIDATOR; + else + { + std::cerr << "Invalid mode. 'observer' or 'validator' expected.\n"; + return -1; + } + startup_mode = cfg.node.role; + } + catch (const std::exception &e) + { + std::cerr << "Required node config field " << extract_missing_field(e.what()) << " missing at " << ctx.config_file << std::endl; return -1; } - - cfg.node.private_key = util::to_bin(cfg.node.private_key_hex); - if (cfg.node.private_key.empty()) - { - std::cerr << "Error decoding hex private key.\n"; - return -1; - } - - if (node["role"] == ROLE_OBSERVER) - cfg.node.role = ROLE::OBSERVER; - else if (node["role"] == ROLE_VALIDATOR) - cfg.node.role = ROLE::VALIDATOR; - else - { - std::cerr << "Invalid mode. 'observer' or 'validator' expected.\n"; - return -1; - } - startup_mode = cfg.node.role; } // contract { - const jsoncons::json &contract = d["contract"]; - cfg.contract.id = contract["id"].as(); - cfg.contract.version = contract["version"].as(); - if (cfg.contract.id.empty()) + try { - std::cerr << "Contract id not specified.\n"; - return -1; - } - else if (cfg.contract.version.empty()) - { - std::cerr << "Contract version not specified.\n"; - return -1; - } - cfg.contract.unl.clear(); - for (auto &nodepk : contract["unl"].array_range()) - { - // Convert the public key hex of each node to binary and store it. - const std::string bin_pubkey = util::to_bin(nodepk.as()); - if (bin_pubkey.empty()) + const jsoncons::json &contract = d["contract"]; + cfg.contract.id = contract["id"].as(); + cfg.contract.version = contract["version"].as(); + if (cfg.contract.id.empty()) { - std::cerr << "Error decoding unl list.\n"; + std::cerr << "Contract id not specified.\n"; return -1; } - cfg.contract.unl.emplace(bin_pubkey); - } - cfg.contract.bin_path = contract["bin_path"].as(); - cfg.contract.bin_args = contract["bin_args"].as(); - cfg.contract.roundtime = contract["roundtime"].as(); + else if (cfg.contract.version.empty()) + { + std::cerr << "Contract version not specified.\n"; + return -1; + } + cfg.contract.unl.clear(); + for (auto &nodepk : contract["unl"].array_range()) + { + // Convert the public key hex of each node to binary and store it. + const std::string bin_pubkey = util::to_bin(nodepk.as()); + if (bin_pubkey.empty()) + { + std::cerr << "Error decoding unl list.\n"; + return -1; + } + cfg.contract.unl.emplace(bin_pubkey); + } + cfg.contract.bin_path = contract["bin_path"].as(); + cfg.contract.bin_args = contract["bin_args"].as(); + cfg.contract.roundtime = contract["roundtime"].as(); - if (contract["consensus"] != PUBLIC && contract["consensus"] != PRIVATE) + if (contract["consensus"] != PUBLIC && contract["consensus"] != PRIVATE) + { + std::cerr << "Invalid consensus flag configured. Valid values: public|private\n"; + return -1; + } + cfg.contract.is_consensus_public = contract["consensus"] == PUBLIC; + + if (contract["npl"] != PUBLIC && contract["npl"] != PRIVATE) + { + std::cerr << "Invalid npl flag configured. Valid values: public|private\n"; + return -1; + } + cfg.contract.is_npl_public = contract["npl"] == PUBLIC; + if (!contract["appbill"].contains("mode")) + { + std::cerr << "Required contract appbill config field mode missing at " << ctx.config_file << std::endl; + return -1; + } + cfg.contract.appbill.mode = contract["appbill"]["mode"].as(); + if (!contract["appbill"].contains("bin_args")) + { + std::cerr << "Required contract appbill config field bin_args missing at " << ctx.config_file << std::endl; + return -1; + } + cfg.contract.appbill.bin_args = contract["appbill"]["bin_args"].as(); + } + catch (const std::exception &e) { - std::cerr << "Invalid consensus flag configured. Valid values: public|private\n"; + std::cerr << "Required contract config field " << extract_missing_field(e.what()) << " missing at " << ctx.config_file << std::endl; return -1; } - cfg.contract.is_consensus_public = contract["consensus"] == PUBLIC; - - if (contract["npl"] != PUBLIC && contract["npl"] != PRIVATE) - { - std::cerr << "Invalid npl flag configured. Valid values: public|private\n"; - return -1; - } - cfg.contract.is_npl_public = contract["npl"] == PUBLIC; - cfg.contract.appbill.mode = contract["appbill"]["mode"].as(); - cfg.contract.appbill.bin_args = contract["appbill"]["bin_args"].as(); - // Populate runtime contract execution args. if (!cfg.contract.bin_args.empty()) util::split_string(cfg.contract.runtime_binexec_args, cfg.contract.bin_args, " "); @@ -335,70 +368,104 @@ namespace conf // mesh { - const jsoncons::json &mesh = d["mesh"]; - cfg.mesh.port = mesh["port"].as(); - // Storing peers in unordered map keyed by the concatenated address:port and also saving address and port - // seperately to retrieve easily when handling peer connections. - std::vector splitted_peers; - cfg.mesh.known_peers.clear(); - for (auto &v : mesh["known_peers"].array_range()) + try { - const char *ipport_concat = v.as(); - // Split the address:port text into two - util::split_string(splitted_peers, ipport_concat, ":"); - - // Push the peer address and the port to peers set - if (splitted_peers.size() != 2) + const jsoncons::json &mesh = d["mesh"]; + cfg.mesh.port = mesh["port"].as(); + // Storing peers in unordered map keyed by the concatenated address:port and also saving address and port + // seperately to retrieve easily when handling peer connections. + std::vector splitted_peers; + cfg.mesh.known_peers.clear(); + for (auto &v : mesh["known_peers"].array_range()) { - std::cerr << "Invalid peer: " << ipport_concat << "\n"; + const char *ipport_concat = v.as(); + // Split the address:port text into two + util::split_string(splitted_peers, ipport_concat, ":"); + + // Push the peer address and the port to peers set + if (splitted_peers.size() != 2) + { + std::cerr << "Invalid peer: " << ipport_concat << "\n"; + return -1; + } + + peer_properties peer; + peer.ip_port.host_address = splitted_peers.front(); + peer.ip_port.port = std::stoi(splitted_peers.back()); + + cfg.mesh.known_peers.push_back(peer); + splitted_peers.clear(); + } + cfg.mesh.msg_forwarding = mesh["msg_forwarding"].as(); + cfg.mesh.max_connections = mesh["max_connections"].as(); + cfg.mesh.max_known_connections = mesh["max_known_connections"].as(); + // If max_connections is greater than max_known_connections then show error and stop execution. + if (cfg.mesh.max_known_connections > cfg.mesh.max_connections) + { + std::cerr << "Invalid configuration values: mesh max_known_connections count should not exceed mesh max_connections." << '\n'; return -1; } - - peer_properties peer; - peer.ip_port.host_address = splitted_peers.front(); - peer.ip_port.port = std::stoi(splitted_peers.back()); - - cfg.mesh.known_peers.push_back(peer); - splitted_peers.clear(); + cfg.mesh.max_bytes_per_msg = mesh["max_bytes_per_msg"].as(); + cfg.mesh.max_bytes_per_min = mesh["max_bytes_per_min"].as(); + cfg.mesh.max_bad_msgs_per_min = mesh["max_bad_msgs_per_min"].as(); + cfg.mesh.max_bad_msgsigs_per_min = mesh["max_bad_msgsigs_per_min"].as(); + cfg.mesh.max_dup_msgs_per_min = mesh["max_dup_msgs_per_min"].as(); + cfg.mesh.idle_timeout = mesh["idle_timeout"].as(); + if (!mesh["peer_discovery"].contains("interval")) + { + std::cerr << "Required mesh peer discovery config field interval missing at " << ctx.config_file << std::endl; + return -1; + } + cfg.mesh.peer_discovery.interval = mesh["peer_discovery"]["interval"].as(); + if (!mesh["peer_discovery"].contains("enabled")) + { + std::cerr << "Required mesh peer discovery config field enabled missing at " << ctx.config_file << std::endl; + return -1; + } + cfg.mesh.peer_discovery.enabled = mesh["peer_discovery"]["enabled"].as(); } - cfg.mesh.msg_forwarding = mesh["msg_forwarding"].as(); - cfg.mesh.max_connections = mesh["max_connections"].as(); - cfg.mesh.max_known_connections = mesh["max_known_connections"].as(); - // If max_connections is greater than max_known_connections then show error and stop execution. - if (cfg.mesh.max_known_connections > cfg.mesh.max_connections) + catch (const std::exception &e) { - std::cerr << "Invalid configuration values: mesh max_known_connections count should not exceed mesh max_connections." << '\n'; + std::cerr << "Required mesh config field " << extract_missing_field(e.what()) << " missing at " << ctx.config_file << std::endl; return -1; } - cfg.mesh.max_bytes_per_msg = mesh["max_bytes_per_msg"].as(); - cfg.mesh.max_bytes_per_min = mesh["max_bytes_per_min"].as(); - cfg.mesh.max_bad_msgs_per_min = mesh["max_bad_msgs_per_min"].as(); - cfg.mesh.max_bad_msgsigs_per_min = mesh["max_bad_msgsigs_per_min"].as(); - cfg.mesh.max_dup_msgs_per_min = mesh["max_dup_msgs_per_min"].as(); - cfg.mesh.idle_timeout = mesh["idle_timeout"].as(); - cfg.mesh.peer_discovery.interval = mesh["peer_discovery"]["interval"].as(); - cfg.mesh.peer_discovery.enabled = mesh["peer_discovery"]["enabled"].as(); } // user { - const jsoncons::json &user = d["user"]; - cfg.user.port = user["port"].as(); - cfg.user.max_connections = user["max_connections"].as(); - cfg.user.max_bytes_per_msg = user["max_bytes_per_msg"].as(); - cfg.user.max_bytes_per_min = user["max_bytes_per_min"].as(); - cfg.user.max_bad_msgs_per_min = user["max_bad_msgs_per_min"].as(); - cfg.user.idle_timeout = user["idle_timeout"].as(); + try + { + const jsoncons::json &user = d["user"]; + cfg.user.port = user["port"].as(); + cfg.user.max_connections = user["max_connections"].as(); + cfg.user.max_bytes_per_msg = user["max_bytes_per_msg"].as(); + cfg.user.max_bytes_per_min = user["max_bytes_per_min"].as(); + cfg.user.max_bad_msgs_per_min = user["max_bad_msgs_per_min"].as(); + cfg.user.idle_timeout = user["idle_timeout"].as(); + } + catch (const std::exception &e) + { + std::cerr << "Required user config field " << extract_missing_field(e.what()) << " missing at " << ctx.config_file << std::endl; + return -1; + } } // log { - const jsoncons::json &log = d["log"]; - cfg.log.loglevel = log["loglevel"].as(); - cfg.log.loglevel_type = get_loglevel_type(cfg.log.loglevel); - cfg.log.loggers.clear(); - for (auto &v : log["loggers"].array_range()) - cfg.log.loggers.emplace(v.as()); + try + { + const jsoncons::json &log = d["log"]; + cfg.log.loglevel = log["loglevel"].as(); + cfg.log.loglevel_type = get_loglevel_type(cfg.log.loglevel); + cfg.log.loggers.clear(); + for (auto &v : log["loggers"].array_range()) + cfg.log.loggers.emplace(v.as()); + } + catch (const std::exception &e) + { + std::cerr << "Required log config field " << extract_missing_field(e.what()) << " missing at " << ctx.config_file << std::endl; + return -1; + } } return 0; @@ -653,4 +720,16 @@ namespace conf else return LOG_SEVERITY::ERROR; } + + /** + * Extracts missing config field from the jsoncons exception message. + * @param err_message Jsoncons error message. + * @return Missing config field. + */ + std::string_view extract_missing_field(std::string err_message) + { + err_message.erase(0, err_message.find("'") + 1); + err_message = err_message.substr(0, err_message.find("'")); + return err_message; + } } // namespace conf diff --git a/src/conf.hpp b/src/conf.hpp index 4376f81f..1a495c15 100644 --- a/src/conf.hpp +++ b/src/conf.hpp @@ -192,6 +192,8 @@ namespace conf void change_role(const ROLE role); LOG_SEVERITY get_loglevel_type(std::string_view severity); + + std::string_view extract_missing_field(std::string err_message); } // namespace conf #endif