diff --git a/src/conf.cpp b/src/conf.cpp index 67e6b46a..81032058 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -14,15 +14,18 @@ contract_ctx ctx; // Global configuration struct exposed to the application. contract_config cfg; +const static char *MODE_PASSIVE = "passive"; +const static char *MODE_ACTIVE = "active"; + // provide a safe std::string overload for realpath -std::string realpath(std::string path) { +std::string realpath(std::string path) +{ std::array buffer; ::realpath(path.c_str(), buffer.data()); buffer[PATH_MAX] = '\0'; return buffer.data(); } - /** * Loads and initializes the contract config for execution. Must be called once during application startup. * @return 0 for success. -1 for failure. @@ -37,13 +40,16 @@ int init() if (validate_contract_dir_paths() != 0 || load_config() != 0 || validate_config() != 0) return -1; - // Append self peer to peer list. - const std::string portstr = std::to_string(cfg.peerport); - const std::string peerid = "0.0.0.0:" + portstr; - cfg.peers.emplace(std::move(peerid), std::make_pair("0.0.0.0", portstr)); + if (cfg.mode == OPERATING_MODE::ACTIVE) + { + // Append self peer to peer list. + const std::string portstr = std::to_string(cfg.peerport); + const std::string peerid = "0.0.0.0:" + portstr; + cfg.peers.emplace(std::move(peerid), std::make_pair("0.0.0.0", portstr)); - // Append self pubkey to unl list. - cfg.unl.emplace(cfg.pubkey); + // Append self pubkey to unl list. + cfg.unl.emplace(cfg.pubkey); + } return 0; } @@ -65,7 +71,7 @@ int rekey() if (save_config() != 0) return -1; - std::cout << "New signing keys generated at " << ctx.configFile << std::endl; + std::cout << "New signing keys generated at " << ctx.configfile << std::endl; return 0; } @@ -77,16 +83,16 @@ int rekey() */ int create_contract() { - if (boost::filesystem::exists(ctx.contractDir)) + if (boost::filesystem::exists(ctx.contractdir)) { std::cout << "Contract dir already exists. Cannot create contract at the same location.\n"; return -1; } - boost::filesystem::create_directories(ctx.configDir); - boost::filesystem::create_directories(ctx.histDir); - boost::filesystem::create_directories(ctx.stateDir); - boost::filesystem::create_directories(ctx.stateMapDir); + boost::filesystem::create_directories(ctx.configdir); + boost::filesystem::create_directories(ctx.histdir); + boost::filesystem::create_directories(ctx.statedir); + boost::filesystem::create_directories(ctx.statemapdir); //Create config file with default settings. @@ -96,6 +102,7 @@ int create_contract() if (binpair_to_hex() != 0) return -1; + cfg.mode = OPERATING_MODE::ACTIVE; cfg.listenip = "0.0.0.0"; cfg.peerport = 22860; cfg.roundtime = 1000; @@ -107,14 +114,14 @@ int create_contract() cfg.loglevel = "warn"; #endif cfg.loggers.emplace("console"); - + cfg.binary = ""; //Save the default settings into the config file. if (save_config() != 0) return -1; - std::cout << "Contract directory created at " << ctx.contractDir << std::endl; + std::cout << "Contract directory created at " << ctx.contractdir << std::endl; return 0; } @@ -125,24 +132,25 @@ int create_contract() */ void set_contract_dir_paths(std::string basedir) { - if (basedir == "") { + if (basedir == "") + { // this code branch will never execute the way main is currently coded, but it might change in future std::cerr << "a contract directory must be specified\n"; exit(1); } - + // resolving the path through realpath will remove any trailing slash if present basedir = realpath(basedir); - ctx.contractDir = basedir; - ctx.configDir = basedir + "/cfg"; - ctx.configFile = ctx.configDir + "/hp.cfg"; - ctx.tlsKeyFile = ctx.configDir + "/tlskey.pem"; - ctx.tlsCertFile = ctx.configDir + "/tlscert.pem"; - ctx.histDir = basedir + "/hist"; - ctx.stateDir = basedir + "/state"; - ctx.stateMapDir = basedir + "/statemap"; - ctx.logDir = basedir + "/log"; + ctx.contractdir = basedir; + ctx.configdir = basedir + "/cfg"; + ctx.configfile = ctx.configdir + "/hp.cfg"; + ctx.tlskeyfile = ctx.configdir + "/tlskey.pem"; + ctx.tlscertfile = ctx.configdir + "/tlscert.pem"; + ctx.histdir = basedir + "/hist"; + ctx.statedir = basedir + "/state"; + ctx.statemapdir = basedir + "/statemap"; + ctx.logdir = basedir + "/log"; } /** @@ -154,7 +162,7 @@ int load_config() { // Read the file into json document object. - std::ifstream ifs(ctx.configFile); + std::ifstream ifs(ctx.configfile); rapidjson::IStreamWrapper isw(ifs); rapidjson::Document d; @@ -195,13 +203,22 @@ int load_config() // Load up the values into the struct. + if (d["mode"] == MODE_PASSIVE) + cfg.mode = OPERATING_MODE::PASSIVE; + else if (d["mode"] == MODE_ACTIVE) + cfg.mode = OPERATING_MODE::ACTIVE; + else + { + std::cout << "Invalid mode. 'passive' or 'active' expected.\n"; + return -1; + } + cfg.pubkeyhex = d["pubkeyhex"].GetString(); cfg.seckeyhex = d["seckeyhex"].GetString(); cfg.listenip = d["listenip"].GetString(); - - cfg.binary = realpath(d["binary"].GetString()); - cfg.binargs = d["binargs"].GetString(); + cfg.binary = d["binary"].GetString(); + cfg.binargs = d["binargs"].GetString(); // 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. @@ -277,6 +294,9 @@ int save_config() d.SetObject(); rapidjson::Document::AllocatorType &allocator = d.GetAllocator(); d.AddMember("version", rapidjson::StringRef(util::HP_VERSION), allocator); + d.AddMember("mode", rapidjson::StringRef(cfg.mode == OPERATING_MODE::PASSIVE ? MODE_PASSIVE : MODE_ACTIVE), + allocator); + d.AddMember("pubkeyhex", rapidjson::StringRef(cfg.pubkeyhex.data()), allocator); d.AddMember("seckeyhex", rapidjson::StringRef(cfg.seckeyhex.data()), allocator); d.AddMember("binary", rapidjson::StringRef(cfg.binary.data()), allocator); @@ -338,13 +358,13 @@ int save_config() // Write the json doc to file. - std::ofstream ofs(ctx.configFile); + std::ofstream ofs(ctx.configfile); rapidjson::OStreamWrapper osw(ofs); rapidjson::PrettyWriter writer(osw); if (!d.Accept(writer)) { - std::cout << "Writing to config file failed. " << ctx.configFile << std::endl; + std::cout << "Writing to config file failed. " << ctx.configfile << std::endl; return -1; } ofs.close(); @@ -426,20 +446,20 @@ int validate_config() } // Other required fields. - + bool fields_missing = false; - fields_missing |= cfg.binary.empty() && std::cout << "Missing cfg field: binary\n"; - fields_missing |= cfg.listenip.empty() && std::cout << "Missing cfg field: listenip\n"; - fields_missing |= cfg.peerport ==0 && std::cout << "Missing cfg field: peerport\n"; - fields_missing |= cfg.roundtime == 0 && std::cout << "Missing cfg field: roundtime\n"; - fields_missing |= cfg.pubport == 0 && std::cout << "Missing cfg field: pubport\n"; - fields_missing |= cfg.loglevel.empty() && std::cout << "Missing cfg field: loglevel\n"; - fields_missing |= cfg.loggers.empty() && std::cout << "Missing cfg field: loggers\n"; - + fields_missing |= cfg.binary.empty() && std::cout << "Missing cfg field: binary\n"; + fields_missing |= cfg.listenip.empty() && std::cout << "Missing cfg field: listenip\n"; + fields_missing |= cfg.peerport == 0 && std::cout << "Missing cfg field: peerport\n"; + fields_missing |= cfg.roundtime == 0 && std::cout << "Missing cfg field: roundtime\n"; + fields_missing |= cfg.pubport == 0 && std::cout << "Missing cfg field: pubport\n"; + fields_missing |= cfg.loglevel.empty() && std::cout << "Missing cfg field: loglevel\n"; + fields_missing |= cfg.loggers.empty() && std::cout << "Missing cfg field: loggers\n"; + if (fields_missing) { - std::cout << "Required configuration fields missing at " << ctx.configFile << std::endl; + std::cout << "Required configuration fields missing at " << ctx.configfile << std::endl; return -1; } @@ -488,23 +508,23 @@ int validate_config() int validate_contract_dir_paths() { const std::string paths[7] = { - ctx.contractDir, - ctx.configFile, - ctx.histDir, - ctx.stateDir, - ctx.stateMapDir, - ctx.tlsKeyFile, - ctx.tlsCertFile}; + ctx.contractdir, + ctx.configfile, + ctx.histdir, + ctx.statedir, + ctx.statemapdir, + ctx.tlskeyfile, + ctx.tlscertfile}; for (const std::string &path : paths) { if (!boost::filesystem::exists(path)) { - if (path == ctx.tlsKeyFile || path == ctx.tlsCertFile) + if (path == ctx.tlskeyfile || path == ctx.tlscertfile) { std::cout << path << " does not exist. Please provide self-signed certificates. Can generate using command\n" << "openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout tlskey.pem -out tlscert.pem\n" - << "and add it to " + ctx.configDir << std::endl; + << "and add it to " + ctx.configdir << std::endl; } else { @@ -528,12 +548,13 @@ int is_schema_valid(const rapidjson::Document &d) const char *cfg_schema = "{" "\"type\": \"object\"," - "\"required\": [ \"version\", \"pubkeyhex\", \"seckeyhex\", \"binary\", \"binargs\", \"listenip\"" + "\"required\": [ \"mode\", \"version\", \"pubkeyhex\", \"seckeyhex\", \"binary\", \"binargs\", \"listenip\"" ", \"peers\", \"unl\", \"pubport\", \"peerport\", \"roundtime\"" ", \"pubmaxsize\", \"pubmaxcpm\", \"pubmaxbadmpm\", \"pubmaxcons\"" ", \"peermaxsize\", \"peermaxcpm\", \"peermaxdupmpm\", \"peermaxbadmpm\", \"peermaxbadsigpm\", \"peermaxcons\"" ", \"loglevel\", \"loggers\" ]," "\"properties\": {" + "\"mode\": { \"type\": \"string\" }," "\"version\": { \"type\": \"string\" }," "\"pubkeyhex\": { \"type\": \"string\" }," "\"seckeyhex\": { \"type\": \"string\" }," @@ -566,7 +587,7 @@ int is_schema_valid(const rapidjson::Document &d) "\"loggers\": {" "\"type\": \"array\"," "\"items\": { \"type\": \"string\" }" - "}," + "}" "}" "}"; diff --git a/src/conf.hpp b/src/conf.hpp index af7b22c3..3f1bcce3 100644 --- a/src/conf.hpp +++ b/src/conf.hpp @@ -13,20 +13,27 @@ namespace conf // Typedef to represent ip address and port pair. typedef std::pair ip_port_pair; +// The operating mode of the contract node. +enum OPERATING_MODE +{ + PASSIVE = 0, // Observer mode. Only emits NUPs. Does not participate in voting. + ACTIVE = 1 // Consensus participant mode. +}; + // Holds contextual information about the currently loaded contract. struct contract_ctx { - std::string command; // The CLI command issued to launch HotPocket + std::string command; // The CLI command issued to launch HotPocket - std::string contractDir; // Contract base directory - std::string histDir; // Contract history dir - std::string stateDir; // Contract state dir - std::string stateMapDir; // Contract state map dir (.merkel files) - std::string logDir; // Contract log dir - std::string configDir; // Contract config dir - std::string configFile; // Full path to the contract config file - std::string tlsKeyFile; // Full path to the tls secret key file - std::string tlsCertFile; // Full path to the tls certificate + std::string contractdir; // Contract base directory full path + std::string histdir; // Contract history dir full path + std::string statedir; // Contract state dir full path + std::string statemapdir; // Contract state map dir (.merkel files) full path + std::string logdir; // Contract log dir full path + std::string configdir; // Contract config dir full path + std::string configfile; // Full path to the contract config file + std::string tlskeyfile; // Full path to the tls secret key file + std::string tlscertfile; // Full path to the tls certificate }; // Holds all the contract config values. @@ -34,37 +41,38 @@ 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::string pubkey; // Contract public key bytes + std::string seckey; // Contract secret key bytes // Config elements which are loaded from the config file. - std::string pubkeyhex; // Contract hex public key - std::string seckeyhex; // Contract hex secret key - std::string keytype; // Key generation algorithm used by libsodium - std::string binary; // Full path to the contract binary - std::string binargs; // CLI arguments to pass to the contract binary - std::string listenip; // The IPs to listen on for incoming connections + OPERATING_MODE mode; // Operating mode of the contract (Passive/Active). + std::string pubkeyhex; // Contract hex public key + std::string seckeyhex; // Contract hex secret key + std::string keytype; // Key generation algorithm used by libsodium + std::string binary; // Full path to the contract binary + std::string binargs; // CLI arguments to pass to the contract binary + std::string listenip; // The IPs to listen on for incoming connections std::unordered_map peers; // Map of peers keyed by ":" concatenated format std::unordered_set unl; // Unique node list (list of binary public keys) - uint16_t peerport; // Listening port for peer connections - uint16_t roundtime; // Consensus round time in ms - uint16_t pubport; // Listening port for public user connections + uint16_t peerport; // Listening port for peer connections + uint16_t roundtime; // Consensus round time in ms + uint16_t pubport; // Listening port for public user connections - uint64_t pubmaxsize; // User message max size in bytes - uint64_t pubmaxcpm; // User message rate (characters(bytes) per minute) - uint64_t pubmaxbadmpm; // User bad messages per minute - uint16_t pubmaxcons; // Max inbound user connections + uint64_t pubmaxsize; // User message max size in bytes + uint64_t pubmaxcpm; // User message rate (characters(bytes) per minute) + uint64_t pubmaxbadmpm; // User bad messages per minute + uint16_t pubmaxcons; // Max inbound user connections - uint64_t peermaxsize; // Peer message max size in bytes - uint64_t peermaxcpm; // Peer message rate (characters(bytes) per minute) - uint64_t peermaxdupmpm; // Peer max duplicate messages per minute - uint64_t peermaxbadmpm; // Peer bad messages per minute - uint64_t peermaxbadsigpm; // Peer bad signatures per minute - uint16_t peermaxcons; // Max inbound peer connections + uint64_t peermaxsize; // Peer message max size in bytes + uint64_t peermaxcpm; // Peer message rate (characters(bytes) per minute) + uint64_t peermaxdupmpm; // Peer max duplicate messages per minute + uint64_t peermaxbadmpm; // Peer bad messages per minute + uint64_t peermaxbadsigpm; // Peer bad signatures per minute + uint16_t peermaxcons; // Max inbound peer connections - std::string loglevel; // Log severity level (debug, info, warn, error) - std::unordered_set loggers; // List of enabled loggers (console, file) + std::string loglevel; // Log severity level (debug, info, warn, error) + std::unordered_set loggers; // List of enabled loggers (console, file) }; // Global contract context struct exposed to the application. diff --git a/src/cons/cons.cpp b/src/cons/cons.cpp index b327c4c0..bcf6daae 100644 --- a/src/cons/cons.cpp +++ b/src/cons/cons.cpp @@ -357,6 +357,10 @@ p2p::proposal create_stage123_proposal(vote_counter &votes) */ void broadcast_proposal(const p2p::proposal &p) { + // In passive mode, we do not send out any propopsals. + if (conf::cfg.mode == conf::OPERATING_MODE::PASSIVE) + return; + p2p::peer_outbound_message msg(std::make_shared(1024)); p2pmsg::create_msg_from_proposal(msg.builder(), p); p2p::broadcast_message(msg); @@ -382,7 +386,7 @@ void check_majority_stage(bool &is_desync, bool &should_reset, uint8_t &majority // todo:vote for lcl checking condtion } - majority_stage = -1; + majority_stage = 0; is_desync = false; int32_t highest_votes = 0; diff --git a/src/cons/ledger_handler.cpp b/src/cons/ledger_handler.cpp index f09d6c93..746e9113 100644 --- a/src/cons/ledger_handler.cpp +++ b/src/cons/ledger_handler.cpp @@ -28,8 +28,8 @@ std::string save_ledger(const p2p::proposal &proposal, const uint64_t led_seq_no //file name -> [ledger sequnce numer]-[lcl hex] std::string path; const std::string seq_no = std::to_string(led_seq_no); - path.reserve(conf::ctx.histDir.size() + lcl_hash.size() + seq_no.size() + 6); - path.append(conf::ctx.histDir); + path.reserve(conf::ctx.histdir.size() + lcl_hash.size() + seq_no.size() + 6); + path.append(conf::ctx.histdir); path.append("/"); path.append(seq_no); path.append("-"); @@ -54,18 +54,18 @@ ledger_history load_ledger() //Get all records at lcl history direcory and find the last closed ledger. std::string latest_file_name; size_t latest_pos = 0; - for (const auto &entry : boost::filesystem::directory_iterator(conf::ctx.histDir)) + for (const auto &entry : boost::filesystem::directory_iterator(conf::ctx.histdir)) { const boost::filesystem::path file_path = entry.path(); const std::string file_name = entry.path().filename().string(); if (boost::filesystem::is_directory(file_path)) { - LOG_ERR << "Found directory " << file_name << " in " << conf::ctx.histDir << ". There should be no folders in this directory"; + LOG_ERR << "Found directory " << file_name << " in " << conf::ctx.histdir << ". There should be no folders in this directory"; } else if (file_path.extension() != ".lcl") { - LOG_ERR << "Found invalid file extension: " << file_path.extension() << " for lcl file " << file_name << " in " << conf::ctx.histDir; + LOG_ERR << "Found invalid file extension: " << file_path.extension() << " for lcl file " << file_name << " in " << conf::ctx.histdir; } else { diff --git a/src/cons/statemap_handler.cpp b/src/cons/statemap_handler.cpp index 26c7a1e0..2e161192 100644 --- a/src/cons/statemap_handler.cpp +++ b/src/cons/statemap_handler.cpp @@ -46,10 +46,10 @@ int update_file_blockmap(const std::string &filepath, const std::set & { // .merkel file path will be corresponding path in "statemap" directory. std::string merkle_fn; - const size_t relative_path_len = filepath.length() - conf::ctx.stateDir.length(); - merkle_fn.reserve(conf::ctx.stateMapDir.length() + relative_path_len + 7); - merkle_fn.append(conf::ctx.stateMapDir); - merkle_fn.append(filepath.substr(conf::ctx.stateDir.length(), relative_path_len)); + const size_t relative_path_len = filepath.length() - conf::ctx.statedir.length(); + merkle_fn.reserve(conf::ctx.statemapdir.length() + relative_path_len + 7); + merkle_fn.append(conf::ctx.statemapdir); + merkle_fn.append(filepath.substr(conf::ctx.statedir.length(), relative_path_len)); merkle_fn.append(MERKLE_EXTENSION); // To benefit from hint mode, the .merkle file must already exist. If not we simply disable hint mode diff --git a/src/hplog.cpp b/src/hplog.cpp index 73c44eb2..001bf592 100644 --- a/src/hplog.cpp +++ b/src/hplog.cpp @@ -57,8 +57,8 @@ void init() if (conf::cfg.loggers.count("file") == 1) { logging::add_file_log( - keywords::target = conf::ctx.logDir, // Log file directory. - keywords::file_name = conf::ctx.logDir + "/hp_%N.log", // File name pattern "hp_1.log". + keywords::target = conf::ctx.logdir, // Log file directory. + keywords::file_name = conf::ctx.logdir + "/hp_%N.log", // File name pattern "hp_1.log". keywords::rotation_size = 10 * 1024 * 1024, // Rotate files every 10 MB. keywords::max_size = 500 * 1024 * 1024, // Do not exceed 500 MB total logs. keywords::filter = (a_severity >= severity), diff --git a/src/main.cpp b/src/main.cpp index 34389952..81757547 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -157,11 +157,15 @@ int main(int argc, char **argv) if (conf::init() != 0) return -1; - // Set HP process cwd to the contract directory. - chdir(conf::ctx.contractDir.c_str()); + // Set HP process cwd to the contract directory. This will make both HP and contract process + // both have the same cwd. + chdir(conf::ctx.contractdir.c_str()); hplog::init(); + LOG_INFO << "Operating mode: " + << (conf::cfg.mode == conf::OPERATING_MODE::PASSIVE ? "Passive" : "Active"); + if (p2p::init() != 0 || usr::init() != 0 || cons::init() != 0) return -1; diff --git a/src/proc/ptrace_capture.cpp b/src/proc/ptrace_capture.cpp index 6ba63e61..c304d4d7 100644 --- a/src/proc/ptrace_capture.cpp +++ b/src/proc/ptrace_capture.cpp @@ -139,7 +139,7 @@ int ptrace_capture(const pid_t child, contract_fblockmap_t &updated_blocks) realpath(filenameptr, buf); // We ignore anything outside the state dir. - if (strncmp (buf, conf::ctx.stateDir.c_str(), conf::ctx.stateDir.size()) != 0) + if (strncmp (buf, conf::ctx.statedir.c_str(), conf::ctx.statedir.size()) != 0) continue; fd_map[fd] = {std::string(buf), 0}; diff --git a/src/sock/socket_server.cpp b/src/sock/socket_server.cpp index a87fabb5..23e46b53 100644 --- a/src/sock/socket_server.cpp +++ b/src/sock/socket_server.cpp @@ -58,11 +58,11 @@ void socket_server::run() boost::asio::ssl::context::no_sslv3); //Providing the certification file for ssl context - ctx.use_certificate_chain_file(conf::ctx.tlsCertFile); + ctx.use_certificate_chain_file(conf::ctx.tlscertfile); // Providing key file for the ssl context ctx.use_private_key_file( - conf::ctx.tlsKeyFile, + conf::ctx.tlskeyfile, boost::asio::ssl::context::pem); // Start accepting a connection