diff --git a/src/conf.cpp b/src/conf.cpp index 41fe6a15..e14603d3 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -13,6 +13,9 @@ namespace conf // Global configuration struct exposed to the application. contract_config cfg; + // Stores the initial startup mode of the node. + OPERATING_MODE startup_mode; + const static char *MODE_OBSERVER = "observer"; const static char *MODE_PROPOSER = "proposer"; @@ -27,8 +30,14 @@ namespace conf // 2. Read and load the contract config into memory // 3. Validate the loaded config values - if (validate_contract_dir_paths() != 0 || load_config() != 0 || validate_config() != 0) + contract_config cfg = {}; + if (validate_contract_dir_paths() == -1 || + read_config(cfg) == -1 || + validate_config(cfg) == -1 || + populate_runtime_config(cfg) == -1) + { return -1; + } return 0; } @@ -39,14 +48,14 @@ namespace conf int rekey() { // Load the contract config and re-save with the newly generated keys. - - if (load_config() != 0) + contract_config cfg = {}; + if (read_config(cfg) != 0) return -1; crypto::generate_signing_keys(cfg.pubkey, cfg.seckey); - binpair_to_hex(); + binpair_to_hex(cfg); - if (save_config() != 0) + if (write_config(cfg) != 0) return -1; std::cout << "New signing keys generated at " << ctx.config_file << std::endl; @@ -77,10 +86,11 @@ namespace conf //We populate the in-memory struct with default settings and then save it to the file. + contract_config cfg = {}; crypto::generate_signing_keys(cfg.pubkey, cfg.seckey); - binpair_to_hex(); + binpair_to_hex(cfg); - cfg.startup_mode = OPERATING_MODE::PROPOSER; + cfg.operating_mode = OPERATING_MODE::PROPOSER; cfg.peerport = 22860; cfg.roundtime = 1000; cfg.pubport = 8080; @@ -96,7 +106,7 @@ namespace conf cfg.loglevel = "dbg"; #else cfg.loglevel_type = conf::LOG_SEVERITY::WARN; - cfg.loglevel = "wrn"; + cfg.loglevel = "inf"; #endif cfg.loggers.emplace("console"); @@ -104,7 +114,7 @@ namespace conf cfg.binary = ""; //Save the default settings into the config file. - if (save_config() != 0) + if (write_config(cfg) != 0) return -1; std::cout << "Contract directory created at " << ctx.contract_dir << std::endl; @@ -154,14 +164,27 @@ namespace conf ctx.log_dir = basedir + "/log"; } + int persist_unl_update(const std::set &updated_unl) + { + contract_config cfg = {}; + if (read_config(cfg) == -1) + return -1; + + cfg.unl = updated_unl; + + if (write_config(cfg) == -1) + return -1; + + return 0; + } + /** * Reads the config file on disk and populates the in-memory 'cfg' struct. - * * @return 0 for successful loading of config. -1 for failure. */ - int load_config() + int read_config(contract_config &cfg) { - // Read the file into json document object. + // Read the config file into json document object. std::ifstream ifs(ctx.config_file); jsoncons::json d; @@ -202,44 +225,23 @@ namespace conf // Load up the values into the struct. if (d["mode"] == MODE_OBSERVER) - cfg.startup_mode = OPERATING_MODE::OBSERVER; + cfg.operating_mode = OPERATING_MODE::OBSERVER; else if (d["mode"] == MODE_PROPOSER) - cfg.startup_mode = OPERATING_MODE::PROPOSER; + cfg.operating_mode = OPERATING_MODE::PROPOSER; else { std::cout << "Invalid mode. 'observer' or 'proposer' expected.\n"; return -1; } - cfg.current_mode = cfg.startup_mode; cfg.pubkeyhex = d["pubkeyhex"].as(); cfg.seckeyhex = d["seckeyhex"].as(); - // Convert the hex keys to binary and keep for later use. - if (hexpair_to_bin() != 0) - return -1; cfg.binary = d["binary"].as(); cfg.binargs = d["binargs"].as(); cfg.appbill = d["appbill"].as(); cfg.appbillargs = d["appbillargs"].as(); - // Populate runtime contract execution args. - if (!cfg.binargs.empty()) - util::split_string(cfg.runtime_binexec_args, cfg.binargs, " "); - cfg.runtime_binexec_args.insert(cfg.runtime_binexec_args.begin(), (cfg.binary[0] == '/' ? cfg.binary : util::realpath(ctx.contract_dir + "/bin/" + cfg.binary))); - - // Populate runtime app bill args. - if (!cfg.appbillargs.empty()) - util::split_string(cfg.runtime_appbill_args, cfg.appbillargs, " "); - - cfg.runtime_appbill_args.insert(cfg.runtime_appbill_args.begin(), (cfg.appbill[0] == '/' ? cfg.appbill : util::realpath(ctx.contract_dir + "/bin/" + cfg.appbill))); - - // Uncomment for docker-based execution. - // std::string volumearg; - // volumearg.append("type=bind,source=").append(ctx.state_dir).append(",target=/state"); - // const char *dockerargs[] = {"/usr/bin/docker", "run", "--rm", "-i", "--mount", volumearg.data(), cfg.binary.data()}; - // cfg.runtime_binexec_args.insert(cfg.runtime_binexec_args.begin(), std::begin(dockerargs), std::end(dockerargs)); - // 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; @@ -279,14 +281,9 @@ namespace conf std::cerr << "Error decoding unl list.\n"; return -1; } - cfg.unl.push_back(bin_pubkey); + cfg.unl.emplace(bin_pubkey); } - cfg.unl.push_back(cfg.pubkey); // Add self pubkey. - unl::add(cfg.unl); - // Remove self pubkey after sending to unl list. This is so that it doesn't get saved in the config in "rekey" mode. - cfg.unl.pop_back(); - cfg.peerport = d["peerport"].as(); cfg.pubport = d["pubport"].as(); cfg.roundtime = d["roundtime"].as(); @@ -326,18 +323,18 @@ namespace conf return 0; } + /** - * Saves the current values of the 'cfg' struct into the config file. - * + * Saves the provided 'cfg' struct into the config file. * @return 0 for successful save. -1 for failure. */ - int save_config() + int write_config(const contract_config &cfg) { // Popualte json document with 'cfg' values. - // ojson is used instead of json to preserve insertion order + // ojson is used instead of json to preserve insertion order. jsoncons::ojson d; d.insert_or_assign("version", util::HP_VERSION); - d.insert_or_assign("mode", cfg.startup_mode == OPERATING_MODE::OBSERVER ? MODE_OBSERVER : MODE_PROPOSER); + d.insert_or_assign("mode", cfg.operating_mode == OPERATING_MODE::OBSERVER ? MODE_OBSERVER : MODE_PROPOSER); d.insert_or_assign("pubkeyhex", cfg.pubkeyhex.data()); d.insert_or_assign("seckeyhex", cfg.seckeyhex.data()); @@ -362,7 +359,9 @@ namespace conf hex_pubkey, reinterpret_cast(nodepk.data()), nodepk.length()); - unl.push_back(hex_pubkey); + + if (hex_pubkey != cfg.pubkeyhex) + unl.push_back(hex_pubkey); // We do not save our own pubkey in config file. } d.insert_or_assign("unl", unl); @@ -404,11 +403,13 @@ namespace conf std::ofstream ofs(ctx.config_file); try { - ofs << jsoncons::pretty_print(d); + jsoncons::json_options options; + options.object_array_line_splits(jsoncons::line_split_kind::multi_line); + ofs << jsoncons::pretty_print(d, options); } catch (const std::exception &e) { - std::cout << "Writing to config file failed. " << ctx.config_file << std::endl; + std::cerr << "Writing to config file failed. " << ctx.config_file << std::endl; ofs.close(); return -1; } @@ -417,33 +418,13 @@ namespace conf return 0; } - /** - * Decode current binary keys in 'cfg' and populate the it with hex keys. - * - * @return Always returns 0. - */ - int binpair_to_hex() + int populate_runtime_config(contract_config &parsed_cfg) { - util::bin2hex( - cfg.pubkeyhex, - reinterpret_cast(cfg.pubkey.data()), - cfg.pubkey.length()); + cfg = parsed_cfg; + startup_mode = cfg.operating_mode; - util::bin2hex( - cfg.seckeyhex, - reinterpret_cast(cfg.seckey.data()), - cfg.seckey.length()); + // Convert the hex keys to binary. - return 0; - } - - /** - * Decode current hex keys in 'cfg' and populate the it with binary keys. - * - * @return 0 for successful conversion. -1 for failure. - */ - int hexpair_to_bin() - { cfg.pubkey.resize(crypto::PFXD_PUBKEY_BYTES); if (util::hex2bin( reinterpret_cast(cfg.pubkey.data()), @@ -464,6 +445,47 @@ namespace conf return -1; } + // Populate unl. + cfg.unl.emplace(cfg.pubkey); // Add self pubkey to unl. + unl::init(cfg.unl); + + // Populate runtime contract execution args. + if (!cfg.binargs.empty()) + util::split_string(cfg.runtime_binexec_args, cfg.binargs, " "); + cfg.runtime_binexec_args.insert(cfg.runtime_binexec_args.begin(), (cfg.binary[0] == '/' ? cfg.binary : util::realpath(ctx.contract_dir + "/bin/" + cfg.binary))); + + // Populate runtime app bill args. + if (!cfg.appbillargs.empty()) + util::split_string(cfg.runtime_appbill_args, cfg.appbillargs, " "); + + cfg.runtime_appbill_args.insert(cfg.runtime_appbill_args.begin(), (cfg.appbill[0] == '/' ? cfg.appbill : util::realpath(ctx.contract_dir + "/bin/" + cfg.appbill))); + + // Uncomment for docker-based execution. + // std::string volumearg; + // volumearg.append("type=bind,source=").append(ctx.state_dir).append(",target=/state"); + // const char *dockerargs[] = {"/usr/bin/docker", "run", "--rm", "-i", "--mount", volumearg.data(), cfg.binary.data()}; + // cfg.runtime_binexec_args.insert(cfg.runtime_binexec_args.begin(), std::begin(dockerargs), std::end(dockerargs)); + + return 0; + } + + /** + * Decode current binary keys in 'cfg' and populate the it with hex keys. + * + * @return Always returns 0. + */ + int binpair_to_hex(contract_config &cfg) + { + util::bin2hex( + cfg.pubkeyhex, + reinterpret_cast(cfg.pubkey.data()), + cfg.pubkey.length()); + + util::bin2hex( + cfg.seckeyhex, + reinterpret_cast(cfg.seckey.data()), + cfg.seckey.length()); + return 0; } @@ -472,7 +494,7 @@ namespace conf * * @return 0 for successful validation. -1 for failure. */ - int validate_config() + int validate_config(const contract_config &cfg) { // Check for non-empty signing keys. // We also check for key pair validity as well in the below code. @@ -569,10 +591,10 @@ namespace conf void change_operating_mode(const OPERATING_MODE mode) { // Do not allow to change the mode if the node was started as an observer. - if (cfg.startup_mode == OPERATING_MODE::OBSERVER || cfg.current_mode == mode) + if (startup_mode == OPERATING_MODE::OBSERVER || cfg.operating_mode == mode) return; - cfg.current_mode = mode; + cfg.operating_mode = mode; if (mode == OPERATING_MODE::OBSERVER) LOG_INFO << "Switched to OBSERVER mode."; diff --git a/src/conf.hpp b/src/conf.hpp index 8af32470..9c876ec5 100644 --- a/src/conf.hpp +++ b/src/conf.hpp @@ -76,28 +76,25 @@ namespace conf struct contract_config { // Config elements which are initialized in memory (these are not directly loaded from the config file) - - std::string pubkey; // Contract public key bytes - std::string seckey; // Contract secret key bytes - std::vector runtime_binexec_args; // Contract binary execution args used during runtime. - std::vector runtime_appbill_args; // Appbill execution args used during runtime. - OPERATING_MODE current_mode = OPERATING_MODE::OBSERVER; // Current operating mode of the contract (Observer/Proposer) + std::string pubkey; // Contract public key bytes + std::string seckey; // Contract secret key bytes + std::vector runtime_binexec_args; // Contract binary execution args used during runtime. + std::vector runtime_appbill_args; // Appbill execution args used during runtime. // Config elements which are loaded from the config file. - - OPERATING_MODE startup_mode = OPERATING_MODE::OBSERVER; // Configured startup operating mode of the contract (Observer/Proposer). - std::string pubkeyhex; // Contract hex public key - std::string seckeyhex; // Contract hex secret key - std::string binary; // Full path to the contract binary - std::string binargs; // CLI arguments to pass to the contract binary - std::string appbill; // binary to execute for appbill - std::string appbillargs; // any arguments to supply to appbill binary by default - std::vector peers; // Vector of peers with ip_port, timestamp, capacity - std::vector unl; // Unique node list (list of binary public keys) - uint16_t peerport = 0; // Listening port for peer connections - uint16_t roundtime = 0; // Consensus round time in ms - uint16_t pubport = 0; // Listening port for public user connections - uint16_t peerdiscoverytime = 0; // Time interval in ms to find for peers dynamicpeerdiscovery should be on for this + OPERATING_MODE operating_mode = OPERATING_MODE::OBSERVER; // Configured startup operating mode of the contract (Observer/Proposer). + std::string pubkeyhex; // Contract hex public key + std::string seckeyhex; // Contract hex secret key + std::string binary; // Full path to the contract binary + std::string binargs; // CLI arguments to pass to the contract binary + std::string appbill; // binary to execute for appbill + std::string appbillargs; // any arguments to supply to appbill binary by default + std::vector peers; // Vector of peers with ip_port, timestamp, capacity + std::set unl; // Unique node list (list of binary public keys) + uint16_t peerport = 0; // Listening port for peer connections + uint16_t roundtime = 0; // Consensus round time in ms + uint16_t pubport = 0; // Listening port for public user connections + uint16_t peerdiscoverytime = 0; // Time interval in ms to find for peers dynamicpeerdiscovery should be on for this uint16_t peeridletimeout = 0; // Idle connection timeout for peer connections in seconds. uint16_t pubidletimeout = 0; // Idle connection timeout for user connections in seconds. @@ -139,19 +136,21 @@ namespace conf void set_contract_dir_paths(std::string exepath, std::string basedir); + int persist_unl_update(const std::set &updated_unl); + //------Internal-use functions for this namespace. - int load_config(); + int read_config(contract_config &cfg); - int save_config(); + int write_config(const contract_config &cfg); - int validate_config(); + int populate_runtime_config(contract_config &parsed_cfg); + + int validate_config(const contract_config &cfg); int validate_contract_dir_paths(); - int binpair_to_hex(); - - int hexpair_to_bin(); + int binpair_to_hex(contract_config &cfg); void change_operating_mode(const OPERATING_MODE mode); diff --git a/src/consensus.cpp b/src/consensus.cpp index 9848940e..ba263cd7 100644 --- a/src/consensus.cpp +++ b/src/consensus.cpp @@ -623,7 +623,7 @@ namespace consensus p2pmsg::create_msg_from_proposal(fbuf, p); // In observer mode, we only send out the proposal to ourselves. - if (conf::cfg.current_mode == conf::OPERATING_MODE::OBSERVER) + if (conf::cfg.operating_mode == conf::OPERATING_MODE::OBSERVER) p2p::send_message_to_self(fbuf); else p2p::broadcast_message(fbuf, true); diff --git a/src/main.cpp b/src/main.cpp index a4b58ebd..fc3c1e1a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -194,7 +194,7 @@ int main(int argc, char **argv) hplog::init(); LOG_INFO << "Operating mode: " - << (conf::cfg.startup_mode == conf::OPERATING_MODE::OBSERVER ? "Observer" : "Proposer"); + << (conf::cfg.operating_mode == conf::OPERATING_MODE::OBSERVER ? "Observer" : "Proposer"); LOG_INFO << "Public key: " << conf::cfg.pubkeyhex.substr(2); // Public key without 'ed' prefix. if (ledger::init() || diff --git a/src/unl.cpp b/src/unl.cpp index d8b7629b..f8a72c77 100644 --- a/src/unl.cpp +++ b/src/unl.cpp @@ -1,5 +1,6 @@ #include "util/util.hpp" #include "hplog.hpp" +#include "conf.hpp" #include "unl.hpp" /** @@ -11,6 +12,19 @@ namespace unl std::string json_list; // Stringified json array of UNL. (To be fed into the contract args) std::shared_mutex unl_mutex; + /** + * Called by conf during startup to populate configured unl list. + */ + void init(const std::set &init_list) + { + if (init_list.empty()) + return; + + std::unique_lock lock(unl_mutex); + list = init_list; + update_json_list(); + } + size_t count() { std::shared_lock lock(unl_mutex); @@ -35,19 +49,9 @@ namespace unl return list.find(bin_pubkey) != list.end(); } - void add(const std::vector &additions) - { - if (additions.empty()) - return; - - std::unique_lock lock(unl_mutex); - - for (const std::string &pubkey : additions) - list.emplace(pubkey); - - update_json_list(); - } - + /** + * Called by contract to update unl at runtime. + */ void update(const std::vector &additions, const std::vector &removals) { if (additions.empty() && removals.empty()) @@ -62,9 +66,9 @@ namespace unl list.erase(pubkey); update_json_list(); + conf::persist_unl_update(list); LOG_INFO << "UNL updated. Count:" << list.size(); - ; } void update_json_list() diff --git a/src/unl.hpp b/src/unl.hpp index d76e8084..f06a4b90 100644 --- a/src/unl.hpp +++ b/src/unl.hpp @@ -12,7 +12,7 @@ namespace unl std::set get(); std::string get_json(); bool exists(const std::string &bin_pubkey); - void add(const std::vector &additions); + void init(const std::set &init_list); void update(const std::vector &additions, const std::vector &removals); void update_json_list();