From 610b67cec04d6f452827da2070d794dd8b1e9997 Mon Sep 17 00:00:00 2001 From: Ravin Perera <33562092+ravinsp@users.noreply.github.com> Date: Fri, 22 Jan 2021 14:33:07 +0530 Subject: [PATCH] Contract execute config flag. (#226) * Added contract execute flag. Made bin_path optional. * Updated docker cluster default config. * Renamed contract config to hp config. --- README.md | 2 +- src/conf.cpp | 62 +++++++++++++++------------- src/conf.hpp | 29 ++++++------- src/consensus.cpp | 2 +- src/crypto.hpp | 2 +- src/p2p/peer_session_handler.cpp | 2 +- src/usr/user_session_handler.cpp | 2 +- src/util/util.hpp | 4 +- test/local-cluster/cluster-create.sh | 12 +++++- test/vm-cluster/cluster.sh | 2 +- 10 files changed, 67 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 6fd6ede1..9edc872b 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Example: When you make a change to `p2pmsg_content.fbc` defnition file, you need ## Code structure Code is divided into subsystems via namespaces. -**conf::** Handles contract configuration. Loads and holds the central configuration object. Used by most of the subsystems. +**conf::** Handles configuration. Loads and holds the central configuration object. Used by most of the subsystems. **crypto::** Handles cryptographic activities. Wraps libsodium and offers convenience functions. diff --git a/src/conf.cpp b/src/conf.cpp index b1764c7b..80090526 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -11,7 +11,7 @@ namespace conf contract_ctx ctx; // Global configuration struct exposed to the application. - contract_config cfg; + hp_config cfg; // Stores the initial startup mode of the node. ROLE startup_mode; @@ -26,17 +26,17 @@ namespace conf bool init_success = false; /** - * Loads and initializes the contract config for execution. Must be called once during application startup. + * Loads and initializes the config for execution. Must be called once during application startup. * @return 0 for success. -1 for failure. */ int init() { // The validations/loading needs to be in this order. // 1. Validate contract directories - // 2. Read and load the contract config into memory + // 2. Read and load the config into memory // 4. Validate the loaded config values // 5. Initialize logging subsystem. - // 6. Update and validate contract config if patch file exists. + // 6. Update and validate config if patch file exists. if (validate_contract_dir_paths() == -1 || set_config_lock() == -1 || @@ -64,7 +64,7 @@ namespace conf } /** - * Generates and saves new signing keys in the contract config. + * Generates and saves new signing keys in the config. */ int rekey() { @@ -72,8 +72,8 @@ namespace conf if (set_config_lock() == -1) return -1; - // Load the contract config and re-save with the newly generated keys. - contract_config cfg = {}; + // Load the config and re-save with the newly generated keys. + hp_config cfg = {}; if (read_config(cfg) != 0) return -1; @@ -93,7 +93,7 @@ namespace conf } /** - * Creates a new contract directory with the default contract config. + * Creates a new contract directory with the default config. * By the time this gets called, the 'ctx' struct must be populated. * This function makes use of the paths populated in the ctx. */ @@ -106,14 +106,14 @@ namespace conf } // Recursivly create contract directories. Return an error if unable to create - if(util::create_dir_tree_recursive(ctx.config_dir) == -1 || + if (util::create_dir_tree_recursive(ctx.config_dir) == -1 || util::create_dir_tree_recursive(ctx.hist_dir) == -1 || util::create_dir_tree_recursive(ctx.full_hist_dir) == -1 || util::create_dir_tree_recursive(ctx.log_dir) == -1 || util::create_dir_tree_recursive(ctx.hpfs_dir + "/seed" + hpfs::STATE_DIR_PATH) == -1 || util::create_dir_tree_recursive(ctx.hpfs_mount_dir) == -1) { - std::cerr << "ERROR: unable to create directories.\n"; + std::cerr << "ERROR: unable to create directories.\n"; return -1; } @@ -121,7 +121,7 @@ namespace conf //We populate the in-memory struct with default settings and then save it to the file. - contract_config cfg = {}; + hp_config cfg = {}; crypto::generate_signing_keys(cfg.node.public_key, cfg.node.private_key); cfg.node.public_key_hex = util::to_hex(cfg.node.public_key); @@ -133,6 +133,7 @@ namespace conf cfg.node.full_history = false; cfg.contract.id = crypto::generate_uuid(); + cfg.contract.execute = true; cfg.contract.version = "1.0"; //Add self pubkey to the unl. cfg.contract.unl.emplace(cfg.node.public_key); @@ -211,7 +212,7 @@ namespace conf * Reads the config file on disk and populates the in-memory 'cfg' struct. * @return 0 for successful loading of config. -1 for failure. */ - int read_config(contract_config &cfg) + int read_config(hp_config &cfg) { // Read the config file into json document object. std::string buf; @@ -239,7 +240,7 @@ namespace conf cfg.hp_version = d["hp_version"].as(); if (cfg.hp_version.empty()) { - std::cerr << "Contract config HP version missing.\n"; + std::cerr << "Config HP version missing.\n"; return -1; } @@ -307,7 +308,8 @@ namespace conf // contract { - parse_contract_section_json(cfg.contract, d["contract"], true); + if (parse_contract_section_json(cfg.contract, d["contract"], false) == -1) + return -1; } // mesh @@ -420,7 +422,7 @@ namespace conf * Saves the provided 'cfg' struct into the config file. * @return 0 for successful save. -1 for failure. */ - int write_config(const contract_config &cfg) + int write_config(const hp_config &cfg) { // Popualte json document with 'cfg' values. // ojson is used instead of json to preserve insertion order. @@ -437,7 +439,7 @@ namespace conf // Contract config section. jsoncons::ojson contract; - populate_contract_section_json(contract, cfg.contract, true); + populate_contract_section_json(contract, cfg.contract, false); d.insert_or_assign("contract", contract); // Mesh configs. @@ -499,7 +501,7 @@ namespace conf * * @return 0 for successful validation. -1 for failure. */ - int validate_config(const contract_config &cfg) + int validate_config(const hp_config &cfg) { // Check for non-empty signing keys. // We also check for key pair validity as well in the below code. @@ -513,7 +515,6 @@ namespace conf bool fields_missing = false; - fields_missing |= cfg.contract.bin_path.empty() && std::cerr << "Missing cfg field: bin_path\n"; fields_missing |= cfg.contract.roundtime == 0 && std::cerr << "Missing cfg field: roundtime\n"; fields_missing |= cfg.contract.unl.empty() && std::cerr << "Missing cfg field: unl. Unl list cannot be empty.\n"; fields_missing |= cfg.contract.id.empty() && std::cerr << "Missing cfg field: contract id.\n"; @@ -651,14 +652,14 @@ namespace conf int populate_patch_config() { jsoncons::ojson jdoc; - populate_contract_section_json(jdoc, cfg.contract, false); + populate_contract_section_json(jdoc, cfg.contract, true); const std::string patch_file_path = hpfs::physical_path(hpfs::RW_SESSION_NAME, hpfs::PATCH_FILE_PATH); return write_json_file(patch_file_path, jdoc); } /** - * Validate and update contract config section if a patch file detected. Whenever patch file change is detected, + * Validate and update config section if a patch file detected. Whenever patch file change is detected, * 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. @@ -699,7 +700,7 @@ namespace conf buf.clear(); // Persist new changes to HP config file. - if (parse_contract_section_json(cfg.contract, jdoc, false) == -1 || + if (parse_contract_section_json(cfg.contract, jdoc, true) == -1 || write_config(cfg) == -1) { LOG_ERROR << "Error applying patch config."; @@ -751,12 +752,15 @@ namespace conf * Populates contract section field values into the provided json doc. * @param jdoc The json doc to populate contract section field values. * @param contract The contract fields struct containing current field values. - * @param include_id Whether to populate the contract id field or not. + * @param is_patch_config Whether this is called for patch config or not. */ - void populate_contract_section_json(jsoncons::ojson &jdoc, const contract_params &contract, const bool include_id) + void populate_contract_section_json(jsoncons::ojson &jdoc, const contract_config &contract, const bool is_patch_config) { - if (include_id) + if (!is_patch_config) + { jdoc.insert_or_assign("id", contract.id); + jdoc.insert_or_assign("execute", contract.execute); + } jdoc.insert_or_assign("version", contract.version); jsoncons::ojson unl(jsoncons::json_array_arg); @@ -782,14 +786,14 @@ namespace conf * Validates the provided json doc and populate the provided contract struct with values from json doc. * @param contract The contract fields struct to populate. * @param jdoc The json doc containing the contract section field values. - * @param parse_id Whether to parse the contract id field or not. + * @param is_patch_config Whether this is called for patch config or not. * @return 0 on success. -1 on error. */ - int parse_contract_section_json(contract_params &contract, const jsoncons::ojson &jdoc, const bool parse_id) + int parse_contract_section_json(contract_config &contract, const jsoncons::ojson &jdoc, const bool is_patch_config) { try { - if (parse_id) + if (!is_patch_config) { contract.id = jdoc["id"].as(); if (contract.id.empty()) @@ -797,6 +801,8 @@ namespace conf std::cerr << "Contract id not specified.\n"; return -1; } + + contract.execute = jdoc["execute"].as(); } contract.version = jdoc["version"].as(); @@ -863,7 +869,7 @@ namespace conf } catch (const std::exception &e) { - std::cerr << "Required contract config field " << extract_missing_field(e.what()) << " missing at " << ctx.config_file << std::endl; + std::cerr << "Required contract config field '" << extract_missing_field(e.what()) << "' missing at " << ctx.config_file << std::endl; return -1; } diff --git a/src/conf.hpp b/src/conf.hpp index 29608ff1..c784087e 100644 --- a/src/conf.hpp +++ b/src/conf.hpp @@ -4,8 +4,8 @@ #include "pchheader.hpp" /** - * Manages the central contract config and context structs. - * Contains functions to contract config operations such as create/rekey/load. + * Manages the central config and context structs. + * Contains functions to config operations such as create/rekey/load. */ namespace conf { @@ -80,9 +80,10 @@ namespace conf // Config element which are initialized in memory (This is not directly loaded from the config file) std::vector runtime_args; // Appbill execution args used during runtime. }; - struct contract_params + struct contract_config { std::string id; // Contract guid. + bool execute; // Whether or not to execute the contract on the node. std::string version; // Contract version string. std::set unl; // Unique node list (list of binary public keys) std::string bin_path; // Full path to the contract binary @@ -144,8 +145,8 @@ namespace conf std::string hpfs_mount_dir; // hpfs fuse file system mount path. std::string hpfs_rw_dir; // hpfs read/write fs session path. std::string log_dir; // Contract log dir full path. - std::string config_dir; // Contract config dir full path. - std::string config_file; // Full path to the contract config file. + std::string config_dir; // Config dir full path. + std::string config_file; // Full path to the config file. std::string tls_key_file; // Full path to the tls private key file. std::string tls_cert_file; // Full path to the tls certificate. @@ -153,13 +154,13 @@ namespace conf struct flock config_lock; // Config file lock. }; - // Holds all the contract config values. - struct contract_config + // Holds all the config values. + struct hp_config { // Config elements which are loaded from the config file. std::string hp_version; // Version of Hot Pocket that generated the config. node_config node; - contract_params contract; + contract_config contract; mesh_config mesh; user_config user; log_config log; @@ -171,7 +172,7 @@ namespace conf // Global configuration struct exposed to the application. // Other modeuls will access config values via this. - extern contract_config cfg; + extern hp_config cfg; int init(); @@ -185,11 +186,11 @@ namespace conf //------Internal-use functions for this namespace. - int read_config(contract_config &cfg); + int read_config(hp_config &cfg); - int write_config(const contract_config &cfg); + int write_config(const hp_config &cfg); - int validate_config(const contract_config &cfg); + int validate_config(const hp_config &cfg); int validate_contract_dir_paths(); @@ -207,9 +208,9 @@ namespace conf int release_config_lock(); - void populate_contract_section_json(jsoncons::ojson &jdoc, const contract_params &contract, const bool include_id); + void populate_contract_section_json(jsoncons::ojson &jdoc, const contract_config &contract, const bool is_patch_config); - int parse_contract_section_json(contract_params &contract, const jsoncons::ojson &json, const bool parse_id); + int parse_contract_section_json(contract_config &contract, const jsoncons::ojson &json, const bool is_patch_config); int write_json_file(const std::string &file_path, const jsoncons::ojson &d); diff --git a/src/consensus.cpp b/src/consensus.cpp index 4250898e..b59bedfa 100644 --- a/src/consensus.cpp +++ b/src/consensus.cpp @@ -849,7 +849,7 @@ namespace consensus return -1; // Execute the contract - if (!ctx.is_shutting_down) + if (conf::cfg.contract.execute && !ctx.is_shutting_down) { { std::scoped_lock lock(ctx.contract_ctx_mutex); diff --git a/src/crypto.hpp b/src/crypto.hpp index d3276a77..99ce3a7d 100644 --- a/src/crypto.hpp +++ b/src/crypto.hpp @@ -5,7 +5,7 @@ /** * Offers convenience functions for cryptographic operations wrapping libsodium. - * These functions are used for contract config and user/peer message authentication. + * These functions are used for config and user/peer message authentication. */ namespace crypto { diff --git a/src/p2p/peer_session_handler.cpp b/src/p2p/peer_session_handler.cpp index 6618d4a4..b57faaa9 100644 --- a/src/p2p/peer_session_handler.cpp +++ b/src/p2p/peer_session_handler.cpp @@ -23,7 +23,7 @@ namespace p2p util::rollover_hashset recent_peermsg_hashes(200); /** - * This gets hit every time a peer connects to HP via the peer port (configured in contract config). + * This gets hit every time a peer connects to HP via the peer port (configured in config). * @param session connected session. * @return returns 0 if connection is successful and peer challenge is sent otherwise, -1. */ diff --git a/src/usr/user_session_handler.cpp b/src/usr/user_session_handler.cpp index c5ac648a..40653a15 100644 --- a/src/usr/user_session_handler.cpp +++ b/src/usr/user_session_handler.cpp @@ -11,7 +11,7 @@ namespace jusrmsg = msg::usrmsg::json; namespace usr { /** - * This gets hit every time a client connects to HP via the public port (configured in contract config). + * This gets hit every time a client connects to HP via the public port (configured in config). * @param session connected session. * @return returns 0 if connection is successful and user challenge is sent, otherwise -1. */ diff --git a/src/util/util.hpp b/src/util/util.hpp index 0bfaffbc..b9f21ff9 100644 --- a/src/util/util.hpp +++ b/src/util/util.hpp @@ -12,10 +12,10 @@ namespace util { - // Hot Pocket version. Displayed on 'hotpocket version' and written to new contract configs. + // Hot Pocket version. Displayed on 'hotpocket version' and written to new configs. constexpr const char *HP_VERSION = "0.1"; - // Minimum compatible config version (this will be used to validate contract configs) + // Minimum compatible config version (this will be used to validate configs) constexpr const char *MIN_CONFIG_VERSION = "0.1"; // Current version of the peer message protocol. diff --git a/test/local-cluster/cluster-create.sh b/test/local-cluster/cluster-create.sh index d50972f1..637c5281 100755 --- a/test/local-cluster/cluster-create.sh +++ b/test/local-cluster/cluster-create.sh @@ -82,18 +82,26 @@ do # So each node is reachable via 'node' name. peers[i]="node${n}:${peerport}" - # Update contract config. + # Update config. contract_json=$(node -p "JSON.stringify({...require('./tmp.json').contract, id: '3c349abe-4d70-4f50-9fa6-018f1f2530ab', \ bin_path: '$binary', \ bin_args: '$binargs', \ roundtime: $roundtime, \ + consensus: 'public', \ + npl: 'public', \ appbill: { \ mode: '', \ bin_args: '' \ },}, null, 2)") - mesh_json=$(node -p "JSON.stringify({...require('./tmp.json').mesh, port:${peerport}}, null, 2)") + mesh_json=$(node -p "JSON.stringify({...require('./tmp.json').mesh, \ + port:${peerport}, \ + peer_discovery: { \ + enabled: true, \ + interval: 10000 \ + } + }, null, 2)") user_json=$(node -p "JSON.stringify({...require('./tmp.json').user, port:${pubport}}, null, 2)") node -p "JSON.stringify({...require('./tmp.json'), \ diff --git a/test/vm-cluster/cluster.sh b/test/vm-cluster/cluster.sh index 11210b13..233fe195 100755 --- a/test/vm-cluster/cluster.sh +++ b/test/vm-cluster/cluster.sh @@ -362,7 +362,7 @@ do # Skip param is passed as -1 to stop skipping self pubkey. myunl=$(joinarr pubkeys -1) - # Merge json contents to produce final contract config. + # Merge json contents to produce final config. echo "$(cat ./cfg/node$n.cfg)" \ '{"contract": {"id": "3c349abe-4d70-4f50-9fa6-018f1f2530ab", "bin_path": "/usr/bin/node", "bin_args": "'$basedir'/hpfiles/nodejs_contract/echo_contract.js", "unl": '${myunl}'}}'\ '{"mesh": {"known_peers": '${mypeers}'}}'\